diff --git a/bundles/org.openhab.binding.growatt/README.md b/bundles/org.openhab.binding.growatt/README.md index 328a2b05097b0..2ff4e17136d88 100644 --- a/bundles/org.openhab.binding.growatt/README.md +++ b/bundles/org.openhab.binding.growatt/README.md @@ -43,96 +43,105 @@ All channels are read-only. Depending on the inverter model, and its configuration, not all of the channels will be present. The list of all possible channels is as follows: -| Channel | Type | Description | Advanced | -|-------------------------------|---------------------------|------------------------------------------------------|----------| -| system-status | Number:Dimensionless | Inverter status code. | | -| pv1-voltage | Number:ElectricPotential | DC voltage from solar panel string #1. | yes | -| pv2-voltage | Number:ElectricPotential | DC voltage from solar panel string #2. | yes | -| pv1-current | Number:ElectricCurrent | DC current from solar panel string #1. | yes | -| pv2-current | Number:ElectricCurrent | DC current from solar panel string #2. | yes | -| pv-power | Number:Power | Total DC solar input power. | | -| pv1-power | Number:Power | DC power from solar panel string #1. | yes | -| pv2-power | Number:Power | DC power from solar panel string #2. | yes | -| grid-frequency | Number:Frequency | Frequency of the grid. | yes | -| grid-voltage-r | Number:ElectricPotential | Voltage of the grid (phase #R). | | -| grid-voltage-s | Number:ElectricPotential | Voltage of the grid phase #S. | yes | -| grid-voltage-t | Number:ElectricPotential | Voltage of the grid phase #T. | yes | -| grid-voltage-rs | Number:ElectricPotential | Voltage of the grid phases #RS. | yes | -| grid-voltage-st | Number:ElectricPotential | Voltage of the grid phases #ST. | yes | -| grid-voltage-tr | Number:ElectricPotential | Voltage of the grid phases #TR. | yes | -| inverter-current-r | Number:ElectricCurrent | AC current from inverter (phase #R). | yes | -| inverter-current-s | Number:ElectricCurrent | AC current from inverter phase #S. | yes | -| inverter-current-t | Number:ElectricCurrent | AC current from inverter phase #T. | yes | -| inverter-power | Number:Power | Total AC output power from inverter. | | -| inverter-power-r | Number:Power | AC power from inverter (phase #R). | | -| inverter-power-s | Number:Power | AC power from inverter phase #S. | yes | -| inverter-power-t | Number:Power | AC power from inverter phase #T. | yes | -| inverter-va | Number:Power | AC VA from inverter. | yes | -| export-power | Number:Power | Power exported to grid. | | -| export-power-r | Number:Power | Power exported to grid phase #R. | yes | -| export-power-s | Number:Power | Power exported to grid phase #S. | yes | -| export-power-t | Number:Power | Power exported to grid phase #T. | yes | -| import-power | Number:Power | Power imported from grid. | | -| import-power-r | Number:Power | Power imported from grid phase #R. | yes | -| import-power-s | Number:Power | Power imported from grid phase #S. | yes | -| import-power-t | Number:Power | Power imported from grid phase #T. | yes | -| load-power | Number:Power | Power supplied to load. | | -| load-power-r | Number:Power | Power supplied to load phase #R. | yes | -| load-power-s | Number:Power | Power supplied to load phase #S. | yes | -| load-power-t | Number:Power | Power supplied to load phase #T. | yes | -| charge-power | Number:Power | Battery charge power. | | -| charge-current | Number:ElectricCurrent | Battery charge current. | yes | -| discharge-power | Number:Power | Battery discharge power. | | -| discharge-va | Number:Power | Battery discharge VA. | yes | -| pv-energy-today | Number:Energy | DC energy collected by solar panels today. | | -| pv1-energy-today | Number:Energy | DC energy collected by solar panels string #1 today. | yes | -| pv2-energy-today | Number:Energy | DC energy collected by solar panels string #2 today. | yes | -| pv-energy-total | Number:Energy | Total DC energy collected by solar panels. | | -| pv1-energy-total | Number:Energy | Total DC energy collected by solar panels string #1. | yes | -| pv2-energy-total | Number:Energy | Total DC energy collected by solar panels string #2. | yes | -| inverter-energy-today | Number:Energy | AC energy produced by inverter today. | | -| inverter-energy-total | Number:Energy | Total AC energy produced by inverter. | | -| export-energy-today | Number:Energy | Energy exported today. | | -| export-energy-total | Number:Energy | Total energy exported. | | -| import-energy-today | Number:Energy | Energy imported today. | | -| import-energy-total | Number:Energy | Total energy imported. | | -| load-energy-today | Number:Energy | Energy supplied to load today. | | -| load-energy-total | Number:Energy | Total energy supplied to load. | | -| import-charge-energy-today | Number:Energy | Energy imported to charge battery today. | | -| import-charge-energy-total | Number:Energy | Total energy imported to charge battery. | | -| inverter-charge-energy-today | Number:Energy | Inverter energy to charge battery today. | | -| inverter-charge-energy-total | Number:Energy | Total inverter energy to charge battery. | | -| discharge-energy-today | Number:Energy | Energy consumed from battery. | | -| discharge-energy-total | Number:Energy | Total energy consumed from battery. | | -| total-work-time | Number:Time | Total work time of the system. | yes | -| p-bus-voltage | Number:ElectricPotential | P Bus voltage. | yes | -| n-bus-voltage | Number:ElectricPotential | N Bus voltage. | yes | -| sp-bus-voltage | Number:ElectricPotential | SP Bus voltage. | yes | -| pv-temperature | Number:Temperature | Temperature of the solar panels (string #1). | yes | -| pv-ipm-temperature | Number:Temperature | Temperature of the IPM. | yes | -| pv-boost-temperature | Number:Temperature | Boost temperature. | yes | -| temperature-4 | Number:Temperature | Temperature #4. | yes | -| pv2-temperature | Number:Temperature | Temperature of the solar panels (string #2). | yes | -| battery-type | Number:Dimensionless | Type code of the battery. | yes | -| battery-temperature | Number:Temperature | Battery temperature. | yes | -| battery-voltage | Number:ElectricPotential | Battery voltage. | yes | -| battery-display | Number:Dimensionless | Battery display code. | yes | -| battery-soc | Number:Dimensionless | Battery State of Charge percent. | yes | -| system-fault-0 | Number:Dimensionless | System fault code #0. | yes | -| system-fault-1 | Number:Dimensionless | System fault code #1. | yes | -| system-fault-2 | Number:Dimensionless | System fault code #2. | yes | -| system-fault-3 | Number:Dimensionless | System fault code #3. | yes | -| system-fault-4 | Number:Dimensionless | System fault code #4. | yes | -| system-fault-5 | Number:Dimensionless | System fault code #5. | yes | -| system-fault-6 | Number:Dimensionless | System fault code #6. | yes | -| system-fault-7 | Number:Dimensionless | System fault code #7. | yes | -| system-work-mode | Number:Dimensionless | System work mode code. | yes | -| sp-display-status | Number:Dimensionless | Solar panel display status code. | yes | -| constant-power-ok | Number:Dimensionless | Constant power OK code. | yes | -| load-percent | Number:Dimensionless | Percent of full load. | yes | -| rac | Number:Power | Reactive 'power' (var). | yes | -| erac-today | Number:Energy | Reactive 'energy' today (kvarh). | yes | -| erac-total | Number:Energy | Total reactive 'energy' (kvarh). | yes | +| Channel | Type | Description | Advanced | +|--------------------------------|---------------------------|------------------------------------------------------|----------| +| system-status | Number:Dimensionless | Inverter status code. | | +| pv1-voltage | Number:ElectricPotential | DC voltage from solar panel string #1. | yes | +| pv2-voltage | Number:ElectricPotential | DC voltage from solar panel string #2. | yes | +| pv1-current | Number:ElectricCurrent | DC current from solar panel string #1. | yes | +| pv2-current | Number:ElectricCurrent | DC current from solar panel string #2. | yes | +| pv-power | Number:Power | Total DC solar input power. | | +| pv1-power | Number:Power | DC power from solar panel string #1. | yes | +| pv2-power | Number:Power | DC power from solar panel string #2. | yes | +| grid-frequency | Number:Frequency | Frequency of the grid. | yes | +| grid-voltage-r | Number:ElectricPotential | Voltage of the grid (phase #R). | | +| grid-voltage-s | Number:ElectricPotential | Voltage of the grid phase #S. | yes | +| grid-voltage-t | Number:ElectricPotential | Voltage of the grid phase #T. | yes | +| grid-voltage-rs | Number:ElectricPotential | Voltage of the grid phases #RS. | yes | +| grid-voltage-st | Number:ElectricPotential | Voltage of the grid phases #ST. | yes | +| grid-voltage-tr | Number:ElectricPotential | Voltage of the grid phases #TR. | yes | +| inverter-current | Number:ElectricCurrent | AC current from inverter. | yes | +| inverter-current-r | Number:ElectricCurrent | AC current from inverter (phase #R). | yes | +| inverter-current-s | Number:ElectricCurrent | AC current from inverter phase #S. | yes | +| inverter-current-t | Number:ElectricCurrent | AC current from inverter phase #T. | yes | +| inverter-power | Number:Power | Total AC output power from inverter. | | +| inverter-power-r | Number:Power | AC power from inverter (phase #R). | | +| inverter-power-s | Number:Power | AC power from inverter phase #S. | yes | +| inverter-power-t | Number:Power | AC power from inverter phase #T. | yes | +| inverter-va | Number:Power | AC VA from inverter. | yes | +| export-power | Number:Power | Power exported to grid. | | +| export-power-r | Number:Power | Power exported to grid phase #R. | yes | +| export-power-s | Number:Power | Power exported to grid phase #S. | yes | +| export-power-t | Number:Power | Power exported to grid phase #T. | yes | +| import-power | Number:Power | Power imported from grid. | | +| import-power-r | Number:Power | Power imported from grid phase #R. | yes | +| import-power-s | Number:Power | Power imported from grid phase #S. | yes | +| import-power-t | Number:Power | Power imported from grid phase #T. | yes | +| load-power | Number:Power | Power supplied to load. | | +| load-power-r | Number:Power | Power supplied to load phase #R. | yes | +| load-power-s | Number:Power | Power supplied to load phase #S. | yes | +| load-power-t | Number:Power | Power supplied to load phase #T. | yes | +| charge-power | Number:Power | Battery charge power. | | +| charge-current | Number:ElectricCurrent | Battery charge current. | yes | +| discharge-power | Number:Power | Battery discharge power. | | +| discharge-va | Number:Power | Battery discharge VA. | yes | +| pv-energy-today | Number:Energy | DC energy collected by solar panels today. | | +| pv1-energy-today | Number:Energy | DC energy collected by solar panels string #1 today. | yes | +| pv2-energy-today | Number:Energy | DC energy collected by solar panels string #2 today. | yes | +| pv-energy-total | Number:Energy | Total DC energy collected by solar panels. | | +| pv1-energy-total | Number:Energy | Total DC energy collected by solar panels string #1. | yes | +| pv2-energy-total | Number:Energy | Total DC energy collected by solar panels string #2. | yes | +| inverter-energy-today | Number:Energy | AC energy produced by inverter today. | | +| inverter-energy-total | Number:Energy | Total AC energy produced by inverter. | | +| export-energy-today | Number:Energy | Energy exported today. | | +| export-energy-total | Number:Energy | Total energy exported. | | +| import-energy-today | Number:Energy | Energy imported today. | | +| import-energy-total | Number:Energy | Total energy imported. | | +| load-energy-today | Number:Energy | Energy supplied to load today. | | +| load-energy-total | Number:Energy | Total energy supplied to load. | | +| import-charge-energy-today | Number:Energy | Energy imported to charge battery today. | | +| import-charge-energy-total | Number:Energy | Total energy imported to charge battery. | | +| inverter-charge-energy-today | Number:Energy | Inverter energy to charge battery today. | | +| inverter-charge-energy-total | Number:Energy | Total inverter energy to charge battery. | | +| discharge-energy-today | Number:Energy | Energy consumed from battery. | | +| discharge-energy-total | Number:Energy | Total energy consumed from battery. | | +| total-work-time | Number:Time | Total work time of the system. | yes | +| p-bus-voltage | Number:ElectricPotential | P Bus voltage. | yes | +| n-bus-voltage | Number:ElectricPotential | N Bus voltage. | yes | +| sp-bus-voltage | Number:ElectricPotential | SP Bus voltage. | yes | +| pv-temperature | Number:Temperature | Temperature of the solar panels (string #1). | yes | +| pv-ipm-temperature | Number:Temperature | Temperature of the IPM. | yes | +| pv-boost-temperature | Number:Temperature | Boost temperature. | yes | +| temperature-4 | Number:Temperature | Temperature #4. | yes | +| pv2-temperature | Number:Temperature | Temperature of the solar panels (string #2). | yes | +| battery-type | Number:Dimensionless | Type code of the battery. | yes | +| battery-temperature | Number:Temperature | Battery temperature. | yes | +| battery-voltage | Number:ElectricPotential | Battery voltage. | yes | +| battery-voltage2 | Number:ElectricPotential | Battery voltage #2. | yes | +| battery-display | Number:Dimensionless | Battery display code. | yes | +| battery-soc | Number:Dimensionless | Battery State of Charge percent. | yes | +| battery-discharge-va | Number:Power | Battery discharging reactive power. | yes | +| battery-discharge-watt | Number:Power | Battery discharging power. | yes | +| battery-discharge-energy-today | Number:Energy | Battery discharge energy today. | yes | +| battery-discharge-energy-total | Number:Energy | Total battery discharge energy. | yes | +| system-fault-0 | Number:Dimensionless | System fault code #0. | yes | +| system-fault-1 | Number:Dimensionless | System fault code #1. | yes | +| system-fault-2 | Number:Dimensionless | System fault code #2. | yes | +| system-fault-3 | Number:Dimensionless | System fault code #3. | yes | +| system-fault-4 | Number:Dimensionless | System fault code #4. | yes | +| system-fault-5 | Number:Dimensionless | System fault code #5. | yes | +| system-fault-6 | Number:Dimensionless | System fault code #6. | yes | +| system-fault-7 | Number:Dimensionless | System fault code #7. | yes | +| system-work-mode | Number:Dimensionless | System work mode code. | yes | +| sp-display-status | Number:Dimensionless | Solar panel display status code. | yes | +| constant-power-ok | Number:Dimensionless | Constant power OK code. | yes | +| load-percent | Number:Dimensionless | Percent of full load. | yes | +| rac | Number:Power | Reactive 'power' (var). | yes | +| erac-today | Number:Energy | Reactive 'energy' today (kvarh). | yes | +| erac-total | Number:Energy | Total reactive 'energy' (kvarh). | yes | +| charge-va | Number:Power | Charging reactive power. | yes | +| inverter-clock-offset | Number:Time | Time offset of inverter clock vs. OH system clock. | yes | +| inverter-fan-speed | Number:Dimensionless | Inverter fan speed percent. | yes | ## Rule Actions diff --git a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattBindingConstants.java b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattBindingConstants.java index 9fc854880064e..8fe6bb64aafba 100644 --- a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattBindingConstants.java +++ b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattBindingConstants.java @@ -28,4 +28,6 @@ public class GrowattBindingConstants { public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge"); public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "inverter"); + + public static final String CHANNEL_INVERTER_CLOCK_OFFSET = "inverter-clock-offset"; } diff --git a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattChannels.java b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattChannels.java index a03519e005bf9..09ad616fe35d0 100644 --- a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattChannels.java +++ b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/GrowattChannels.java @@ -186,7 +186,27 @@ public UoM(Unit units, float divisor) { // reactive 'power' resp. 'energy' new AbstractMap.SimpleEntry("rac", new UoM(Units.VAR, 10)), new AbstractMap.SimpleEntry("erac-today", new UoM(Units.KILOVAR_HOUR, 10)), - new AbstractMap.SimpleEntry("erac-total", new UoM(Units.KILOVAR_HOUR, 10)) + new AbstractMap.SimpleEntry("erac-total", new UoM(Units.KILOVAR_HOUR, 10)), + + /* + * ============== CHANNELS ADDED IN PR #17795 ============== + */ + + // battery instantaneous measurements + new AbstractMap.SimpleEntry("battery-voltage2", new UoM(Units.VOLT, 100)), + new AbstractMap.SimpleEntry("charge-va", new UoM(Units.VOLT_AMPERE, 10)), + new AbstractMap.SimpleEntry("battery-discharge-va", new UoM(Units.VOLT_AMPERE, 10)), + new AbstractMap.SimpleEntry("battery-discharge-watt", new UoM(Units.WATT, 10)), + + // battery energy + new AbstractMap.SimpleEntry("battery-discharge-energy-today", + new UoM(Units.KILOWATT_HOUR, 10)), + new AbstractMap.SimpleEntry("battery-discharge-energy-total", + new UoM(Units.KILOWATT_HOUR, 10)), + + // inverter + new AbstractMap.SimpleEntry("inverter-current", new UoM(Units.AMPERE, 10)), + new AbstractMap.SimpleEntry("inverter-fan-speed", new UoM(Units.PERCENT, 1)) // ); diff --git a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottDevice.java b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottDevice.java index 140078d1b6a38..20e5191e96dab 100644 --- a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottDevice.java +++ b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottDevice.java @@ -13,6 +13,11 @@ package org.openhab.binding.growatt.internal.dto; import java.lang.reflect.Type; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -34,6 +39,7 @@ public class GrottDevice { // @formatter:on private @Nullable @SerializedName("device") String deviceId; + private @Nullable @SerializedName("time") String timeStamp; private @Nullable GrottValues values; public String getDeviceId() { @@ -44,4 +50,24 @@ public String getDeviceId() { public @Nullable GrottValues getValues() { return values; } + + /** + * Return the time stamp of the data DTO sent by the inverter data-logger. + *

