[growatt] Enhance support for SPF inverters (#17795)

* [growatt] tweak channel aliases; add missing channels

Signed-off-by: AndrewFG <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2024-11-26 05:45:26 +00:00 committed by GitHub
parent 3ae0203197
commit 81e488d65f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 390 additions and 115 deletions

View File

@ -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. 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: The list of all possible channels is as follows:
| Channel | Type | Description | Advanced | | Channel | Type | Description | Advanced |
|-------------------------------|---------------------------|------------------------------------------------------|----------| |--------------------------------|---------------------------|------------------------------------------------------|----------|
| system-status | Number:Dimensionless | Inverter status code. | | | system-status | Number:Dimensionless | Inverter status code. | |
| pv1-voltage | Number:ElectricPotential | DC voltage from solar panel string #1. | yes | | pv1-voltage | Number:ElectricPotential | DC voltage from solar panel string #1. | yes |
| pv2-voltage | Number:ElectricPotential | DC voltage from solar panel string #2. | 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 | | pv1-current | Number:ElectricCurrent | DC current from solar panel string #1. | yes |
| pv2-current | Number:ElectricCurrent | DC current from solar panel string #2. | yes | | pv2-current | Number:ElectricCurrent | DC current from solar panel string #2. | yes |
| pv-power | Number:Power | Total DC solar input power. | | | pv-power | Number:Power | Total DC solar input power. | |
| pv1-power | Number:Power | DC power from solar panel string #1. | yes | | pv1-power | Number:Power | DC power from solar panel string #1. | yes |
| pv2-power | Number:Power | DC power from solar panel string #2. | yes | | pv2-power | Number:Power | DC power from solar panel string #2. | yes |
| grid-frequency | Number:Frequency | Frequency of the grid. | yes | | grid-frequency | Number:Frequency | Frequency of the grid. | yes |
| grid-voltage-r | Number:ElectricPotential | Voltage of the grid (phase #R). | | | 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-s | Number:ElectricPotential | Voltage of the grid phase #S. | yes |
| grid-voltage-t | Number:ElectricPotential | Voltage of the grid phase #T. | 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-rs | Number:ElectricPotential | Voltage of the grid phases #RS. | yes |
| grid-voltage-st | Number:ElectricPotential | Voltage of the grid phases #ST. | 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 | | 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 | Number:ElectricCurrent | AC current from inverter. | yes |
| inverter-current-s | Number:ElectricCurrent | AC current from inverter phase #S. | yes | | inverter-current-r | Number:ElectricCurrent | AC current from inverter (phase #R). | yes |
| inverter-current-t | Number:ElectricCurrent | AC current from inverter phase #T. | yes | | inverter-current-s | Number:ElectricCurrent | AC current from inverter phase #S. | yes |
| inverter-power | Number:Power | Total AC output power from inverter. | | | inverter-current-t | Number:ElectricCurrent | AC current from inverter phase #T. | yes |
| inverter-power-r | Number:Power | AC power from inverter (phase #R). | | | inverter-power | Number:Power | Total AC output power from inverter. | |
| inverter-power-s | Number:Power | AC power from inverter phase #S. | yes | | inverter-power-r | Number:Power | AC power from inverter (phase #R). | |
| inverter-power-t | Number:Power | AC power from inverter phase #T. | yes | | inverter-power-s | Number:Power | AC power from inverter phase #S. | yes |
| inverter-va | Number:Power | AC VA from inverter. | yes | | inverter-power-t | Number:Power | AC power from inverter phase #T. | yes |
| export-power | Number:Power | Power exported to grid. | | | inverter-va | Number:Power | AC VA from inverter. | yes |
| export-power-r | Number:Power | Power exported to grid phase #R. | yes | | export-power | Number:Power | Power exported to grid. | |
| export-power-s | Number:Power | Power exported to grid phase #S. | yes | | export-power-r | Number:Power | Power exported to grid phase #R. | yes |
| export-power-t | Number:Power | Power exported to grid phase #T. | yes | | export-power-s | Number:Power | Power exported to grid phase #S. | yes |
| import-power | Number:Power | Power imported from grid. | | | export-power-t | Number:Power | Power exported to grid phase #T. | yes |
| import-power-r | Number:Power | Power imported from grid phase #R. | yes | | import-power | Number:Power | Power imported from grid. | |
| import-power-s | Number:Power | Power imported from grid phase #S. | yes | | import-power-r | Number:Power | Power imported from grid phase #R. | yes |
| import-power-t | Number:Power | Power imported from grid phase #T. | yes | | import-power-s | Number:Power | Power imported from grid phase #S. | yes |
| load-power | Number:Power | Power supplied to load. | | | import-power-t | Number:Power | Power imported from grid phase #T. | yes |
| load-power-r | Number:Power | Power supplied to load phase #R. | yes | | load-power | Number:Power | Power supplied to load. | |
| load-power-s | Number:Power | Power supplied to load phase #S. | yes | | load-power-r | Number:Power | Power supplied to load phase #R. | yes |
| load-power-t | Number:Power | Power supplied to load phase #T. | yes | | load-power-s | Number:Power | Power supplied to load phase #S. | yes |
| charge-power | Number:Power | Battery charge power. | | | load-power-t | Number:Power | Power supplied to load phase #T. | yes |
| charge-current | Number:ElectricCurrent | Battery charge current. | yes | | charge-power | Number:Power | Battery charge power. | |
| discharge-power | Number:Power | Battery discharge power. | | | charge-current | Number:ElectricCurrent | Battery charge current. | yes |
| discharge-va | Number:Power | Battery discharge VA. | yes | | discharge-power | Number:Power | Battery discharge power. | |
| pv-energy-today | Number:Energy | DC energy collected by solar panels today. | | | discharge-va | Number:Power | Battery discharge VA. | yes |
| pv1-energy-today | Number:Energy | DC energy collected by solar panels string #1 today. | yes | | pv-energy-today | Number:Energy | DC energy collected by solar panels today. | |
| pv2-energy-today | Number:Energy | DC energy collected by solar panels string #2 today. | yes | | pv1-energy-today | Number:Energy | DC energy collected by solar panels string #1 today. | yes |
| pv-energy-total | Number:Energy | Total DC energy collected by solar panels. | | | pv2-energy-today | Number:Energy | DC energy collected by solar panels string #2 today. | yes |
| pv1-energy-total | Number:Energy | Total DC energy collected by solar panels string #1. | yes | | pv-energy-total | Number:Energy | Total DC energy collected by solar panels. | |
| pv2-energy-total | Number:Energy | Total DC energy collected by solar panels string #2. | yes | | pv1-energy-total | Number:Energy | Total DC energy collected by solar panels string #1. | yes |
| inverter-energy-today | Number:Energy | AC energy produced by inverter today. | | | pv2-energy-total | Number:Energy | Total DC energy collected by solar panels string #2. | yes |
| inverter-energy-total | Number:Energy | Total AC energy produced by inverter. | | | inverter-energy-today | Number:Energy | AC energy produced by inverter today. | |
| export-energy-today | Number:Energy | Energy exported today. | | | inverter-energy-total | Number:Energy | Total AC energy produced by inverter. | |
| export-energy-total | Number:Energy | Total energy exported. | | | export-energy-today | Number:Energy | Energy exported today. | |
| import-energy-today | Number:Energy | Energy imported today. | | | export-energy-total | Number:Energy | Total energy exported. | |
| import-energy-total | Number:Energy | Total energy imported. | | | import-energy-today | Number:Energy | Energy imported today. | |
| load-energy-today | Number:Energy | Energy supplied to load today. | | | import-energy-total | Number:Energy | Total energy imported. | |
| load-energy-total | Number:Energy | Total energy supplied to load. | | | load-energy-today | Number:Energy | Energy supplied to load today. | |
| import-charge-energy-today | Number:Energy | Energy imported to charge battery today. | | | load-energy-total | Number:Energy | Total energy supplied to load. | |
| import-charge-energy-total | Number:Energy | Total energy imported to charge battery. | | | import-charge-energy-today | Number:Energy | Energy imported to charge battery today. | |
| inverter-charge-energy-today | Number:Energy | Inverter energy to charge battery today. | | | import-charge-energy-total | Number:Energy | Total energy imported to charge battery. | |
| inverter-charge-energy-total | Number:Energy | Total inverter energy to charge battery. | | | inverter-charge-energy-today | Number:Energy | Inverter energy to charge battery today. | |
| discharge-energy-today | Number:Energy | Energy consumed from battery. | | | inverter-charge-energy-total | Number:Energy | Total inverter energy to charge battery. | |
| discharge-energy-total | Number:Energy | Total energy consumed from battery. | | | discharge-energy-today | Number:Energy | Energy consumed from battery. | |
| total-work-time | Number:Time | Total work time of the system. | yes | | discharge-energy-total | Number:Energy | Total energy consumed from battery. | |
| p-bus-voltage | Number:ElectricPotential | P Bus voltage. | yes | | total-work-time | Number:Time | Total work time of the system. | yes |
| n-bus-voltage | Number:ElectricPotential | N Bus voltage. | yes | | p-bus-voltage | Number:ElectricPotential | P Bus voltage. | yes |
| sp-bus-voltage | Number:ElectricPotential | SP Bus voltage. | yes | | n-bus-voltage | Number:ElectricPotential | N Bus voltage. | yes |
| pv-temperature | Number:Temperature | Temperature of the solar panels (string #1). | yes | | sp-bus-voltage | Number:ElectricPotential | SP Bus voltage. | yes |
| pv-ipm-temperature | Number:Temperature | Temperature of the IPM. | yes | | pv-temperature | Number:Temperature | Temperature of the solar panels (string #1). | yes |
| pv-boost-temperature | Number:Temperature | Boost temperature. | yes | | pv-ipm-temperature | Number:Temperature | Temperature of the IPM. | yes |
| temperature-4 | Number:Temperature | Temperature #4. | yes | | pv-boost-temperature | Number:Temperature | Boost temperature. | yes |
| pv2-temperature | Number:Temperature | Temperature of the solar panels (string #2). | yes | | temperature-4 | Number:Temperature | Temperature #4. | yes |
| battery-type | Number:Dimensionless | Type code of the battery. | yes | | pv2-temperature | Number:Temperature | Temperature of the solar panels (string #2). | yes |
| battery-temperature | Number:Temperature | Battery temperature. | yes | | battery-type | Number:Dimensionless | Type code of the battery. | yes |
| battery-voltage | Number:ElectricPotential | Battery voltage. | yes | | battery-temperature | Number:Temperature | Battery temperature. | yes |
| battery-display | Number:Dimensionless | Battery display code. | yes | | battery-voltage | Number:ElectricPotential | Battery voltage. | yes |
| battery-soc | Number:Dimensionless | Battery State of Charge percent. | yes | | battery-voltage2 | Number:ElectricPotential | Battery voltage #2. | yes |
| system-fault-0 | Number:Dimensionless | System fault code #0. | yes | | battery-display | Number:Dimensionless | Battery display code. | yes |
| system-fault-1 | Number:Dimensionless | System fault code #1. | yes | | battery-soc | Number:Dimensionless | Battery State of Charge percent. | yes |
| system-fault-2 | Number:Dimensionless | System fault code #2. | yes | | battery-discharge-va | Number:Power | Battery discharging reactive power. | yes |
| system-fault-3 | Number:Dimensionless | System fault code #3. | yes | | battery-discharge-watt | Number:Power | Battery discharging power. | yes |
| system-fault-4 | Number:Dimensionless | System fault code #4. | yes | | battery-discharge-energy-today | Number:Energy | Battery discharge energy today. | yes |
| system-fault-5 | Number:Dimensionless | System fault code #5. | yes | | battery-discharge-energy-total | Number:Energy | Total battery discharge energy. | yes |
| system-fault-6 | Number:Dimensionless | System fault code #6. | yes | | system-fault-0 | Number:Dimensionless | System fault code #0. | yes |
| system-fault-7 | Number:Dimensionless | System fault code #7. | yes | | system-fault-1 | Number:Dimensionless | System fault code #1. | yes |
| system-work-mode | Number:Dimensionless | System work mode code. | yes | | system-fault-2 | Number:Dimensionless | System fault code #2. | yes |
| sp-display-status | Number:Dimensionless | Solar panel display status code. | yes | | system-fault-3 | Number:Dimensionless | System fault code #3. | yes |
| constant-power-ok | Number:Dimensionless | Constant power OK code. | yes | | system-fault-4 | Number:Dimensionless | System fault code #4. | yes |
| load-percent | Number:Dimensionless | Percent of full load. | yes | | system-fault-5 | Number:Dimensionless | System fault code #5. | yes |
| rac | Number:Power | Reactive 'power' (var). | yes | | system-fault-6 | Number:Dimensionless | System fault code #6. | yes |
| erac-today | Number:Energy | Reactive 'energy' today (kvarh). | yes | | system-fault-7 | Number:Dimensionless | System fault code #7. | yes |
| erac-total | Number:Energy | Total reactive 'energy' (kvarh). | 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 ## Rule Actions

View File

@ -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_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "inverter"); public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "inverter");
public static final String CHANNEL_INVERTER_CLOCK_OFFSET = "inverter-clock-offset";
} }

View File

@ -186,7 +186,27 @@ public class GrowattChannels {
// reactive 'power' resp. 'energy' // reactive 'power' resp. 'energy'
new AbstractMap.SimpleEntry<String, UoM>("rac", new UoM(Units.VAR, 10)), new AbstractMap.SimpleEntry<String, UoM>("rac", new UoM(Units.VAR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("erac-today", new UoM(Units.KILOVAR_HOUR, 10)), new AbstractMap.SimpleEntry<String, UoM>("erac-today", new UoM(Units.KILOVAR_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("erac-total", new UoM(Units.KILOVAR_HOUR, 10)) new AbstractMap.SimpleEntry<String, UoM>("erac-total", new UoM(Units.KILOVAR_HOUR, 10)),
/*
* ============== CHANNELS ADDED IN PR #17795 ==============
*/
// battery instantaneous measurements
new AbstractMap.SimpleEntry<String, UoM>("battery-voltage2", new UoM(Units.VOLT, 100)),
new AbstractMap.SimpleEntry<String, UoM>("charge-va", new UoM(Units.VOLT_AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-discharge-va", new UoM(Units.VOLT_AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-discharge-watt", new UoM(Units.WATT, 10)),
// battery energy
new AbstractMap.SimpleEntry<String, UoM>("battery-discharge-energy-today",
new UoM(Units.KILOWATT_HOUR, 10)),
new AbstractMap.SimpleEntry<String, UoM>("battery-discharge-energy-total",
new UoM(Units.KILOWATT_HOUR, 10)),
// inverter
new AbstractMap.SimpleEntry<String, UoM>("inverter-current", new UoM(Units.AMPERE, 10)),
new AbstractMap.SimpleEntry<String, UoM>("inverter-fan-speed", new UoM(Units.PERCENT, 1))
// //
); );

View File

@ -13,6 +13,11 @@
package org.openhab.binding.growatt.internal.dto; package org.openhab.binding.growatt.internal.dto;
import java.lang.reflect.Type; 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 java.util.ArrayList;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
@ -34,6 +39,7 @@ public class GrottDevice {
// @formatter:on // @formatter:on
private @Nullable @SerializedName("device") String deviceId; private @Nullable @SerializedName("device") String deviceId;
private @Nullable @SerializedName("time") String timeStamp;
private @Nullable GrottValues values; private @Nullable GrottValues values;
public String getDeviceId() { public String getDeviceId() {
@ -44,4 +50,24 @@ public class GrottDevice {
public @Nullable GrottValues getValues() { public @Nullable GrottValues getValues() {
return values; return values;
} }
/**
* Return the time stamp of the data DTO sent by the inverter data-logger.
* <p>
* 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;
}
} }

View File

@ -67,24 +67,24 @@ public class GrottValues {
public @Nullable @SerializedName(value = "Vac_TR", alternate = { "vactr", "L3-1_voltage" }) Integer grid_voltage_tr; public @Nullable @SerializedName(value = "Vac_TR", alternate = { "vactr", "L3-1_voltage" }) Integer grid_voltage_tr;
// solar AC mains power // 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 = "pvgridcurrent2", alternate = { "Current_l2" }) Integer inverter_current_s;
public @Nullable @SerializedName(value = "pvgridcurrent3", alternate = { "Current_l3" }) Integer inverter_current_t; 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 = "pvgridpower2") Integer inverter_power_s;
public @Nullable @SerializedName(value = "pvgridpower3") Integer inverter_power_t; public @Nullable @SerializedName(value = "pvgridpower3") Integer inverter_power_t;
// apparent power VA // 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 // battery discharge / charge power
public @Nullable @SerializedName(value = "p1charge1", alternate = { "acchr_watt", "BatWatt", "bdc1_pchr" }) Integer charge_power; public @Nullable @SerializedName(value = "p1charge1", alternate = { "acchr_watt", "bdc1_pchr" }) Integer charge_power;
public @Nullable @SerializedName(value = "pdischarge1", alternate = { "ACDischarWatt", "BatDischarWatt", "bdc1_pdischr" }) Integer discharge_power; public @Nullable @SerializedName(value = "pdischarge1", alternate = { "ACDischarWatt", "bdc1_pdischr" }) Integer discharge_power;
// miscellaneous battery // miscellaneous battery
public @Nullable @SerializedName(value = "ACCharCurr") Integer charge_current; 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 // power exported to utility company
public @Nullable @SerializedName(value = "pactogridtot", alternate = { "ptogridtotal" }) Integer export_power; public @Nullable @SerializedName(value = "pactogridtot", alternate = { "ptogridtotal" }) Integer export_power;
@ -93,7 +93,7 @@ public class GrottValues {
public @Nullable @SerializedName(value = "pactogridt") Integer export_power_t; public @Nullable @SerializedName(value = "pactogridt") Integer export_power_t;
// power imported from utility company // 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 = "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 = "pactousers", alternate = { "act_power_l2" }) Integer import_power_s;
public @Nullable @SerializedName(value = "pactousert", alternate = { "act_power_l3" }) Integer import_power_t; public @Nullable @SerializedName(value = "pactousert", alternate = { "act_power_l3" }) Integer import_power_t;
@ -138,8 +138,8 @@ public class GrottValues {
public @Nullable @SerializedName(value = "eharge1_tot", alternate = { "echrtotal" }) Integer inverter_charge_energy_total; public @Nullable @SerializedName(value = "eharge1_tot", alternate = { "echrtotal" }) Integer inverter_charge_energy_total;
// discharging energy // discharging energy
public @Nullable @SerializedName(value = "edischarge1_tod", alternate = { "eacDischarToday", "ebatDischarToday", "edischrtoday" }) Integer discharge_energy_today; public @Nullable @SerializedName(value = "edischarge1_tod", alternate = { "eacDischarToday", "edischrtoday" }) Integer discharge_energy_today;
public @Nullable @SerializedName(value = "edischarge1_tot", alternate = { "eacDischarTotal", "ebatDischarTotal", "edischrtotal" }) Integer discharge_energy_total; public @Nullable @SerializedName(value = "edischarge1_tot", alternate = { "eacDischarTotal", "edischrtotal" }) Integer discharge_energy_total;
// inverter up time // inverter up time
public @Nullable @SerializedName(value = "totworktime") Integer total_work_time; public @Nullable @SerializedName(value = "totworktime") Integer total_work_time;
@ -159,7 +159,7 @@ public class GrottValues {
// battery data // battery data
public @Nullable @SerializedName(value = "batterytype") Integer battery_type; public @Nullable @SerializedName(value = "batterytype") Integer battery_type;
public @Nullable @SerializedName(value = "batttemp", alternate = { "bdc1_tempa" }) Integer battery_temperature; 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 = "bat_dsp") Integer battery_display;
public @Nullable @SerializedName(value = "SOC", alternate = { "batterySOC", "batterySoc", "bms_soc" }) Integer battery_soc; public @Nullable @SerializedName(value = "SOC", alternate = { "batterySOC", "batterySoc", "bms_soc" }) Integer battery_soc;
@ -180,9 +180,27 @@ public class GrottValues {
public @Nullable @SerializedName(value = "loadpercent") Integer load_percent; public @Nullable @SerializedName(value = "loadpercent") Integer load_percent;
// reactive 'power' resp. 'energy' // 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 = "eractoday", alternate = { "react_energy_kvar" }) Integer erac_today;
public @Nullable @SerializedName(value = "eractotal") Integer erac_total; 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 // @formatter:on
} }

View File

@ -12,15 +12,17 @@
*/ */
package org.openhab.binding.growatt.internal.handler; package org.openhab.binding.growatt.internal.handler;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.growatt.internal.GrowattBindingConstants;
import org.openhab.binding.growatt.internal.action.GrowattActions; import org.openhab.binding.growatt.internal.action.GrowattActions;
import org.openhab.binding.growatt.internal.cloud.GrowattApiException; import org.openhab.binding.growatt.internal.cloud.GrowattApiException;
import org.openhab.binding.growatt.internal.cloud.GrowattCloud; import org.openhab.binding.growatt.internal.cloud.GrowattCloud;
@ -29,6 +31,7 @@ import org.openhab.binding.growatt.internal.dto.GrottDevice;
import org.openhab.binding.growatt.internal.dto.GrottValues; import org.openhab.binding.growatt.internal.dto.GrottValues;
import org.openhab.binding.growatt.internal.dto.helper.GrottValuesHelper; import org.openhab.binding.growatt.internal.dto.helper.GrottValuesHelper;
import org.openhab.core.library.types.QuantityType; 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.Bridge;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
@ -103,18 +106,25 @@ public class GrowattInverterHandler extends BaseThingHandler {
/** /**
* Receives a collection of GrottDevice inverter objects containing potential data for this thing. If the collection * 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 * contains an entry matching the things's deviceId, and it contains GrottValues and a time-stamp, then process it
* go offline with a configuration error. * further. Otherwise go offline with a configuration or communication error.
* *
* @param inverters collection of GrottDevice objects. * @param inverters collection of GrottDevice objects.
*/ */
public void updateInverters(Collection<GrottDevice> inverters) { public void updateInverters(Collection<GrottDevice> inverters) {
inverters.stream().filter(inverter -> deviceId.equals(inverter.getDeviceId())) inverters.stream().filter(inverter -> deviceId.equals(inverter.getDeviceId())).findAny()
.map(inverter -> inverter.getValues()).filter(values -> values != null).findAny() .ifPresentOrElse(thisInverter -> {
.ifPresentOrElse(values -> { GrottValues grottValues = thisInverter.getValues();
updateStatus(ThingStatus.ONLINE); Instant dtoTimeStamp = thisInverter.getTimeStamp();
scheduleAwaitingDataTimeoutTask(); if (grottValues != null && dtoTimeStamp != null) {
updateInverterValues(values); 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); updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}); });
@ -140,7 +150,10 @@ public class GrowattInverterHandler extends BaseThingHandler {
// find unused channels // find unused channels
List<Channel> actualChannels = thing.getChannels(); List<Channel> actualChannels = thing.getChannels();
List<Channel> unusedChannels = actualChannels.stream() List<Channel> 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 // remove unused channels
if (!unusedChannels.isEmpty()) { if (!unusedChannels.isEmpty()) {
@ -149,8 +162,7 @@ public class GrowattInverterHandler extends BaseThingHandler {
unusedChannels.size(), thing.getChannels().size()); unusedChannels.size(), thing.getChannels().size());
} }
List<String> thingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId()) List<String> thingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId()).toList();
.collect(Collectors.toList());
// update channel states // update channel states
channelStates.forEach((channelId, state) -> { channelStates.forEach((channelId, state) -> {

View File

@ -9,6 +9,14 @@ thing-type.growatt.bridge.label = Growatt Bridge
thing-type.growatt.bridge.description = Bridge Thing for Growatt Binding thing-type.growatt.bridge.description = Bridge Thing for Growatt Binding
thing-type.growatt.inverter.label = Growatt Inverter thing-type.growatt.inverter.label = Growatt Inverter
thing-type.growatt.inverter.description = Inverter Thing for Growatt Binding 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.label = Battery Display
thing-type.growatt.inverter.channel.battery-display.description = Battery display voltage. thing-type.growatt.inverter.channel.battery-display.description = Battery display voltage.
thing-type.growatt.inverter.channel.battery-soc.label = Battery Charge 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-type.description = Type code of the battery.
thing-type.growatt.inverter.channel.battery-voltage.label = Battery Voltage 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-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.label = Charge Current
thing-type.growatt.inverter.channel.charge-current.description = Charge current to battery. 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.label = Charge Power
thing-type.growatt.inverter.channel.charge-power.description = Charge power to battery. 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.label = Constant Power OK
thing-type.growatt.inverter.channel.constant-power-ok.description = Constant power OK code. 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 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-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.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-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.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-r.description = AC current from inverter (phase #R).
thing-type.growatt.inverter.channel.inverter-current-s.label = Inverter Current #S 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-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.label = Inverter Energy Total
thing-type.growatt.inverter.channel.inverter-energy-total.description = Total inverter output energy produced. 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.label = Inverter Power
thing-type.growatt.inverter.channel.inverter-power.description = AC power the inverter (total). 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) 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-outdoor-temperature.label = Outdoor Temperature
channel-type.growatt.advanced-percent.label = Percentage channel-type.growatt.advanced-percent.label = Percentage
channel-type.growatt.advanced-status-code.label = Status Code 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.advanced-work-time.label = Work Time
channel-type.growatt.system-status-code.label = Status Code channel-type.growatt.system-status-code.label = Status Code

View File

@ -439,8 +439,53 @@
<description>Total reactive energy supplied.</description> <description>Total reactive energy supplied.</description>
</channel> </channel>
<!-- ============== CHANNELS ADDED IN PR #17795 ============== -->
<!-- battery instantaneous measurements -->
<channel id="battery-voltage2" typeId="advanced-electric-voltage">
<label>Battery Voltage #2</label>
<description>Battery voltage.</description>
</channel>
<channel id="charge-va" typeId="advanced-electric-var">
<label>Charge VA</label>
<description>Charging reactive power.</description>
</channel>
<channel id="battery-discharge-va" typeId="advanced-electric-var">
<label>Battery discharge VA</label>
<description>Discharging reactive power.</description>
</channel>
<channel id="battery-discharge-watt" typeId="advanced-electric-power">
<label>Battery discharge power</label>
<description>Battery discharging power.</description>
</channel>
<!-- battery energy -->
<channel id="battery-discharge-energy-today" typeId="advanced-electric-energy">
<label>Discharge Energy Today</label>
<description>Battery discharge energy today.</description>
</channel>
<channel id="battery-discharge-energy-total" typeId="advanced-electric-energy">
<label>Discharge Energy Total</label>
<description>Total battery discharge energy.</description>
</channel>
<!-- inverter -->
<channel id="inverter-clock-offset" typeId="advanced-time"/>
<channel id="inverter-current" typeId="advanced-electric-current">
<label>Inverter Current</label>
<description>Inverter current.</description>
</channel>
<channel id="inverter-fan-speed" typeId="advanced-percent">
<label>Inverter Fan</label>
<description>Inverter fan speed.</description>
</channel>
</channels> </channels>
<properties>
<property name="thingTypeVersion">1</property>
</properties>
<config-description> <config-description>
<parameter name="deviceId" type="text" required="true"> <parameter name="deviceId" type="text" required="true">
<label>Device Id</label> <label>Device Id</label>
@ -548,4 +593,12 @@
<state readOnly="true" pattern="%0.0f °C"/> <state readOnly="true" pattern="%0.0f °C"/>
</channel-type> </channel-type>
<channel-type id="advanced-time" advanced="true">
<item-type unitHint="s">Number:Time</item-type>
<label>Inverter Clock Offset</label>
<description>Time offset of inverter clock vs. OH system clock.</description>
<category>Time</category>
<state readOnly="true" pattern="%0.0f s"/>
</channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -0,0 +1,54 @@
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="growatt:inverter">
<!-- ============== CHANNELS ADDED IN PR #17795 ============== -->
<instruction-set targetVersion="1">
<add-channel id="battery-voltage2">
<type>growatt:advanced-electric-voltage</type>
<label>Battery Voltage #2</label>
<description>Battery voltage.</description>
</add-channel>
<add-channel id="charge-va">
<type>growatt:advanced-electric-var</type>
<label>Charge VA</label>
<description>Charging reactive power.</description>
</add-channel>
<add-channel id="battery-discharge-va">
<type>growatt:advanced-electric-var</type>
<label>Battery discharge VA</label>
<description>Discharging reactive power.</description>
</add-channel>
<add-channel id="battery-discharge-watt">
<type>growatt:advanced-electric-power</type>
<label>Battery discharge power</label>
<description>Battery discharging power.</description>
</add-channel>
<add-channel id="battery-discharge-energy-today">
<type>growatt:advanced-electric-energy</type>
<label>Discharge Energy Today</label>
<description>Battery discharge energy today.</description>
</add-channel>
<add-channel id="battery-discharge-energy-total">
<type>growatt:advanced-electric-energy</type>
<label>Discharge Energy Total</label>
<description>Total battery discharge energy.</description>
</add-channel>
<add-channel id="inverter-clock-offset">
<type>growatt:advanced-time</type>
</add-channel>
<add-channel id="inverter-current">
<type>growatt:advanced-electric-current</type>
<label>Inverter Current</label>
<description>Inverter current.</description>
</add-channel>
<add-channel id="inverter-fan-speed">
<type>growatt:advanced-percent</type>
<label>Inverter Fan</label>
<description>Inverter fan speed.</description>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>

View File

@ -105,6 +105,9 @@ public class GrowattTest {
void testGrottValuesAccessibility() throws FileNotFoundException, IOException { void testGrottValuesAccessibility() throws FileNotFoundException, IOException {
testGrottValuesAccessibility("simple"); testGrottValuesAccessibility("simple");
testGrottValuesAccessibility("sph"); testGrottValuesAccessibility("sph");
testGrottValuesAccessibility("spf");
testGrottValuesAccessibility("mid");
testGrottValuesAccessibility("meter");
} }
/** /**
@ -236,6 +239,7 @@ public class GrowattTest {
void testJsonFieldsMappedToDto() throws FileNotFoundException, IOException { void testJsonFieldsMappedToDto() throws FileNotFoundException, IOException {
testJsonFieldsMappedToDto("simple"); testJsonFieldsMappedToDto("simple");
testJsonFieldsMappedToDto("sph"); testJsonFieldsMappedToDto("sph");
testJsonFieldsMappedToDto("spf");
} }
/** /**
@ -353,7 +357,7 @@ public class GrowattTest {
@Test @Test
void testThreePhaseGrottValuesContents() throws FileNotFoundException, IOException, NoSuchFieldException, void testThreePhaseGrottValuesContents() throws FileNotFoundException, IOException, NoSuchFieldException,
SecurityException, IllegalAccessException, IllegalArgumentException { SecurityException, IllegalAccessException, IllegalArgumentException {
GrottValues grottValues = loadGrottValues("3phase"); GrottValues grottValues = loadGrottValues("mid");
assertNotNull(grottValues); assertNotNull(grottValues);
Map<String, QuantityType<?>> channelStates = GrottValuesHelper.getChannelStates(grottValues); Map<String, QuantityType<?>> channelStates = GrottValuesHelper.getChannelStates(grottValues);

View File

@ -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
}
}