[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.HttpMethod;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MimeTypes; 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.Logger;
import org.slf4j.LoggerFactory; 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.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient; 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.cache.ExpiringCache;
import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType; 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); private static final BigDecimal DIVISOR_MILLI_TO_KILO = BigDecimal.valueOf(1000000);
// divisor to transform from milli to "iso" UNIT (e.g. mV => V) // divisor to transform from milli to "iso" UNIT (e.g. mV => V)
private static final BigDecimal DIVISOR_MILLI_TO_ISO = BigDecimal.valueOf(1000); 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 // ix (x=1,3,8) types => hex encoded integer value
private static final String VALUE_TYPE_INT1 = "i1"; private static final String VALUE_TYPE_INT1 = "i1";
public static final String VALUE_TYPE_INT3 = "i3"; public static final String VALUE_TYPE_INT3 = "i3";
@ -82,7 +80,7 @@ public class SenecHomeHandler extends BaseThingHandler {
private @Nullable ScheduledFuture<?> refreshJob; private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable PowerLimitationStatusDTO limitationStatus = null; private @Nullable PowerLimitationStatusDTO limitationStatus = null;
private final @Nullable SenecHomeApi senecHomeApi; private final SenecHomeApi senecHomeApi;
private SenecHomeConfigurationDTO config = new SenecHomeConfigurationDTO(); private SenecHomeConfigurationDTO config = new SenecHomeConfigurationDTO();
private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState); private final ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofSeconds(5), this::refreshState);
@ -133,6 +131,18 @@ public class SenecHomeHandler extends BaseThingHandler {
refreshCache.getValue(); 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() { public @Nullable Boolean refreshState() {
SenecHomeResponse response = null; SenecHomeResponse response = null;
try { 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_VOLTAGE_PH3, response.grid.currentGridVoltagePerPhase[2], 2, Units.VOLT);
updateQtyState(CHANNEL_SENEC_GRID_FREQUENCY, response.grid.currentGridFrequency, 2, Units.HERTZ); updateQtyState(CHANNEL_SENEC_GRID_FREQUENCY, response.grid.currentGridFrequency, 2, Units.HERTZ);
if (response.battery.chargedEnergy != null) { updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK1, response.battery.chargedEnergy, 0, 2,
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK1, response.battery.chargedEnergy[0], 2, Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK2, response.battery.chargedEnergy, 1, 2,
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK2, response.battery.chargedEnergy[1], 2, Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK3, response.battery.chargedEnergy, 2, 2,
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK3, response.battery.chargedEnergy[2], 2, Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); updateQtyStateIfAvailable(CHANNEL_SENEC_CHARGED_ENERGY_PACK4, response.battery.chargedEnergy, 3, 2,
updateQtyState(CHANNEL_SENEC_CHARGED_ENERGY_PACK4, response.battery.chargedEnergy[3], 2, Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
} updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK1, response.battery.dischargedEnergy, 0, 2,
if (response.battery.dischargedEnergy != null) { Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK1, response.battery.dischargedEnergy[0], 2, updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK2, response.battery.dischargedEnergy, 1, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK2, response.battery.dischargedEnergy[1], 2, updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK3, response.battery.dischargedEnergy, 2, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO);
updateQtyState(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK3, response.battery.dischargedEnergy[2], 2, updateQtyStateIfAvailable(CHANNEL_SENEC_DISCHARGED_ENERGY_PACK4, response.battery.dischargedEnergy, 3, 2,
Units.KILOWATT_HOUR, DIVISOR_MILLI_TO_KILO); 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);
}
if (response.battery.cycles != null) { if (response.battery.cycles != null) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK1, response.battery.cycles[0]); int length = response.battery.cycles.length;
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK2, response.battery.cycles[1]); if (length > 0) {
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK3, response.battery.cycles[2]); updateDecimalState(CHANNEL_SENEC_CYCLES_PACK1, response.battery.cycles[0]);
updateDecimalState(CHANNEL_SENEC_CYCLES_PACK4, response.battery.cycles[3]); }
} if (length > 1) {
if (response.battery.current != null) { updateDecimalState(CHANNEL_SENEC_CYCLES_PACK2, response.battery.cycles[1]);
updateQtyState(CHANNEL_SENEC_CURRENT_PACK1, response.battery.current[0], 2, Units.AMPERE); }
updateQtyState(CHANNEL_SENEC_CURRENT_PACK2, response.battery.current[1], 2, Units.AMPERE); if (length > 2) {
updateQtyState(CHANNEL_SENEC_CURRENT_PACK3, response.battery.current[2], 2, Units.AMPERE); updateDecimalState(CHANNEL_SENEC_CYCLES_PACK3, response.battery.cycles[2]);
updateQtyState(CHANNEL_SENEC_CURRENT_PACK4, response.battery.current[3], 2, Units.AMPERE); }
} if (length > 3) {
if (response.battery.voltage != null) { updateDecimalState(CHANNEL_SENEC_CYCLES_PACK4, response.battery.cycles[3]);
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);
} }
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) { if (response.temperature != null) {
updateQtyState(CHANNEL_SENEC_BATTERY_TEMPERATURE, response.temperature.batteryTemperature, 0, updateQtyState(CHANNEL_SENEC_BATTERY_TEMPERATURE, response.temperature.batteryTemperature, 0,
SIUnits.CELSIUS); SIUnits.CELSIUS);
@ -376,9 +389,10 @@ public class SenecHomeHandler extends BaseThingHandler {
} }
protected void updatePowerLimitationStatus(Channel channel, boolean status, int duration) { protected void updatePowerLimitationStatus(Channel channel, boolean status, int duration) {
if (this.limitationStatus != null) { PowerLimitationStatusDTO limitationStatus = this.limitationStatus;
if (this.limitationStatus.state == status) { if (limitationStatus != null) {
long stateSince = new Date().getTime() - this.limitationStatus.time; if (limitationStatus.state == status) {
long stateSince = new Date().getTime() - limitationStatus.time;
if (((int) (stateSince / 1000)) < duration) { if (((int) (stateSince / 1000)) < duration) {
// skip updating state (possible flapping state) // skip updating state (possible flapping state)
@ -387,15 +401,17 @@ public class SenecHomeHandler extends BaseThingHandler {
logger.debug("{} longer than required duration {}", status, duration); logger.debug("{} longer than required duration {}", status, duration);
} }
} else { } else {
this.limitationStatus.state = status; limitationStatus.state = status;
this.limitationStatus.time = new Date().getTime(); limitationStatus.time = new Date().getTime();
this.limitationStatus = limitationStatus;
// skip updating state (state changed, possible flapping state) // skip updating state (state changed, possible flapping state)
return; return;
} }
} else { } else {
this.limitationStatus = new PowerLimitationStatusDTO(); limitationStatus = new PowerLimitationStatusDTO();
this.limitationStatus.state = status; limitationStatus.state = status;
this.limitationStatus = limitationStatus;
} }
logger.debug("Updating power limitation state {}", status); logger.debug("Updating power limitation state {}", status);

View File

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

View File

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

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;
import java.util.Arrays; import java.util.Arrays;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;
import java.util.Arrays; import java.util.Arrays;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;
import java.util.Arrays; import java.util.Arrays;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * 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.io.Serializable;
import java.util.Arrays; import java.util.Arrays;