From c5b6ce97bb35db5b70d8bc6df065d198c8c84a1e Mon Sep 17 00:00:00 2001 From: jimtng <2554958+jimtng@users.noreply.github.com> Date: Mon, 28 Mar 2022 03:10:24 +1000 Subject: [PATCH] [fronius] Add Autonomy and Self Consumption channels (#12420) * Add Autonomy and Self Consumption channels * Change Inverter channels from DecimalType to QuantityType * Convert ValueUnit to QuantityType, honour the unit from ValueUnit * Replace custom unit converter with QuantityType's unit conversion * Return QuantityType of zero for missing ValueUnit data, to be consistent with P_AC. * Return UnDefType.NULL for other missing data * De-duplicate URL parsing routine Signed-off-by: Jimmy Tanagra --- bundles/org.openhab.binding.fronius/README.md | 62 ++++----- .../internal/FroniusBindingConstants.java | 56 +++++--- .../fronius/internal/api/ValueUnit.java | 27 +++- .../handler/FroniusBaseThingHandler.java | 38 ++---- .../internal/handler/FroniusMeterHandler.java | 18 +-- .../handler/FroniusOhmpilotHandler.java | 7 +- .../handler/FroniusSymoInverterHandler.java | 124 ++++++++---------- .../internal/math/KilowattConverter.java | 33 ----- .../internal/math/SiPrefixFactors.java | 62 --------- .../resources/OH-INF/i18n/fronius.properties | 4 + .../resources/OH-INF/thing/thing-types.xml | 33 +++-- 11 files changed, 198 insertions(+), 266 deletions(-) delete mode 100644 bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/KilowattConverter.java delete mode 100644 bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/SiPrefixFactors.java diff --git a/bundles/org.openhab.binding.fronius/README.md b/bundles/org.openhab.binding.fronius/README.md index 777d73c44..ebe794dde 100644 --- a/bundles/org.openhab.binding.fronius/README.md +++ b/bundles/org.openhab.binding.fronius/README.md @@ -57,25 +57,27 @@ The binding has no configuration options, all configuration is done at `bridge`, ### Channels for `powerinverter` Thing -| Channel ID | Item Type | Description | -| ------------------------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------- | -| `inverterdatachanneldayenergy` | Number | Energy generated on current day | -| `inverterdatachannelpac` | Number | AC power | -| `inverterdatachanneltotal` | Number | Energy generated overall | -| `inverterdatachannelyear` | Number | Energy generated in current year | -| `inverterdatachannelfac` | Number | AC frequency | -| `inverterdatachanneliac` | Number | AC current | -| `inverterdatachannelidc` | Number | DC current | -| `inverterdatachanneluac` | Number | AC voltage | -| `inverterdatachanneludc` | Number | DC voltage | -| `inverterdatadevicestatuserrorcode` | Number | Device error code | -| `inverterdatadevicestatusstatuscode` | Number | Device status code
`0` - `6` Startup
`7` Running
`8` Standby
`9` Bootloading
`10` Error | -| `powerflowchannelpgrid` | Number:Power | Power (+ from grid, - to grid) | -| `powerflowchannelpload` | Number:Power | Power (+ generator, - consumer) | -| `powerflowchannelpakku` | Number:Power | Power (+ charge, - discharge) | -| `powerflowchannelppv` | Number:Power | Power (+ production) | -| `powerflowinverter1power` | Number:Power | Current power of inverter 1, null if not running (+ produce/export, - consume/import) | -| `powerflowinverter1soc` | Number:Dimensionless | Current state of charge of inverter 1 in percent | +| Channel ID | Item Type | Description | +| ------------------------------------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------- | +| `inverterdatachannelpac` | Number:Power | Power generated | +| `inverterdatachannelfac` | Number:Frequency | AC frequency | +| `inverterdatachanneliac` | Number:ElectricCurrent | AC current | +| `inverterdatachannelidc` | Number:ElectricCurrent | DC current | +| `inverterdatachanneluac` | Number:ElectricPotential | AC voltage | +| `inverterdatachanneludc` | Number:ElectricPotential | DC voltage | +| `inverterdatachanneldayenergy` | Number:Energy | Energy generated on current day | +| `inverterdatachannelyear` | Number:Energy | Energy generated in current year | +| `inverterdatachanneltotal` | Number:Energy | Energy generated overall | +| `inverterdatadevicestatuserrorcode` | Number | Device error code | +| `inverterdatadevicestatusstatuscode` | Number | Device status code
`0` - `6` Startup
`7` Running
`8` Standby
`9` Bootloading
`10` Error | +| `powerflowchannelpgrid` | Number:Power | Grid Power (+ from grid, - to grid) | +| `powerflowchannelpload` | Number:Power | Load Power (+ generator, - consumer) | +| `powerflowchannelpakku` | Number:Power | Battery Power (+ charge, - discharge) | +| `powerflowchannelppv` | Number:Power | Solar Power (+ production) | +| `powerflowautonomy` | Number:Dimensionless | The current relative autonomy in % | +| `powerflowselfconsumption` | Number:Dimensionless | The current relative self consumption in % | +| `powerflowinverter1power` | Number:Power | Current power of inverter 1, null if not running (+ produce/export, - consume/import) | +| `powerflowinverter1soc` | Number:Dimensionless | Current state of charge of inverter 1 in percent | ### Channels for `meter` Thing @@ -98,7 +100,6 @@ The binding has no configuration options, all configuration is done at `bridge`, | `powerfactorphase3` | Number | Power Factor on Phase 3 | | `energyrealsumconsumed` | Number:Energy | Real Energy consumed | | `energyrealsumproduced` | Number:Energy | Real Energy produced | -| | ### Channels for `ohmpilot` Thing @@ -110,7 +111,6 @@ The binding has no configuration options, all configuration is done at `bridge`, | `temperaturechannel1` | Number:Temperature | Temperature | | `errorcode` | Number | Device error code | | `statecode` | Number | Device state code
`0` up and running
`1` keep minimum temperature
`2` legionella protection
`3` critical fault
`4` fault
`5` boost mode | -| | ## Properties @@ -144,21 +144,23 @@ Bridge fronius:bridge:mybridge [hostname="192.168.66.148", refreshInterval=5] { demo.items: ``` -Number AC_Power { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelpac" } -Number Day_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneldayenergy" } -Number Total_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneltotal" } -Number Year_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelyear" } -Number FAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelfac" } -Number IAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneliac" } -Number IDC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelidc" } -Number UAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneluac" } -Number UDC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneludc" } +Number:Power AC_Power { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelpac" } +Number:Energy Day_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneldayenergy" } +Number:Energy Total_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneltotal" } +Number:Energy Year_Energy { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelyear" } +Number:Frequency FAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelfac" } +Number:ElectricCurrent IAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneliac" } +Number:ElectricCurrent IDC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachannelidc" } +Number:ElectricPotential UAC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneluac" } +Number:ElectricPotential UDC { channel="fronius:powerinverter:mybridge:myinverter:inverterdatachanneludc" } Number ErrorCode { channel="fronius:powerinverter:mybridge:myinverter:inverterdatadevicestatuserrorcode" } Number StatusCode { channel="fronius:powerinverter:mybridge:myinverter:inverterdatadevicestatusstatuscode" } Number:Power Grid_Power { channel="fronius:powerinverter:mybridge:myinverter:powerflowchannelpgrid" } Number:Power Load_Power { channel="fronius:powerinverter:mybridge:myinverter:powerflowchannelpload" } Number:Power Battery_Power { channel="fronius:powerinverter:mybridge:myinverter:powerflowchannelpakku" } Number:Power Production_Power { channel="fronius:powerinverter:mybridge:myinverter:powerflowchannelppv" } +Number:Dimensionless Power_Autonomy { channel="fronius:powerinverter:mybridge:myinverter:powerflowautonomy" } +Number:Dimensionless Power_SelfConsumption { channel="fronius:powerinverter:mybridge:myinverter:powerflowselfconsumption" } Number:Power Inverter1_Power { channel="fronius:powerinverter:mybridge:myinverter:powerflowinverter1power" } Number:Dimensionless Inverter1_SOC { channel="fronius:powerinverter:mybridge:myinverter:powerflowinverter1soc" } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/FroniusBindingConstants.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/FroniusBindingConstants.java index 2067ec250..02e260d88 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/FroniusBindingConstants.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/FroniusBindingConstants.java @@ -23,6 +23,7 @@ import org.openhab.core.thing.ThingTypeUID; * @author Peter Schraffl - Added device status and error status channels * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield * @author Hannes Spenger - Added ohmpilot & meter power sum + * @author Jimmy Tanagra - Implement a common url parsing method */ @NonNullByDefault public class FroniusBindingConstants { @@ -30,12 +31,12 @@ public class FroniusBindingConstants { private static final String BINDING_ID = "fronius"; // List of all Thing Type UIDs - public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "powerinverter"); public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); + public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "powerinverter"); public static final ThingTypeUID THING_TYPE_METER = new ThingTypeUID(BINDING_ID, "meter"); public static final ThingTypeUID THING_TYPE_OHMPILOT = new ThingTypeUID(BINDING_ID, "ohmpilot"); - // List of all Channel ids + // Inverter channels public static final String INVERTER_DATA_CHANNEL_DAY_ENERGY = "inverterdatachanneldayenergy"; public static final String INVERTER_DATA_CHANNEL_PAC = "inverterdatachannelpac"; public static final String INVERTER_DATA_CHANNEL_TOTAL = "inverterdatachanneltotal"; @@ -47,10 +48,19 @@ public class FroniusBindingConstants { public static final String INVERTER_DATA_CHANNEL_UDC = "inverterdatachanneludc"; public static final String INVERTER_DATA_CHANNEL_DEVICE_STATUS_ERROR_CODE = "inverterdatadevicestatuserrorcode"; public static final String INVERTER_DATA_CHANNEL_DEVICE_STATUS_STATUS_CODE = "inverterdatadevicestatusstatuscode"; + + // Power Flow channels public static final String POWER_FLOW_P_GRID = "powerflowchannelpgrid"; public static final String POWER_FLOW_P_LOAD = "powerflowchannelpload"; public static final String POWER_FLOW_P_AKKU = "powerflowchannelpakku"; public static final String POWER_FLOW_P_PV = "powerflowchannelppv"; + public static final String POWER_FLOW_AUTONOMY = "powerflowautonomy"; + public static final String POWER_FLOW_SELF_CONSUMPTION = "powerflowselfconsumption"; + + public static final String POWER_FLOW_INVERTER_1_POWER = "powerflowinverter1power"; + public static final String POWER_FLOW_INVERTER_1_SOC = "powerflowinverter1soc"; + + // Meter channels public static final String METER_ENABLE = "enable"; public static final String METER_LOCATION = "location"; public static final String METER_CURRENT_AC_PHASE_1 = "currentacphase1"; @@ -68,29 +78,41 @@ public class FroniusBindingConstants { public static final String METER_POWER_FACTOR_PHASE_3 = "powerfactorphase3"; public static final String METER_ENERGY_REAL_SUM_CONSUMED = "energyrealsumconsumed"; public static final String METER_ENERGY_REAL_SUM_PRODUCED = "energyrealsumproduced"; + + // OhmPilot channels public static final String OHMPILOT_POWER_REAL_SUM = "powerrealsum"; public static final String OHMPILOT_ENERGY_REAL_SUM_CONSUMED = "energyrealsumconsumed"; public static final String OHMPILOT_ENERGY_SENSOR_TEMPERATURE_CHANNEL_1 = "temperaturechannel1"; public static final String OHMPILOT_ERROR_CODE = "errorcode"; public static final String OHMPILOT_STATE_CODE = "statecode"; - /* - * part of POWERFLOW_REALTIME_DATA using Symo Gen24 - * "Inverters" : { - * "1" : { - * "Battery_Mode" : "normal", - * "DT" : 1, - * "P" : 356, - * "SOC" : 95.199996948242188 - * } - * }, - */ - public static final String POWER_FLOW_INVERTER_1_POWER = "powerflowinverter1power"; - public static final String POWER_FLOW_INVERTER_1_SOC = "powerflowinverter1soc"; - // List of all Urls public static final String INVERTER_REALTIME_DATA_URL = "http://%IP%/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceId=%DEVICEID%&DataCollection=CommonInverterData"; - public static final String POWERFLOW_REALTIME_DATA = "http://%IP%/solar_api/v1/GetPowerFlowRealtimeData.fcgi"; + public static final String POWERFLOW_REALTIME_DATA_URL = "http://%IP%/solar_api/v1/GetPowerFlowRealtimeData.fcgi"; public static final String METER_REALTIME_DATA_URL = "http://%IP%/solar_api/v1/GetMeterRealtimeData.cgi?Scope=Device&DeviceId=%DEVICEID%&DataCollection=MeterRealtimeData"; public static final String OHMPILOT_REALTIME_DATA_URL = "http://%IP%/solar_api/v1/GetOhmPilotRealtimeData.cgi?Scope=Device&DeviceId=%DEVICEID%"; + + public static String getInverterDataUrl(String ip, int deviceId) { + return parseUrl(INVERTER_REALTIME_DATA_URL, ip, deviceId); + } + + public static String getPowerFlowDataUrl(String ip) { + return parseUrl(POWERFLOW_REALTIME_DATA_URL, ip); + } + + public static String getMeterDataUrl(String ip, int deviceId) { + return parseUrl(METER_REALTIME_DATA_URL, ip, deviceId); + } + + public static String getOhmPilotDataUrl(String ip, int deviceId) { + return parseUrl(OHMPILOT_REALTIME_DATA_URL, ip, deviceId); + } + + public static String parseUrl(String url, String ip) { + return url.replace("%IP%", ip == null ? "" : ip.trim()); + } + + public static String parseUrl(String url, String ip, int deviceId) { + return parseUrl(url, ip).replace("%DEVICEID%", Integer.toString(deviceId)); + } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/ValueUnit.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/ValueUnit.java index 3820fd8c9..79cd33214 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/ValueUnit.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/api/ValueUnit.java @@ -12,7 +12,12 @@ */ package org.openhab.binding.fronius.internal.api; -import org.openhab.binding.fronius.internal.math.KilowattConverter; +import javax.measure.Unit; + +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.util.UnitUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.annotations.SerializedName; @@ -21,12 +26,14 @@ import com.google.gson.annotations.SerializedName; * a value * * @author Thomas Rokohl - Initial contribution + * @author Jimmy Tanagra - Add conversion to QuantityType */ public class ValueUnit { + @SerializedName("Value") private double value; @SerializedName("Unit") - private String unit; + private String unit = ""; public double getValue() { return value; @@ -37,14 +44,20 @@ public class ValueUnit { } public String getUnit() { - if (unit == null) { - unit = ""; - } - return unit; + return this.unit == null ? "" : this.unit; } public void setUnit(String unit) { - this.setValue(KilowattConverter.convertTo(this.getValue(), this.getUnit(), unit)); this.unit = unit; } + + public QuantityType asQuantityType() { + Unit unit = UnitUtils.parseUnit(getUnit()); + if (unit == null) { + final Logger logger = LoggerFactory.getLogger(ValueUnit.class); + logger.debug("The unit for ValueUnit ({})/({}) cannot be parsed", value, this.unit); + unit = QuantityType.ONE.getUnit(); + } + return new QuantityType<>(value, unit); + } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBaseThingHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBaseThingHandler.java index f0caee8cf..5ab345761 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBaseThingHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusBaseThingHandler.java @@ -18,10 +18,6 @@ import org.openhab.binding.fronius.internal.FroniusCommunicationException; import org.openhab.binding.fronius.internal.FroniusHttpUtil; import org.openhab.binding.fronius.internal.api.BaseFroniusResponse; import org.openhab.binding.fronius.internal.api.HeadStatus; -import org.openhab.binding.fronius.internal.api.ValueUnit; -import org.openhab.core.library.types.DecimalType; -import org.openhab.core.library.types.QuantityType; -import org.openhab.core.library.types.StringType; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -32,6 +28,7 @@ import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +42,8 @@ import com.google.gson.JsonSyntaxException; * @author Thomas Rokohl - Refactoring to merge the concepts * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield * @author Jimmy Tanagra - Implement connection retry + * Convert ValueUnit to QuantityType + * Support NULL value */ public abstract class FroniusBaseThingHandler extends BaseThingHandler { @@ -100,31 +99,16 @@ public abstract class FroniusBaseThingHandler extends BaseThingHandler { return; } - Object value = getValue(channelId); - if (value == null) { - logger.debug("Value retrieved for channel '{}' was null. Can't update.", channelId); - return; + State state = getValue(channelId); + if (state == null) { + state = UnDefType.NULL; } - State state = null; - if (value instanceof QuantityType) { - state = (QuantityType) value; - } else if (value instanceof Number) { - state = new DecimalType((Number) value); - } else if (value instanceof ValueUnit) { - state = new DecimalType(((ValueUnit) value).getValue()); - } else if (value instanceof String) { - state = new StringType((String) value); - } else { - logger.warn("Update channel {}: Unsupported value type {}", channelId, value.getClass().getSimpleName()); - } - logger.trace("Update channel {} with state {} ({})", channelId, (state == null) ? "null" : state.toString(), - value.getClass().getSimpleName()); - - // Update the channel - if (state != null) { - updateState(channelId, state); + if (logger.isTraceEnabled()) { + logger.trace("Update channel {} with state {} ({})", channelId, state.toString(), + state.getClass().getSimpleName()); } + updateState(channelId, state); } /** @@ -140,7 +124,7 @@ public abstract class FroniusBaseThingHandler extends BaseThingHandler { * @param channelId the id identifying the channel * @return the "new" associated value */ - protected abstract Object getValue(String channelId); + protected abstract State getValue(String channelId); /** * Called by the bridge to fetch data and update channels diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java index 5d3910275..2b62ef9f7 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusMeterHandler.java @@ -20,9 +20,11 @@ import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration; import org.openhab.binding.fronius.internal.FroniusCommunicationException; import org.openhab.binding.fronius.internal.api.MeterRealtimeBodyDataDTO; import org.openhab.binding.fronius.internal.api.MeterRealtimeResponseDTO; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; /** * The {@link FroniusMeterHandler} is responsible for updating the data, which are @@ -66,7 +68,7 @@ public class FroniusMeterHandler extends FroniusBaseThingHandler { * @return the last retrieved data */ @Override - protected Object getValue(String channelId) { + protected State getValue(String channelId) { if (meterRealtimeBodyData == null) { return null; } @@ -79,9 +81,9 @@ public class FroniusMeterHandler extends FroniusBaseThingHandler { switch (fieldName) { case FroniusBindingConstants.METER_ENABLE: - return meterRealtimeBodyData.getEnable(); + return new DecimalType(meterRealtimeBodyData.getEnable()); case FroniusBindingConstants.METER_LOCATION: - return meterRealtimeBodyData.getMeterLocationCurrent(); + return new DecimalType(meterRealtimeBodyData.getMeterLocationCurrent()); case FroniusBindingConstants.METER_CURRENT_AC_PHASE_1: return new QuantityType<>(meterRealtimeBodyData.getCurrentACPhase1(), Units.AMPERE); case FroniusBindingConstants.METER_CURRENT_AC_PHASE_2: @@ -103,11 +105,11 @@ public class FroniusMeterHandler extends FroniusBaseThingHandler { case FroniusBindingConstants.METER_POWER_SUM: return new QuantityType<>(meterRealtimeBodyData.getPowerRealPSum(), Units.WATT); case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_1: - return meterRealtimeBodyData.getPowerFactorPhase1(); + return new DecimalType(meterRealtimeBodyData.getPowerFactorPhase1()); case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_2: - return meterRealtimeBodyData.getPowerFactorPhase2(); + return new DecimalType(meterRealtimeBodyData.getPowerFactorPhase2()); case FroniusBindingConstants.METER_POWER_FACTOR_PHASE_3: - return meterRealtimeBodyData.getPowerFactorPhase3(); + return new DecimalType(meterRealtimeBodyData.getPowerFactorPhase3()); case FroniusBindingConstants.METER_ENERGY_REAL_SUM_CONSUMED: return new QuantityType<>(meterRealtimeBodyData.getEnergyRealWACSumConsumed(), Units.WATT_HOUR); case FroniusBindingConstants.METER_ENERGY_REAL_SUM_PRODUCED: @@ -151,9 +153,7 @@ public class FroniusMeterHandler extends FroniusBaseThingHandler { */ private MeterRealtimeResponseDTO getMeterRealtimeData(String ip, int deviceId) throws FroniusCommunicationException { - String location = FroniusBindingConstants.METER_REALTIME_DATA_URL.replace("%IP%", - (ip != null ? ip.trim() : "")); - location = location.replace("%DEVICEID%", Integer.toString(deviceId)); + String location = FroniusBindingConstants.getMeterDataUrl(ip, deviceId); return collectDataFromUrl(MeterRealtimeResponseDTO.class, location); } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusOhmpilotHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusOhmpilotHandler.java index a61917a96..11dfd1dd7 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusOhmpilotHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusOhmpilotHandler.java @@ -24,6 +24,7 @@ import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; /** * The {@link FroniusOhmpilotHandler} is responsible for updating the data, which are @@ -66,7 +67,7 @@ public class FroniusOhmpilotHandler extends FroniusBaseThingHandler { * @return the last retrieved data */ @Override - protected Object getValue(String channelId) { + protected State getValue(String channelId) { if (ohmpilotRealtimeBodyData == null) { return null; } @@ -128,9 +129,7 @@ public class FroniusOhmpilotHandler extends FroniusBaseThingHandler { */ private OhmpilotRealtimeResponseDTO getOhmpilotRealtimeData(String ip, int deviceId) throws FroniusCommunicationException { - String location = FroniusBindingConstants.OHMPILOT_REALTIME_DATA_URL.replace("%IP%", - (ip != null ? ip.trim() : "")); - location = location.replace("%DEVICEID%", Integer.toString(deviceId)); + String location = FroniusBindingConstants.getOhmPilotDataUrl(ip, deviceId); return collectDataFromUrl(OhmpilotRealtimeResponseDTO.class, location); } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java index d5ea05953..1cada21e3 100644 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java +++ b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/handler/FroniusSymoInverterHandler.java @@ -12,19 +12,25 @@ */ package org.openhab.binding.fronius.internal.handler; -import java.util.Map; +import java.util.Optional; + +import javax.measure.Unit; import org.openhab.binding.fronius.internal.FroniusBaseDeviceConfiguration; import org.openhab.binding.fronius.internal.FroniusBindingConstants; import org.openhab.binding.fronius.internal.FroniusBridgeConfiguration; import org.openhab.binding.fronius.internal.FroniusCommunicationException; +import org.openhab.binding.fronius.internal.api.InverterRealtimeBodyData; import org.openhab.binding.fronius.internal.api.InverterRealtimeResponse; import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeInverter; import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeResponse; +import org.openhab.binding.fronius.internal.api.PowerFlowRealtimeSite; import org.openhab.binding.fronius.internal.api.ValueUnit; +import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,14 +41,10 @@ import org.slf4j.LoggerFactory; * @author Thomas Rokohl - Initial contribution * @author Peter Schraffl - Added device status and error status channels * @author Thomas Kordelle - Added inverter power, battery state of charge and PV solar yield + * @author Jimmy Tanagra - Add powerflow autonomy, self consumption channels */ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { - /* power produced/handled by the inverter. */ - public static final String INVERTER_POWER = "power"; - /* state of charge of the battery or other storage device */ - public static final String INVERTER_SOC = "soc"; - private final Logger logger = LoggerFactory.getLogger(FroniusSymoInverterHandler.class); private InverterRealtimeResponse inverterRealtimeResponse; private PowerFlowRealtimeResponse powerFlowResponse; @@ -76,7 +78,7 @@ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { * @return the last retrieved data */ @Override - protected Object getValue(String channelId) { + protected State getValue(String channelId) { final String[] fields = channelId.split("#"); if (fields.length < 1) { return null; @@ -84,65 +86,57 @@ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { final String fieldName = fields[0]; if (inverterRealtimeResponse != null) { + InverterRealtimeBodyData inverterData = inverterRealtimeResponse.getBody().getData(); switch (fieldName) { - case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DAY_ENERGY: - ValueUnit day = inverterRealtimeResponse.getBody().getData().getDayEnergy(); - if (day != null) { - day.setUnit("kWh"); - } - return day; case FroniusBindingConstants.INVERTER_DATA_CHANNEL_PAC: - ValueUnit pac = inverterRealtimeResponse.getBody().getData().getPac(); - if (pac == null) { - pac = new ValueUnit(); - pac.setValue(0); - } - return pac; - case FroniusBindingConstants.INVERTER_DATA_CHANNEL_TOTAL: - ValueUnit total = inverterRealtimeResponse.getBody().getData().getTotalEnergy(); - if (total != null) { - total.setUnit("MWh"); - } - return total; - case FroniusBindingConstants.INVERTER_DATA_CHANNEL_YEAR: - ValueUnit year = inverterRealtimeResponse.getBody().getData().getYearEnergy(); - if (year != null) { - year.setUnit("MWh"); - } - return year; + return getQuantityOrZero(inverterData.getPac(), Units.WATT); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_FAC: - return inverterRealtimeResponse.getBody().getData().getFac(); + return getQuantityOrZero(inverterData.getFac(), Units.HERTZ); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IAC: - return inverterRealtimeResponse.getBody().getData().getIac(); + return getQuantityOrZero(inverterData.getIac(), Units.AMPERE); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_IDC: - return inverterRealtimeResponse.getBody().getData().getIdc(); + return getQuantityOrZero(inverterData.getIdc(), Units.AMPERE); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UAC: - return inverterRealtimeResponse.getBody().getData().getUac(); + return getQuantityOrZero(inverterData.getUac(), Units.VOLT); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_UDC: - return inverterRealtimeResponse.getBody().getData().getUdc(); + return getQuantityOrZero(inverterData.getUdc(), Units.VOLT); + case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DAY_ENERGY: + // Convert the unit to kWh for backwards compatibility with non-quantity type + return getQuantityOrZero(inverterData.getDayEnergy(), Units.KILOWATT_HOUR).toUnit("kWh"); + case FroniusBindingConstants.INVERTER_DATA_CHANNEL_TOTAL: + // Convert the unit to MWh for backwards compatibility with non-quantity type + return getQuantityOrZero(inverterData.getTotalEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh"); + case FroniusBindingConstants.INVERTER_DATA_CHANNEL_YEAR: + // Convert the unit to MWh for backwards compatibility with non-quantity type + return getQuantityOrZero(inverterData.getYearEnergy(), Units.MEGAWATT_HOUR).toUnit("MWh"); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_ERROR_CODE: - return inverterRealtimeResponse.getBody().getData().getDeviceStatus().getErrorCode(); + return new DecimalType(inverterData.getDeviceStatus().getErrorCode()); case FroniusBindingConstants.INVERTER_DATA_CHANNEL_DEVICE_STATUS_STATUS_CODE: - return inverterRealtimeResponse.getBody().getData().getDeviceStatus().getStatusCode(); + return new DecimalType(inverterData.getDeviceStatus().getStatusCode()); default: break; } } if (powerFlowResponse != null) { + PowerFlowRealtimeSite site = powerFlowResponse.getBody().getData().getSite(); switch (fieldName) { case FroniusBindingConstants.POWER_FLOW_P_GRID: - return new QuantityType<>(powerFlowResponse.getBody().getData().getSite().getPgrid(), Units.WATT); + return new QuantityType<>(site.getPgrid(), Units.WATT); case FroniusBindingConstants.POWER_FLOW_P_LOAD: - return new QuantityType<>(powerFlowResponse.getBody().getData().getSite().getPload(), Units.WATT); + return new QuantityType<>(site.getPload(), Units.WATT); case FroniusBindingConstants.POWER_FLOW_P_AKKU: - return new QuantityType<>(powerFlowResponse.getBody().getData().getSite().getPakku(), Units.WATT); + return new QuantityType<>(site.getPakku(), Units.WATT); case FroniusBindingConstants.POWER_FLOW_P_PV: - return new QuantityType<>(powerFlowResponse.getBody().getData().getSite().getPpv(), Units.WATT); + return new QuantityType<>(site.getPpv(), Units.WATT); + case FroniusBindingConstants.POWER_FLOW_AUTONOMY: + return new QuantityType<>(site.getRelAutonomy(), Units.PERCENT); + case FroniusBindingConstants.POWER_FLOW_SELF_CONSUMPTION: + return new QuantityType<>(site.getRelSelfConsumption(), Units.PERCENT); case FroniusBindingConstants.POWER_FLOW_INVERTER_1_POWER: - return getInverterFlowValue(INVERTER_POWER, "1"); + return new QuantityType<>(getInverter("1").getP(), Units.WATT); case FroniusBindingConstants.POWER_FLOW_INVERTER_1_SOC: - return getInverterFlowValue(INVERTER_SOC, "1"); + return new QuantityType<>(getInverter("1").getSoc(), Units.PERCENT); default: break; } @@ -154,25 +148,24 @@ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { /** * get flow data for a specific inverter. * - * @param fieldName - * @param number - * @return + * @param number The inverter object of the given index + * @return an PowerFlowRealtimeInverter object. */ - private Object getInverterFlowValue(final String fieldName, final String number) { - final Map inverters = powerFlowResponse.getBody().getData().getInverters(); - if ((inverters == null) || (inverters.get(number) == null)) { - logger.debug("No data for inverter '{}' found.", number); - return null; - } - switch (fieldName) { - case INVERTER_POWER: - return new QuantityType<>(inverters.get(number).getP(), Units.WATT); - case INVERTER_SOC: - return new QuantityType<>(inverters.get(number).getSoc(), Units.PERCENT); - default: - break; - } - return null; + private PowerFlowRealtimeInverter getInverter(final String number) { + return powerFlowResponse.getBody().getData().getInverters().get(number); + } + + /** + * Return the value as QuantityType with the unit extracted from ValueUnit + * or a zero QuantityType with the given unit argument when value is null + * + * @param value The ValueUnit data + * @param unit The default unit to use when value is null + * @return a QuantityType from the given value + */ + private QuantityType getQuantityOrZero(ValueUnit value, Unit unit) { + return Optional.ofNullable(value).map(val -> val.asQuantityType().toUnit(unit)) + .orElse(new QuantityType<>(0, unit)); } /** @@ -191,8 +184,7 @@ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { * @return {PowerFlowRealtimeResponse} the object representation of the json response */ private PowerFlowRealtimeResponse getPowerFlowRealtime(String ip) throws FroniusCommunicationException { - String location = FroniusBindingConstants.POWERFLOW_REALTIME_DATA.replace("%IP%", - (ip != null ? ip.trim() : "")); + String location = FroniusBindingConstants.getPowerFlowDataUrl(ip); return collectDataFromUrl(PowerFlowRealtimeResponse.class, location); } @@ -204,9 +196,7 @@ public class FroniusSymoInverterHandler extends FroniusBaseThingHandler { * @return {InverterRealtimeResponse} the object representation of the json response */ private InverterRealtimeResponse getRealtimeData(String ip, int deviceId) throws FroniusCommunicationException { - String location = FroniusBindingConstants.INVERTER_REALTIME_DATA_URL.replace("%IP%", - (ip != null ? ip.trim() : "")); - location = location.replace("%DEVICEID%", Integer.toString(deviceId)); + String location = FroniusBindingConstants.getInverterDataUrl(ip, deviceId); return collectDataFromUrl(InverterRealtimeResponse.class, location); } } diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/KilowattConverter.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/KilowattConverter.java deleted file mode 100644 index e1d0a0685..000000000 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/KilowattConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.fronius.internal.math; - -/** - * Helper class for unit conversions - * - * @author Thomas Rokohl - Initial contribution - * - */ -public class KilowattConverter { - - public static double getConvertFactor(String fromUnit, String toUnit) { - String adjustedFromUnit = fromUnit.replace("Wh", ""); - String adjustedtoUnit = toUnit.replace("Wh", ""); - return SiPrefixFactors.getFactorToBaseUnit(adjustedFromUnit) * 1 - / SiPrefixFactors.getFactorToBaseUnit(adjustedtoUnit); - } - - public static double convertTo(double value, String fromUnit, String toUnit) { - return value * getConvertFactor(fromUnit, toUnit); - } -} diff --git a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/SiPrefixFactors.java b/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/SiPrefixFactors.java deleted file mode 100644 index ef37ec9f1..000000000 --- a/bundles/org.openhab.binding.fronius/src/main/java/org/openhab/binding/fronius/internal/math/SiPrefixFactors.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.fronius.internal.math; - -/** - * Helper class for unit conversions - * - * @author Thomas Rokohl - Initial contribution - * - */ -public class SiPrefixFactors { - - /** - * return the relative factor to the base unit - * k == 1000, M = 1000000 ... - * Not completely!!! Rank from n to T - * - * @param prefix of the unit - * @return relative factor to the base unit - */ - - public static double getFactorToBaseUnit(String prefix) { - if (prefix.isEmpty()) { - return 1; - } - switch (prefix) { - case "T": - return 1000000000000d; - case "G": - return 1000000000; - case "M": - return 1000000; - case "k": - return 1000; - case "h": - return 100; - case "da": - return 10; - case "d": - return 0.1; - case "c": - return 0.01; - case "m": - return 0.001; - case "ยต": - return 0.000001; - case "n": - return 0.000000001; - } - return 1; - } -} diff --git a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties index a01821504..c510a1532 100644 --- a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties +++ b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/i18n/fronius.properties @@ -90,6 +90,10 @@ channel-type.fronius.pPv.label = Solar Plant Power channel-type.fronius.pPv.description = Current Solar Plant Power channel-type.fronius.pac.label = AC Power channel-type.fronius.pac.description = AC power +channel-type.fronius.powerflow_rel_autonomy.label = Autonomy +channel-type.fronius.powerflow_rel_autonomy.description = The current relative autonomy in %, NULL if no smart meter is connected +channel-type.fronius.powerflow_rel_selfconsumption.label = Self Consumption +channel-type.fronius.powerflow_rel_selfconsumption.description = The current relative self consumption in %, NULL if no smart meter is connected channel-type.fronius.total_energy.label = Total Energy channel-type.fronius.total_energy.description = Energy generated overall channel-type.fronius.uac.label = AC Voltage diff --git a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/thing/thing-types.xml index f9b1c2e85..0c6ffa97f 100644 --- a/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.fronius/src/main/resources/OH-INF/thing/thing-types.xml @@ -34,6 +34,8 @@ + + @@ -144,58 +146,58 @@ - Number + Number:Energy Energy generated on current day - Number + Number:Power AC power - Number + Number:Energy Energy generated overall - Number + Number:Energy Energy generated in current year - Number + Number:Frequency AC frequency - Number + Number:ElectricCurrent AC current - Number + Number:ElectricCurrent DC current - Number + Number:ElectricPotential AC voltage - Number + Number:ElectricPotential DC voltage @@ -238,6 +240,18 @@ Current Solar Plant Power + + Number:Dimensionless + + The current relative autonomy in %, NULL if no smart meter is connected + + + + Number:Dimensionless + + The current relative self consumption in %, NULL if no smart meter is connected + + Number:Power @@ -310,5 +324,4 @@ -