+ * Note: the inverter provides a time stamp formatted as a {@link LocalDateTime} without any time zone information, + * so we convert it to an {@link Instant} based on the OH system time zone. i.e. we are forced to assume the + * inverter and the OH PC are both physically in the same time zone. + * + * @return the time stamp {@link Instant} + */ + public @Nullable Instant getTimeStamp() { + String timeStamp = this.timeStamp; + if (timeStamp != null) { + try { + return ZonedDateTime.of(LocalDateTime.parse(timeStamp), ZoneId.systemDefault()).toInstant(); + } catch (DateTimeException e) { + } + } + return null; + } } diff --git a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottValues.java b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottValues.java index 1e40b3336f4c7..ad419e269145d 100644 --- a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottValues.java +++ b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/dto/GrottValues.java @@ -67,24 +67,24 @@ public static String getFieldName(String channelId) { public @Nullable @SerializedName(value = "Vac_TR", alternate = { "vactr", "L3-1_voltage" }) Integer grid_voltage_tr; // solar AC mains power - public @Nullable @SerializedName(value = "pvgridcurrent", alternate = { "OP_Curr", "Inv_Curr", "Current_l1" }) Integer inverter_current_r; + public @Nullable @SerializedName(value = "pvgridcurrent", alternate = { "OP_Curr", "Current_l1" }) Integer inverter_current_r; public @Nullable @SerializedName(value = "pvgridcurrent2", alternate = { "Current_l2" }) Integer inverter_current_s; public @Nullable @SerializedName(value = "pvgridcurrent3", alternate = { "Current_l3" }) Integer inverter_current_t; - public @Nullable @SerializedName(value = "pvgridpower", alternate = { "op_watt", "AC_InWatt" }) Integer inverter_power_r; + public @Nullable @SerializedName(value = "pvgridpower", alternate = { "op_watt" }) Integer inverter_power_r; public @Nullable @SerializedName(value = "pvgridpower2") Integer inverter_power_s; public @Nullable @SerializedName(value = "pvgridpower3") Integer inverter_power_t; // apparent power VA - public @Nullable @SerializedName(value = "op_va", alternate = { "AC_InVA" }) Integer inverter_va; + public @Nullable @SerializedName(value = "op_va") Integer inverter_va; // battery discharge / charge power - public @Nullable @SerializedName(value = "p1charge1", alternate = { "acchr_watt", "BatWatt", "bdc1_pchr" }) Integer charge_power; - public @Nullable @SerializedName(value = "pdischarge1", alternate = { "ACDischarWatt", "BatDischarWatt", "bdc1_pdischr" }) Integer discharge_power; + public @Nullable @SerializedName(value = "p1charge1", alternate = { "acchr_watt", "bdc1_pchr" }) Integer charge_power; + public @Nullable @SerializedName(value = "pdischarge1", alternate = { "ACDischarWatt", "bdc1_pdischr" }) Integer discharge_power; // miscellaneous battery public @Nullable @SerializedName(value = "ACCharCurr") Integer charge_current; - public @Nullable @SerializedName(value = "ACDischarVA", alternate = { "BatDischarVA", "acchar_VA" }) Integer discharge_va; + public @Nullable @SerializedName(value = "ACDischarVA") Integer discharge_va; // power exported to utility company public @Nullable @SerializedName(value = "pactogridtot", alternate = { "ptogridtotal" }) Integer export_power; @@ -93,7 +93,7 @@ public static String getFieldName(String channelId) { public @Nullable @SerializedName(value = "pactogridt") Integer export_power_t; // power imported from utility company - public @Nullable @SerializedName(value = "pactousertot", alternate = { "ptousertotal", "pos_rev_act_power" }) Integer import_power; + public @Nullable @SerializedName(value = "pactousertot", alternate = { "ptousertotal", "AC_InWatt", "pos_rev_act_power" }) Integer import_power; public @Nullable @SerializedName(value = "pactouserr", alternate = { "act_power_l1" }) Integer import_power_r; public @Nullable @SerializedName(value = "pactousers", alternate = { "act_power_l2" }) Integer import_power_s; public @Nullable @SerializedName(value = "pactousert", alternate = { "act_power_l3" }) Integer import_power_t; @@ -138,8 +138,8 @@ public static String getFieldName(String channelId) { public @Nullable @SerializedName(value = "eharge1_tot", alternate = { "echrtotal" }) Integer inverter_charge_energy_total; // discharging energy - public @Nullable @SerializedName(value = "edischarge1_tod", alternate = { "eacDischarToday", "ebatDischarToday", "edischrtoday" }) Integer discharge_energy_today; - public @Nullable @SerializedName(value = "edischarge1_tot", alternate = { "eacDischarTotal", "ebatDischarTotal", "edischrtotal" }) Integer discharge_energy_total; + public @Nullable @SerializedName(value = "edischarge1_tod", alternate = { "eacDischarToday", "edischrtoday" }) Integer discharge_energy_today; + public @Nullable @SerializedName(value = "edischarge1_tot", alternate = { "eacDischarTotal", "edischrtotal" }) Integer discharge_energy_total; // inverter up time public @Nullable @SerializedName(value = "totworktime") Integer total_work_time; @@ -159,7 +159,7 @@ public static String getFieldName(String channelId) { // battery data public @Nullable @SerializedName(value = "batterytype") Integer battery_type; public @Nullable @SerializedName(value = "batttemp", alternate = { "bdc1_tempa" }) Integer battery_temperature; - public @Nullable @SerializedName(value = "vbat", alternate = { "uwBatVolt_DSP", "bat_Volt", "bms_batteryvolt" }) Integer battery_voltage; + public @Nullable @SerializedName(value = "vbat", alternate = { "uwBatVolt_DSP", "bms_batteryvolt" }) Integer battery_voltage; public @Nullable @SerializedName(value = "bat_dsp") Integer battery_display; public @Nullable @SerializedName(value = "SOC", alternate = { "batterySOC", "batterySoc", "bms_soc" }) Integer battery_soc; @@ -180,9 +180,27 @@ public static String getFieldName(String channelId) { public @Nullable @SerializedName(value = "loadpercent") Integer load_percent; // reactive 'power' resp. 'energy' - public @Nullable @SerializedName(value = "rac", alternate = { "react_power" }) Integer rac; + public @Nullable @SerializedName(value = "rac", alternate = { "react_power", "AC_InVA" }) Integer rac; public @Nullable @SerializedName(value = "eractoday", alternate = { "react_energy_kvar" }) Integer erac_today; public @Nullable @SerializedName(value = "eractotal") Integer erac_total; + /* + * ============== CHANNELS ADDED IN PR #17795 ============== + */ + + // battery instantaneous measurements + public @Nullable @SerializedName(value = "bat_Volt") Integer battery_voltage2; + public @Nullable @SerializedName(value = "acchr_VA") Integer charge_va; + public @Nullable @SerializedName(value = "BatDischarVA") Integer battery_discharge_va; + public @Nullable @SerializedName(value = "BatDischarWatt", alternate = { "BatWatt" }) Integer battery_discharge_watt; + + // battery energy + public @Nullable @SerializedName(value = "ebatDischarToday") Integer battery_discharge_energy_today; + public @Nullable @SerializedName(value = "ebatDischarTotal") Integer battery_discharge_energy_total; + + // inverter + public @Nullable @SerializedName(value = "Inv_Curr") Integer inverter_current; + public @Nullable @SerializedName(value = "invfanspeed") Integer inverter_fan_speed; + // @formatter:on } diff --git a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/handler/GrowattInverterHandler.java b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/handler/GrowattInverterHandler.java index 23cac36b84e61..a49274dcefec6 100644 --- a/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/handler/GrowattInverterHandler.java +++ b/bundles/org.openhab.binding.growatt/src/main/java/org/openhab/binding/growatt/internal/handler/GrowattInverterHandler.java @@ -12,15 +12,17 @@ */ package org.openhab.binding.growatt.internal.handler; +import java.time.Duration; +import java.time.Instant; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.growatt.internal.GrowattBindingConstants; import org.openhab.binding.growatt.internal.action.GrowattActions; import org.openhab.binding.growatt.internal.cloud.GrowattApiException; import org.openhab.binding.growatt.internal.cloud.GrowattCloud; @@ -29,6 +31,7 @@ import org.openhab.binding.growatt.internal.dto.GrottValues; import org.openhab.binding.growatt.internal.dto.helper.GrottValuesHelper; import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; @@ -103,18 +106,25 @@ private void scheduleAwaitingDataTimeoutTask() { /** * Receives a collection of GrottDevice inverter objects containing potential data for this thing. If the collection - * contains an entry matching the things's deviceId, and it contains GrottValues, then process it further. Otherwise - * go offline with a configuration error. + * contains an entry matching the things's deviceId, and it contains GrottValues and a time-stamp, then process it + * further. Otherwise go offline with a configuration or communication error. * * @param inverters collection of GrottDevice objects. */ public void updateInverters(Collection inverters) { - inverters.stream().filter(inverter -> deviceId.equals(inverter.getDeviceId())) - .map(inverter -> inverter.getValues()).filter(values -> values != null).findAny() - .ifPresentOrElse(values -> { - updateStatus(ThingStatus.ONLINE); - scheduleAwaitingDataTimeoutTask(); - updateInverterValues(values); + inverters.stream().filter(inverter -> deviceId.equals(inverter.getDeviceId())).findAny() + .ifPresentOrElse(thisInverter -> { + GrottValues grottValues = thisInverter.getValues(); + Instant dtoTimeStamp = thisInverter.getTimeStamp(); + if (grottValues != null && dtoTimeStamp != null) { + updateStatus(ThingStatus.ONLINE); + updateInverterValues(grottValues); + updateState(GrowattBindingConstants.CHANNEL_INVERTER_CLOCK_OFFSET, QuantityType + .valueOf(Duration.between(Instant.now(), dtoTimeStamp).toSeconds(), Units.SECOND)); + scheduleAwaitingDataTimeoutTask(); + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + } }, () -> { updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); }); @@ -140,7 +150,10 @@ public void updateInverterValues(GrottValues inverterValues) { // find unused channels List actualChannels = thing.getChannels(); List unusedChannels = actualChannels.stream() - .filter(channel -> !channelStates.containsKey(channel.getUID().getId())).collect(Collectors.toList()); + .filter(channel -> !channelStates.containsKey(channel.getUID().getId())) + .filter(channel -> !GrowattBindingConstants.CHANNEL_INVERTER_CLOCK_OFFSET + .equals(channel.getUID().getId())) + .toList(); // remove unused channels if (!unusedChannels.isEmpty()) { @@ -149,8 +162,7 @@ public void updateInverterValues(GrottValues inverterValues) { unusedChannels.size(), thing.getChannels().size()); } - List thingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId()) - .collect(Collectors.toList()); + List thingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId()).toList(); // update channel states channelStates.forEach((channelId, state) -> { diff --git a/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/i18n/growatt.properties b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/i18n/growatt.properties index 20aea4191702a..055bf3b1cccb6 100644 --- a/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/i18n/growatt.properties +++ b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/i18n/growatt.properties @@ -9,6 +9,14 @@ thing-type.growatt.bridge.label = Growatt Bridge thing-type.growatt.bridge.description = Bridge Thing for Growatt Binding thing-type.growatt.inverter.label = Growatt Inverter thing-type.growatt.inverter.description = Inverter Thing for Growatt Binding +thing-type.growatt.inverter.channel.battery-discharge-energy-today.label = Discharge Energy Today +thing-type.growatt.inverter.channel.battery-discharge-energy-today.description = Battery discharge energy today. +thing-type.growatt.inverter.channel.battery-discharge-energy-total.label = Discharge Energy Total +thing-type.growatt.inverter.channel.battery-discharge-energy-total.description = Total battery discharge energy. +thing-type.growatt.inverter.channel.battery-discharge-va.label = Battery discharge VA +thing-type.growatt.inverter.channel.battery-discharge-va.description = Discharging reactive power. +thing-type.growatt.inverter.channel.battery-discharge-watt.label = Battery discharge power +thing-type.growatt.inverter.channel.battery-discharge-watt.description = Battery discharging power. thing-type.growatt.inverter.channel.battery-display.label = Battery Display thing-type.growatt.inverter.channel.battery-display.description = Battery display voltage. thing-type.growatt.inverter.channel.battery-soc.label = Battery Charge @@ -19,10 +27,14 @@ thing-type.growatt.inverter.channel.battery-type.label = Battery Type thing-type.growatt.inverter.channel.battery-type.description = Type code of the battery. thing-type.growatt.inverter.channel.battery-voltage.label = Battery Voltage thing-type.growatt.inverter.channel.battery-voltage.description = Battery voltage. +thing-type.growatt.inverter.channel.battery-voltage2.label = Battery Voltage #2 +thing-type.growatt.inverter.channel.battery-voltage2.description = Battery voltage. thing-type.growatt.inverter.channel.charge-current.label = Charge Current thing-type.growatt.inverter.channel.charge-current.description = Charge current to battery. thing-type.growatt.inverter.channel.charge-power.label = Charge Power thing-type.growatt.inverter.channel.charge-power.description = Charge power to battery. +thing-type.growatt.inverter.channel.charge-va.label = Charge VA +thing-type.growatt.inverter.channel.charge-va.description = Charging reactive power. thing-type.growatt.inverter.channel.constant-power-ok.label = Constant Power OK thing-type.growatt.inverter.channel.constant-power-ok.description = Constant power OK code. thing-type.growatt.inverter.channel.discharge-energy-today.label = Battery Energy Today @@ -83,6 +95,8 @@ thing-type.growatt.inverter.channel.inverter-charge-energy-today.label = Battery thing-type.growatt.inverter.channel.inverter-charge-energy-today.description = Energy from inverter to charge battery today. thing-type.growatt.inverter.channel.inverter-charge-energy-total.label = Battery Inverter Energy Total thing-type.growatt.inverter.channel.inverter-charge-energy-total.description = Total energy from inverter to charge battery. +thing-type.growatt.inverter.channel.inverter-current.label = Inverter Current +thing-type.growatt.inverter.channel.inverter-current.description = Inverter current. thing-type.growatt.inverter.channel.inverter-current-r.label = Inverter Current (#R) thing-type.growatt.inverter.channel.inverter-current-r.description = AC current from inverter (phase #R). thing-type.growatt.inverter.channel.inverter-current-s.label = Inverter Current #S @@ -93,6 +107,8 @@ thing-type.growatt.inverter.channel.inverter-energy-today.label = Inverter Energ thing-type.growatt.inverter.channel.inverter-energy-today.description = Inverter output energy produced today. thing-type.growatt.inverter.channel.inverter-energy-total.label = Inverter Energy Total thing-type.growatt.inverter.channel.inverter-energy-total.description = Total inverter output energy produced. +thing-type.growatt.inverter.channel.inverter-fan-speed.label = Inverter Fan +thing-type.growatt.inverter.channel.inverter-fan-speed.description = Inverter fan speed. thing-type.growatt.inverter.channel.inverter-power.label = Inverter Power thing-type.growatt.inverter.channel.inverter-power.description = AC power the inverter (total). thing-type.growatt.inverter.channel.inverter-power-r.label = Inverter Power (#R) @@ -209,6 +225,8 @@ channel-type.growatt.advanced-fault-code.label = Fault Code channel-type.growatt.advanced-outdoor-temperature.label = Outdoor Temperature channel-type.growatt.advanced-percent.label = Percentage channel-type.growatt.advanced-status-code.label = Status Code +channel-type.growatt.advanced-time.label = Inverter Clock Offset +channel-type.growatt.advanced-time.description = Time offset of inverter clock vs. OH system clock. channel-type.growatt.advanced-work-time.label = Work Time channel-type.growatt.system-status-code.label = Status Code diff --git a/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/thing/thing-types.xml index 0fe5bccadf06e..5b99ab7f49546 100644 --- a/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/thing/thing-types.xml @@ -439,8 +439,53 @@ Total reactive energy supplied. + + + + + + Battery voltage. + + + + Charging reactive power. + + + + Discharging reactive power. + + + + Battery discharging power. + + + + + + Battery discharge energy today. + + + + Total battery discharge energy. + + + + + + + Inverter current. + + + + Inverter fan speed. + + + + 1 + + @@ -548,4 +593,12 @@ + + Number:Time + + Time offset of inverter clock vs. OH system clock. + Time + + + diff --git a/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/update/instructions.xml new file mode 100644 index 0000000000000..3856eed4cd46c --- /dev/null +++ b/bundles/org.openhab.binding.growatt/src/main/resources/OH-INF/update/instructions.xml @@ -0,0 +1,54 @@ + + + + + + + growatt:advanced-electric-voltage + + Battery voltage. + + + growatt:advanced-electric-var + + Charging reactive power. + + + growatt:advanced-electric-var + + Discharging reactive power. + + + growatt:advanced-electric-power + + Battery discharging power. + + + growatt:advanced-electric-energy + + Battery discharge energy today. + + + growatt:advanced-electric-energy + + Total battery discharge energy. + + + growatt:advanced-time + + + growatt:advanced-electric-current + + Inverter current. + + + growatt:advanced-percent + + Inverter fan speed. + + + + + diff --git a/bundles/org.openhab.binding.growatt/src/test/java/org/openhab/binding/growatt/test/GrowattTest.java b/bundles/org.openhab.binding.growatt/src/test/java/org/openhab/binding/growatt/test/GrowattTest.java index 474092ae622e7..45f790c614e55 100644 --- a/bundles/org.openhab.binding.growatt/src/test/java/org/openhab/binding/growatt/test/GrowattTest.java +++ b/bundles/org.openhab.binding.growatt/src/test/java/org/openhab/binding/growatt/test/GrowattTest.java @@ -105,6 +105,9 @@ private GrottValues loadGrottValues(String fileName) throws FileNotFoundExceptio void testGrottValuesAccessibility() throws FileNotFoundException, IOException { testGrottValuesAccessibility("simple"); testGrottValuesAccessibility("sph"); + testGrottValuesAccessibility("spf"); + testGrottValuesAccessibility("mid"); + testGrottValuesAccessibility("meter"); } /** @@ -236,6 +239,7 @@ void testGrottValuesContents() throws FileNotFoundException, IOException, NoSuch void testJsonFieldsMappedToDto() throws FileNotFoundException, IOException { testJsonFieldsMappedToDto("simple"); testJsonFieldsMappedToDto("sph"); + testJsonFieldsMappedToDto("spf"); } /** @@ -353,7 +357,7 @@ void testServer() throws Exception { @Test void testThreePhaseGrottValuesContents() throws FileNotFoundException, IOException, NoSuchFieldException, SecurityException, IllegalAccessException, IllegalArgumentException { - GrottValues grottValues = loadGrottValues("3phase"); + GrottValues grottValues = loadGrottValues("mid"); assertNotNull(grottValues); Map> channelStates = GrottValuesHelper.getChannelStates(grottValues); diff --git a/bundles/org.openhab.binding.growatt/src/test/resources/3phase.json b/bundles/org.openhab.binding.growatt/src/test/resources/mid.json similarity index 100% rename from bundles/org.openhab.binding.growatt/src/test/resources/3phase.json rename to bundles/org.openhab.binding.growatt/src/test/resources/mid.json diff --git a/bundles/org.openhab.binding.growatt/src/test/resources/spf.json b/bundles/org.openhab.binding.growatt/src/test/resources/spf.json new file mode 100644 index 0000000000000..2ed55d92d7542 --- /dev/null +++ b/bundles/org.openhab.binding.growatt/src/test/resources/spf.json @@ -0,0 +1,59 @@ +{ + "device": "RKG5BEL00F", + "time": "2024-11-21T17:37:32", + "buffered": "no", + "values": { + "datalogserial": "DDD0BD605Y", + "pvserial": "RKG5BEL00F", + "pvstatus": 2, + "vpv1": 0, + "vpv2": 0, + "ppv1": 10, + "ppv2": 0, + "buck1curr": 0, + "buck2curr": 0, + "op_watt": 1100, + "pvpowerout": 1100, + "op_va": 422400, + "acchr_watt": 0, + "acchr_VA": 0, + "bat_Volt": 5307, + "batterySoc": 77, + "bus_volt": 4231, + "grid_volt": 0, + "line_freq": 0, + "outputvolt": 2297, + "pvgridvoltage": 2297, + "outputfreq": 5000, + "invtemp": 223, + "dcdctemp": 202, + "loadpercent": 22, + "buck1_ntc": 199, + "buck2_ntc": 0, + "OP_Curr": 7, + "Inv_Curr": 22, + "AC_InWatt": 0, + "AC_InVA": 0, + "faultBit": 0, + "warningBit": 0, + "faultValue": 0, + "warningValue": 0, + "constantPowerOK": 1, + "epvtoday": 68, + "pvenergytoday": 68, + "epvtotal": 87818, + "eacCharToday": 54, + "eacCharTotal": 25693, + "ebatDischarToday": 36, + "ebatDischarTotal": 44274, + "eacDischarToday": 3, + "eacDischarTotal": 62336, + "ACCharCurr": 0, + "ACDischarWatt": 1130, + "ACDischarVA": 1650, + "BatDischarWatt": 1110, + "BatDischarVA": 1110, + "BatWatt": 1110, + "invfanspeed": 22 + } +}