[senechome] Fix ArrayIndexOutOfBoundsException when less than 4 packs (#17299)

* Fix issue and warnings
* Fix operator

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2024-09-09 19:12:23 +02:00 committed by Leo Siepel
parent bec9863d4a
commit d5ad854aac
12 changed files with 98 additions and 76 deletions

View File

@ -27,7 +27,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes;
import org.openhab.binding.senechome.internal.json.SenecHomeResponse;
import org.openhab.binding.senechome.internal.dto.SenecHomeResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -32,7 +32,7 @@ import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.senechome.internal.json.SenecHomeResponse;
import org.openhab.binding.senechome.internal.dto.SenecHomeResponse;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
@ -67,8 +67,6 @@ public class SenecHomeHandler extends BaseThingHandler {
private static final BigDecimal DIVISOR_MILLI_TO_KILO = BigDecimal.valueOf(1000000);
// divisor to transform from milli to "iso" UNIT (e.g. mV => V)
private static final BigDecimal DIVISOR_MILLI_TO_ISO = BigDecimal.valueOf(1000);
// divisor to transform from "iso" to kilo UNIT (e.g. W => kW)
private static final BigDecimal DIVISOR_ISO_TO_KILO = BigDecimal.valueOf(1000);
// ix (x=1,3,8) types => hex encoded integer value
private static final String VALUE_TYPE_INT1 = "i1";
public static final String VALUE_TYPE_INT3 = "i3";
@ -82,7 +80,7 @@ public class SenecHomeHandler extends BaseThingHandler {
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable PowerLimitationStatusDTO limitationStatus = null;
private final @Nullable SenecHomeApi senecHomeApi;
private final SenecHomeApi senecHomeApi;
private SenecHomeConfigurationDTO config = new SenecHomeConfigurationDTO();
private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState);
@ -133,6 +131,18 @@ public class SenecHomeHandler extends BaseThingHandler {
refreshCache.getValue();
}
private <Q extends Quantity<Q>> void updateQtyStateIfAvailable(String channel, String @Nullable [] valueArray,
int arrayIndex, int scale, Unit<Q> unit) {
updateQtyStateIfAvailable(channel, valueArray, arrayIndex, scale, unit, null);
}
private <Q extends Quantity<Q>> void updateQtyStateIfAvailable(String channel, String @Nullable [] valueArray,
int arrayIndex, int scale, Unit<Q> unit, @Nullable BigDecimal divisor) {
if (valueArray != null && valueArray.length > arrayIndex) {
updateQtyState(channel, valueArray[arrayIndex], scale, unit, divisor);
}
}
public @Nullable Boolean refreshState() {
SenecHomeResponse response = null;
try {
@ -197,65 +207,68 @@ public class SenecHomeHandler extends BaseThingHandler {
updateQtyState(CHANNEL_SENEC_GRID_VOLTAGE_PH3, response.grid.currentGridVoltagePerPhase[2], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_GRID_FREQUENCY, response.grid.currentGridFrequency, 2, Units.HERTZ);
if (response.battery.chargedEnergy != null) {
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK1, response.battery.chargedEnergy[0], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK2, response.battery.chargedEnergy[1], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK3, response.battery.chargedEnergy[2], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK4, response.battery.chargedEnergy[3], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
}
if (response.battery.dischargedEnergy != null) {
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK1, response.battery.dischargedEnergy[0], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK2, response.battery.dischargedEnergy[1], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK3, response.battery.dischargedEnergy[2], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK4, response.battery.dischargedEnergy[3], 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
}
updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK1, response.battery.chargedEnergy, 0, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK2, response.battery.chargedEnergy, 1, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK3, response.battery.chargedEnergy, 2, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK4, response.battery.chargedEnergy, 3, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK1, response.battery.dischargedEnergy, 0, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK2, response.battery.dischargedEnergy, 1, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK3, response.battery.dischargedEnergy, 2, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK4, response.battery.dischargedEnergy, 3, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
if (response.battery.cycles != null) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK1, response.battery.cycles[0]);
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK2, response.battery.cycles[1]);
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK3, response.battery.cycles[2]);
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK4, response.battery.cycles[3]);
}
if (response.battery.current != null) {
updateQtyState(CHANNEL_SENEC_CURRENT_PACK1, response.battery.current[0], 2, Units.AMPERE);
updateQtyState(CHANNEL_SENEC_CURRENT_PACK2, response.battery.current[1], 2, Units.AMPERE);
updateQtyState(CHANNEL_SENEC_CURRENT_PACK3, response.battery.current[2], 2, Units.AMPERE);
updateQtyState(CHANNEL_SENEC_CURRENT_PACK4, response.battery.current[3], 2, Units.AMPERE);
}
if (response.battery.voltage != null) {
updateQtyState(CHANNEL_SENEC_VOLTAGE_PACK1, response.battery.voltage[0], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_VOLTAGE_PACK2, response.battery.voltage[1], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_VOLTAGE_PACK3, response.battery.voltage[2], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_VOLTAGE_PACK4, response.battery.voltage[3], 2, Units.VOLT);
}
if (response.battery.maxCellVoltage != null) {
updateQtyState(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK1, response.battery.maxCellVoltage[0], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK2, response.battery.maxCellVoltage[1], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK3, response.battery.maxCellVoltage[2], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK4, response.battery.maxCellVoltage[3], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
}
if (response.battery.minCellVoltage != null) {
updateQtyState(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK1, response.battery.minCellVoltage[0], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK2, response.battery.minCellVoltage[1], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK3, response.battery.minCellVoltage[2], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
updateQtyState(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK4, response.battery.minCellVoltage[3], 3, Units.VOLT,
DIVISOR_MILLI_TO_ISO);
int length = response.battery.cycles.length;
if (length > 0) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK1, response.battery.cycles[0]);
}
if (length > 1) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK2, response.battery.cycles[1]);
}
if (length > 2) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK3, response.battery.cycles[2]);
}
if (length > 3) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK4, response.battery.cycles[3]);
}
}
updateQtyStateIfAvailable(CHANNEL_SENEC_CURRENT_PACK1, response.battery.current, 0, 2, Units.AMPERE);
updateQtyStateIfAvailable(CHANNEL_SENEC_CURRENT_PACK2, response.battery.current, 1, 2, Units.AMPERE);
updateQtyStateIfAvailable(CHANNEL_SENEC_CURRENT_PACK3, response.battery.current, 2, 2, Units.AMPERE);
updateQtyStateIfAvailable(CHANNEL_SENEC_CURRENT_PACK4, response.battery.current, 3, 2, Units.AMPERE);
updateQtyStateIfAvailable(CHANNEL_SENEC_VOLTAGE_PACK1, response.battery.voltage, 0, 2, Units.VOLT);
updateQtyStateIfAvailable(CHANNEL_SENEC_VOLTAGE_PACK2, response.battery.voltage, 1, 2, Units.VOLT);
updateQtyStateIfAvailable(CHANNEL_SENEC_VOLTAGE_PACK3, response.battery.voltage, 2, 2, Units.VOLT);
updateQtyStateIfAvailable(CHANNEL_SENEC_VOLTAGE_PACK4, response.battery.voltage, 3, 2, Units.VOLT);
updateQtyStateIfAvailable(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK1, response.battery.maxCellVoltage, 0, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK2, response.battery.maxCellVoltage, 1, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK3, response.battery.maxCellVoltage, 2, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK4, response.battery.maxCellVoltage, 3, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK1, response.battery.minCellVoltage, 0, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK2, response.battery.minCellVoltage, 1, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK3, response.battery.minCellVoltage, 2, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
updateQtyStateIfAvailable(CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK4, response.battery.minCellVoltage, 3, 3,
Units.VOLT, DIVISOR_MILLI_TO_ISO);
if (response.temperature != null) {
updateQtyState(CHANNEL_SENEC_BATTERY_TEMPERATURE, response.temperature.batteryTemperature, 0,
SIUnits.CELSIUS);
@ -376,9 +389,10 @@ public class SenecHomeHandler extends BaseThingHandler {
}
protected void updatePowerLimitationStatus(Channel channel, boolean status, int duration) {
if (this.limitationStatus != null) {
if (this.limitationStatus.state == status) {
long stateSince = new Date().getTime() - this.limitationStatus.time;
PowerLimitationStatusDTO limitationStatus = this.limitationStatus;
if (limitationStatus != null) {
if (limitationStatus.state == status) {
long stateSince = new Date().getTime() - limitationStatus.time;
if (((int) (stateSince / 1000)) < duration) {
// skip updating state (possible flapping state)
@ -387,15 +401,17 @@ public class SenecHomeHandler extends BaseThingHandler {
logger.debug("{} longer than required duration {}", status, duration);
}
} else {
this.limitationStatus.state = status;
this.limitationStatus.time = new Date().getTime();
limitationStatus.state = status;
limitationStatus.time = new Date().getTime();
this.limitationStatus = limitationStatus;
// skip updating state (state changed, possible flapping state)
return;
}
} else {
this.limitationStatus = new PowerLimitationStatusDTO();
this.limitationStatus.state = status;
limitationStatus = new PowerLimitationStatusDTO();
limitationStatus.state = status;
this.limitationStatus = limitationStatus;
}
logger.debug("Updating power limitation state {}", status);

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.senechome.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link SenecSystemStatus} class defines available Senec specific
* system states.
@ -19,6 +21,7 @@ package org.openhab.binding.senechome.internal;
* @author Steven Schwarznau - Initial contribution
*
*/
@NonNullByDefault
public enum SenecSystemStatus {
INITIALSTATE(0, "INITIAL STATE"),

View File

@ -12,11 +12,14 @@
*/
package org.openhab.binding.senechome.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Enum with available Senec specific wallbox states.
*
* @author Erwin Guib - Initial Contribution
*/
@NonNullByDefault
public enum SenecWallboxStatus {
WAIT_FOR_EV(0xA1, "Waiting for EV"),
EV_ASKING_CHARGE(0xB1, "EV asking for charge"),

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;
import java.util.Arrays;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;
import java.util.Arrays;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;
import java.util.Arrays;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.senechome.internal.json;
package org.openhab.binding.senechome.internal.dto;
import java.io.Serializable;
import java.util.Arrays;