From: eguib Date: Sat, 29 May 2021 12:29:20 +0000 (+0200) Subject: [senechome] issue #10679: Extend with additional channels; refactor and various impro... X-Git-Url: https://git.basschouten.com/?a=commitdiff_plain;h=3875d2aca58f3a5b960bf5593eba8d4d63000e46;p=openhab-addons.git [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 --- diff --git a/CODEOWNERS b/CODEOWNERS index bed0f44786..ca38e94d9a 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 49fc88900a..9e8075fbac 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/SenecBatteryStatus.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecBatteryStatus.java deleted file mode 100644 index cd39184fc3..0000000000 --- a/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecBatteryStatus.java +++ /dev/null @@ -1,151 +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; - -/** - * The {@link SenecBatteryStatus} class defines available Senec specific - * battery states. - * - * @author Steven Schwarznau - Initial contribution - * - */ -public enum SenecBatteryStatus { - - INITIALSTATE(0, "INITIAL STATE"), - ERROR_INVERTER_COMMUNICATION(1, "ERROR INVERTER COMMUNICATION"), - ERROR_ELECTRICY_METER(2, "ERROR ELECTRICY METER"), - RIPPLE_CONTROL_RECEIVER(3, "RIPPLE CONTROL RECEIVER"), - INITIAL_CHARGE(4, "INITIAL CHARGE"), - MAINTENANCE_CHARGE(5, "MAINTENANCE CHARGE"), - MAINTENANCE_READY(6, "MAINTENANCE READY"), - MAINTENANCE_REQUIRED(7, "MAINTENANCE REQUIRED"), - MAN_SAFETY_CHARGE(8, "MAN. SAFETY CHARGE"), - SAFETY_CHARGE_READY(9, "SAFETY CHARGE READY"), - FULL_CHARGE(10, "FULL CHARGE"), - EQUALIZATION_CHARGE(11, "EQUALIZATION: CHARGE"), - DESULFATATION_CHARGE(12, "DESULFATATION: CHARGE"), - BATTERY_FULL(13, "BATTERY FULL"), - CHARGE(14, "CHARGE"), - BATTERY_EMPTY(15, "BATTERY EMPTY"), - DISCHARGE(16, "DISCHARGE"), - PV_AND_DISCHARGE(17, "PV + DISCHARGE"), - GRID_AND_DISCHARGE(18, "GRID + DISCHARGE"), - PASSIVE(19, "PASSIVE"), - OFF(20, "OFF"), - OWN_CONSUMPTION(21, "OWN CONSUMPTION"), - RESTART(22, "RESTART"), - MAN_EQUALIZATION_CHARGE(23, "MAN. EQUALIZATION: CHARGE"), - MAN_DESULFATATION_CHARGE(24, "MAN. DESULFATATION: CHARGE"), - SAFETY_CHARGE(25, "SAFETY CHARGE"), - BATTERY_PROTECTION_MODE(26, "BATTERY PROTECTION MODE"), - EG_ERROR(27, "EG ERROR"), - EG_CHARGE(28, "EG CHARGE"), - EG_DISCHARGE(29, "EG DISCHARGE"), - EG_PASSIVE(30, "EG PASSIVE"), - EG_PROHIBIT_CHARGE(31, "EG PROHIBIT CHARGE"), - EG_PROHIBIT_DISCHARGE(32, "EG PROHIBIT DISCHARGE"), - EMERGANCY_CHARGE(33, "EMERGANCY CHARGE"), - SOFTWARE_UPDATE(34, "SOFTWARE UPDATE"), - NSP_ERROR(35, "NSP ERROR"), - NSP_ERROR_GRID(36, "NSP ERROR: GRID"), - NSP_ERROR_HARDWRE(37, "NSP ERROR: HARDWRE"), - NO_SERVER_CONNECTION(38, "NO SERVER CONNECTION"), - BMS_ERROR(39, "BMS ERROR"), - MAINTENANCE_FILTER(40, "MAINTENANCE: FILTER"), - SLEEPING_MODE(41, "SLEEPING MODE"), - WAITING_EXCESS(42, "WAITING EXCESS"), - CAPACITY_TEST_CHARGE(43, "CAPACITY TEST: CHARGE"), - CAPACITY_TEST_DISCHARGE(44, "CAPACITY TEST: DISCHARGE"), - MAN_DESULFATATION_WAIT(45, "MAN. DESULFATATION: WAIT"), - MAN_DESULFATATION_READY(46, "MAN. DESULFATATION: READY"), - MAN_DESULFATATION_ERROR(47, "MAN. DESULFATATION: ERROR"), - EQUALIZATION_WAIT(48, "EQUALIZATION: WAIT"), - EMERGANCY_CHARGE_ERROR(49, "EMERGANCY CHARGE: ERROR"), - MAN_EQUALIZATION_WAIT(50, "MAN. EQUALIZATION: WAIT"), - MAN_EQUALIZATION_ERROR(51, "MAN. EQUALIZATION: ERROR"), - MAN_EQUALIZATION_READY(52, "MAN: EQUALIZATION: READY"), - AUTO_DESULFATATION_WAIT(53, "AUTO. DESULFATATION: WAIT"), - ABSORPTION_PHASE(54, "ABSORPTION PHASE"), - DCSWITCH_OFF(55, "DC-SWITCH OFF"), - PEAKSHAVING_WAIT(56, "PEAK-SHAVING: WAIT"), - ERROR_BATTERY_INVERTER(57, "ERROR BATTERY INVERTER"), - NPUERROR(58, "NPU-ERROR"), - BMS_OFFLINE(59, "BMS OFFLINE"), - MAINTENANCE_CHARGE_ERROR(60, "MAINTENANCE CHARGE ERROR"), - MAN_SAFETY_CHARGE_ERROR(61, "MAN. SAFETY CHARGE ERROR"), - SAFETY_CHARGE_ERROR(62, "SAFETY CHARGE ERROR"), - NO_CONNECTION_TO_MASTER(63, "NO CONNECTION TO MASTER"), - LITHIUM_SAFE_MODE_ACTIVE(64, "LITHIUM SAFE MODE ACTIVE"), - LITHIUM_SAFE_MODE_DONE(65, "LITHIUM SAFE MODE DONE"), - BATTERY_VOLTAGE_ERROR(66, "BATTERY VOLTAGE ERROR"), - BMS_DC_SWITCHED_OFF(67, "BMS DC SWITCHED OFF"), - GRID_INITIALIZATION(68, "GRID INITIALIZATION"), - GRID_STABILIZATION(69, "GRID STABILIZATION"), - REMOTE_SHUTDOWN(70, "REMOTE SHUTDOWN"), - OFFPEAKCHARGE(71, "OFFPEAK-CHARGE"), - ERROR_HALFBRIDGE(72, "ERROR HALFBRIDGE"), - BMS_ERROR_OPERATING_TEMPERATURE(73, "BMS: ERROR OPERATING TEMPERATURE"), - FACOTRY_SETTINGS_NOT_FOUND(74, "FACOTRY SETTINGS NOT FOUND"), - BACKUP_POWER_MODE_ACTIVE(75, "BACKUP POWER MODE - ACTIVE"), - BACKUP_POWER_MODE_BATTERY_EMPTY(76, "BACKUP POWER MODE - BATTERY EMPTY"), - BACKUP_POWER_MODE_ERROR(77, "BACKUP POWER MODE ERROR"), - INITIALISING(78, "INITIALISING"), - INSTALLATION_MODE(79, "INSTALLATION MODE"), - GRID_OFFLINE(80, "GRID OFFLINE"), - BMS_UPDATE_NEEDED(81, "BMS UPDATE NEEDED"), - BMS_CONFIGURATION_NEEDED(82, "BMS CONFIGURATION NEEDED"), - INSULATION_TEST(83, "INSULATION TEST"), - SELFTEST(84, "SELFTEST"), - EXTERNAL_CONTROL(85, "EXTERNAL CONTROL"), - ERROR_TEMPERATURESENSOR(86, "ERROR: TEMPERATURESENSOR"), - GRID_OPERATOR_CHARGE_PROHIBITED(87, "GRID OPERATOR: CHARGE PROHIBITED"), - GRID_OPERATOR_DISCHARGE_PROHIBITED(88, "GRID OPERATOR: DISCHARGE PROHIBITED"), - SPARE_CAPACITY(89, "SPARE CAPACITY"), - SELFTEST_ERROR(90, "SELFTEST ERROR"), - EARTH_FAULT(91, "EARTH FAULT"), - UNKNOWN(-1, "UNKNOWN"); - - private int code; - private String description; - - SenecBatteryStatus(int index, String description) { - this.code = index; - this.description = description; - } - - public int getCode() { - return code; - } - - public String getDescription() { - return description; - } - - public static SenecBatteryStatus fromCode(int code) { - for (SenecBatteryStatus state : SenecBatteryStatus.values()) { - if (state.code == code) { - return state; - } - } - return SenecBatteryStatus.UNKNOWN; - } - - public static String descriptionFromCode(int code) { - for (SenecBatteryStatus state : SenecBatteryStatus.values()) { - if (state.code == code) { - return state.description; - } - } - return SenecBatteryStatus.UNKNOWN.description; - } -} 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 afdd61a268..6f6e02fb76 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 class SenecHomeApi { * * 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 a06e62e3de..253678e035 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 e70a97abae..26d9239928 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.ExecutionException; 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.DecimalType; 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.openhab.core.types.Command; 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 @@ public class SenecHomeHandler extends BaseThingHandler { 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 @@ public class SenecHomeHandler extends BaseThingHandler { 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 @@ public class SenecHomeHandler extends BaseThingHandler { 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/SenecSystemStatus.java b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecSystemStatus.java new file mode 100644 index 0000000000..9426a06552 --- /dev/null +++ b/bundles/org.openhab.binding.senechome/src/main/java/org/openhab/binding/senechome/internal/SenecSystemStatus.java @@ -0,0 +1,151 @@ +/** + * 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; + +/** + * The {@link SenecSystemStatus} class defines available Senec specific + * system states. + * + * @author Steven Schwarznau - Initial contribution + * + */ +public enum SenecSystemStatus { + + INITIALSTATE(0, "INITIAL STATE"), + ERROR_INVERTER_COMMUNICATION(1, "ERROR INVERTER COMMUNICATION"), + ERROR_ELECTRICY_METER(2, "ERROR ELECTRICY METER"), + RIPPLE_CONTROL_RECEIVER(3, "RIPPLE CONTROL RECEIVER"), + INITIAL_CHARGE(4, "INITIAL CHARGE"), + MAINTENANCE_CHARGE(5, "MAINTENANCE CHARGE"), + MAINTENANCE_READY(6, "MAINTENANCE READY"), + MAINTENANCE_REQUIRED(7, "MAINTENANCE REQUIRED"), + MAN_SAFETY_CHARGE(8, "MAN. SAFETY CHARGE"), + SAFETY_CHARGE_READY(9, "SAFETY CHARGE READY"), + FULL_CHARGE(10, "FULL CHARGE"), + EQUALIZATION_CHARGE(11, "EQUALIZATION: CHARGE"), + DESULFATATION_CHARGE(12, "DESULFATATION: CHARGE"), + BATTERY_FULL(13, "BATTERY FULL"), + CHARGE(14, "CHARGE"), + BATTERY_EMPTY(15, "BATTERY EMPTY"), + DISCHARGE(16, "DISCHARGE"), + PV_AND_DISCHARGE(17, "PV + DISCHARGE"), + GRID_AND_DISCHARGE(18, "GRID + DISCHARGE"), + PASSIVE(19, "PASSIVE"), + OFF(20, "OFF"), + OWN_CONSUMPTION(21, "OWN CONSUMPTION"), + RESTART(22, "RESTART"), + MAN_EQUALIZATION_CHARGE(23, "MAN. EQUALIZATION: CHARGE"), + MAN_DESULFATATION_CHARGE(24, "MAN. DESULFATATION: CHARGE"), + SAFETY_CHARGE(25, "SAFETY CHARGE"), + BATTERY_PROTECTION_MODE(26, "BATTERY PROTECTION MODE"), + EG_ERROR(27, "EG ERROR"), + EG_CHARGE(28, "EG CHARGE"), + EG_DISCHARGE(29, "EG DISCHARGE"), + EG_PASSIVE(30, "EG PASSIVE"), + EG_PROHIBIT_CHARGE(31, "EG PROHIBIT CHARGE"), + EG_PROHIBIT_DISCHARGE(32, "EG PROHIBIT DISCHARGE"), + EMERGANCY_CHARGE(33, "EMERGANCY CHARGE"), + SOFTWARE_UPDATE(34, "SOFTWARE UPDATE"), + NSP_ERROR(35, "NSP ERROR"), + NSP_ERROR_GRID(36, "NSP ERROR: GRID"), + NSP_ERROR_HARDWRE(37, "NSP ERROR: HARDWRE"), + NO_SERVER_CONNECTION(38, "NO SERVER CONNECTION"), + BMS_ERROR(39, "BMS ERROR"), + MAINTENANCE_FILTER(40, "MAINTENANCE: FILTER"), + SLEEPING_MODE(41, "SLEEPING MODE"), + WAITING_EXCESS(42, "WAITING EXCESS"), + CAPACITY_TEST_CHARGE(43, "CAPACITY TEST: CHARGE"), + CAPACITY_TEST_DISCHARGE(44, "CAPACITY TEST: DISCHARGE"), + MAN_DESULFATATION_WAIT(45, "MAN. DESULFATATION: WAIT"), + MAN_DESULFATATION_READY(46, "MAN. DESULFATATION: READY"), + MAN_DESULFATATION_ERROR(47, "MAN. DESULFATATION: ERROR"), + EQUALIZATION_WAIT(48, "EQUALIZATION: WAIT"), + EMERGANCY_CHARGE_ERROR(49, "EMERGANCY CHARGE: ERROR"), + MAN_EQUALIZATION_WAIT(50, "MAN. EQUALIZATION: WAIT"), + MAN_EQUALIZATION_ERROR(51, "MAN. EQUALIZATION: ERROR"), + MAN_EQUALIZATION_READY(52, "MAN: EQUALIZATION: READY"), + AUTO_DESULFATATION_WAIT(53, "AUTO. DESULFATATION: WAIT"), + ABSORPTION_PHASE(54, "ABSORPTION PHASE"), + DCSWITCH_OFF(55, "DC-SWITCH OFF"), + PEAKSHAVING_WAIT(56, "PEAK-SHAVING: WAIT"), + ERROR_BATTERY_INVERTER(57, "ERROR BATTERY INVERTER"), + NPUERROR(58, "NPU-ERROR"), + BMS_OFFLINE(59, "BMS OFFLINE"), + MAINTENANCE_CHARGE_ERROR(60, "MAINTENANCE CHARGE ERROR"), + MAN_SAFETY_CHARGE_ERROR(61, "MAN. SAFETY CHARGE ERROR"), + SAFETY_CHARGE_ERROR(62, "SAFETY CHARGE ERROR"), + NO_CONNECTION_TO_MASTER(63, "NO CONNECTION TO MASTER"), + LITHIUM_SAFE_MODE_ACTIVE(64, "LITHIUM SAFE MODE ACTIVE"), + LITHIUM_SAFE_MODE_DONE(65, "LITHIUM SAFE MODE DONE"), + BATTERY_VOLTAGE_ERROR(66, "BATTERY VOLTAGE ERROR"), + BMS_DC_SWITCHED_OFF(67, "BMS DC SWITCHED OFF"), + GRID_INITIALIZATION(68, "GRID INITIALIZATION"), + GRID_STABILIZATION(69, "GRID STABILIZATION"), + REMOTE_SHUTDOWN(70, "REMOTE SHUTDOWN"), + OFFPEAKCHARGE(71, "OFFPEAK-CHARGE"), + ERROR_HALFBRIDGE(72, "ERROR HALFBRIDGE"), + BMS_ERROR_OPERATING_TEMPERATURE(73, "BMS: ERROR OPERATING TEMPERATURE"), + FACOTRY_SETTINGS_NOT_FOUND(74, "FACOTRY SETTINGS NOT FOUND"), + BACKUP_POWER_MODE_ACTIVE(75, "BACKUP POWER MODE - ACTIVE"), + BACKUP_POWER_MODE_BATTERY_EMPTY(76, "BACKUP POWER MODE - BATTERY EMPTY"), + BACKUP_POWER_MODE_ERROR(77, "BACKUP POWER MODE ERROR"), + INITIALISING(78, "INITIALISING"), + INSTALLATION_MODE(79, "INSTALLATION MODE"), + GRID_OFFLINE(80, "GRID OFFLINE"), + BMS_UPDATE_NEEDED(81, "BMS UPDATE NEEDED"), + BMS_CONFIGURATION_NEEDED(82, "BMS CONFIGURATION NEEDED"), + INSULATION_TEST(83, "INSULATION TEST"), + SELFTEST(84, "SELFTEST"), + EXTERNAL_CONTROL(85, "EXTERNAL CONTROL"), + ERROR_TEMPERATURESENSOR(86, "ERROR: TEMPERATURESENSOR"), + GRID_OPERATOR_CHARGE_PROHIBITED(87, "GRID OPERATOR: CHARGE PROHIBITED"), + GRID_OPERATOR_DISCHARGE_PROHIBITED(88, "GRID OPERATOR: DISCHARGE PROHIBITED"), + SPARE_CAPACITY(89, "SPARE CAPACITY"), + SELFTEST_ERROR(90, "SELFTEST ERROR"), + EARTH_FAULT(91, "EARTH FAULT"), + UNKNOWN(-1, "UNKNOWN"); + + private int code; + private String description; + + SenecSystemStatus(int index, String description) { + this.code = index; + this.description = description; + } + + public int getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public static SenecSystemStatus fromCode(int code) { + for (SenecSystemStatus state : SenecSystemStatus.values()) { + if (state.code == code) { + return state; + } + } + return SenecSystemStatus.UNKNOWN; + } + + public static String descriptionFromCode(int code) { + for (SenecSystemStatus state : SenecSystemStatus.values()) { + if (state.code == code) { + return state.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 0000000000..495c950cf8 --- /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 0000000000..cf209e2e22 --- /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 a1937c7c10..82471eacfb 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 @@ import com.google.gson.annotations.SerializedName; * 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 1c9794a412..1464c10741 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 java.io.Serializable; 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 832a331d06..0000000000 --- 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 0000000000..152b350793 --- /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 e59721a8cc..8d5b306af5 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 @@ import com.google.gson.annotations.SerializedName; */ 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 4d10f3fa14..8c450361dc 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 0000000000..ba41e1a312 --- /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 0000000000..a117e8b2b1 --- /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 e05d8d2301..4e9e51b594 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 0000000000..d338f5e993 --- /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)); + } +}