mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[solax] Cloud connection support (#16124)
* Initial rearrangement of classes and cloud response in test Signed-off-by: Konstantin Polihronov <polychronov@gmail.com> Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
parent
6982f7a058
commit
0f6ae6506e
@ -14,7 +14,8 @@ In case the parsed information that comes with the binding out of the box differ
|
||||
|
||||
| Thing | Thing Type | Description |
|
||||
|------------------------|------------|-------------------------------------------------------------------------------------|
|
||||
| local-connect-inverter | Thing | This is model representation of inverter with all the data available as a channels |
|
||||
| local-connect-inverter | Thing | An inverter representation with all the data available as a channels (directly retrieved from the wi-fi module |
|
||||
| cloud-connect-inverter | Thing | An inverter representation with all the data available as a channels (retrieved from the Solax cloud API) |
|
||||
|
||||
Note: Channels may vary depending on the inverter type and the availability of information for parsing the raw data.
|
||||
If you're missing a channel this means that it's not supported for your inverter type.
|
||||
@ -66,13 +67,13 @@ If you're missing a channel this means that it's not supported for your inverter
|
||||
|
||||
### Battery channels
|
||||
|
||||
| Channel | Type | Description |
|
||||
|---------------------------|----------------------------|------------------------------------------------------------------------------------------------|
|
||||
| battery-power | Number:Power | The power to / from battery (negative means power is pulled from battery and vice-versa) [W] |
|
||||
| battery-current | Number:ElectricCurrent | The current to / from battery (negative means power is pulled from battery and vice-versa) [A] |
|
||||
| battery-voltage | Number:ElectricPotential | The voltage of the battery [V] |
|
||||
| battery-temperature | Number:Temperature | The temperature of the battery [C/F] |
|
||||
| battery-state-of-charge | Number | The state of charge of the battery [%] |
|
||||
| Channel | Type | Description |
|
||||
|---------------------------|----------------------------|----------------------------------------------------------------------------------------------------|
|
||||
| battery-power | Number:Power | The power to / from battery (negative means power is pulled from the battery and vice-versa) [W] |
|
||||
| battery-current | Number:ElectricCurrent | The current to / from battery (negative means power is pulled from the battery and vice-versa) [A] |
|
||||
| battery-voltage | Number:ElectricPotential | The voltage of the battery [V] |
|
||||
| battery-temperature | Number:Temperature | The temperature of the battery [C/F] |
|
||||
| battery-level | Number | The state of charge of the battery [%] |
|
||||
|
||||
### Grid related channels
|
||||
|
||||
@ -111,6 +112,40 @@ If you're missing a channel this means that it's not supported for your inverter
|
||||
| serialNumber | The serial number of the Wi-Fi module |
|
||||
| inverterType | Inverter Type (for example X1_HYBRID_G4) |
|
||||
|
||||
|
||||
### Cloud Connect Inverter Configuration
|
||||
|
||||
| Parameter | Description |
|
||||
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| refreshInterval | Defines the refresh interval when the binding polls from from the Solax cloud (in seconds). Optional parameter(min=15, max=600). Default is 30 seconds. Be advised that the cloud API is limited to max 10 calls per minute and 10000 calls per day. |
|
||||
| password | The registration number, shown in the Solax Cloud web portal. Mandatory parameter. |
|
||||
| token | Token for accessing the Solax Cloud API. Can be obtained via Service -> API on the Solax cloud web portal. Mandatory parameter. |
|
||||
|
||||
### Channels
|
||||
|
||||
| Channel | Type | Description |
|
||||
|---------------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| inverter-output-power | Number:Power | The output power of the inverter [W] |
|
||||
| pv1-power | Number:Power | The output power PV1 string [W] |
|
||||
| pv2-power | Number:Power | The output power PV2 string [W] |
|
||||
| pv3-power | Number:Power | The output power PV3 string [W] |
|
||||
| pv4-power | Number:Power | The output power PV4 string [W] |
|
||||
| pv-total-power | Number:Power | The output power of all the photovoltaic strings [W] |
|
||||
| battery-power | Number:Power | The power to / from battery (negative means power is pulled from the battery and vice-versa) [W] |
|
||||
| battery-level | Number | The state of charge of the battery [%] |
|
||||
| feed-in-power | Number:Power | The power to / from grid (negative means power is pulled from the grid and vice-versa) [W] |
|
||||
| total-feed-in-energy | Number:Energy | Total energy consumed from the electricity provider [kWh] |
|
||||
| total-consumption | Number:Energy | Total energy consumed for the building [kWh] |
|
||||
| today-energy | Number:Energy | Energy output from the inverter for the day [kWh] |
|
||||
| total-energy | Number:Energy | Total energy output from the inverter [kWh] |
|
||||
| raw-data | String | The raw data retrieved from inverter in JSON format. (Usable for channels not implemented. Can be consumed with the JSONpath transformation |
|
||||
| inverter-status | String | The status of the inverter. (For the various status types, refer to the API documentation) |
|
||||
| last-update-time | DateTime | Last time when a call has been made to the inverter |
|
||||
| inverter-meter2-power | Number:Power | Inverter power on meter2 [W] |
|
||||
| inverter-eps-power-r | Number:Power | Inverter AC EPS power R [W] |
|
||||
| inverter-eps-power-s | Number:Power | Inverter AC EPS power S [W] |
|
||||
| inverter-eps-power-t | Number:Power | Inverter AC EPS power T [W] |
|
||||
|
||||
## Full Example
|
||||
|
||||
Here are some file based examples.
|
||||
@ -120,6 +155,7 @@ Here are some file based examples.
|
||||
```java
|
||||
// The local connect inverter thing
|
||||
Thing solax:local-connect-inverter:localInverter [ refreshInterval=10, password="<SERIAL NUMBER OF THE WIFI MODULE>", hostname="<local IP/hostname in the network>" ]
|
||||
Thing solax:cloud-connect-inverter:cloudInverter [ refresh=30, password="<REG_NUMBER>", token="<TOKEN>" ]
|
||||
```
|
||||
|
||||
### Item Configuration
|
||||
@ -128,17 +164,38 @@ Thing solax:local-connect-inverter:localInverter [ refreshInterval=10, password
|
||||
Group gSolaxInverter "Solax Inverter" <energy> (boilerRoom)
|
||||
Group solarPanels "Solar panels" <energy> (gSolaxInverter)
|
||||
|
||||
Number solaxPowerWest "West [%.0f W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv1-power" }
|
||||
Number solaxPowerEast "East [%.0f W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv2-power" }
|
||||
// Direct connect
|
||||
Number solaxPowerWest "West Power [%d W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv1-power" }
|
||||
Number solaxPowerEast "East Power [%d W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv2-power" }
|
||||
Number solaxGenerationTotal "Total generаtion now [%.0f W]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels) { channel="solax:local-connect-inverter:localInverter:pv-total-power" }
|
||||
Number solaxVoltageWest "West Voltage [%.1f V]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv1-voltage" }
|
||||
Number solaxVoltageEast "East Voltage [%.1f V]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv2-voltage" }
|
||||
Number solaxCurrentWest "West Current [%.1f A]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv1-current" }
|
||||
Number solaxCurrentEast "East Current [%.1f A]" <solarplant> (gsolax_inverter,EveryChangePersist,solarPanels){ channel="solax:local-connect-inverter:localInverter:pv2-current" }
|
||||
Number solaxBatteryPower "Battery power [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-power" }
|
||||
Number solaxBatterySoc "Battery SoC [%.0f %%]" <batterylevel> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-state-of-charge" }
|
||||
Number solaxBatterySoc "Battery SoC [%.0f %%]" <batterylevel> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-level" }
|
||||
Number solaxBatteryTemperature "Battery temperature [%d °C]" <temperature> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-temperature" }
|
||||
Number solaxBatteryCurrent "Battery current [%.1f A]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-current" }
|
||||
Number solaxBatteryVoltage "Battery voltage [%.1f V]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:battery-voltage" }
|
||||
|
||||
Number solaxFeedInPower "Feed-in power (CEZ) [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:local-connect-inverter:localInverter:feed-in-power" }
|
||||
Number solaxCalculatedTotalFeedInPower "Calculated feed-in total power (CEZ) [%.0f KWh]" <energy> (gsolax_inverter,EveryChangePersist)
|
||||
Number solaxCalculatedTotalFeedInPowerThisMonth "Calculated feed-in total power this month (CEZ) [%.0f KWh]" <energy> (gsolax_inverter,EveryChangePersist)
|
||||
Number solaxAcPower "Invertor output power [%.0f W]" <energy> (gsolax_inverter,EveryChangePersist){ channel="solax:local-connect-inverter:localInverter:inverter-output-power" }
|
||||
Number solaxFrequency "Invertor frequency [%.2f Hz]" <energy> (gsolax_inverter,EveryChangePersist){ channel="solax:local-connect-inverter:localInverter:inverter-frequency" }
|
||||
Number solaxVoltage "Invertor voltage [%.1f V]" <energy> (gsolax_inverter,EveryChangePersist){ channel="solax:local-connect-inverter:localInverter:inverter-voltage" }
|
||||
|
||||
String solaxInverterType "Inverter Type [%s]" <energy> (gsolax_inverter) { channel="solax:local-connect-inverter:localInverter:inverter-type"}
|
||||
String solaxUploadTime "Last update time [%s]" <calendar> (gsolax_inverter) { channel="solax:local-connect-inverter:localInverter:last-update-time" }
|
||||
String solaxRawData "Raw data [%s]" <data> (gsolax_inverter) { channel="solax:local-connect-inverter:localInverter:raw-data" }
|
||||
String solaxLocalUploadTime "Local update time [%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1tS]" <calendar> (gsolax_inverter) { channel="solax:local-connect-inverter:localInverter:last-update-time" }
|
||||
String solaxCloudUploadTime "Cloud update time [%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1tS]" <calendar> (gsolax_inverter) { channel="solax:cloud-connect-inverter:cloudInverter:last-update-time" }
|
||||
|
||||
String solaxLocalRawData "Local raw data [%s]" <data> (gsolax_inverter) { channel="solax:local-connect-inverter:localInverter:raw-data" }
|
||||
String solaxCloudRawData "Cloud raw data [%s]" <data> (gsolax_inverter) { channel="solax:cloud-connect-inverter:cloudInverter:raw-data" }
|
||||
|
||||
// Cloud
|
||||
Number solaxYieldToday "Yield today [%.0f kWh]" <energy> (gsolax_inverter){ channel="solax:cloud-connect-inverter:cloudInverter:today-energy" }
|
||||
Number solaxYieldTotal "Yield total [%.0f kWh]" <energy> (gsolax_inverter) { channel="solax:cloud-connect-inverter:cloudInverter:total-energy" }
|
||||
Number solaxFeedInEnergy "Total Feed-in (CEZ) Power [%.0f kWh]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:cloud-connect-inverter:cloudInverter:total-feed-in-energy" }
|
||||
String solaxInverterStatus "Inverter Status [%s]" <energy> (gsolax_inverter,EveryChangePersist) { channel="solax:cloud-connect-inverter:cloudInverter:inverter-status" }
|
||||
```
|
||||
|
||||
### Sitemap Configuration
|
||||
@ -149,74 +206,146 @@ Frame label="Solar power strings" {
|
||||
Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxPowerWest valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"] {
|
||||
Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxVoltageEast valuecolor=[==0="gray",>0="green", >480="orange", >=500="red"]
|
||||
Text item=solaxVoltageWest valuecolor=[==0="gray",>0="green", >480="orange", >=500="red"]
|
||||
Text item=solaxCurrentEast valuecolor=[==0="gray",>0="green", >5="orange", >=10="red"]
|
||||
Text item=solaxCurrentWest valuecolor=[==0="gray",>0="green", >5="orange", >=10="red"]
|
||||
Text item=solaxGenerationTotal valuecolor=[<=100="gray",<=500="red", <2000="orange", >=2000="green"] {
|
||||
Text item=solaxPowerEast icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Text item=solaxPowerWest icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Text item=solaxGenerationTotal icon="energy" valuecolor=[<=30="gray",<=300="red", <1500="orange", >=1500="green"]
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solarPanels period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solarPanels period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solarPanels period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solarPanels period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solarPanels period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
}
|
||||
Frame label="Consumption" {
|
||||
Text item=solaxAcPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxAcPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
|
||||
Chart item=solaxAcPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxAcPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxAcPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxAcPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxAcPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solaxAcPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxAcPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxAcPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxAcPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxAcPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxFeedInPower valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxFeedInPower icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
|
||||
Chart item=solaxFeedInPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxFeedInPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxFeedInPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxFeedInPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxFeedInPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solaxFeedInPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxFeedInPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxFeedInPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxFeedInPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxFeedInPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxFrequency valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxFrequency icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
|
||||
Chart item=solaxFrequency period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxFrequency period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxFrequency period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxFrequency period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxFrequency period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxVoltage valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxVoltage icon="energy" valuecolor=[<=30="gray", <800="green", <1500="orange", >=1500="red"]
|
||||
Chart item=solaxVoltage period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxVoltage period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxVoltage period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxVoltage period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxVoltage period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
}
|
||||
Frame label="Battery" {
|
||||
Text item=solaxBatteryPower valuecolor=[<=-500="red", <0="orange", ==0="gray", >0="green"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxBatteryPower icon="energy" valuecolor=[<-800="red", <0="orange", ==0="gray", >=0="green"]
|
||||
Chart item=solaxBatteryPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatteryPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatteryPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatteryPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatteryPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solaxBatteryPower period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatteryPower period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatteryPower period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatteryPower period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatteryPower period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxBatteryCurrent valuecolor=[<=-5="red", <0="orange", ==0="gray", >0="green"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxBatteryCurrent icon="energy" valuecolor=[<-800="red", <0="orange", ==0="gray", >=0="green"]
|
||||
Chart item=solaxBatteryCurrent period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatteryCurrent period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatteryCurrent period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatteryCurrent period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatteryCurrent period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxBatteryVoltage valuecolor=[<=-500="red", <0="orange", ==0="gray", >0="green"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxBatteryVoltage icon="energy" valuecolor=[<-800="red", <0="orange", ==0="gray", >=0="green"]
|
||||
Chart item=solaxBatteryVoltage period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatteryVoltage period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatteryVoltage period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatteryVoltage period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatteryVoltage period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxBatterySoc valuecolor=[<=30="red", <50="orange", >=50="green"]
|
||||
Chart item=solaxBatterySoc period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatterySoc period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatterySoc period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatterySoc period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatterySoc period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
Chart item=solaxBatterySoc period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatterySoc period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatterySoc period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatterySoc period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatterySoc period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
Text item=solaxBatteryTemperature valuecolor=[<=35="green", <45="orange", >=45="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxBatteryTemperature valuecolor=[<=25="green", <40="orange", >=40="red"]
|
||||
Chart item=solaxBatteryTemperature period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxBatteryTemperature period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxBatteryTemperature period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxBatteryTemperature period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxBatteryTemperature period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
}
|
||||
Frame label="Statistics" {
|
||||
Text item=solaxYieldToday
|
||||
Text item=solaxYieldTotal
|
||||
Text item=solaxConsumeEnergy
|
||||
Text item=solaxFeedInEnergy
|
||||
Text item=solaxCalculatedTotalFeedInPower
|
||||
Text item=solaxCalculatedTotalFeedInPowerThisMonth valuecolor=[<200="green", >=200="orange", >=300="red"] {
|
||||
Switch item=Chart_Period label="Chart Period" mappings=[0="H", 1="D", 2="W", 3="M", 4="Y"]
|
||||
Text item=solaxCalculatedTotalFeedInPowerThisMonth valuecolor=[<=30="red", <50="orange", >=50="green"]
|
||||
Chart item=solaxCalculatedTotalFeedInPowerThisMonth period=h refresh=600 visibility=[Chart_Period==0]
|
||||
Chart item=solaxCalculatedTotalFeedInPowerThisMonth period=D refresh=3600 visibility=[Chart_Period==1]
|
||||
Chart item=solaxCalculatedTotalFeedInPowerThisMonth period=W refresh=3600 visibility=[Chart_Period==2, Chart_Period==Uninitialized]
|
||||
Chart item=solaxCalculatedTotalFeedInPowerThisMonth period=M refresh=3600 visibility=[Chart_Period==3]
|
||||
Chart item=solaxCalculatedTotalFeedInPowerThisMonth period=Y refresh=3600 visibility=[Chart_Period==4]
|
||||
}
|
||||
}
|
||||
Frame label="General" {
|
||||
Text item=solaxInverterStatus
|
||||
Text item=solaxLocalUploadTime
|
||||
Text item=solaxCloudUploadTime
|
||||
}
|
||||
Frame label="Raw data" {
|
||||
Text item=solaxLocalRawData
|
||||
Text item=solaxCloudRawData
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -26,14 +26,18 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
@NonNullByDefault
|
||||
public class SolaxBindingConstants {
|
||||
|
||||
protected static final String BINDING_ID = "solax";
|
||||
public static final String BINDING_ID = "solax";
|
||||
private static final String THING_LOCAL_CONNECT_INVERTER_ID = "local-connect-inverter";
|
||||
private static final String THING_CLOUD_CONNECT_INVERTER_ID = "cloud-connect-inverter";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_LOCAL_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID,
|
||||
THING_LOCAL_CONNECT_INVERTER_ID);
|
||||
public static final ThingTypeUID THING_TYPE_CLOUD_CONNECT_INVERTER = new ThingTypeUID(BINDING_ID,
|
||||
THING_CLOUD_CONNECT_INVERTER_ID);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LOCAL_CONNECT_INVERTER,
|
||||
THING_TYPE_CLOUD_CONNECT_INVERTER);
|
||||
|
||||
// List of properties
|
||||
public static final String PROPERTY_INVERTER_TYPE = "inverterType";
|
||||
@ -104,6 +108,14 @@ public class SolaxBindingConstants {
|
||||
public static final String CHANNEL_TODAY_FEED_IN_ENERGY = "today-feed-in-energy";
|
||||
public static final String CHANNEL_TODAY_CONSUMPTION = "today-consumption";
|
||||
|
||||
// Cloud specific channels
|
||||
public static final String CHANNEL_INVERTER_PV3_POWER = "pv3-power";
|
||||
public static final String CHANNEL_INVERTER_PV4_POWER = "pv4-power";
|
||||
public static final String CHANNEL_INVERTER_OUTPUT_POWER_METER2 = "inverter-meter2-power";
|
||||
public static final String CHANNEL_INVERTER_EPS_POWER_R = "inverter-eps-power-r";
|
||||
public static final String CHANNEL_INVERTER_EPS_POWER_S = "inverter-eps-power-s";
|
||||
public static final String CHANNEL_INVERTER_EPS_POWER_T = "inverter-eps-power-t";
|
||||
|
||||
// I18N Keys
|
||||
protected static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved";
|
||||
public static final String I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED = "@text/offline.communication-error.json-cannot-be-retrieved";
|
||||
}
|
||||
|
@ -25,4 +25,5 @@ public class SolaxConfiguration {
|
||||
public String hostname = "";
|
||||
public String password = "";
|
||||
public int refreshInterval = 10;
|
||||
public String token = "";
|
||||
}
|
||||
|
@ -16,12 +16,19 @@ import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.handlers.SolaxCloudHandler;
|
||||
import org.openhab.binding.solax.internal.handlers.SolaxLocalAccessHandler;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link SolaxHandlerFactory} is responsible for creating things and thing
|
||||
@ -33,6 +40,18 @@ import org.osgi.service.component.annotations.Component;
|
||||
@Component(configurationPid = "binding.solax", service = ThingHandlerFactory.class)
|
||||
public class SolaxHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private TranslationProvider i18nProvider;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
private LocaleProvider localeProvider;
|
||||
|
||||
@Activate
|
||||
public SolaxHandlerFactory(final @Reference TranslationProvider i18nProvider,
|
||||
final @Reference TimeZoneProvider timeZoneProvider, final @Reference LocaleProvider localeProvider) {
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
@ -42,7 +61,9 @@ public class SolaxHandlerFactory extends BaseThingHandlerFactory {
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (THING_TYPE_LOCAL_CONNECT_INVERTER.equals(thingTypeUID)) {
|
||||
return new SolaxLocalAccessHandler(thing);
|
||||
return new SolaxLocalAccessHandler(thing, i18nProvider, timeZoneProvider);
|
||||
} else if (THING_TYPE_CLOUD_CONNECT_INVERTER.equals(thingTypeUID)) {
|
||||
return new SolaxCloudHandler(thing, i18nProvider, timeZoneProvider, localeProvider);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.connectivity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link CloudHttpConnector} class uses HttpUtil to retrieve the raw JSON data from Inverter's Wi-Fi module.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CloudHttpConnector implements SolaxConnector {
|
||||
|
||||
private static final int HTTP_REQUEST_TIME_OUT = 5000;
|
||||
|
||||
private static final String CONTENT_TYPE = "application/json; charset=utf-8";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CloudHttpConnector.class);
|
||||
|
||||
private static final String URI = """
|
||||
https://www.solaxcloud.com/proxyApp/proxy/api/getRealtimeInfo.do?tokenId={tokenId}&sn={serialNumber}
|
||||
""";
|
||||
|
||||
private String uri;
|
||||
|
||||
public CloudHttpConnector(String tokenId, String serialNumber) {
|
||||
this(URI, tokenId, serialNumber);
|
||||
}
|
||||
|
||||
public CloudHttpConnector(String uri, String tokenId, String serialNumber) {
|
||||
this.uri = uri.replace("{tokenId}", tokenId).replace("{serialNumber}", serialNumber).trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String retrieveData() throws IOException {
|
||||
logger.debug("About to retrieve data from Uri: {}", uri);
|
||||
String result = HttpUtil.executeUrl(HttpMethod.GET.name(), uri, null, CONTENT_TYPE, HTTP_REQUEST_TIME_OUT);
|
||||
logger.trace("Retrieved content = {}", result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -25,4 +25,6 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
public interface RawDataBean {
|
||||
@Nullable
|
||||
String getRawData();
|
||||
|
||||
public void setRawData(String rawData);
|
||||
}
|
||||
|
@ -0,0 +1,268 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.connectivity.rawdata.cloud;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.directory.api.util.Strings;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.openhab.binding.solax.internal.model.cloud.CloudInverterData;
|
||||
import org.openhab.binding.solax.internal.util.GsonSupplier;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link CloudRawDataBean} is used as a storage for mapping the raw data collected from the cloud to this object.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CloudRawDataBean implements CloudInverterData {
|
||||
|
||||
public static final String QUERY_SUCCESS = "Query success!";
|
||||
public static final String ERROR = "error";
|
||||
|
||||
private static final DateTimeFormatter CUSTOM_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private boolean success;
|
||||
private @NonNullByDefault({}) String exception;
|
||||
private @NonNullByDefault({}) Result result;
|
||||
private int code;
|
||||
private @NonNullByDefault({}) String rawData;
|
||||
|
||||
// For JSON serialization / deserialization purposes
|
||||
public CloudRawDataBean() {
|
||||
}
|
||||
|
||||
public CloudRawDataBean(boolean isSuccess) {
|
||||
this.success = isSuccess;
|
||||
this.exception = isSuccess ? QUERY_SUCCESS : ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public @Nullable String getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public void setException(String exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public @Nullable Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setResult(Result result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
// Inner Implementation / DTO of the CloudInverterData starts here
|
||||
|
||||
@Override
|
||||
public String getInverterSerialNumber() {
|
||||
String inverterSN = result.getInverterSN();
|
||||
return inverterSN != null ? inverterSN : Strings.EMPTY_STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWifiSerialNumber() {
|
||||
String serialNumber = result.getSn();
|
||||
return serialNumber != null ? serialNumber : Strings.EMPTY_STRING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOverallResult() {
|
||||
// Why the ternary operator does not get taken into account by the JDT and the maven build?
|
||||
String result = exception;
|
||||
if (result == null) {
|
||||
result = "Retrieved message from the cloud API is null. Something wrong happened.";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getInverterOutputPower() {
|
||||
return notNullResult(() -> result.getAcPower());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getYieldToday() {
|
||||
return notNullResult(() -> result.getYieldToday());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getYieldTotal() {
|
||||
return notNullResult(() -> result.getYieldTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFeedInPower() {
|
||||
return notNullResult(() -> result.getFeedInPower());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFeedInEnergy() {
|
||||
return notNullResult(() -> result.getFeedInEnergy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getConsumeEnergy() {
|
||||
return notNullResult(() -> result.getConsumeEnergy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFeedInPowerM2() {
|
||||
return notNullResult(() -> result.getFeedInPowerM2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBatteryLevel() {
|
||||
return notNullResult(() -> result.getSoc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEPSPowerR() {
|
||||
return notNullResult(() -> result.getPeps1());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEPSPowerS() {
|
||||
return notNullResult(() -> result.getPeps2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEPSPowerT() {
|
||||
return notNullResult(() -> result.getPeps3());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InverterType getInverterType() {
|
||||
return InverterType.fromIndex(result.getInverterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZonedDateTime getUploadTime(ZoneId zoneId) {
|
||||
String uploadTime = result.getUploadTime();
|
||||
if (uploadTime != null) {
|
||||
return ZonedDateTime.of(LocalDateTime.parse(uploadTime, CUSTOM_DATE_FORMATTER), zoneId);
|
||||
}
|
||||
|
||||
return ZonedDateTime.of(LocalDateTime.MIN, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBatteryPower() {
|
||||
return notNullResult(() -> result.getBatPower());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPowerPv1() {
|
||||
return notNullResult(() -> result.getPowerDc1());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPowerPv2() {
|
||||
return notNullResult(() -> result.getPowerDc2());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPowerPv3() {
|
||||
return notNullResult(() -> result.getPowerDc3());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPowerPv4() {
|
||||
return notNullResult(() -> result.getPowerDc4());
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getInverterWorkModeCode() {
|
||||
return (short) result.getInverterStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryStatus() {
|
||||
return result.getBatStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPVTotalPower() {
|
||||
return getPowerPv1() + getPowerPv2() + getPowerPv3() + getPowerPv4();
|
||||
}
|
||||
|
||||
public static CloudRawDataBean fromJson(String json) {
|
||||
if (json.isEmpty()) {
|
||||
return new CloudRawDataBean(false);
|
||||
}
|
||||
|
||||
Gson gson = GsonSupplier.getInstance();
|
||||
CloudRawDataBean deserializedObject = gson.fromJson(json, CloudRawDataBean.class);
|
||||
if (deserializedObject == null) {
|
||||
return new CloudRawDataBean(false);
|
||||
}
|
||||
deserializedObject.setRawData(json);
|
||||
return deserializedObject;
|
||||
}
|
||||
|
||||
public void setRawData(String json) {
|
||||
this.rawData = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getRawData() {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
String exception = getException();
|
||||
return ERROR.equals(exception);
|
||||
}
|
||||
|
||||
private double notNullResult(Supplier<@Nullable Double> supplier) {
|
||||
Double returnValue = supplier.get();
|
||||
return returnValue != null ? returnValue : Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return getResult() != null && getRawData() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CloudRawDataBean [success=" + success + ", exception=" + exception + ", result=" + result + ", code="
|
||||
+ code + ", rawData=" + rawData + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.connectivity.rawdata.cloud;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link Result} is a sub-class used in the JSON response which provides the actual inverter data from the cloud.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Result {
|
||||
|
||||
private @Nullable String inverterSN;
|
||||
private @Nullable String sn;
|
||||
@SerializedName("acpower")
|
||||
private @Nullable Double acPower;
|
||||
@SerializedName("yieldtoday")
|
||||
private @Nullable Double yieldToday;
|
||||
@SerializedName("yieldtotal")
|
||||
private @Nullable Double yieldTotal;
|
||||
@SerializedName("feedinpower")
|
||||
private @Nullable Double feedInPower;
|
||||
@SerializedName("feedinenergy")
|
||||
private @Nullable Double feedInEnergy;
|
||||
@SerializedName("consumeenergy")
|
||||
private @Nullable Double consumeEnergy;
|
||||
@SerializedName("feedinpowerM2")
|
||||
private @Nullable Double feedInPowerM2;
|
||||
private @Nullable Double soc;
|
||||
private @Nullable Double peps1;
|
||||
private @Nullable Double peps2;
|
||||
private @Nullable Double peps3;
|
||||
private int inverterType;
|
||||
private int inverterStatus;
|
||||
private @Nullable String uploadTime;
|
||||
private @Nullable Double batPower;
|
||||
@SerializedName("powerdc1")
|
||||
private @Nullable Double powerDc1;
|
||||
@SerializedName("powerdc2")
|
||||
private @Nullable Double powerDc2;
|
||||
@SerializedName("powerdc3")
|
||||
private @Nullable Double powerDc3;
|
||||
@SerializedName("powerdc4")
|
||||
private @Nullable Double powerDc4;
|
||||
private int batStatus;
|
||||
|
||||
public @Nullable String getInverterSN() {
|
||||
return inverterSN;
|
||||
}
|
||||
|
||||
public void setInverterSN(String inverterSN) {
|
||||
this.inverterSN = inverterSN;
|
||||
}
|
||||
|
||||
public @Nullable String getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSn(String sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
public @Nullable Double getAcPower() {
|
||||
return acPower;
|
||||
}
|
||||
|
||||
public void setAcPower(Double acPower) {
|
||||
this.acPower = acPower;
|
||||
}
|
||||
|
||||
public @Nullable Double getYieldToday() {
|
||||
return yieldToday;
|
||||
}
|
||||
|
||||
public void setYieldToday(Double yieldToday) {
|
||||
this.yieldToday = yieldToday;
|
||||
}
|
||||
|
||||
public @Nullable Double getYieldTotal() {
|
||||
return yieldTotal;
|
||||
}
|
||||
|
||||
public void setYieldTotal(Double yieldTotal) {
|
||||
this.yieldTotal = yieldTotal;
|
||||
}
|
||||
|
||||
public @Nullable Double getFeedInPower() {
|
||||
return feedInPower;
|
||||
}
|
||||
|
||||
public void setFeedInPower(Double feedInPower) {
|
||||
this.feedInPower = feedInPower;
|
||||
}
|
||||
|
||||
public @Nullable Double getFeedInEnergy() {
|
||||
return feedInEnergy;
|
||||
}
|
||||
|
||||
public void setFeedInEnergy(Double feedInEnergy) {
|
||||
this.feedInEnergy = feedInEnergy;
|
||||
}
|
||||
|
||||
public @Nullable Double getConsumeEnergy() {
|
||||
return consumeEnergy;
|
||||
}
|
||||
|
||||
public void setConsumeEnergy(Double consumeEnergy) {
|
||||
this.consumeEnergy = consumeEnergy;
|
||||
}
|
||||
|
||||
public @Nullable Double getFeedInPowerM2() {
|
||||
return feedInPowerM2;
|
||||
}
|
||||
|
||||
public void setFeedInPowerM2(Double feedInPowerM2) {
|
||||
this.feedInPowerM2 = feedInPowerM2;
|
||||
}
|
||||
|
||||
public @Nullable Double getSoc() {
|
||||
return soc;
|
||||
}
|
||||
|
||||
public void setSoc(Double soc) {
|
||||
this.soc = soc;
|
||||
}
|
||||
|
||||
public @Nullable Double getPeps1() {
|
||||
return peps1;
|
||||
}
|
||||
|
||||
public void setPeps1(Double peps1) {
|
||||
this.peps1 = peps1;
|
||||
}
|
||||
|
||||
public @Nullable Double getPeps2() {
|
||||
return peps2;
|
||||
}
|
||||
|
||||
public void setPeps2(Double peps2) {
|
||||
this.peps2 = peps2;
|
||||
}
|
||||
|
||||
public @Nullable Double getPeps3() {
|
||||
return peps3;
|
||||
}
|
||||
|
||||
public void setPeps3(Double peps3) {
|
||||
this.peps3 = peps3;
|
||||
}
|
||||
|
||||
public int getInverterType() {
|
||||
return inverterType;
|
||||
}
|
||||
|
||||
public void setInverterType(int inverterType) {
|
||||
this.inverterType = inverterType;
|
||||
}
|
||||
|
||||
public int getInverterStatus() {
|
||||
return inverterStatus;
|
||||
}
|
||||
|
||||
public void setInverterStatus(int inverterStatus) {
|
||||
this.inverterStatus = inverterStatus;
|
||||
}
|
||||
|
||||
public @Nullable String getUploadTime() {
|
||||
return uploadTime;
|
||||
}
|
||||
|
||||
public void setUploadTime(String uploadTime) {
|
||||
this.uploadTime = uploadTime;
|
||||
}
|
||||
|
||||
public @Nullable Double getBatPower() {
|
||||
return batPower;
|
||||
}
|
||||
|
||||
public void setBatPower(Double batPower) {
|
||||
this.batPower = batPower;
|
||||
}
|
||||
|
||||
public @Nullable Double getPowerDc1() {
|
||||
return powerDc1;
|
||||
}
|
||||
|
||||
public void setPowerDc1(Double powerDc1) {
|
||||
this.powerDc1 = powerDc1;
|
||||
}
|
||||
|
||||
public @Nullable Double getPowerDc2() {
|
||||
return powerDc2;
|
||||
}
|
||||
|
||||
public void setPowerDc2(Double powerDc2) {
|
||||
this.powerDc2 = powerDc2;
|
||||
}
|
||||
|
||||
public @Nullable Double getPowerDc3() {
|
||||
return powerDc3;
|
||||
}
|
||||
|
||||
public void setPowerDc3(Double powerDc3) {
|
||||
this.powerDc3 = powerDc3;
|
||||
}
|
||||
|
||||
public @Nullable Double getPowerDc4() {
|
||||
return powerDc4;
|
||||
}
|
||||
|
||||
public void setPowerDc4(Double powerDc4) {
|
||||
this.powerDc4 = powerDc4;
|
||||
}
|
||||
|
||||
public int getBatStatus() {
|
||||
return batStatus;
|
||||
}
|
||||
|
||||
public void setBatStatus(int batStatus) {
|
||||
this.batStatus = batStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Result [inverterSN=" + inverterSN + ", sn=" + sn + ", acPower=" + acPower + ", yieldToday=" + yieldToday
|
||||
+ ", yieldTotal=" + yieldTotal + ", feedInPower=" + feedInPower + ", feedInEnergy=" + feedInEnergy
|
||||
+ ", consumeEnergy=" + consumeEnergy + ", feedInPowerM2=" + feedInPowerM2 + ", soc=" + soc + ", peps1="
|
||||
+ peps1 + ", peps2=" + peps2 + ", peps3=" + peps3 + ", inverterType=" + inverterType
|
||||
+ ", inverterStatus=" + inverterStatus + ", uploadTime=" + uploadTime + ", batPower=" + batPower
|
||||
+ ", powerDc1=" + powerDc1 + ", powerDc2=" + powerDc2 + ", powerDc3=" + powerDc3 + ", powerDc4="
|
||||
+ powerDc4 + ", batStatus=" + batStatus + "]";
|
||||
}
|
||||
}
|
@ -10,12 +10,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.connectivity.rawdata;
|
||||
package org.openhab.binding.solax.internal.connectivity.rawdata.local;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.RawDataBean;
|
||||
import org.openhab.binding.solax.internal.util.GsonSupplier;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@ -23,7 +24,6 @@ import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The {@link LocalConnectRawDataBean} collects the raw data and the specific implementation to return the parsed data.
|
||||
* If there are differences between the inverters probably would be wise to split the parsing in seprate class(es)
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@ -90,6 +90,7 @@ public class LocalConnectRawDataBean implements RawDataBean {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRawData(String rawData) {
|
||||
this.rawData = rawData;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SolaxUpdateException} exception thrown to the abstract class from the sub-classes if something goes wrong
|
||||
* with the data update
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolaxUpdateException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private @Nullable Object[] args;
|
||||
|
||||
public SolaxUpdateException(String message, @Nullable Object... args) {
|
||||
super(message);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public @Nullable Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.SolaxBindingConstants;
|
||||
import org.openhab.binding.solax.internal.SolaxConfiguration;
|
||||
import org.openhab.binding.solax.internal.connectivity.SolaxConnector;
|
||||
import org.openhab.binding.solax.internal.exceptions.SolaxUpdateException;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SolaxCloudHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AbstractSolaxHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AbstractSolaxHandler.class);
|
||||
|
||||
private static final int INITIAL_SCHEDULE_DELAY_SECONDS = 0;
|
||||
|
||||
private @NonNullByDefault({}) SolaxConnector connector;
|
||||
|
||||
private @Nullable ScheduledFuture<?> schedule;
|
||||
|
||||
private final ReentrantLock retrieveDataCallLock = new ReentrantLock();
|
||||
|
||||
protected final TranslationProvider i18nProvider;
|
||||
|
||||
protected final ZoneId timeZone;
|
||||
|
||||
public AbstractSolaxHandler(Thing thing, TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.timeZone = timeZoneProvider.getTimeZone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
SolaxConfiguration config = getConfigAs(SolaxConfiguration.class);
|
||||
connector = createConnector(config);
|
||||
int refreshInterval = config.refreshInterval;
|
||||
TimeUnit timeUnit = TimeUnit.SECONDS;
|
||||
|
||||
logger.debug("Scheduling regular interval retrieval every {} {}", refreshInterval, timeUnit);
|
||||
schedule = scheduler.scheduleWithFixedDelay(this::retrieveData, INITIAL_SCHEDULE_DELAY_SECONDS, refreshInterval,
|
||||
timeUnit);
|
||||
}
|
||||
|
||||
private void retrieveData() {
|
||||
if (retrieveDataCallLock.tryLock()) {
|
||||
try {
|
||||
String rawJsonData = connector.retrieveData();
|
||||
logger.debug("Raw data retrieved = {}", rawJsonData);
|
||||
|
||||
if (rawJsonData != null && !rawJsonData.isEmpty()) {
|
||||
updateFromData(rawJsonData);
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception received while attempting to retrieve data via HTTP", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (SolaxUpdateException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} finally {
|
||||
retrieveDataCallLock.unlock();
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unable to retrieve data because a request is already in progress.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
scheduler.execute(this::retrieveData);
|
||||
} else {
|
||||
logger.debug("Binding {} only supports refresh command", SolaxBindingConstants.BINDING_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
cancelSchedule();
|
||||
}
|
||||
|
||||
protected void cancelSchedule() {
|
||||
ScheduledFuture<?> schedule = this.schedule;
|
||||
if (schedule != null) {
|
||||
logger.debug("Cancelling schedule {}", schedule);
|
||||
schedule.cancel(true);
|
||||
this.schedule = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract SolaxConnector createConnector(SolaxConfiguration config);
|
||||
|
||||
protected abstract void updateFromData(String rawJsonData) throws SolaxUpdateException;
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.handlers;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.SolaxBindingConstants;
|
||||
import org.openhab.binding.solax.internal.SolaxConfiguration;
|
||||
import org.openhab.binding.solax.internal.connectivity.CloudHttpConnector;
|
||||
import org.openhab.binding.solax.internal.connectivity.SolaxConnector;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.cloud.CloudRawDataBean;
|
||||
import org.openhab.binding.solax.internal.exceptions.SolaxUpdateException;
|
||||
import org.openhab.binding.solax.internal.model.cloud.CloudInverterData;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
* The {@link SolaxCloudHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolaxCloudHandler extends AbstractSolaxHandler {
|
||||
|
||||
public SolaxCloudHandler(Thing thing, TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider,
|
||||
LocaleProvider localeProvider) {
|
||||
super(thing, i18nProvider, timeZoneProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SolaxConnector createConnector(SolaxConfiguration config) {
|
||||
return new CloudHttpConnector(config.token, config.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFromData(String rawJsonData) throws SolaxUpdateException {
|
||||
CloudRawDataBean rawCloudBean = CloudRawDataBean.fromJson(rawJsonData);
|
||||
if (!rawCloudBean.isValid()) {
|
||||
throw new SolaxUpdateException("Deserialized JSON response is not valid. Bean = {}, rawJsonData = {}",
|
||||
rawCloudBean, rawJsonData);
|
||||
}
|
||||
|
||||
if (!rawCloudBean.isSuccess() || rawCloudBean.isError()) {
|
||||
throw new SolaxUpdateException(
|
||||
"Connection to cloud was successful but the cloud API returned error. response = {}", rawCloudBean);
|
||||
}
|
||||
|
||||
updateProperties(rawCloudBean);
|
||||
updateChannels(rawCloudBean);
|
||||
}
|
||||
|
||||
private void updateProperties(CloudInverterData cloudData) {
|
||||
updateProperty(Thing.PROPERTY_SERIAL_NUMBER, cloudData.getInverterSerialNumber());
|
||||
updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, cloudData.getInverterType().name());
|
||||
}
|
||||
|
||||
private void updateChannels(CloudInverterData inverterData) {
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_PV1_POWER,
|
||||
new QuantityType<>(inverterData.getPowerPv1(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_PV2_POWER,
|
||||
new QuantityType<>(inverterData.getPowerPv2(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_PV3_POWER,
|
||||
new QuantityType<>(inverterData.getPowerPv3(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_PV4_POWER,
|
||||
new QuantityType<>(inverterData.getPowerPv4(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_PV_TOTAL_POWER,
|
||||
new QuantityType<>(inverterData.getPVTotalPower(), Units.WATT));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_BATTERY_POWER,
|
||||
new QuantityType<>(inverterData.getBatteryPower(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_BATTERY_STATE_OF_CHARGE,
|
||||
new QuantityType<>(inverterData.getBatteryLevel(), Units.PERCENT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_FEED_IN_POWER,
|
||||
new QuantityType<>(inverterData.getFeedInPower(), Units.WATT));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_TOTAL_FEED_IN_ENERGY,
|
||||
new QuantityType<>(inverterData.getFeedInEnergy(), Units.KILOWATT_HOUR));
|
||||
updateState(SolaxBindingConstants.CHANNEL_TOTAL_CONSUMPTION,
|
||||
new QuantityType<>(inverterData.getConsumeEnergy(), Units.KILOWATT_HOUR));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_OUTPUT_POWER_METER2,
|
||||
new QuantityType<>(inverterData.getFeedInPowerM2(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_EPS_POWER_R,
|
||||
new QuantityType<>(inverterData.getEPSPowerR(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_EPS_POWER_S,
|
||||
new QuantityType<>(inverterData.getEPSPowerS(), Units.WATT));
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_EPS_POWER_T,
|
||||
new QuantityType<>(inverterData.getEPSPowerT(), Units.WATT));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_TODAY_ENERGY,
|
||||
new QuantityType<>(inverterData.getYieldToday(), Units.KILOWATT_HOUR));
|
||||
updateState(SolaxBindingConstants.CHANNEL_TOTAL_ENERGY,
|
||||
new QuantityType<>(inverterData.getYieldTotal(), Units.KILOWATT_HOUR));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_INVERTER_WORKMODE,
|
||||
new StringType(inverterData.getInverterWorkMode()));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_TIMESTAMP, new DateTimeType(inverterData.getUploadTime(timeZone)));
|
||||
|
||||
updateState(SolaxBindingConstants.CHANNEL_RAW_DATA, new StringType(inverterData.getRawData()));
|
||||
}
|
||||
}
|
@ -10,40 +10,36 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal;
|
||||
package org.openhab.binding.solax.internal.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.measure.Quantity;
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.SolaxBindingConstants;
|
||||
import org.openhab.binding.solax.internal.SolaxConfiguration;
|
||||
import org.openhab.binding.solax.internal.connectivity.LocalHttpConnector;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.SolaxConnector;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.openhab.binding.solax.internal.model.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -57,64 +53,25 @@ import com.google.gson.JsonParseException;
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
public class SolaxLocalAccessHandler extends AbstractSolaxHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SolaxLocalAccessHandler.class);
|
||||
|
||||
private static final int INITIAL_SCHEDULE_DELAY_SECONDS = 5;
|
||||
|
||||
private @NonNullByDefault({}) LocalHttpConnector localHttpConnector;
|
||||
|
||||
private @Nullable ScheduledFuture<?> schedule;
|
||||
|
||||
private boolean alreadyRemovedUnsupportedChannels;
|
||||
|
||||
private final Set<String> unsupportedExistingChannels = new HashSet<>();
|
||||
|
||||
private final ReentrantLock retrieveDataCallLock = new ReentrantLock();
|
||||
|
||||
public SolaxLocalAccessHandler(Thing thing) {
|
||||
super(thing);
|
||||
public SolaxLocalAccessHandler(Thing thing, TranslationProvider i18nProvider, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing, i18nProvider, timeZoneProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
SolaxConfiguration config = getConfigAs(SolaxConfiguration.class);
|
||||
localHttpConnector = new LocalHttpConnector(config.password, config.hostname);
|
||||
int refreshInterval = config.refreshInterval;
|
||||
TimeUnit timeUnit = TimeUnit.SECONDS;
|
||||
|
||||
logger.debug("Scheduling regular interval retrieval every {} {}", refreshInterval, timeUnit);
|
||||
schedule = scheduler.scheduleWithFixedDelay(this::retrieveData, INITIAL_SCHEDULE_DELAY_SECONDS, refreshInterval,
|
||||
timeUnit);
|
||||
protected SolaxConnector createConnector(SolaxConfiguration config) {
|
||||
return new LocalHttpConnector(config.password, config.hostname);
|
||||
}
|
||||
|
||||
private void retrieveData() {
|
||||
if (retrieveDataCallLock.tryLock()) {
|
||||
try {
|
||||
String rawJsonData = localHttpConnector.retrieveData();
|
||||
logger.debug("Raw data retrieved = {}", rawJsonData);
|
||||
|
||||
if (rawJsonData != null && !rawJsonData.isEmpty()) {
|
||||
updateFromData(rawJsonData);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
SolaxBindingConstants.I18N_KEY_OFFLINE_COMMUNICATION_ERROR_JSON_CANNOT_BE_RETRIEVED);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception received while attempting to retrieve data via HTTP", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} finally {
|
||||
retrieveDataCallLock.unlock();
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unable to retrieve data because a request is already in progress.");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFromData(String rawJsonData) {
|
||||
@Override
|
||||
protected void updateFromData(String rawJsonData) {
|
||||
try {
|
||||
LocalConnectRawDataBean rawDataBean = parseJson(rawJsonData);
|
||||
InverterType inverterType = calculateInverterType(rawDataBean);
|
||||
@ -125,13 +82,9 @@ public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
alreadyRemovedUnsupportedChannels = true;
|
||||
}
|
||||
|
||||
InverterData genericInverterData = parser.getData(rawDataBean);
|
||||
LocalInverterData genericInverterData = parser.getData(rawDataBean);
|
||||
updateChannels(parser, genericInverterData);
|
||||
updateProperties(genericInverterData);
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} else {
|
||||
cancelSchedule();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
@ -144,9 +97,9 @@ public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
}
|
||||
|
||||
private LocalConnectRawDataBean parseJson(String rawJsonData) {
|
||||
LocalConnectRawDataBean inverterParsedData = LocalConnectRawDataBean.fromJson(rawJsonData);
|
||||
logger.debug("Received a new inverter JSON object. Data = {}", inverterParsedData.toString());
|
||||
return inverterParsedData;
|
||||
LocalConnectRawDataBean fromJson = LocalConnectRawDataBean.fromJson(rawJsonData);
|
||||
logger.debug("Received a new inverter JSON object. Data = {}", fromJson.toString());
|
||||
return fromJson;
|
||||
}
|
||||
|
||||
private InverterType calculateInverterType(LocalConnectRawDataBean rawDataBean) {
|
||||
@ -154,12 +107,12 @@ public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
return InverterType.fromIndex(type);
|
||||
}
|
||||
|
||||
private void updateProperties(InverterData genericInverterData) {
|
||||
private void updateProperties(LocalInverterData genericInverterData) {
|
||||
updateProperty(Thing.PROPERTY_SERIAL_NUMBER, genericInverterData.getWifiSerial());
|
||||
updateProperty(SolaxBindingConstants.PROPERTY_INVERTER_TYPE, genericInverterData.getInverterType().name());
|
||||
}
|
||||
|
||||
private void updateChannels(RawDataParser parser, InverterData inverterData) {
|
||||
private void updateChannels(RawDataParser parser, LocalInverterData inverterData) {
|
||||
updateState(SolaxBindingConstants.CHANNEL_RAW_DATA, new StringType(inverterData.getRawData()));
|
||||
|
||||
Set<String> supportedChannels = parser.getSupportedChannels();
|
||||
@ -190,10 +143,6 @@ public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
supportedChannels);
|
||||
updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_TEMPERATURE, inverterData.getBatteryTemperature(),
|
||||
SIUnits.CELSIUS, supportedChannels);
|
||||
updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_TEMPERATURE1, inverterData.getInverterTemperature1(),
|
||||
SIUnits.CELSIUS, supportedChannels);
|
||||
updateChannel(SolaxBindingConstants.CHANNEL_INVERTER_TEMPERATURE2, inverterData.getInverterTemperature2(),
|
||||
SIUnits.CELSIUS, supportedChannels);
|
||||
updateChannel(SolaxBindingConstants.CHANNEL_BATTERY_STATE_OF_CHARGE, inverterData.getBatteryLevel(),
|
||||
Units.PERCENT, supportedChannels);
|
||||
updateChannel(SolaxBindingConstants.CHANNEL_FEED_IN_POWER, inverterData.getFeedInPower(), Units.WATT,
|
||||
@ -297,29 +246,6 @@ public class SolaxLocalAccessHandler extends BaseThingHandler {
|
||||
channelsToRemoveForLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
scheduler.execute(this::retrieveData);
|
||||
} else {
|
||||
logger.debug("Binding {} only supports refresh command", SolaxBindingConstants.BINDING_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
cancelSchedule();
|
||||
}
|
||||
|
||||
private void cancelSchedule() {
|
||||
ScheduledFuture<?> schedule = this.schedule;
|
||||
if (schedule != null) {
|
||||
schedule.cancel(true);
|
||||
this.schedule = null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Quantity<T>> void updateChannel(String channelID, double value, Unit<T> unit,
|
||||
Set<String> supportedChannels) {
|
||||
if (supportedChannels.contains(channelID)) {
|
@ -18,10 +18,10 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.model.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.parsers.X1HybridG4DataParser;
|
||||
import org.openhab.binding.solax.internal.model.parsers.X3HybridG4DataParser;
|
||||
import org.openhab.binding.solax.internal.model.parsers.X3MicOrProG2DataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.X1HybridG4DataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.X3HybridG4DataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.X3MicOrProG2DataParser;
|
||||
|
||||
/**
|
||||
* The {@link InverterType} class is enum representing the different inverter types with a simple logic to convert from
|
||||
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.model.cloud;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
|
||||
/**
|
||||
* The {@link CloudInverterData} Interface for the parsed inverter data in meaningful format. Currently the cloud
|
||||
* responds with the same response for all type of inverters, so it's modeled in a single interface.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface CloudInverterData {
|
||||
boolean isSuccess();
|
||||
|
||||
String getOverallResult();
|
||||
|
||||
int getCode();
|
||||
|
||||
String getInverterSerialNumber();
|
||||
|
||||
String getWifiSerialNumber();
|
||||
|
||||
double getInverterOutputPower();
|
||||
|
||||
double getYieldToday();
|
||||
|
||||
double getYieldTotal();
|
||||
|
||||
double getFeedInPower();
|
||||
|
||||
double getFeedInEnergy();
|
||||
|
||||
double getConsumeEnergy();
|
||||
|
||||
double getFeedInPowerM2();
|
||||
|
||||
double getEPSPowerR();
|
||||
|
||||
double getEPSPowerS();
|
||||
|
||||
double getEPSPowerT();
|
||||
|
||||
InverterType getInverterType();
|
||||
|
||||
double getBatteryLevel();
|
||||
|
||||
double getBatteryPower();
|
||||
|
||||
double getPowerPv1();
|
||||
|
||||
double getPowerPv2();
|
||||
|
||||
double getPowerPv3();
|
||||
|
||||
double getPowerPv4();
|
||||
|
||||
short getInverterWorkModeCode();
|
||||
|
||||
default String getInverterWorkMode() {
|
||||
return String.valueOf(getInverterWorkModeCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented in the API so currently it's only an int probably the code of the battery status
|
||||
*
|
||||
* @return battery status code as int
|
||||
*/
|
||||
int getBatteryStatus();
|
||||
|
||||
@Nullable
|
||||
String getRawData();
|
||||
|
||||
double getPVTotalPower();
|
||||
|
||||
ZonedDateTime getUploadTime(ZoneId zoneId);
|
||||
}
|
@ -10,30 +10,29 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.impl;
|
||||
package org.openhab.binding.solax.internal.model.local;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link CommonInverterData} is an abstract class that contains the common information, applicable for all
|
||||
* The {@link CommonLocalInverterData} is an abstract class that contains the common information, applicable for all
|
||||
* inverters.
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class CommonInverterData implements InverterData {
|
||||
public abstract class CommonLocalInverterData implements LocalInverterData {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CommonInverterData.class);
|
||||
private final Logger logger = LoggerFactory.getLogger(CommonLocalInverterData.class);
|
||||
|
||||
private LocalConnectRawDataBean data;
|
||||
|
||||
public CommonInverterData(LocalConnectRawDataBean data) {
|
||||
public CommonLocalInverterData(LocalConnectRawDataBean data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
@ -10,18 +10,19 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model;
|
||||
package org.openhab.binding.solax.internal.model.local;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
|
||||
/**
|
||||
* The {@link InverterData} Interface for the parsed inverter data in meaningful format
|
||||
* The {@link LocalInverterData} Interface for the parsed inverter data in meaningful format
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface InverterData {
|
||||
public interface LocalInverterData {
|
||||
|
||||
@Nullable
|
||||
String getWifiSerial();
|
||||
@ -90,14 +91,6 @@ public interface InverterData {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
|
||||
default short getInverterWorkModeCode() {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
|
||||
default String getInverterWorkMode() {
|
||||
return String.valueOf(getInverterWorkModeCode());
|
||||
}
|
||||
|
||||
default short getBatteryLevel() {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
@ -222,6 +215,14 @@ public interface InverterData {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
|
||||
default short getInverterWorkModeCode() {
|
||||
return Short.MIN_VALUE;
|
||||
}
|
||||
|
||||
default String getInverterWorkMode() {
|
||||
return String.valueOf(getInverterWorkModeCode());
|
||||
}
|
||||
|
||||
default String toStringDetailed() {
|
||||
return "WifiSerial = " + getWifiSerial() + ", WifiVersion = " + getWifiVersion() + ", InverterType = "
|
||||
+ getInverterType() + ", BatteryPower = " + getBatteryPower() + "W, Battery SoC = " + getBatteryLevel()
|
@ -10,10 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.impl;
|
||||
package org.openhab.binding.solax.internal.model.local;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
|
||||
/**
|
||||
* The {@link X1HybridG4InverterData} is an implementation of the single phased inverter data interface for X1 Hybrid G4
|
||||
@ -22,7 +22,7 @@ import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDa
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class X1HybridG4InverterData extends CommonInverterData {
|
||||
public class X1HybridG4InverterData extends CommonLocalInverterData {
|
||||
|
||||
public X1HybridG4InverterData(LocalConnectRawDataBean data) {
|
||||
super(data);
|
||||
@ -83,11 +83,6 @@ public class X1HybridG4InverterData extends CommonInverterData {
|
||||
return getData(9);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getInverterWorkModeCode() {
|
||||
return getData(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBatteryVoltage() {
|
||||
return ((double) getData(14)) / 100;
|
||||
@ -112,4 +107,9 @@ public class X1HybridG4InverterData extends CommonInverterData {
|
||||
public short getBatteryLevel() {
|
||||
return getData(18);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getInverterWorkModeCode() {
|
||||
return getData(10);
|
||||
}
|
||||
}
|
@ -10,10 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.impl;
|
||||
package org.openhab.binding.solax.internal.model.local;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
|
||||
/**
|
||||
* The {@link X3HybridG4InverterData} is responsible for handling commands, which are
|
||||
@ -22,7 +22,7 @@ import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDa
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class X3HybridG4InverterData extends CommonInverterData {
|
||||
public class X3HybridG4InverterData extends CommonLocalInverterData {
|
||||
|
||||
public X3HybridG4InverterData(LocalConnectRawDataBean data) {
|
||||
super(data);
|
||||
@ -125,11 +125,6 @@ public class X3HybridG4InverterData extends CommonInverterData {
|
||||
return ((double) getData(18)) / 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getInverterWorkModeCode() {
|
||||
return getData(19);
|
||||
}
|
||||
|
||||
// Battery
|
||||
|
||||
@Override
|
||||
@ -225,4 +220,9 @@ public class X3HybridG4InverterData extends CommonInverterData {
|
||||
public double getTodayBatteryChargeEnergy() {
|
||||
return ((double) getData(79)) / 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getInverterWorkModeCode() {
|
||||
return getData(19);
|
||||
}
|
||||
}
|
@ -10,10 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.impl;
|
||||
package org.openhab.binding.solax.internal.model.local;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
|
||||
/**
|
||||
* The {@link X3HybridG4InverterData} is responsible for handling commands, which are
|
||||
@ -23,7 +23,7 @@ import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDa
|
||||
* (based on X1/X3 G4 parser from Konstantin Polihronov)
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class X3MicOrProG2InverterData extends CommonInverterData {
|
||||
public class X3MicOrProG2InverterData extends CommonLocalInverterData {
|
||||
|
||||
public X3MicOrProG2InverterData(LocalConnectRawDataBean data) {
|
||||
super(data);
|
@ -10,13 +10,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.parsers;
|
||||
package org.openhab.binding.solax.internal.model.local.parsers;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
|
||||
/**
|
||||
* The {@link RawDataParser} declares generic parser implementation that parses raw data to generic inverter data which
|
||||
@ -27,7 +27,7 @@ import org.openhab.binding.solax.internal.model.InverterData;
|
||||
@NonNullByDefault
|
||||
public interface RawDataParser {
|
||||
|
||||
InverterData getData(LocalConnectRawDataBean bean);
|
||||
LocalInverterData getData(LocalConnectRawDataBean bean);
|
||||
|
||||
Set<String> getSupportedChannels();
|
||||
}
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.parsers;
|
||||
package org.openhab.binding.solax.internal.model.local.parsers;
|
||||
|
||||
import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.model.impl.X1HybridG4InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.X1HybridG4InverterData;
|
||||
|
||||
/**
|
||||
* The {@link SinglePhaseDataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
|
||||
@ -39,7 +39,7 @@ public class X1HybridG4DataParser implements RawDataParser {
|
||||
CHANNEL_INVERTER_OUTPUT_VOLTAGE, CHANNEL_INVERTER_OUTPUT_FREQUENCY, CHANNEL_INVERTER_WORKMODE);
|
||||
|
||||
@Override
|
||||
public InverterData getData(LocalConnectRawDataBean rawData) {
|
||||
public LocalInverterData getData(LocalConnectRawDataBean rawData) {
|
||||
return new X1HybridG4InverterData(rawData);
|
||||
}
|
||||
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.parsers;
|
||||
package org.openhab.binding.solax.internal.model.local.parsers;
|
||||
|
||||
import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.model.impl.X3HybridG4InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.X3HybridG4InverterData;
|
||||
|
||||
/**
|
||||
* The {@link X3HybridG4DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
|
||||
@ -47,7 +47,7 @@ public class X3HybridG4DataParser implements RawDataParser {
|
||||
CHANNEL_TODAY_BATTERY_CHARGE_ENERGY, CHANNEL_TODAY_BATTERY_DISCHARGE_ENERGY, CHANNEL_INVERTER_WORKMODE);
|
||||
|
||||
@Override
|
||||
public InverterData getData(LocalConnectRawDataBean rawData) {
|
||||
public LocalInverterData getData(LocalConnectRawDataBean rawData) {
|
||||
return new X3HybridG4InverterData(rawData);
|
||||
}
|
||||
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal.model.parsers;
|
||||
package org.openhab.binding.solax.internal.model.local.parsers;
|
||||
|
||||
import static org.openhab.binding.solax.internal.SolaxBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.model.impl.X3MicOrProG2InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.X3MicOrProG2InverterData;
|
||||
|
||||
/**
|
||||
* The {@link X3MicOrProG2DataParser} is the implementation that parses raw data into a SinglePhaseInverterData for the
|
||||
@ -44,7 +44,7 @@ public class X3MicOrProG2DataParser implements RawDataParser {
|
||||
CHANNEL_INVERTER_TEMPERATURE2, CHANNEL_INVERTER_WORKMODE, CHANNEL_RAW_DATA);
|
||||
|
||||
@Override
|
||||
public InverterData getData(LocalConnectRawDataBean rawData) {
|
||||
public LocalInverterData getData(LocalConnectRawDataBean rawData) {
|
||||
return new X3MicOrProG2InverterData(rawData);
|
||||
}
|
||||
|
@ -5,6 +5,42 @@ addon.solax.description = This is the binding for Solax inverters.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.solax.cloud-connect-inverter.label = Cloud Connect Inverter
|
||||
thing-type.solax.cloud-connect-inverter.description = The inverter representation that is retrieved from Solax cloud API
|
||||
thing-type.solax.cloud-connect-inverter.channel.battery-level.label = Battery Level
|
||||
thing-type.solax.cloud-connect-inverter.channel.battery-level.description = The battery state of charge in percent
|
||||
thing-type.solax.cloud-connect-inverter.channel.battery-power.label = Battery Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.battery-power.description = Power to/from the battery
|
||||
thing-type.solax.cloud-connect-inverter.channel.feed-in-power.label = Feed-In Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.feed-in-power.description = Power to/from the electricity network.
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-r.label = EPS power R
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-r.description = Inverter AC EPS power R
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-s.label = EPS power S
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-s.description = Inverter AC EPS power S
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-t.label = EPS power T
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-eps-power-t.description = Inverter AC EPS power T
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.label = Meter2 Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-meter2-power.description = Inverter power on meter2.
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.label = Inverter Input/Output Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.inverter-output-power.description = Power to/from the inverter
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv-total-power.label = PV Total Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv-total-power.description = The sum of PV powers from all PV strings
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv1-power.label = PV 1 Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv1-power.description = Electric power of PV String 1
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv2-power.label = PV 2 Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv2-power.description = Electric power of PV String 2
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv3-power.label = PV 3 Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv3-power.description = Electric power of PV String 3
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv4-power.label = PV 4 Power
|
||||
thing-type.solax.cloud-connect-inverter.channel.pv4-power.description = Electric power of PV String 4
|
||||
thing-type.solax.cloud-connect-inverter.channel.today-energy.label = Yield today
|
||||
thing-type.solax.cloud-connect-inverter.channel.today-energy.description = Inverter output energy for the day
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-consumption.label = Total Consumed Energy
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-consumption.description = Total Energy consumed from the electricity network.
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-energy.label = Yield total
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-energy.description = Total inverter output energy
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.label = Total Feed-In Energy
|
||||
thing-type.solax.cloud-connect-inverter.channel.total-feed-in-energy.description = Total energy feed-in to the electricity network.
|
||||
thing-type.solax.local-connect-inverter.label = Local Connect Inverter
|
||||
thing-type.solax.local-connect-inverter.description = The inverter representation that supports local connections via HTTP
|
||||
thing-type.solax.local-connect-inverter.channel.battery-current.label = Battery Current
|
||||
@ -100,6 +136,12 @@ thing-type.solax.local-connect-inverter.channel.total-pv-energy.description = To
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.solax.cloud-connect-inverter.password.label = Password
|
||||
thing-type.config.solax.cloud-connect-inverter.password.description = Password for accessing cloud API (the serial number of the Wi-Fi module)
|
||||
thing-type.config.solax.cloud-connect-inverter.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.solax.cloud-connect-inverter.refreshInterval.description = Refresh interval in seconds. (Cloud API is limited to max 10 calls per minute and 10000 times per day)
|
||||
thing-type.config.solax.cloud-connect-inverter.token.label = Token
|
||||
thing-type.config.solax.cloud-connect-inverter.token.description = Token to access the Solax cloud API
|
||||
thing-type.config.solax.local-connect-inverter.hostname.label = Network Address
|
||||
thing-type.config.solax.local-connect-inverter.hostname.description = IP address or the host name of the Wi-Fi module
|
||||
thing-type.config.solax.local-connect-inverter.password.label = Password
|
||||
@ -113,6 +155,8 @@ channel-type.solax.battery-temperature.label = Battery Temperature
|
||||
channel-type.solax.battery-temperature.description = Battery Temperature
|
||||
channel-type.solax.frequency.label = Electric Frequency
|
||||
channel-type.solax.frequency.description = Frequency of the electricity to/from the inverter
|
||||
channel-type.solax.inverter-status-type.label = Inverter Status
|
||||
channel-type.solax.inverter-status-type.description = The status of the inverter.
|
||||
channel-type.solax.inverter-temperature.label = Inverter Temperature
|
||||
channel-type.solax.inverter-temperature.description = Inverter Temperature
|
||||
channel-type.solax.inverter-workmode.label = Inverter Workmode
|
||||
@ -138,5 +182,19 @@ channel-type.solax.raw-data-type.description = The raw JSON data retrieved from
|
||||
|
||||
# thing status descriptions
|
||||
|
||||
cloud.inverter.status.wait = Wait
|
||||
cloud.inverter.status.check = Check
|
||||
cloud.inverter.status.normal = Normal
|
||||
cloud.inverter.status.fault = Fault
|
||||
cloud.inverter.status.permanent-fault = Permanent Fault
|
||||
cloud.inverter.status.update = Update
|
||||
cloud.inverter.status.eps-check = EPS Check
|
||||
cloud.inverter.status.self-test = Self Test
|
||||
cloud.inverter.status.idle = Idle
|
||||
cloud.inverter.status.standby = Standby
|
||||
cloud.inverter.status.pv-wake-up-battery = PV Wake-up Battery
|
||||
cloud.inverter.status.gen-check = Gen Check
|
||||
cloud.inverter.status.gen-run = Gen Run
|
||||
cloud.inverter.status.unknown = Unknown
|
||||
offline.communication-error.json-cannot-be-retrieved = JSON data could not be retrieved.
|
||||
offline.configuration-error.parser-not-implemented = Parser for inverter of type {0} is not implemented.
|
||||
|
@ -57,6 +57,29 @@
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="inverter-workmode-cloud">
|
||||
<item-type>String</item-type>
|
||||
<label>Inverter Workmode</label>
|
||||
<description>Inverter Workmode</description>
|
||||
<state pattern="%s" readOnly="true">
|
||||
<options>
|
||||
<option value="100">Waiting</option>
|
||||
<option value="101">Checking</option>
|
||||
<option value="102">Normal</option>
|
||||
<option value="103">Fault</option>
|
||||
<option value="104">Permanent Fault</option>
|
||||
<option value="105">Updating</option>
|
||||
<option value="106">EPS Check</option>
|
||||
<option value="107">EPS Normal</option>
|
||||
<option value="108">Self Test</option>
|
||||
<option value="109">Idle</option>
|
||||
<option value="110">Standby</option>
|
||||
<option value="111">PV Wake-up Battery</option>
|
||||
<option value="112">GEN Check</option>
|
||||
<option value="113">GEN Run</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="last-retrieve-time-stamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last Retrieve Time Stamp</label>
|
||||
|
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="solax"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="cloud-connect-inverter">
|
||||
|
||||
<label>Cloud Connect Inverter</label>
|
||||
<description>The inverter representation that is retrieved from Solax cloud API</description>
|
||||
|
||||
<channels>
|
||||
<channel id="inverter-output-power" typeId="system.electric-power">
|
||||
<label>Inverter Input/Output Power</label>
|
||||
<description>Power to/from the inverter</description>
|
||||
</channel>
|
||||
<channel id="pv1-power" typeId="system.electric-power">
|
||||
<label>PV 1 Power</label>
|
||||
<description>Electric power of PV String 1</description>
|
||||
</channel>
|
||||
<channel id="pv2-power" typeId="system.electric-power">
|
||||
<label>PV 2 Power</label>
|
||||
<description>Electric power of PV String 2</description>
|
||||
</channel>
|
||||
<channel id="pv3-power" typeId="system.electric-power">
|
||||
<label>PV 3 Power</label>
|
||||
<description>Electric power of PV String 3</description>
|
||||
</channel>
|
||||
<channel id="pv4-power" typeId="system.electric-power">
|
||||
<label>PV 4 Power</label>
|
||||
<description>Electric power of PV String 4</description>
|
||||
</channel>
|
||||
<channel id="pv-total-power" typeId="system.electric-power">
|
||||
<label>PV Total Power</label>
|
||||
<description>The sum of PV powers from all PV strings</description>
|
||||
</channel>
|
||||
<channel id="battery-level" typeId="system.battery-level">
|
||||
<label>Battery Level</label>
|
||||
<description>The battery state of charge in percent</description>
|
||||
</channel>
|
||||
<channel id="battery-power" typeId="system.electric-power">
|
||||
<label>Battery Power</label>
|
||||
<description>Power to/from the battery</description>
|
||||
</channel>
|
||||
<channel id="feed-in-power" typeId="system.electric-power">
|
||||
<label>Feed-In Power</label>
|
||||
<description>Power to/from the electricity network.</description>
|
||||
</channel>
|
||||
<channel id="total-feed-in-energy" typeId="system.electric-energy">
|
||||
<label>Total Feed-In Energy</label>
|
||||
<description>Total energy feed-in to the electricity network.</description>
|
||||
</channel>
|
||||
<channel id="total-consumption" typeId="system.electric-energy">
|
||||
<label>Total Consumed Energy</label>
|
||||
<description>Total Energy consumed from the electricity network.</description>
|
||||
</channel>
|
||||
<channel id="inverter-meter2-power" typeId="system.electric-power">
|
||||
<label>Meter2 Power</label>
|
||||
<description>Inverter power on meter2.</description>
|
||||
</channel>
|
||||
<channel id="inverter-eps-power-r" typeId="system.electric-power">
|
||||
<label>EPS power R</label>
|
||||
<description>Inverter AC EPS power R</description>
|
||||
</channel>
|
||||
<channel id="inverter-eps-power-s" typeId="system.electric-power">
|
||||
<label>EPS power S</label>
|
||||
<description>Inverter AC EPS power S</description>
|
||||
</channel>
|
||||
<channel id="inverter-eps-power-t" typeId="system.electric-power">
|
||||
<label>EPS power T</label>
|
||||
<description>Inverter AC EPS power T</description>
|
||||
</channel>
|
||||
|
||||
<channel id="today-energy" typeId="system.electric-energy">
|
||||
<label>Yield today</label>
|
||||
<description>Inverter output energy for the day</description>
|
||||
</channel>
|
||||
<channel id="total-energy" typeId="system.electric-energy">
|
||||
<label>Yield total</label>
|
||||
<description>Total inverter output energy</description>
|
||||
</channel>
|
||||
|
||||
<channel id="last-update-time" typeId="last-retrieve-time-stamp"/>
|
||||
|
||||
<channel id="inverter-workmode" typeId="inverter-workmode-cloud">
|
||||
<label>Inverter Workmode</label>
|
||||
<description>Inverter Workmode</description>
|
||||
</channel>
|
||||
|
||||
<channel id="raw-data" typeId="raw-data-type"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="refreshInterval" type="integer" min="9" max="600">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval in seconds. (Cloud API is limited to max 10 calls per minute and 10000 times per day)</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="password" type="text" required="true">
|
||||
<label>Password</label>
|
||||
<description>Password for accessing cloud API (the serial number of the Wi-Fi module)</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="token" type="text" required="true">
|
||||
<label>Token</label>
|
||||
<description>Token to access the Solax cloud API</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.solax.internal.cloud;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.cloud.CloudRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
|
||||
/**
|
||||
* The {@link TestCloudParser} Simple test that tests for proper parsing against a real data from the cloud
|
||||
*
|
||||
* @author Konstantin Polihronov - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TestCloudParser {
|
||||
private static final String CLOUD_RESPONSE_SUCCESS = """
|
||||
{
|
||||
"success": true,
|
||||
"exception": "Query success!",
|
||||
"result": {
|
||||
"inverterSN": "xxx",
|
||||
"sn": "xxxx",
|
||||
"acpower": 151.0,
|
||||
"yieldtoday": 0.9,
|
||||
"yieldtotal": 7339.5,
|
||||
"feedinpower": -925.0,
|
||||
"feedinenergy": 147.2,
|
||||
"consumeenergy": 2536.4,
|
||||
"feedinpowerM2": 0.0,
|
||||
"soc": 27.0,
|
||||
"peps1": 56.3,
|
||||
"peps2": null,
|
||||
"peps3": null,
|
||||
"inverterType": "15",
|
||||
"inverterStatus": "110",
|
||||
"uploadTime": "2023-11-28 18:34:17",
|
||||
"batPower": 1245.0,
|
||||
"powerdc1": 55.0,
|
||||
"powerdc2": 670.0,
|
||||
"powerdc3": null,
|
||||
"powerdc4": null,
|
||||
"batStatus": "0"
|
||||
},
|
||||
"code": 0
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLOUD_RESPONSE_ERROR = """
|
||||
{"success":false,"exception":"error","result":null,"code":2001}
|
||||
""";
|
||||
|
||||
@Test
|
||||
public void testPositiveScenario() throws IOException {
|
||||
CloudRawDataBean bean = CloudRawDataBean.fromJson(CLOUD_RESPONSE_SUCCESS);
|
||||
assertTrue(bean.isSuccess(), "Overall response success");
|
||||
assertEquals(bean.getOverallResult(), CloudRawDataBean.QUERY_SUCCESS, "Query success string response");
|
||||
|
||||
InverterType type = bean.getInverterType();
|
||||
assertEquals(InverterType.X1_HYBRID_G4, type, "Inverter type not recognized properly");
|
||||
|
||||
assertEquals(110, bean.getInverterWorkModeCode());
|
||||
assertEquals("110", bean.getInverterWorkMode());
|
||||
|
||||
assertEquals("xxx", bean.getInverterSerialNumber());
|
||||
assertEquals("xxxx", bean.getWifiSerialNumber());
|
||||
|
||||
assertEquals(151, bean.getInverterOutputPower(), "AC/Inverter output power");
|
||||
assertEquals(0.9, bean.getYieldToday(), "Yield today");
|
||||
assertEquals(7339.5, bean.getYieldTotal(), "Yield total");
|
||||
assertEquals(-925.0, bean.getFeedInPower(), "Feed-in power");
|
||||
assertEquals(147.2, bean.getFeedInEnergy(), "Feed-in energy");
|
||||
assertEquals(2536.4, bean.getConsumeEnergy(), "Consume energy");
|
||||
assertEquals(0, bean.getFeedInPowerM2(), "Feed in power M2");
|
||||
assertEquals(56.3, bean.getEPSPowerR(), "EPS power R");
|
||||
|
||||
assertEquals(1245, bean.getBatteryPower(), "Battery power");
|
||||
assertEquals(55, bean.getPowerPv1(), "PV1");
|
||||
assertEquals(670, bean.getPowerPv2(), "PV2");
|
||||
assertEquals(0, bean.getBatteryStatus(), "Battery status");
|
||||
assertEquals(0, bean.getCode(), "Return code");
|
||||
|
||||
ZoneId zoneId = ZoneId.of("CET");
|
||||
assertEquals(ZonedDateTime.of(LocalDateTime.of(2023, 11, 28, 18, 34, 17), zoneId), bean.getUploadTime(zoneId),
|
||||
"Upload time");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeScenario() throws IOException {
|
||||
CloudRawDataBean bean = CloudRawDataBean.fromJson(CLOUD_RESPONSE_ERROR);
|
||||
assertFalse(bean.isSuccess(), "Overall response success");
|
||||
assertEquals(bean.getOverallResult(), CloudRawDataBean.ERROR, "Expected error as a response");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyResponse() throws IOException {
|
||||
CloudRawDataBean bean = CloudRawDataBean.fromJson("");
|
||||
assertFalse(bean.isSuccess(), "Overall response success");
|
||||
assertEquals(bean.getOverallResult(), CloudRawDataBean.ERROR, "Expected error as a response");
|
||||
}
|
||||
}
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal;
|
||||
package org.openhab.binding.solax.internal.local;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.openhab.binding.solax.internal.model.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
|
||||
|
||||
/**
|
||||
* The {@link TestX1HybridG4Parser} Simple test that tests for proper parsing against a real data from the inverter
|
||||
@ -53,7 +53,7 @@ public class TestX1HybridG4Parser {
|
||||
RawDataParser parser = inverterType.getParser();
|
||||
assertNotNull(parser);
|
||||
|
||||
InverterData data = parser.getData(bean);
|
||||
LocalInverterData data = parser.getData(bean);
|
||||
assertEquals("SOME_SERIAL_NUMBER", data.getWifiSerial());
|
||||
assertEquals("3.008.10", data.getWifiVersion());
|
||||
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal;
|
||||
package org.openhab.binding.solax.internal.local;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.openhab.binding.solax.internal.model.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
|
||||
|
||||
/**
|
||||
* The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
|
||||
@ -60,7 +60,7 @@ public class TestX3HybridG4Parser {
|
||||
RawDataParser parser = inverterType.getParser();
|
||||
assertNotNull(parser);
|
||||
|
||||
InverterData data = parser.getData(bean);
|
||||
LocalInverterData data = parser.getData(bean);
|
||||
assertEquals("XYZ", data.getWifiSerial());
|
||||
assertEquals("3.005.01", data.getWifiVersion());
|
||||
|
@ -10,16 +10,16 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.solax.internal;
|
||||
package org.openhab.binding.solax.internal.local;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterData;
|
||||
import org.openhab.binding.solax.internal.connectivity.rawdata.local.LocalConnectRawDataBean;
|
||||
import org.openhab.binding.solax.internal.model.InverterType;
|
||||
import org.openhab.binding.solax.internal.model.parsers.RawDataParser;
|
||||
import org.openhab.binding.solax.internal.model.local.LocalInverterData;
|
||||
import org.openhab.binding.solax.internal.model.local.parsers.RawDataParser;
|
||||
|
||||
/**
|
||||
* The {@link TestX3HybridG4Parser} simple test that tests for proper parsing against a real data from the inverter
|
||||
@ -56,7 +56,7 @@ public class TestX3MicOrProG2Parser {
|
||||
RawDataParser parser = inverterType.getParser();
|
||||
assertNotNull(parser);
|
||||
|
||||
InverterData data = parser.getData(bean);
|
||||
LocalInverterData data = parser.getData(bean);
|
||||
assertEquals("XYZ", data.getWifiSerial());
|
||||
assertEquals("3.003.02", data.getWifiVersion());
|
||||
|
Loading…
Reference in New Issue
Block a user