From b672d52f0a51c472e1e85104e4c176956d35d858 Mon Sep 17 00:00:00 2001 From: eguib Date: Sat, 29 May 2021 14:29:20 +0200 Subject: [PATCH] [senechome] issue #10679: Extend with additional channels; refactor and various improvements (#10687) * [senechome] Extend with additional channels; refactor; improve error handling (#10679). Merged PR 9535 (rename batteryState to system state) - charge/discharge energy/current/voltage/min/maxCellVoltage/load cycles per battery pack - current/power/voltage per MPP - liveHouseConsumption/livePowerGenerator - battery,case,MCU temperature - wallbox1: state, charging power/current, liveEnergy - improve error handling, catch malformed JSON exception to prevent crashing the binding - merged PR: 9535 (rename batteryState to systemState) - renamed SenecHomeLimitation to SenecHomePower and added more channels - refactored SenecHomeHandler to reduce code duplication (added some nice helper methods) Signed-off-by: Erwin Guib --- CODEOWNERS | 2 +- .../org.openhab.binding.senechome/README.md | 81 +++- .../senechome/internal/SenecHomeApi.java | 1 - .../internal/SenecHomeBindingConstants.java | 69 ++- .../senechome/internal/SenecHomeHandler.java | 377 ++++++++------- ...teryStatus.java => SenecSystemStatus.java} | 18 +- .../internal/SenecWallboxStatus.java | 79 ++++ .../internal/json/SenecHomeBattery.java | 71 +++ .../internal/json/SenecHomeEnergy.java | 46 +- .../internal/json/SenecHomeGrid.java | 4 +- .../internal/json/SenecHomeLimitation.java | 34 -- .../internal/json/SenecHomePower.java | 56 +++ .../internal/json/SenecHomeResponse.java | 11 +- .../internal/json/SenecHomeStatistics.java | 34 +- .../internal/json/SenecHomeTemperature.java | 48 ++ .../internal/json/SenecHomeWallbox.java | 60 +++ .../resources/OH-INF/thing/thing-types.xml | 432 ++++++++++++++++-- .../internal/SenecHomeHandlerTest.java | 84 ++++ 18 files changed, 1222 insertions(+), 285 deletions(-) rename bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/{SenecBatteryStatus.java => SenecSystemStatus.java} (92%) create mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecWallboxStatus.java create mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeBattery.java delete mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeLimitation.java create mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomePower.java create mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeTemperature.java create mode 100644 bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeWallbox.java create mode 100644 bundles/org.openhab.binding.senechome/src/test/java/org/openhab/binding/senechome/internal/SenecHomeHandlerTest.java diff --git a/CODEOWNERS b/CODEOWNERS index ca331880cf9d5..93f981cad8b80 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -251,7 +251,7 @@ /bundles/org.openhab.binding.samsungtv/ @paulianttila /bundles/org.openhab.binding.satel/ @druciak /bundles/org.openhab.binding.semsportal/ @itb3 -/bundles/org.openhab.binding.senechome/ @vctender @KorbinianP +/bundles/org.openhab.binding.senechome/ @vctender @KorbinianP @eguib /bundles/org.openhab.binding.seneye/ @nikotanghe /bundles/org.openhab.binding.sensebox/ @hakan42 /bundles/org.openhab.binding.sensibo/ @seime diff --git a/bundles/org.openhab.binding.senechome/README.md b/bundles/org.openhab.binding.senechome/README.md index 49fc88900a242..9e8075fbac276 100644 --- a/bundles/org.openhab.binding.senechome/README.md +++ b/bundles/org.openhab.binding.senechome/README.md @@ -11,8 +11,13 @@ Examples: Lights, pool filters, wash machines, ... | Thing type id | Name | |----------------------|-----------------------------------------------| -| senechome | Senec Home Lithium Battery, V2.0, V2.1 and V3 | +| senechome | Senec Home Lithium Battery, V2.0, V2.1, V3 and V3duo | +**Note:** Not all channels are available for all Senec Home types. E.g. + +* only V3, V3duo have a power generator and thus MPPs (V3 has 2 MPP, V3duo has 3 MPP) +* not equipped battery packs will return 0 for all ...Pack channels +* currently channels for the first wallbox are implemented (senec could handle 4 wallboxes) ## Thing Configuration @@ -34,12 +39,23 @@ The property `limitationTresholdValue` is used as threshold for channel `powerLi |-----------------------|-----------|--------------------------------------------------------------------------| | powerLimitation | percent | How much is your pv generator limited (0% if not limited anyway) | | powerLimitationState | ON/OFF | Power limitation active (based on configuration) | +| currentMPP1 | ampere | PV generator current on MPP1 | +| currentMPP2 | ampere | PV generator current on MPP2 | +| currentMPP3 | ampere | PV generator current on MPP3 | +| powerMPP1 | watt | PV generator power on MPP1 | +| powerMPP2 | watt | PV generator power on MPP2 | +| powerMPP3 | watt | PV generator power on MPP3 | +| voltageMPP1 | volt | PV generator voltage on MPP1 | +| voltageMPP2 | volt | PV generator voltage on MPP2 | +| voltageMPP3 | volt | PV generator voltage on MPP3 | | houseConsumption | watt | Current power consumption of your house/living | | energyProduction | watt | Energy generated by your pv / inverter | -| batteryPower | watt | Energy processed by batterie itself, for example while charging | +| batteryPower | watt | Energy processed by battery negative discharge, positive charge | +| batteryCurrent | ampere | Battery current negative discharge, positive charge | +| batteryVoltage | volt | Battery Voltage | | batteryFuelCharge | percent | Fuel charge of your battery (0 - 100%) | -| batteryState | | Text describing current action of battery (e.g. CHARGE) | -| batteryStateValue | | Value describing current action of battery (e.g. 14) | +| systemState | | Text describing current action of the senec home system (e.g. CHARGE) | +| systemStateValue | | Value describing current action of the senec home system (e.g. 14) | | gridPower | watt | Grid power level, negative for supply, positive values for drawing power | | gridPowerDraw | watt | Absolute power level of power draw, zero while supplying | | gridPowerSupply | watt | Absolute power level of power supply, zero while drawing | @@ -53,11 +69,50 @@ The property `limitationTresholdValue` is used as threshold for channel `powerLi | gridVoltagePhase2 | volt | Grid voltage on Phase 2 | | gridVoltagePhase3 | volt | Grid voltage on Phase 3 | | gridFrequency | hertz | Grid frequency | -| SenecBatteryVoltage | volt | Battery Voltage | -| SenecLiveBatCharge | watt hour | Live Bat Charge | -| SenecLiveBatDischarge | watt hour | Live Bat Discharge | -| SenecLiveGridImport | watt hour | Live Grid Import | -| SenecLiveGridExport | watt hour | Live Grid Export | +| liveBatCharge | kilo watt hour | Live Total Bat Charge | +| liveBatDischarge | kilo watt hour | Live Total Bat Discharge | +| liveGridImport | kilo watt hour | Live Total Grid Import | +| liveGridExport | kilo watt hour | Live Total Grid Export | +| liveHouseConsumption | kilo watt hour | Live Total House Consumption (without WB) | +| livePowerGenerator | kilo watt hour | Live Total PV generator generated energy | +| liveEnergyWallbox1 | kilo watt hour | Live Total Wallbox 1 charged energy | +| chargedEnergyPack1 | kilo watt hour | total charged energy battery pack 1 | +| chargedEnergyPack2 | kilo watt hour | total charged energy battery pack 2 | +| chargedEnergyPack3 | kilo watt hour | total charged energy battery pack 3 | +| chargedEnergyPack4 | kilo watt hour | total charged energy battery pack 4 | +| dischargedEnergyPack1 | kilo watt hour | total discharged energy battery pack 1 | +| dischargedEnergyPack2 | kilo watt hour | total discharged energy battery pack 2 | +| dischargedEnergyPack3 | kilo watt hour | total discharged energy battery pack 3 | +| dischargedEnergyPack4 | kilo watt hour | total discharged energy battery pack 4 | +| cyclesPack1 | | battery charge/discharge cycles pack 1 | +| cyclesPack2 | | battery charge/discharge cycles pack 2 | +| cyclesPack3 | | battery charge/discharge cycles pack 3 | +| cyclesPack4 | | battery charge/discharge cycles pack 4 | +| currentPack1 | ampere | battery current pack 1 | +| currentPack2 | ampere | battery current pack 2 | +| currentPack3 | ampere | battery current pack 3 | +| currentPack4 | ampere | battery current pack 4 | +| voltagePack1 | volt | battery voltage pack 1 | +| voltagePack2 | volt | battery voltage pack 2 | +| voltagePack3 | volt | battery voltage pack 3 | +| voltagePack4 | volt | battery voltage pack 4 | +| maxCellVoltagePack1 | volt | maximum cell voltage battery pack 1 | +| maxCellVoltagePack2 | volt | maximum cell voltage battery pack 2 | +| maxCellVoltagePack3 | volt | maximum cell voltage battery pack 3 | +| maxCellVoltagePack4 | volt | maximum cell voltage battery pack 4 | +| minCellVoltagePack1 | volt | minimum cell voltage battery pack 1 | +| minCellVoltagePack2 | volt | minimum cell voltage battery pack 2 | +| minCellVoltagePack3 | volt | minimum cell voltage battery pack 3 | +| minCellVoltagePack4 | volt | minimum cell voltage battery pack 4 | +| batteryTemperature | celsius | battery temperature (maximum of all battery packs) | +| caseTemperature | celsius | case temperature | +| mcuTemperature | celsius | MCU (main control unit) temperature | +| wallbox1State | | Wallbox 1 state as Text (e.g. Charging) | +| wallbox1StateValue | | Wallbox 1 state as value (e.g. 194) | +| wallbox1ChargingCurrentPhase1 | ampere | Wallbox 1 charging current Phase 1 | +| wallbox1ChargingCurrentPhase2 | ampere | Wallbox 1 charging current Phase 2 | +| wallbox1ChargingCurrentPhase3 | ampere | Wallbox 1 charging current Phase 3 | +| wallbox1ChargingPower | watt | Wallbox 1 charging power | ## Items @@ -70,8 +125,8 @@ Number SenecHouseConsumption "Current power consumption [%d W]" { channel="senechome:senechome:pvbattery:energyProduction" } Number SenecBatteryPower "Energy processed by battery [%d W]" { channel="senechome:senechome:pvbattery:batteryPower" } Number SenecBatteryFuelCharge "State of Charge [%d %%]" { channel="senechome:senechome:pvbattery:batteryFuelCharge" } -String SenecBatteryState "Current action [%s]" { channel="senechome:senechome:pvbattery:batteryState" } -Number SenecBatteryStateValue "Current action [%d]" { channel="senechome:senechome:pvbattery:batteryStateValue" } +String SenecSystemState "Current system state [%s]" { channel="senechome:senechome:pvbattery:systemState" } +Number SenecSystemStateValue "Current system state [%d]" { channel="senechome:senechome:pvbattery:systemStateValue" } Number SenecGridPower "Grid power level [%d W]" { channel="senechome:senechome:pvbattery:gridPower" } Number SenecGridPowerDraw "Power draw from grid [%d W]" { channel="senechome:senechome:pvbattery:gridPowerDraw" } Number SenecGridPowerSupply "Power supply to grid [%d W]" { channel="senechome:senechome:pvbattery:gridPowerSupply" } @@ -105,8 +160,8 @@ Text label="Power Grid"{ Default item=SenecEnergyProduction Default item=SenecBatteryPower Default item=SenecBatteryFuelCharge - Default item=SenecBatteryState - Default item=SenecBatteryStateValue + Default item=SenecSystemState + Default item=SenecSystemStateValue Default item=SenecGridPower Default item=SenecGridPowerDraw Default item=SenecGridPowerSupply diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeApi.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeApi.java index afdd61a268aef..6f6e02fb76438 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeApi.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeApi.java @@ -65,7 +65,6 @@ public void setHostname(String hostname) { * * To receive new values, just modify the Json objects and add them to the thing channels * - * @param hostname Hostname or ip address of senec battery * @return Instance of SenecHomeResponse * @throws MalformedURLException Configuration/URL is wrong * @throws IOException Communication failed diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeBindingConstants.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeBindingConstants.java index a06e62e3dee99..253678e035ae1 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeBindingConstants.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeBindingConstants.java @@ -27,14 +27,30 @@ public class SenecHomeBindingConstants { private static final String THING_BASE_ID = "senechome"; public static final ThingTypeUID THING_TYPE_SENEC_HOME_BATTERY = new ThingTypeUID(BINDING_ID, THING_BASE_ID); + // SenecHomePower public static final String CHANNEL_SENEC_POWER_LIMITATION = "powerLimitation"; public static final String CHANNEL_SENEC_POWER_LIMITATION_STATE = "powerLimitationState"; - public static final String CHANNEL_SENEC_BATTERY_STATE = "batteryState"; - public static final String CHANNEL_SENEC_BATTERY_STATE_VALUE = "batteryStateValue"; + public static final String CHANNEL_SENEC_CURRENT_MPP1 = "currentMpp1"; + public static final String CHANNEL_SENEC_CURRENT_MPP2 = "currentMpp2"; + public static final String CHANNEL_SENEC_CURRENT_MPP3 = "currentMpp3"; + public static final String CHANNEL_SENEC_POWER_MPP1 = "powerMpp1"; + public static final String CHANNEL_SENEC_POWER_MPP2 = "powerMpp2"; + public static final String CHANNEL_SENEC_POWER_MPP3 = "powerMpp3"; + public static final String CHANNEL_SENEC_VOLTAGE_MPP1 = "voltageMpp1"; + public static final String CHANNEL_SENEC_VOLTAGE_MPP2 = "voltageMpp2"; + public static final String CHANNEL_SENEC_VOLTAGE_MPP3 = "voltageMpp3"; + + // SenecHomeEnergy + public static final String CHANNEL_SENEC_SYSTEM_STATE = "systemState"; + public static final String CHANNEL_SENEC_SYSTEM_STATE_VALUE = "systemStateValue"; public static final String CHANNEL_SENEC_POWER_CONSUMPTION = "houseConsumption"; public static final String CHANNEL_SENEC_ENERGY_PRODUCTION = "energyProduction"; public static final String CHANNEL_SENEC_BATTERY_POWER = "batteryPower"; public static final String CHANNEL_SENEC_BATTERY_FUEL_CHARGE = "batteryFuelCharge"; + public static final String CHANNEL_SENEC_BATTERY_VOLTAGE = "batteryVoltage"; + public static final String CHANNEL_SENEC_BATTERY_CURRENT = "batteryCurrent"; + + // SenecHomeGrid public static final String CHANNEL_SENEC_GRID_POWER = "gridPower"; public static final String CHANNEL_SENEC_GRID_POWER_SUPPLY = "gridPowerSupply"; public static final String CHANNEL_SENEC_GRID_POWER_DRAW = "gridPowerDraw"; @@ -48,9 +64,56 @@ public class SenecHomeBindingConstants { public static final String CHANNEL_SENEC_GRID_VOLTAGE_PH2 = "gridVoltagePhase2"; public static final String CHANNEL_SENEC_GRID_VOLTAGE_PH3 = "gridVoltagePhase3"; public static final String CHANNEL_SENEC_GRID_FREQUENCY = "gridFrequency"; + + // SenecHomeStatistics public static final String CHANNEL_SENEC_LIVE_BAT_CHARGE = "liveBatCharge"; public static final String CHANNEL_SENEC_LIVE_BAT_DISCHARGE = "liveBatDischarge"; public static final String CHANNEL_SENEC_LIVE_GRID_IMPORT = "liveGridImport"; public static final String CHANNEL_SENEC_LIVE_GRID_EXPORT = "liveGridExport"; - public static final String CHANNEL_SENEC_BATTERY_VOLTAGE = "batteryVoltage"; + public static final String CHANNEL_SENEC_LIVE_HOUSE_CONSUMPTION = "liveHouseConsumption"; + public static final String CHANNEL_SENEC_LIVE_POWER_GENERATOR = "livePowerGenerator"; + public static final String CHANNEL_SENEC_LIVE_ENERGY_WALLBOX1 = "liveEnergyWallbox1"; + + // SenecHomeBattery + public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK1 = "chargedEnergyPack1"; + public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK2 = "chargedEnergyPack2"; + public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK3 = "chargedEnergyPack3"; + public static final String CHANNEL_SENEC_CHARGED_ENERGY_PACK4 = "chargedEnergyPack4"; + public static final String CHANNEL_SENEC_DISCHARGED_ENERGY_PACK1 = "dischargedEnergyPack1"; + public static final String CHANNEL_SENEC_DISCHARGED_ENERGY_PACK2 = "dischargedEnergyPack2"; + public static final String CHANNEL_SENEC_DISCHARGED_ENERGY_PACK3 = "dischargedEnergyPack3"; + public static final String CHANNEL_SENEC_DISCHARGED_ENERGY_PACK4 = "dischargedEnergyPack4"; + public static final String CHANNEL_SENEC_CYCLES_PACK1 = "cyclesPack1"; + public static final String CHANNEL_SENEC_CYCLES_PACK2 = "cyclesPack2"; + public static final String CHANNEL_SENEC_CYCLES_PACK3 = "cyclesPack3"; + public static final String CHANNEL_SENEC_CYCLES_PACK4 = "cyclesPack4"; + public static final String CHANNEL_SENEC_CURRENT_PACK1 = "currentPack1"; + public static final String CHANNEL_SENEC_CURRENT_PACK2 = "currentPack2"; + public static final String CHANNEL_SENEC_CURRENT_PACK3 = "currentPack3"; + public static final String CHANNEL_SENEC_CURRENT_PACK4 = "currentPack4"; + public static final String CHANNEL_SENEC_VOLTAGE_PACK1 = "voltagePack1"; + public static final String CHANNEL_SENEC_VOLTAGE_PACK2 = "voltagePack2"; + public static final String CHANNEL_SENEC_VOLTAGE_PACK3 = "voltagePack3"; + public static final String CHANNEL_SENEC_VOLTAGE_PACK4 = "voltagePack4"; + public static final String CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK1 = "maxCellVoltagePack1"; + public static final String CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK2 = "maxCellVoltagePack2"; + public static final String CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK3 = "maxCellVoltagePack3"; + public static final String CHANNEL_SENEC_MAX_CELL_VOLTAGE_PACK4 = "maxCellVoltagePack4"; + public static final String CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK1 = "minCellVoltagePack1"; + public static final String CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK2 = "minCellVoltagePack2"; + public static final String CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK3 = "minCellVoltagePack3"; + public static final String CHANNEL_SENEC_MIN_CELL_VOLTAGE_PACK4 = "minCellVoltagePack4"; + + // SenecHomeTemperature + public static final String CHANNEL_SENEC_BATTERY_TEMPERATURE = "batteryTemperature"; + public static final String CHANNEL_SENEC_CASE_TEMPERATURE = "caseTemperature"; + public static final String CHANNEL_SENEC_MCU_TEMPERATURE = "mcuTemperature"; + + // SenecHomeWallbox + public static final String CHANNEL_SENEC_WALLBOX1_STATE = "wallbox1State"; + public static final String CHANNEL_SENEC_WALLBOX1_STATE_VALUE = "wallbox1StateValue"; + public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH1 = "wallbox1ChargingCurrentPhase1"; + public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH2 = "wallbox1ChargingCurrentPhase2"; + public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH3 = "wallbox1ChargingCurrentPhase3"; + public static final String CHANNEL_SENEC_WALLBOX1_CHARGING_POWER = "wallbox1ChargingPower"; } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeHandler.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeHandler.java index e70a97abae5ca..26d9239928af8 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeHandler.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecHomeHandler.java @@ -12,6 +12,7 @@ */ package org.openhab.binding.senechome.internal; +import static org.openhab.binding.senechome.internal.SenecHomeBindingConstants.*; import static org.openhab.core.types.RefreshType.REFRESH; import java.io.IOException; @@ -23,13 +24,10 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Function; -import javax.measure.quantity.Dimensionless; -import javax.measure.quantity.ElectricCurrent; -import javax.measure.quantity.ElectricPotential; -import javax.measure.quantity.Energy; -import javax.measure.quantity.Frequency; -import javax.measure.quantity.Power; +import javax.measure.Quantity; +import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -40,6 +38,7 @@ import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -51,21 +50,35 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.gson.JsonSyntaxException; +import com.google.gson.JsonParseException; /** * The {@link SenecHomeHandler} is responsible for handling commands, which are * sent to one of the channels. * * @author Steven Schwarznau - Initial contribution + * @author Erwin Guib - added more channels, added some convenience methods to reduce code duplication */ @NonNullByDefault public class SenecHomeHandler extends BaseThingHandler { - private final Logger logger = LoggerFactory.getLogger(SenecHomeHandler.class); - private final static String VALUE_TYPE_INT = "u3"; - private final static String VALUE_TYPE_UNSIGNED_INT = "u8"; - private final static String VALUE_TYPE_FLOAT = "fl"; + + // divisor to transform from milli to kilo UNIT (e.g. mW => kW) + 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"; + public static final String VALUE_TYPE_INT8 = "i8"; + // ux (x=1,3,6,8) types => hex encoded unsigned value + private static final String VALUE_TYPE_DECIMAL = "u"; + // fl => hex encoded float + private static final String VALUE_TYPE_FLOAT = "fl"; + // st => string + // public static final String VALUE_TYPE_STRING = "st"; private @Nullable ScheduledFuture refreshJob; private @Nullable PowerLimitationStatusDTO limitationStatus = null; @@ -126,160 +139,146 @@ private void refresh() { response = senecHomeApi.getStatistics(); logger.trace("received {}", response); - BigDecimal pvLimitation = new BigDecimal(100).subtract(getSenecValue(response.limitation.powerLimitation)) + BigDecimal pvLimitation = new BigDecimal(100).subtract(getSenecValue(response.power.powerLimitation)) .setScale(0, RoundingMode.HALF_UP); + updateState(CHANNEL_SENEC_POWER_LIMITATION, new QuantityType<>(pvLimitation, Units.PERCENT)); - updateState(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_LIMITATION, - new QuantityType(pvLimitation, Units.PERCENT)); - - Channel channelLimitationState = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_LIMITATION_STATE); + Channel channelLimitationState = getThing().getChannel(CHANNEL_SENEC_POWER_LIMITATION_STATE); if (channelLimitationState != null) { updatePowerLimitationStatus(channelLimitationState, (100 - pvLimitation.intValue()) <= config.limitationTresholdValue, config.limitationDuration); } - - Channel channelConsumption = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_POWER_CONSUMPTION); - if (channelConsumption != null) { - updateState(channelConsumption.getUID(), - new QuantityType( - getSenecValue(response.energy.homePowerConsumption).setScale(2, RoundingMode.HALF_UP), - Units.WATT)); + if (response.power.currentPerMpp != null) { + updateQtyState(CHANNEL_SENEC_CURRENT_MPP1, response.power.currentPerMpp[0], 2, Units.AMPERE); + updateQtyState(CHANNEL_SENEC_CURRENT_MPP2, response.power.currentPerMpp[1], 2, Units.AMPERE); + if (response.power.currentPerMpp.length > 2) { + // only Home V3 duo + updateQtyState(CHANNEL_SENEC_CURRENT_MPP3, response.power.currentPerMpp[2], 2, Units.AMPERE); + } } - - Channel channelEnergyProduction = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_ENERGY_PRODUCTION); - if (channelEnergyProduction != null) { - updateState(channelEnergyProduction.getUID(), new QuantityType( - getSenecValue(response.energy.inverterPowerGeneration).setScale(0, RoundingMode.HALF_UP), - Units.WATT)); + if (response.power.powerPerMpp != null) { + updateQtyState(CHANNEL_SENEC_POWER_MPP1, response.power.powerPerMpp[0], 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_POWER_MPP2, response.power.powerPerMpp[1], 2, Units.WATT); + if (response.power.powerPerMpp.length > 2) { + updateQtyState(CHANNEL_SENEC_POWER_MPP3, response.power.powerPerMpp[2], 2, Units.WATT); + } } - - Channel channelBatteryPower = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_POWER); - if (channelBatteryPower != null) { - updateState(channelBatteryPower.getUID(), new QuantityType( - getSenecValue(response.energy.batteryPower).setScale(2, RoundingMode.HALF_UP), Units.WATT)); + if (response.power.voltagePerMpp != null) { + updateQtyState(CHANNEL_SENEC_VOLTAGE_MPP1, response.power.voltagePerMpp[0], 2, Units.VOLT); + updateQtyState(CHANNEL_SENEC_VOLTAGE_MPP2, response.power.voltagePerMpp[1], 2, Units.VOLT); + if (response.power.voltagePerMpp.length > 2) { + updateQtyState(CHANNEL_SENEC_VOLTAGE_MPP3, response.power.voltagePerMpp[2], 2, Units.VOLT); + } } - Channel channelBatteryFuelCharge = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_FUEL_CHARGE); - if (channelBatteryFuelCharge != null) { - updateState(channelBatteryFuelCharge.getUID(), - new QuantityType( - getSenecValue(response.energy.batteryFuelCharge).setScale(0, RoundingMode.HALF_UP), - Units.PERCENT)); + updateQtyState(CHANNEL_SENEC_POWER_CONSUMPTION, response.energy.housePowerConsumption, 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_ENERGY_PRODUCTION, response.energy.inverterPowerGeneration, 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_BATTERY_POWER, response.energy.batteryPower, 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_BATTERY_CURRENT, response.energy.batteryCurrent, 2, Units.AMPERE); + updateQtyState(CHANNEL_SENEC_BATTERY_VOLTAGE, response.energy.batteryVoltage, 2, Units.VOLT); + updateStringStateFromInt(CHANNEL_SENEC_SYSTEM_STATE, response.energy.systemState, + SenecSystemStatus::descriptionFromCode); + updateDecimalState(CHANNEL_SENEC_SYSTEM_STATE_VALUE, response.energy.systemState); + updateQtyState(CHANNEL_SENEC_BATTERY_FUEL_CHARGE, response.energy.batteryFuelCharge, 0, Units.PERCENT); + + updateGridPowerValues(getSenecValue(response.grid.currentGridValue)); + updateQtyState(CHANNEL_SENEC_GRID_CURRENT_PH1, response.grid.currentGridCurrentPerPhase[0], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_GRID_CURRENT_PH2, response.grid.currentGridCurrentPerPhase[1], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_GRID_CURRENT_PH3, response.grid.currentGridCurrentPerPhase[2], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_GRID_POWER_PH1, response.grid.currentGridPowerPerPhase[0], 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_GRID_POWER_PH2, response.grid.currentGridPowerPerPhase[1], 2, Units.WATT); + updateQtyState(CHANNEL_SENEC_GRID_POWER_PH3, response.grid.currentGridPowerPerPhase[2], 2, Units.WATT); + + updateQtyState(CHANNEL_SENEC_GRID_VOLTAGE_PH1, response.grid.currentGridVoltagePerPhase[0], 2, Units.VOLT); + updateQtyState(CHANNEL_SENEC_GRID_VOLTAGE_PH2, response.grid.currentGridVoltagePerPhase[1], 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_LIVE_BAT_CHARGE, response.statistics.liveBatCharge, 2, Units.KILOWATT_HOUR); + updateQtyState(CHANNEL_SENEC_LIVE_BAT_DISCHARGE, response.statistics.liveBatDischarge, 2, + Units.KILOWATT_HOUR); + updateQtyState(CHANNEL_SENEC_LIVE_GRID_IMPORT, response.statistics.liveGridImport, 2, Units.KILOWATT_HOUR); + updateQtyState(CHANNEL_SENEC_LIVE_GRID_EXPORT, response.statistics.liveGridExport, 2, Units.KILOWATT_HOUR); + updateQtyState(CHANNEL_SENEC_LIVE_HOUSE_CONSUMPTION, response.statistics.liveHouseConsumption, 2, + Units.KILOWATT_HOUR); + updateQtyState(CHANNEL_SENEC_LIVE_POWER_GENERATOR, response.statistics.livePowerGenerator, 2, + Units.KILOWATT_HOUR); + if (response.statistics.liveWallboxEnergy != null) { + updateQtyState(CHANNEL_SENEC_LIVE_ENERGY_WALLBOX1, response.statistics.liveWallboxEnergy[0], 2, + Units.KILOWATT_HOUR, DIVISOR_ISO_TO_KILO); } - Channel channelGridCurrentPhase1 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_CURRENT_PH1); - updateState(channelGridCurrentPhase1.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridCurrentPerPhase[0]).setScale(2, RoundingMode.HALF_UP), - Units.AMPERE)); - - Channel channelGridCurrentPhase2 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_CURRENT_PH2); - updateState(channelGridCurrentPhase2.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridCurrentPerPhase[1]).setScale(2, RoundingMode.HALF_UP), - Units.AMPERE)); - - Channel channelGridCurrentPhase3 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_CURRENT_PH3); - updateState(channelGridCurrentPhase3.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridCurrentPerPhase[2]).setScale(2, RoundingMode.HALF_UP), - Units.AMPERE)); - - Channel channelGridPowerPhase1 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_PH1); - updateState(channelGridPowerPhase1.getUID(), - new QuantityType( - getSenecValue(response.grid.currentGridPowerPerPhase[0]).setScale(2, RoundingMode.HALF_UP), - Units.WATT)); - - Channel channelGridPowerPhase2 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_PH2); - updateState(channelGridPowerPhase2.getUID(), - new QuantityType( - getSenecValue(response.grid.currentGridPowerPerPhase[1]).setScale(2, RoundingMode.HALF_UP), - Units.WATT)); - - Channel channelGridPowerPhase3 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_PH3); - updateState(channelGridPowerPhase3.getUID(), - new QuantityType( - getSenecValue(response.grid.currentGridPowerPerPhase[2]).setScale(2, RoundingMode.HALF_UP), - Units.WATT)); - - Channel channelGridVoltagePhase1 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_VOLTAGE_PH1); - updateState(channelGridVoltagePhase1.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridVoltagePerPhase[0]).setScale(2, RoundingMode.HALF_UP), - Units.VOLT)); - - Channel channelGridVoltagePhase2 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_VOLTAGE_PH2); - updateState(channelGridVoltagePhase2.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridVoltagePerPhase[1]).setScale(2, RoundingMode.HALF_UP), - Units.VOLT)); - - Channel channelGridVoltagePhase3 = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_VOLTAGE_PH3); - updateState(channelGridVoltagePhase3.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridVoltagePerPhase[2]).setScale(2, RoundingMode.HALF_UP), - Units.VOLT)); - - Channel channelGridFrequency = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_FREQUENCY); - updateState(channelGridFrequency.getUID(), new QuantityType( - getSenecValue(response.grid.currentGridFrequency).setScale(2, RoundingMode.HALF_UP), Units.HERTZ)); - - Channel channelBatteryStateValue = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_STATE_VALUE); - updateState(channelBatteryStateValue.getUID(), - new DecimalType(getSenecValue(response.energy.batteryState).intValue())); - - Channel channelLiveBatCharge = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_LIVE_BAT_CHARGE); - updateState(channelLiveBatCharge.getUID(), - new QuantityType( - getSenecValue(response.statistics.liveBatCharge).setScale(2, RoundingMode.HALF_UP), - Units.WATT_HOUR)); - - Channel channelLiveBatDischarge = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_LIVE_BAT_DISCHARGE); - updateState(channelLiveBatDischarge.getUID(), - new QuantityType( - getSenecValue(response.statistics.liveBatDischarge).setScale(2, RoundingMode.HALF_UP), - Units.WATT_HOUR)); - - Channel channelLiveGridImport = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_LIVE_GRID_IMPORT); - updateState(channelLiveGridImport.getUID(), - new QuantityType( - getSenecValue(response.statistics.liveGridImport).setScale(2, RoundingMode.HALF_UP), - Units.WATT_HOUR)); - - Channel channelLiveGridExport = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_LIVE_GRID_EXPORT); - updateState(channelLiveGridExport.getUID(), - new QuantityType( - getSenecValue(response.statistics.liveGridExport).setScale(2, RoundingMode.HALF_UP), - Units.WATT_HOUR)); - - Channel channelBatteryVoltage = getThing() - .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_VOLTAGE); - updateState(channelBatteryVoltage.getUID(), new QuantityType( - getSenecValue(response.energy.batteryVoltage).setScale(2, RoundingMode.HALF_UP), Units.VOLT)); - - Channel channelBatteryState = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_BATTERY_STATE); - if (channelBatteryState != null) { - updateBatteryState(channelBatteryState, getSenecValue(response.energy.batteryState).intValue()); + 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); + 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); + 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]); + 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); + 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); + 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); + 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); + + if (response.temperature != null) { + updateQtyState(CHANNEL_SENEC_BATTERY_TEMPERATURE, response.temperature.batteryTemperature, 0, + SIUnits.CELSIUS); + updateQtyState(CHANNEL_SENEC_CASE_TEMPERATURE, response.temperature.caseTemperature, 0, + SIUnits.CELSIUS); + updateQtyState(CHANNEL_SENEC_MCU_TEMPERATURE, response.temperature.mcuTemperature, 0, SIUnits.CELSIUS); } - updateGridPowerValues(getSenecValue(response.grid.currentGridValue)); + if (response.wallbox != null && response.wallbox.state != null) { + updateStringStateFromInt(CHANNEL_SENEC_WALLBOX1_STATE, response.wallbox.state[0], + SenecWallboxStatus::descriptionFromCode); + updateDecimalState(CHANNEL_SENEC_WALLBOX1_STATE_VALUE, response.wallbox.state[0]); + updateQtyState(CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH1, response.wallbox.l1ChargingCurrent[0], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH2, response.wallbox.l2ChargingCurrent[0], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_WALLBOX1_CHARGING_CURRENT_PH3, response.wallbox.l3ChargingCurrent[0], 2, + Units.AMPERE); + updateQtyState(CHANNEL_SENEC_WALLBOX1_CHARGING_POWER, response.wallbox.chargingPower[0], 2, Units.WATT); + } updateStatus(ThingStatus.ONLINE); - } catch (JsonSyntaxException | IOException | InterruptedException | TimeoutException | ExecutionException e) { + } catch (JsonParseException | IOException | InterruptedException | TimeoutException | ExecutionException e) { if (response == null) { logger.trace("Faulty response: is null"); } else { @@ -293,19 +292,73 @@ private void refresh() { return Boolean.TRUE; } + protected void updateStringStateFromInt(String channelName, String senecValue, + Function converter) { + Channel channel = getThing().getChannel(channelName); + if (channel != null) { + Integer value = getSenecValue(senecValue).intValue(); + updateState(channel.getUID(), new StringType(converter.apply(value))); + } + } + + protected void updateDecimalState(String channelName, String senecValue) { + Channel channel = getThing().getChannel(channelName); + if (channel != null) { + BigDecimal value = getSenecValue(senecValue); + updateState(channel.getUID(), new DecimalType(value.intValue())); + } + } + + protected > void updateQtyState(String channelName, String senecValue, int scale, + Unit unit) { + updateQtyState(channelName, senecValue, scale, unit, null); + } + + protected > void updateQtyState(String channelName, String senecValue, int scale, + Unit unit, @Nullable BigDecimal divisor) { + Channel channel = getThing().getChannel(channelName); + if (channel == null) { + return; + } + BigDecimal value = getSenecValue(senecValue); + if (divisor != null) { + value = value.divide(divisor, scale, RoundingMode.HALF_UP); + } else { + value = value.setScale(scale, RoundingMode.HALF_UP); + } + updateState(channel.getUID(), new QuantityType(value, unit)); + } + protected BigDecimal getSenecValue(String value) { String[] type = value.split("_"); - if (VALUE_TYPE_INT.equalsIgnoreCase(type[0])) { - return new BigDecimal(Integer.valueOf(type[1], 16)); - } else if (VALUE_TYPE_UNSIGNED_INT.equalsIgnoreCase(type[0])) { - return new BigDecimal(Integer.valueOf(type[1], 16)); - } else if (VALUE_TYPE_FLOAT.equalsIgnoreCase(type[0])) { - return parseFloatValue(type[1]); - } else { - logger.warn("Unknown value type [{}]", type[0]); + if (type[0] != null) { + if (type[0].startsWith(VALUE_TYPE_DECIMAL)) { + return new BigDecimal(Long.valueOf(type[1], 16)); + } else if (type[0].startsWith(VALUE_TYPE_INT1)) { + Integer val = Integer.valueOf(type[1], 16); + if ((val & 0x8000) > 0) { + val = val - 0x10000; + } + return new BigDecimal(val); + } else if (type[0].startsWith(VALUE_TYPE_INT3)) { + Long val = Long.valueOf(type[1], 16); + if ((Math.abs(val & 0x80000000)) > 0) { + val = val - 0x100000000L; + } + return new BigDecimal(val); + } else if (type[0].startsWith(VALUE_TYPE_INT8)) { + Long val = Long.valueOf(type[1], 16); + if ((val & 0x80) > 0) { + val = val - 0x100; + } + return new BigDecimal(val); + } else if (VALUE_TYPE_FLOAT.equalsIgnoreCase(type[0])) { + return parseFloatValue(type[1]); + } } + logger.warn("Unknown value type [{}]", type[0]); return BigDecimal.ZERO; } @@ -349,29 +402,25 @@ protected void updatePowerLimitationStatus(Channel channel, boolean status, int updateState(channel.getUID(), status ? OnOffType.ON : OnOffType.OFF); } - protected void updateBatteryState(Channel channel, int code) { - updateState(channel.getUID(), new StringType(SenecBatteryStatus.descriptionFromCode(code))); - } - protected void updateGridPowerValues(BigDecimal gridTotalValue) { BigDecimal gridTotal = gridTotalValue.setScale(2, RoundingMode.HALF_UP); Channel channelGridPower = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER); if (channelGridPower != null) { - updateState(channelGridPower.getUID(), new QuantityType(gridTotal, Units.WATT)); + updateState(channelGridPower.getUID(), new QuantityType<>(gridTotal, Units.WATT)); } Channel channelGridPowerSupply = getThing() .getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_SUPPLY); if (channelGridPowerSupply != null) { BigDecimal gridSupply = gridTotal.compareTo(BigDecimal.ZERO) < 0 ? gridTotal.abs() : BigDecimal.ZERO; - updateState(channelGridPowerSupply.getUID(), new QuantityType(gridSupply, Units.WATT)); + updateState(channelGridPowerSupply.getUID(), new QuantityType<>(gridSupply, Units.WATT)); } Channel channelGridPowerDraw = getThing().getChannel(SenecHomeBindingConstants.CHANNEL_SENEC_GRID_POWER_DRAW); if (channelGridPowerDraw != null) { BigDecimal gridDraw = gridTotal.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : gridTotal.abs(); - updateState(channelGridPowerDraw.getUID(), new QuantityType(gridDraw, Units.WATT)); + updateState(channelGridPowerDraw.getUID(), new QuantityType<>(gridDraw, Units.WATT)); } } } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecBatteryStatus.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecSystemStatus.java similarity index 92% rename from bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecBatteryStatus.java rename to bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecSystemStatus.java index cd39184fc3861..9426a06552f15 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecBatteryStatus.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecSystemStatus.java @@ -13,13 +13,13 @@ package org.openhab.binding.senechome.internal; /** - * The {@link SenecBatteryStatus} class defines available Senec specific - * battery states. + * The {@link SenecSystemStatus} class defines available Senec specific + * system states. * * @author Steven Schwarznau - Initial contribution * */ -public enum SenecBatteryStatus { +public enum SenecSystemStatus { INITIALSTATE(0, "INITIAL STATE"), ERROR_INVERTER_COMMUNICATION(1, "ERROR INVERTER COMMUNICATION"), @@ -118,7 +118,7 @@ public enum SenecBatteryStatus { private int code; private String description; - SenecBatteryStatus(int index, String description) { + SenecSystemStatus(int index, String description) { this.code = index; this.description = description; } @@ -131,21 +131,21 @@ public String getDescription() { return description; } - public static SenecBatteryStatus fromCode(int code) { - for (SenecBatteryStatus state : SenecBatteryStatus.values()) { + public static SenecSystemStatus fromCode(int code) { + for (SenecSystemStatus state : SenecSystemStatus.values()) { if (state.code == code) { return state; } } - return SenecBatteryStatus.UNKNOWN; + return SenecSystemStatus.UNKNOWN; } public static String descriptionFromCode(int code) { - for (SenecBatteryStatus state : SenecBatteryStatus.values()) { + for (SenecSystemStatus state : SenecSystemStatus.values()) { if (state.code == code) { return state.description; } } - return SenecBatteryStatus.UNKNOWN.description; + return SenecSystemStatus.UNKNOWN.description; } } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecWallboxStatus.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecWallboxStatus.java new file mode 100644 index 0000000000000..495c950cf8f7c --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecWallboxStatus.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal; + +/** + * Enum with available Senec specific wallbox states. + * + * @author Erwin Guib - Initial Contribution + */ +public enum SenecWallboxStatus { + WAIT_FOR_EV(0xA1, "Waiting for EV"), + EV_ASKING_CHARGE(0xB1, "EV asking for charge"), + EV_CHARGE_PERMISSION(0xB2, "EV has charge permission"), + EV_CHARGING(0xC2, "Charging"), + EV_CHARGING_REDUCED_CURRENT_ERROR(0xC3, "Charging reduced current (error F16, F17)"), + EV_CHARGING_REDUCED_CURRENT_IMBALANCE(0xC4, "Charging reduced current (imbalance F15)"), + DISABLED(0xE0, "Wallbox disabled"), + TEST_PRODUCTION(0xE1, "production test"), + EVCC_PROGRAM(0xE2, "EVCC program mode"), + BUS_IDLE(0xE3, "Bus idle"), + UNEXPECTED_CLOSED_CONTACT(0xF1, "unexpected closed contact (welded)"), + INTERNAL_ERROR(0xF2, "Internal error"), + DC_RESIDUAL_CURRENT(0xF3, "DC residual current detected"), + UPSTREAM_COM_TIMEOUT(0xF4, "Upstream communication timeout"), + LOCK_SOCKET_FAILED(0xF5, "Lock of socket failed"), + CS_OUT_OF_RANGE(0xF6, "CS out of range"), + EV_HIGH_TEMP(0xF7, "State D requested by EV"), + CP_OUT_OF_RANGE(0xF8, "CP out of range"), + OVERCURRENT(0xF9, "Overcurrent detected"), + TEMP_OUT_OF_LIMITS(0xFA, "Temperature outside limits"), + UNEXPECTED_OPEN_CONTACT(0xFB, "unexpected opened contact"), + RESERVED_1(0xFC, "Reserved State"), + RESERVED_2(0xFD, "Reserved State"), + UNKNOWN(-1, "UNKNOWN"); + + private final int code; + private final String description; + + SenecWallboxStatus(int index, String description) { + this.code = index; + this.description = description; + } + + public int getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public static SenecWallboxStatus fromCode(int code) { + for (SenecWallboxStatus state : SenecWallboxStatus.values()) { + if (state.code == code) { + return state; + } + } + return SenecWallboxStatus.UNKNOWN; + } + + public static String descriptionFromCode(int code) { + for (SenecWallboxStatus state : SenecWallboxStatus.values()) { + if (state.code == code) { + return state.description; + } + } + return SenecWallboxStatus.UNKNOWN.description; + } +} diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeBattery.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeBattery.java new file mode 100644 index 0000000000000..cf209e2e225f1 --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeBattery.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal.json; + +import java.io.Serializable; +import java.util.Arrays; + +import com.google.gson.annotations.SerializedName; + +/** + * Battery related data from section "BMS". + * + * @author Erwin Guib - Initial Contribution + */ +public class SenecHomeBattery implements Serializable { + + public static final long serialVersionUID = -2850415059107677832L; + + /** + * Total charged energy per battery pack (mWh). + */ + public @SerializedName("CHARGED_ENERGY") String[] chargedEnergy; + + /** + * Total discharged energy per battery pack (mWh). + */ + public @SerializedName("DISCHARGED_ENERGY") String[] dischargedEnergy; + + /** + * Number of load cycles per battery pack. + */ + public @SerializedName("CYCLES") String[] cycles; + + /** + * Current per battery pack (A). + */ + public @SerializedName("CURRENT") String[] current; + + /** + * Voltage per battery pack (V). + */ + public @SerializedName("VOLTAGE") String[] voltage; + + /** + * Maximum cell voltage per battery pack (mV). + */ + public @SerializedName("MAX_CELL_VOLTAGE") String[] maxCellVoltage; + + /** + * Minimum cell voltage per battery pack (mV). + */ + public @SerializedName("MIN_CELL_VOLTAGE") String[] minCellVoltage; + + @Override + public String toString() { + return "SenecHomeBattery{" + "chargedEnergy=" + Arrays.toString(chargedEnergy) + ", dischargedEnergy=" + + Arrays.toString(dischargedEnergy) + ", cycles=" + Arrays.toString(cycles) + ", current=" + + Arrays.toString(current) + ", voltage=" + Arrays.toString(voltage) + ", maxCellVoltage=" + + Arrays.toString(maxCellVoltage) + ", minCellVoltage=" + Arrays.toString(minCellVoltage) + '}'; + } +} diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeEnergy.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeEnergy.java index a1937c7c10ced..82471eacfbb9c 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeEnergy.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeEnergy.java @@ -20,23 +20,55 @@ * Json model of senec home devices: This sub model contains values of current workload, i. e. current consumption and * battery charge. * + * Section is "ENERGY" + * * @author Steven Schwarznau - Initial Contribution */ public class SenecHomeEnergy implements Serializable { - private static final long serialVersionUID = -6171687327416551070L; + private static final long serialVersionUID = -5491226594672777034L; + + /** + * House power consumption (W). + */ + public @SerializedName("GUI_HOUSE_POW") String housePowerConsumption; - public @SerializedName("GUI_HOUSE_POW") String homePowerConsumption; + /** + * Total inverter power (W). + * Named "energyProduction" on channel/thing-type side. + */ public @SerializedName("GUI_INVERTER_POWER") String inverterPowerGeneration; + + /** + * Battery power in W (+values loading, -values unloading) + */ public @SerializedName("GUI_BAT_DATA_POWER") String batteryPower; - public @SerializedName("GUI_BAT_DATA_FUEL_CHARGE") String batteryFuelCharge; - public @SerializedName("STAT_STATE") String batteryState; + + /** + * Battery current (A). + */ + public @SerializedName("GUI_BAT_DATA_CURRENT") String batteryCurrent; + + /** + * Battery voltage (V). + */ public @SerializedName("GUI_BAT_DATA_VOLTAGE") String batteryVoltage; + /** + * Battery charge rate (%). + */ + public @SerializedName("GUI_BAT_DATA_FUEL_CHARGE") String batteryFuelCharge; + + /** + * Encoded system state. + */ + public @SerializedName("STAT_STATE") String systemState; + @Override public String toString() { - return "SenecHomeEnergy [homePowerConsumption=" + homePowerConsumption + ", inverterPowerGeneration=" - + inverterPowerGeneration + ", batteryPower=" + batteryPower + ", batteryFuelCharge=" - + batteryFuelCharge + ", batteryState=" + batteryState + ", batteryVoltage" + batteryVoltage + "]"; + return "SenecHomeEnergy [housePowerConsumption=" + housePowerConsumption + ", inverterPowerGeneration=" + + inverterPowerGeneration + ", batteryPower=" + batteryPower + ", batteryVoltage=" + batteryVoltage + + ", batteryCurrent=" + batteryCurrent + ", batteryFuelCharge=" + batteryFuelCharge + ", systemState=" + + systemState + "]"; } } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeGrid.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeGrid.java index 1c9794a412780..1464c107412df 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeGrid.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeGrid.java @@ -17,7 +17,9 @@ import com.google.gson.annotations.SerializedName; /** - * Json model of senec home devices: This sub model provides the current power statistics by the inverter. + * Json model of senec home devices: This sub model contains grid related power values. + * + * Section "PM1OBJ1" (Enfluri Netz Werte) * * @author Steven Schwarznau - Initial Contribution */ diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeLimitation.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeLimitation.java deleted file mode 100644 index 832a331d06676..0000000000000 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeLimitation.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2010-2021 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.senechome.internal.json; - -import java.io.Serializable; - -import com.google.gson.annotations.SerializedName; - -/** - * Json model of senec home devices: This sub model contains grid related power values. - * - * @author Steven Schwarznau - Initial Contribution - */ -public class SenecHomeLimitation implements Serializable { - - private static final long serialVersionUID = -8990871346958824085L; - - public @SerializedName("POWER_RATIO") String powerLimitation; - - @Override - public String toString() { - return "SenecHomePower [powerLimitation=" + powerLimitation + "]"; - } -} diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomePower.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomePower.java new file mode 100644 index 0000000000000..152b350793dfd --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomePower.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal.json; + +import java.io.Serializable; +import java.util.Arrays; + +import com.google.gson.annotations.SerializedName; + +/** + * Json model of senec home devices: This sub model provides the current power statistics by the inverter. + * + * Section "PV1" in Senec JSON. + * + * @author Steven Schwarznau - Initial Contribution + */ +public class SenecHomePower implements Serializable { + + private static final long serialVersionUID = -7092741166288342343L; + + /** + * Power limitation (%). + */ + public @SerializedName("POWER_RATIO") String powerLimitation; + + /** + * Current DC current per MPP (A). + */ + public @SerializedName("MPP_CUR") String[] currentPerMpp; + + /** + * Current DC power per MPP (W) + */ + public @SerializedName("MPP_POWER") String[] powerPerMpp; + + /** + * Current DC tension per MPP (V). + */ + public @SerializedName("MPP_VOL") String[] voltagePerMpp; + + @Override + public String toString() { + return "SenecHomePower [powerLimitation=" + powerLimitation + ", mppCur=" + Arrays.toString(currentPerMpp) + + ", mppPower=" + Arrays.toString(powerPerMpp) + ", mppVol=" + Arrays.toString(voltagePerMpp) + "]"; + } +} diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeResponse.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeResponse.java index e59721a8cc0c4..8d5b306af580c 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeResponse.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeResponse.java @@ -23,16 +23,19 @@ */ public class SenecHomeResponse implements Serializable { - private static final long serialVersionUID = 5302080655053778494L; + private static final long serialVersionUID = -2672622188872750438L; - public @SerializedName("PV1") SenecHomeLimitation limitation = new SenecHomeLimitation(); + public @SerializedName("PV1") SenecHomePower power = new SenecHomePower(); public @SerializedName("ENERGY") SenecHomeEnergy energy = new SenecHomeEnergy(); public @SerializedName("PM1OBJ1") SenecHomeGrid grid = new SenecHomeGrid(); public @SerializedName("STATISTIC") SenecHomeStatistics statistics = new SenecHomeStatistics(); + public @SerializedName("BMS") SenecHomeBattery battery = new SenecHomeBattery(); + public @SerializedName("TEMPMEASURE") SenecHomeTemperature temperature = new SenecHomeTemperature(); + public @SerializedName("WALLBOX") SenecHomeWallbox wallbox = new SenecHomeWallbox(); @Override public String toString() { - return "SenecHomeResponse [limitation=" + limitation + ", energy=" + energy + ", grid=" + grid + ", statistics=" - + statistics + "]"; + return "SenecHomeResponse [power=" + power + ", energy=" + energy + ", grid=" + grid + ", statistics=" + + statistics + "battery" + battery + "temperature" + temperature + "wallbox" + wallbox + "]"; } } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeStatistics.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeStatistics.java index 4d10f3fa141e6..8c450361dc74e 100644 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeStatistics.java +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeStatistics.java @@ -13,41 +13,61 @@ package org.openhab.binding.senechome.internal.json; import java.io.Serializable; +import java.util.Arrays; import com.google.gson.annotations.SerializedName; /** * Json model of senec home devices: This sub model provides the current statistics by the inverter. * + * Section "STATISTIC" in Senec JSON. + * * @author Korbinian Probst - Initial Contribution */ public class SenecHomeStatistics implements Serializable { - private static final long serialVersionUID = -7479338432170375451L; + private static final long serialVersionUID = -1102310892637495823L; /** - * total Wh charged to the battery + * total Wh charged to the battery (kWh) */ public @SerializedName("LIVE_BAT_CHARGE") String liveBatCharge; /** - * total Wh discharged from the battery + * total Wh discharged from the battery (kWh) */ public @SerializedName("LIVE_BAT_DISCHARGE") String liveBatDischarge; /** - * total Wh imported from grid + * total Wh imported from grid (kWh) */ public @SerializedName("LIVE_GRID_IMPORT") String liveGridImport; /** - * total Wh supplied to the grid + * total Wh supplied to the grid (kWh) */ public @SerializedName("LIVE_GRID_EXPORT") String liveGridExport; + /** + * Total house consumption (kWh) + */ + public @SerializedName("LIVE_HOUSE_CONS") String liveHouseConsumption; + + /** + * Total Wh produced (kWh) + */ + public @SerializedName("LIVE_PV_GEN") String livePowerGenerator; + + /** + * Total Wh provided to Wallbox (Wh) + */ + public @SerializedName("LIVE_WB_ENERGY") String[] liveWallboxEnergy; + @Override public String toString() { - return "SenecHomeStatistics [liveBatCharge=" + liveBatCharge + ", liveBatDischarge= " + liveBatDischarge - + ", liveGridImport= " + liveGridImport + ", liveGridExport= " + liveGridExport + "]"; + return "SenecHomeStatistics [liveBatCharge=" + liveBatCharge + ", liveBatDischarge=" + liveBatDischarge + + ", liveGridImport=" + liveGridImport + ", liveGridExport=" + liveGridExport + + ", liveHouseConsumption=" + liveHouseConsumption + ", livePowerGen=" + livePowerGenerator + + ", liveWallboxEnergy=" + Arrays.toString(liveWallboxEnergy) + "]"; } } diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeTemperature.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeTemperature.java new file mode 100644 index 0000000000000..ba41e1a312591 --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeTemperature.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal.json; + +import java.io.Serializable; + +import com.google.gson.annotations.SerializedName; + +/** + * Senec Temperature information from "TEMPMEASURE" section. + * + * @author Erwin Guib - Initial Contribution + */ +public class SenecHomeTemperature implements Serializable { + + private static final long serialVersionUID = 5300207918289980752L; + + /** + * Battery temperature (°C). + */ + public @SerializedName("BATTERY_TEMP") String batteryTemperature; + + /** + * Case temperature (°C). + */ + public @SerializedName("CASE_TEMP") String caseTemperature; + + /** + * MCU Temperature (°C). + */ + public @SerializedName("MCU_TEMP") String mcuTemperature; + + @Override + public String toString() { + return "SenecHomeTemperature{" + "batteryTemperature='" + batteryTemperature + '\'' + ", caseTemperature='" + + caseTemperature + '\'' + ", mcuTemperature='" + mcuTemperature + '\'' + '}'; + } +} diff --git a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeWallbox.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeWallbox.java new file mode 100644 index 0000000000000..a117e8b2b1b42 --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/json/SenecHomeWallbox.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal.json; + +import java.io.Serializable; +import java.util.Arrays; + +import com.google.gson.annotations.SerializedName; + +/** + * Senec wallbox specific data from "WALLBOX" section. + * + * @author Erwin Guib - Initial Contribution + */ +public class SenecHomeWallbox implements Serializable { + + private static final long serialVersionUID = -664163242812451235L; + + /** + * Encoded wallbox state. + */ + public @SerializedName("STATE") String[] state; + + /** + * L1 Charging current per wallbox (A). + */ + public @SerializedName("L1_CHARGING_CURRENT") String[] l1ChargingCurrent; + + /** + * L2 Charging current per wallbox (A). + */ + public @SerializedName("L2_CHARGING_CURRENT") String[] l2ChargingCurrent; + + /** + * L3 Charging current per wallbox (A). + */ + public @SerializedName("L3_CHARGING_CURRENT") String[] l3ChargingCurrent; + + /** + * Charging power per wallbox (W). + */ + public @SerializedName("APPARENT_CHARGING_POWER") String[] chargingPower; + + @Override + public String toString() { + return "SenecWallbox{" + "l1ChargingCurrent=" + Arrays.toString(l1ChargingCurrent) + ", l2ChargingCurrent=" + + Arrays.toString(l2ChargingCurrent) + ", l3ChargingCurrent=" + Arrays.toString(l3ChargingCurrent) + + '}'; + } +} diff --git a/bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/thing/thing-types.xml index e05d8d2301ebf..4e9e51b594d1f 100644 --- a/bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.senechome/src/main/resources/OH-INF/thing/thing-types.xml @@ -10,14 +10,30 @@ Senec Home + + + + + + + + + + + + + + - - + + + + @@ -31,11 +47,58 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -66,58 +129,120 @@ Number:Dimensionless - Energy + Number - Switch - Energy + Text + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:Power + + Energy + + + + Number:Power + + Energy + + + + Number:Power + + Energy + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + Number:Power - + + Energy + + + + Number:Power + Energy - Number:Power Energy - + + Number:ElectricCurrent + + Current + + + + Number:ElectricPotential + + Voltage + + Number:Dimensionless - Battery + BatteryLevel - - + String - - Battery + + Text - - + Number - - Battery + + Number - - Number:Power - - Energy - - Number:Power @@ -125,84 +250,72 @@ Energy - Number:Power Energy - Number:Power Energy - Number:Power Energy - Number:Power Energy - Number:Power Energy - Number:ElectricCurrent Current - Number:ElectricCurrent Current - Number:ElectricCurrent Current - Number:ElectricPotential Voltage - Number:ElectricPotential Voltage - Number:ElectricPotential Voltage - Number:Frequency @@ -210,38 +323,275 @@ + Number:Energy Energy - Number:Energy Energy - Number:Energy Energy - Number:Energy Energy + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + - + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number:Energy + + Energy + + + + Number + + Number + + + + Number + + Number + + + + Number + + Number + + + + Number + + Number + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + Number:ElectricPotential - + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + Number:ElectricPotential + + Voltage + + + + + + Number:Temperature + + Temperature + + + + Number:Temperature + + Temperature + + + + Number:Temperature + + Temperature + + + + + + String + + Text + + + + Number + + Number + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:ElectricCurrent + + Current + + + + Number:Energy + + Power diff --git a/bundles/org.openhab.binding.senechome/src/test/java/org/openhab/binding/senechome/internal/SenecHomeHandlerTest.java b/bundles/org.openhab.binding.senechome/src/test/java/org/openhab/binding/senechome/internal/SenecHomeHandlerTest.java new file mode 100644 index 0000000000000..d338f5e993ab1 --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/test/java/org/openhab/binding/senechome/internal/SenecHomeHandlerTest.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.senechome.internal; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openhab.core.thing.Thing; + +/** + * Test for senec value parsing. All test data are from "original" senec (using vars.html). + * + * @author Erwin Guib - Initial Contribution + */ +@RunWith(MockitoJUnitRunner.class) +class SenecHomeHandlerTest { + + protected static Object[][] data() { + return new Object[][] { + // unsigned + { "u1_0002", BigDecimal.valueOf(2) }, // + { "u1_07DB", BigDecimal.valueOf(2011) }, // + { "u3_0000194C", BigDecimal.valueOf(6476) }, // + { "u3_817E00E0", BigDecimal.valueOf(2172518624L) }, // + { "u6_0000000000000001", BigDecimal.valueOf(1) }, // + { "u6_00000000000C75D9", BigDecimal.valueOf(816601) }, // + { "u8_64", BigDecimal.valueOf(100) }, // + // int + { "i1_00FA", BigDecimal.valueOf(250) }, // + { "i3_00000078", BigDecimal.valueOf(120) }, // + { "i3_609F8480", BigDecimal.valueOf(1621066880) }, // + { "i3_FFFFFFFF", BigDecimal.valueOf(-1) }, // + { "i8_18", BigDecimal.valueOf(24) }, // + // string (unknown) + { "st_HMI: 3.15.32 PU: 4.1.89", BigDecimal.valueOf(0) } }; + } + + protected static Object[][] floatData() { + return new Object[][] { + // float + { "fl_41C80000", BigDecimal.valueOf(25), 0 }, // + { "fl_4247632F", BigDecimal.valueOf(49.85), 2 }, // + { "fl_C5AB6F0B", BigDecimal.valueOf(-5485.88), 2 }, // + { "fl_4248CCCD", BigDecimal.valueOf(50.2), 1 }, // + }; + } + + @Mock + Thing mockThing; + + @Mock + HttpClient mockHttpClient; + + SenecHomeHandler cut = new SenecHomeHandler(mockThing, mockHttpClient); + + @ParameterizedTest + @MethodSource("data") + void getSenecValue(String value, Object expectedResult) { + Assertions.assertEquals(expectedResult, cut.getSenecValue(value)); + } + + @ParameterizedTest + @MethodSource("floatData") + void getSenecValueFloat(String value, Object expectedResult, int scale) { + Assertions.assertEquals(expectedResult, cut.getSenecValue(value).setScale(scale, RoundingMode.HALF_UP)); + } +}