[solarwatt] Initial contribution for solarwatt energy manager (#10091)

* [ADD] First version which was tested with my local setup.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>
Signed-off-by: Sven Carstens <sven.carstens@aoe.com>

* [ADD] Refactoring, add calculated value for direct self consumption and prepare for more.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [ADD] Add chanel description to README and make some channels advanced.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Remove wrong colon in channel types.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Put colon at the right place in channel types.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Fix spelling of PVPlant in constants and things.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Separate channelName from energy manager tagName.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Reduce loglevels and fix findings from code-analysis.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Move all custom calculations to the handler instances, remove custom tracking of child handlers, fix wrong calculation of the "direct consumed" values.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README and remove unnecessary Nullable and NotNull annotations.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Change wrong ItemType for power items in README

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Change modeConverter to Switch

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Relay stateDevice to Thing status, anything but "OK" means ThingStatus.OFFLINE

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Remove stateDevice from items as it is represented by the device status.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README and remove unnecessary logging.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Allow child things to be configured by hand with a guid.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Allow child things to be configured by hand with a guid.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README and move things config to separate config.xml

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Improve README and remove all trace/debug logging.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

* [MOD] Use real channel timestamp as the refresh trigger.

Signed-off-by: Sven Carstens <s.carstens@gmx.de>

Co-authored-by: Sven Carstens <sven.carstens@aoe.com>
This commit is contained in:
Sven Carstens 2021-06-21 18:21:50 +02:00 committed by GitHub
parent 39c4cd0a4d
commit 806e0a0287
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 4409 additions and 0 deletions

View File

@ -271,6 +271,7 @@
/bundles/org.openhab.binding.snmp/ @openhab/add-ons-maintainers
/bundles/org.openhab.binding.solaredge/ @alexf2015
/bundles/org.openhab.binding.solarlog/ @johannrichard
/bundles/org.openhab.binding.solarwatt/ @sven-carstens
/bundles/org.openhab.binding.somfymylink/ @loungeflyz
/bundles/org.openhab.binding.somfytahoma/ @octa22
/bundles/org.openhab.binding.sonos/ @kgoderis @lolodomo

View File

@ -1331,6 +1331,11 @@
<artifactId>org.openhab.binding.solarlog</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.solarwatt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.somfymylink</artifactId>

View File

@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@ -0,0 +1,252 @@
# Solarwatt Binding
Binding to query a [solarwatt](https://www.solarwatt.de/) [energy manager](https://www.solarwatt.de/energie-management/energymanager) and read the values of all attached devices.
All supported values and devices were discovered while playing with my own energy manager.
## Supported Things
| Thing Type ID | Devices |
|------|---------------|
| energymanager | EnergyManager itself. |
| location | Location part of the EnergyManager. |
| pvplant | Power producing part of the EnergyManager. |
| gridflow | Grid interaction part of the EnergyManager. |
| inverter | inverter producing AC current; e.g. MyReserve, Fronius |
| batteryconverter | battery storage systems; e.g. MyReserve |
| powermeter | powermeters; e.g. S0BusCounter, MyReserve |
| evstation | electric-vehicle charging station; e.g. Keba Wallbox |
## Discovery
You have to enter the hostname or ip-address of the energymanager itself.
The attached devices and supported channels are discovered automatically.
## Thing Configuration
### EnergyManager
| Property | Default | Required | Description |
|----------|---------|----------|-------------|
| hostname | None | Yes | hostname or ip-address of the energy manager. |
| refresh | 30 | No | Refresh interval in seconds for the current values of the channels. |
| rescan | 5 | No | Rescan interval in minutes for the redetection of channgels and things. |
### Child Things
| Property | Default | Required | Description |
|----------|---------|----------|-------------|
| guid | None | Yes | Guid of the device as used by the solarwatt energymanager. |
## Channels
### EnergyManager
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
|timestamp | Number | Milliseconds since the epoch set to the last NTP time sync |
|datetime | DateTime | Date and time of the last NTP time sync in the timezone of the energy manager |
|idTimezone | String | Timezone the energy manager is running in. All timestamps are milliseconds since the epoch within this timezone |
|fractionCPULoadTotal | Number:Dimensionless | Total CPU load in % |
|fractionCPULoadUser | Number:Dimensionless | Userspace CPU load in % |
|fractionCPULoadKernel | Number:Dimensionless | Kernelspace CPU load in % |
|fractionCPULoadAverageLastMinute | Number:Dimensionless | Average 1 minute CPU load in % |
|fractionCPULoadAverageLastFiveMinutes | Number:Dimensionless | Average 5 minute CPU load in % |
|fractionCPULoadAverageLastFifteenMinutes | Number:Dimensionless | Average 15 minute CPU load in % |
### PVPlant
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| powerACOut | Number:Power | Energy produced by the PV in watts |
| workACOut | Number:Energy | Energy produced by the PV in watt hours |
### Location
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| powerBuffered | Number:Power | Power flow into the storage system
| powerSelfConsumed | Number:Power | Power consumed direct from PV plus energy stored
| powerSelfSupplied | Number:Power | Power consumed direct from PV plus energy consumed from storage
| powerConsumedFromGrid | Number:Power | Power consumed from the grid
| powerConsumedFromStorage | Number:Power | Power consumed from storage
| powerConsumedUnmetered | Number:Power | Power consumed in the inner side (outer consumers are subtracted)
| powerConsumed | Number:Power | Total power consumed. All inner and outer consumers.
| powerDirectConsumed | Number:Power | Power consumed directly from PV without buffering
| powerProduced | Number:Power | Power produced by the PV
| powerOut | Number:Power | Power delivered to the grid
| powerDirectConsumed | Number:Power | Power consumed directly without energy put into storage or taken from storage
| workBuffered | Number:Energy | Energy flow into the storage system
| workSelfConsumed | Number:Energy | Energy consumed direct from PV plus energy stored
| workSelfSupplied | Number:Energy | Energy consumed direct from PV plus energy consumed from storage
| workConsumedFromGrid | Number:Energy | Energy consumed from the grid
| workConsumedFromStorage | Number:Energy | Energy consumed from storage
| workConsumedUnmetered | Number:Energy | Energy consumed in the inner side (outer consumers are subtracted)
| workConsumed | Number:Energy | Total energy consumed. All inner and outer consumers.
| workDirectConsumed | Number:Energy | Energy consumed directly from PV without buffering
| workProduced | Number:Energy | Energy produced by the PV
| workOut | Number:Energy | Energy delivered to the grid
| workDirectConsumed | Number:Energy | Energy consumed directly without energy put into storage or taken from storage
### PowerMeter, S0Counter, MyReservePowerMeter
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| channelDirectionMetering | String | Representing which energy flow directions are metered. One off *IN*, *OUT*, *BIDIRECTIONAL*
| powerIn | Number:Power | Power metered flowing into the consumer
| powerOut | Number:Power | Power metered flowing out of the producer
| workIn | Number:Energy | Energy metered flowing into the consumer
| workOut | Number:Energy | Energy metered flowing out of the producer
| consumptionEnergySum | Number:Energy | Total energy in watt hours
### Inverter, MyReserveInverter, SunSpecInverter
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| powerACOutMax | Number:Power | Maximum power production
| powerACOutLimit | Number:Power | Limit of power production
| powerACOut | Number:Power | Power delivered by the inverter
| workACOut | Number:Energy | Energy delivered by the inverter
| powerInstallledPeak | Number:Power | Technical peak power available
### BatteryConverter, MyReserve
All of *Inverter* plus
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| powerACIn | Number:Power | Power fed into battery
| workACIn | Number:Energy | Energy fed into battery
| stateOfCharge | Number | Charging state of battery in percent
| stateOfHealth | Number | Internal health metric in percent
| temperatureBattery | Number:Temperature | Temperature of the battery in celsius
| modeConverter | Switch | Current mode of converter. *ON* or *OFF*
| voltageBatteryCellMin | Number:Voltage | minimum voltage of all batteries
| voltageBatteryCellMean | Number:Voltage | mean voltage of all batteries
| voltageBatteryCellMax | Number:Voltage | maximum voltage of all batteries
### EVStation, KebaEv
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| powerACIn | Number:Power | Power consumed by the charger
| workACIn | Number:Energy | Energy consumed by the charger
| workACInSession | Number:Energy | Work consumed during current/last charging session
| modeStation | String | Current mode of the charger. One off *STANDBY*, *CHARGING*, *OFF*
| connectivityStatus | String | Current state of the charging connection. One off *ONLINE* or *OFFLINE*
### GridFlow
| Channel Type ID | Item Type | Description |
|-----------------|-----------|-------------|
| feedInLimit | Number:Dimensionless | Current derating setting in percent
## Example
demo.things:
```
Bridge solarwatt:energymanager:56f4ac2fa2 [hostname="192.168.0.64", refresh=30, rescan=5]
// the individual things configured with their energy manager guid
Thing solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5 [guid="5c7d5929-8fa4-42c5-8737-48bef77b61f5"] (solarwatt:energymanager:56f4ac2fa2)
Thing solarwatt:gridflow:56f4ac2fa2:urn-kiwigrid-gridflow-ERC05-000008007 [guid="urn:kiwigrid:gridflow:ERC05-000008007"] (solarwatt:energymanager:56f4ac2fa2)
Thing solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876 [guid="urn:keba:evstation:2065287"] (solarwatt:energymanager:56f4ac2fa2)
```
demo.items:
```
// Location DeviceClass com.kiwigrid.devices.location.Location Guid b4e4978b96404e61977bfacd3eab299d
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerBuffered "PowerBuffered [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerBuffered"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerBufferedFromGrid "PowerBufferedFromGrid [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerBufferedFromGrid"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerBufferedFromProducers "PowerBufferedFromProducers [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerBufferedFromProducers"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerConsumed "PowerConsumed [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerConsumed"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerConsumedUnmetered "PowerConsumedUnmetered [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:PowerConsumedUnmetered"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerDirectConsumed "PowerDirectConsumed [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerDirectConsumed"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerConsumedFromGrid "PowerConsumedFromGrid [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerConsumedFromGrid"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerConsumedFromStorage "PowerConsumedFromStorage [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerConsumedFromStorage"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerConsumedFromProducers "PowerConsumedFromProducers [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerConsumedFromProducers"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerIn "PowerIn [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerIn"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerProduced "PowerProduced [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerProduced"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerOut "PowerOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerOut"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerOutFromProducers "PowerOutFromProducers [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerOutFromProducers"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerOutFromStorage "PowerOutFromStorage [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerOutFromStorage"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerReleased "PowerReleased [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerReleased"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerSelfConsumed "PowerSelfConsumed [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerSelfConsumed"}
Number:Power Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_PowerSelfSupplied "PowerSelfSupplied [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:powerSelfSupplied"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkBuffered "WorkBuffered [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workBuffered"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkBufferedFromGrid "WorkBufferedFromGrid [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workBufferedFromGrid"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkBufferedFromProducers "WorkBufferedFromProducers [%.2f Wh]" <energy> [""] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workBufferedFromProducers"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkConsumed "WorkConsumed [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workConsumed"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkConsumedUnmetered "WorkConsumedUnmetered [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:WorkConsumedUnmetered"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkDirectConsumed "WorkDirectConsumed [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workDirectConsumed"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkConsumedFromGrid "WorkConsumedFromGrid [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workConsumedFromGrid"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkConsumedFromStorage "WorkConsumedFromStorage [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workConsumedFromStorage"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkConsumedFromProducers "WorkConsumedFromProducers [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workConsumedFromProducers"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkIn "WorkIn [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workIn"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkProduced "WorkProduced [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workProduced"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkOut "WorkOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workOut"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkOutFromProducers "WorkOutFromProducers [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workOutFromProducers"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkOutFromStorage "WorkOutFromStorage [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workOutFromStorage"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkReleased "WorkReleased [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workReleased"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkSelfConsumed "WorkSelfConsumed [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workSelfConsumed"}
Number:Energy Solarwatt_Location_b4e4978b96404e61977bfacd3eab299d_WorkSelfSupplied "WorkSelfSupplied [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:location:56f4ac2fa2:b4e4978b-9640-4e61-977b-facd3eab299d:workSelfSupplied"}
// Inverter Fronius com.kiwigrid.devices.inverter.Inverter
Number:Power Solarwatt_Inverter_UrnSunspecFroniusInverter31414368_PowerACOut "PowerACOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:inverter:56f4ac2fa2:urn-sunspec-fronius-inverter-31414368:powerACOut"}
Number:Power Solarwatt_Inverter_UrnSunspecFroniusInverter31414368_PowerACOutLimit "PowerACOutLimit [%.2f W]" <energy> ["Point", "Power"] {channel="solarwatt:inverter:56f4ac2fa2:urn-sunspec-fronius-inverter-31414368:powerACOutLimit"}
Number:Energy Solarwatt_Inverter_UrnSunspecFroniusInverter31414368_WorkACOut "WorkACOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:inverter:56f4ac2fa2:urn-sunspec-fronius-inverter-31414368:workACOut"}
// MyReserve BatteryInverter com.kiwigrid.devices.bat…verter.BatteryConverter
Switch Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_ModeConverter "ModeConverter [%s]" <switch> ["Switch"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:modeConverter"}
Number Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_StateOfCharge "StateOfCharge [%.2f %%]" <status> ["Status"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:stateOfCharge"}
Number Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_StateOfHealth "StateOfHealth [%.2f %%]" <status> ["Status"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:stateOfHealth"}
Number:Temperature Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_TemperatureBattery "TemperatureBattery [%.1f °C]" <temperature> ["Measurement", "Temperature"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:temperatureBattery"}
Number:Power Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_PowerACIn "PowerACIn [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:powerACIn"}
Number:Power Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_PowerACOut "PowerACOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:powerACOut"}
Number:Energy Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_WorkACIn "WorkACIn [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:workACIn"}
Number:Energy Solarwatt_BatteryInverter_5c7d59298fa442c5873748bef77b61f5_WorkACOut "WorkACOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:batteryconverter:56f4ac2fa2:5c7d5929-8fa4-42c5-8737-48bef77b61f5:workACOut"}
// S0Bus Meter com.kiwigrid.devices.powermeter.PowerMeter / com.kiwigrid.devices.s0counter.S0Counter
String Solarwatt_Meter_46bb4ee8a9744ecea11ef43c68263ae9_DirectionMetering "DirectionMetering [%s]" <status> ["Status"] {channel="solarwatt:powermeter:56f4ac2fa2:46bb4ee8-a974-4ece-a11e-f43c68263ae9:directionMetering"}
Number:Power Solarwatt_Meter_46bb4ee8a9744ecea11ef43c68263ae9_PowerIn "PowerIn [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:powermeter:56f4ac2fa2:46bb4ee8-a974-4ece-a11e-f43c68263ae9:powerIn"}
Number:Energy Solarwatt_Meter_46bb4ee8a9744ecea11ef43c68263ae9_WorkIn "WorkIn [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:powermeter:56f4ac2fa2:46bb4ee8-a974-4ece-a11e-f43c68263ae9:workIn"}
Number:Energy Solarwatt_Meter_46bb4ee8a9744ecea11ef43c68263ae9_ConsumptionEnergySum "ConsumptionEnergySum [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:powermeter:56f4ac2fa2:46bb4ee8-a974-4ece-a11e-f43c68263ae9:consumptionEnergySum"}
// MyReservePowermeter Meter com.kiwigrid.devices.powermeter.PowerMeter
String Solarwatt_Meter_2c5d089b98854f40ba8a3303bfb53e36_DirectionMetering "DirectionMetering [%s]" <status> ["Status"] {channel="solarwatt:powermeter:56f4ac2fa2:2c5d089b-9885-4f40-ba8a-3303bfb53e36:directionMetering"}
Number:Power Solarwatt_Meter_2c5d089b98854f40ba8a3303bfb53e36_PowerIn "PowerIn [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:powermeter:56f4ac2fa2:2c5d089b-9885-4f40-ba8a-3303bfb53e36:powerIn"}
Number:Power Solarwatt_Meter_2c5d089b98854f40ba8a3303bfb53e36_PowerOut "PowerOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:powermeter:56f4ac2fa2:2c5d089b-9885-4f40-ba8a-3303bfb53e36:powerOut"}
Number:Energy Solarwatt_Meter_2c5d089b98854f40ba8a3303bfb53e36_WorkIn "WorkIn [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:powermeter:56f4ac2fa2:2c5d089b-9885-4f40-ba8a-3303bfb53e36:workIn"}
Number:Energy Solarwatt_Meter_2c5d089b98854f40ba8a3303bfb53e36_WorkOut "WorkOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:powermeter:56f4ac2fa2:2c5d089b-9885-4f40-ba8a-3303bfb53e36:workOut"}
// Inverter MyReserve com.kiwigrid.devices.inverter.Inverter
Number:Power Solarwatt_Inverter_4af659938b1149408a77ff87556389f3_PowerACOut "PowerACOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:inverter:56f4ac2fa2:4af65993-8b11-4940-8a77-ff87556389f3:powerACOut"}
Number:Energy Solarwatt_Inverter_4af659938b1149408a77ff87556389f3_WorkACOut "WorkACOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:inverter:56f4ac2fa2:4af65993-8b11-4940-8a77-ff87556389f3:workACOut"}
// EVStation Keba com.kiwigrid.devices.evstation.EVStation
String Solarwatt_EVStation_UrnKebaEvstation20652876_ModeStation "ModeStation [%s]" <status> ["Status"] {channel="solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876:modeStation"}
String Solarwatt_EVStation_UrnKebaEvstation20652876_ConnectivityStatus "ConnectivityStatus [%s]" <status> ["Status"] {channel="solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876:connectivityStatus"}
Number:Power Solarwatt_EVStation_UrnKebaEvstation20652876_PowerACIn "PowerACIn [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876:powerACIn"}
Number:Energy Solarwatt_EVStation_UrnKebaEvstation20652876_WorkACIn "WorkACIn [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876:workACIn"}
Number:Energy Solarwatt_EVStation_UrnKebaEvstation20652876_WorkACInSession "WorkACInSession [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:evstation:56f4ac2fa2:urn-keba-evstation-20652876:workACInSession"}
// PVPlant com.kiwigrid.devices.pvplant.PVPlant
Number:Power Solarwatt_PVPlant_4575c19cfa0a4f21839a5db2ae06b4d_PowerACOut "PowerACOut [%.2f W]" <energy> ["Measurement", "Power"] {channel="solarwatt:pvplant:56f4ac2fa2:4575c19c-fa0a-4f21-839a-5db2ae06b4dc:powerACOut"}
Number:Energy Solarwatt_PVPlant_4575c19cfa0a4f21839a5db2ae06b4d_WorkACOut "WorkACOut [%.2f Wh]" <energy> ["Measurement", "Energy"] {channel="solarwatt:pvplant:56f4ac2fa2:4575c19c-fa0a-4f21-839a-5db2ae06b4dc:workACOut"}
// Manager com.kiwigrid.devices.em.EnergyManager
String Solarwatt_Manager_ERC05000008007_IdFirmware "IdFirmware [%s]" <status> ["Status"] {channel="solarwatt:energymanager:56f4ac2fa2:idFirmware"}
Number Solarwatt_Manager_ERC05000008007_Timestamp "Timestamp [%d]" <time> ["Status", "Timestamp"] {channel="solarwatt:energymanager:56f4ac2fa2:timestamp"}
DateTime Solarwatt_Manager_ERC05000008007_Datetime "Update [%1$tH:%1$tM:%1$tS]" <time> ["Status", "Timestamp"] {channel="solarwatt:energymanager:56f4ac2fa2:datetime"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadTotal "FractionCPULoadUser [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadTotal"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadUser "FractionCPULoadUser [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadUser"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadKernel "FractionCPULoadKernel [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadKernel"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadAverageLastMinute "FractionCPULoadAverageLastMinute [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadAverageLastMinute"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadAverageLastFiveMinutes "FractionCPULoadAverageLastFiveMinutes [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadAverageLastFiveMinutes"}
Number Solarwatt_Manager_ERC05000008007_FractionCPULoadAverageLastFifteenMinutes "FractionCPULoadAverageLastFifteenMinutes [%.2f]" <status> ["Measurement"] {channel="solarwatt:energymanager:56f4ac2fa2:fractionCPULoadAverageLastFifteenMinutes"}
// Gridflow com.kiwigrid.kiwiapp.gridflow.GridFlow
Number Solarwatt_Gridflow_UrnKiwigridGridflowERC05000008007_CurrentLimit "CurrentLimit [%d A]" <energy> ["Point"] {channel="solarwatt:gridflow:56f4ac2fa2:urn-kiwigrid-gridflow-ERC05-000008007:currentLimit"}
Number Solarwatt_Gridflow_UrnKiwigridGridflowERC05000008007_FeedInLimit "FeedInLimit [%d %%]" <status> ["Point"] {channel="solarwatt:gridflow:56f4ac2fa2:urn-kiwigrid-gridflow-ERC05-000008007:feedInLimit"}
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.solarwatt</artifactId>
<name>openHAB Add-ons :: Bundles :: Solarwatt Binding</name>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.solarwatt-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-solarwatt" description="Solarwatt Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.solarwatt/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,122 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.SolarwattTag;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SolarwattBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattBindingConstants {
private SolarwattBindingConstants() {
}
public static final String BINDING_ID = "solarwatt";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_ENERGY_MANAGER = new ThingTypeUID(BINDING_ID, "energymanager");
public static final ThingTypeUID THING_TYPE_INVERTER = new ThingTypeUID(BINDING_ID, "inverter");
public static final ThingTypeUID THING_TYPE_LOCATION = new ThingTypeUID(BINDING_ID, "location");
public static final ThingTypeUID THING_TYPE_BATTERYCONVERTER = new ThingTypeUID(BINDING_ID, "batteryconverter");
public static final ThingTypeUID THING_TYPE_POWERMETER = new ThingTypeUID(BINDING_ID, "powermeter");
public static final ThingTypeUID THING_TYPE_EVSTATION = new ThingTypeUID(BINDING_ID, "evstation");
public static final ThingTypeUID THING_TYPE_PVPLANT = new ThingTypeUID(BINDING_ID, "pvplant");
public static final ThingTypeUID THING_TYPE_GRIDFLOW = new ThingTypeUID(BINDING_ID, "gridflow");
public static final String PROPERTY_ID_NAME = "IdName";
public static final String PROPERTY_ID_FIRMWARE = "IdFirmware";
public static final String PROPERTY_ID_MANUFACTURER = "IdManufacturer";
// List of all Channel ids, taken from the tagNames
public static final SolarwattTag CHANNEL_WORK_AC_OUT = new SolarwattTag("WorkACOut");
public static final SolarwattTag CHANNEL_WORK_AC_IN = new SolarwattTag("WorkACIn");
public static final SolarwattTag CHANNEL_WORK_AC_IN_SESSION = new SolarwattTag("WorkACInSession");
public static final SolarwattTag CHANNEL_POWER_INSTALLED_PEAK = new SolarwattTag("PowerInstalledPeak");
public static final SolarwattTag CHANNEL_POWER_AC_OUT_MAX = new SolarwattTag("PowerACOutMax");
public static final SolarwattTag CHANNEL_POWER_AC_OUT = new SolarwattTag("PowerACOut");
public static final SolarwattTag CHANNEL_POWER_AC_IN = new SolarwattTag("PowerACIn");
public static final SolarwattTag CHANNEL_POWER_AC_OUT_LIMIT = new SolarwattTag("PowerACOutLimit");
public static final SolarwattTag CHANNEL_DIRECTION_METERING = new SolarwattTag("DirectionMetering");
public static final SolarwattTag CHANNEL_POWER_IN = new SolarwattTag("PowerIn");
public static final SolarwattTag CHANNEL_POWER_OUT = new SolarwattTag("PowerOut");
public static final SolarwattTag CHANNEL_WORK_IN = new SolarwattTag("WorkIn");
public static final SolarwattTag CHANNEL_WORK_OUT = new SolarwattTag("WorkOut");
public static final SolarwattTag CHANNEL_CONSUMPTION_ENERGY_SUM = new SolarwattTag("ConsumptionEnergySum");
public static final SolarwattTag CHANNEL_MODE_STATION = new SolarwattTag("ModeStation");
public static final SolarwattTag CHANNEL_CONNECTIVITY_STATUS = new SolarwattTag("ConnectivityStatus");
public static final SolarwattTag CHANNEL_TIMESTAMP = new SolarwattTag("Timestamp");
public static final SolarwattTag CHANNEL_DATETIME = new SolarwattTag("Datetime");
public static final SolarwattTag CHANNEL_IDTIMEZONE = new SolarwattTag("IdTimezone");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_TOTAL = new SolarwattTag("FractionCPULoadTotal");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_USER = new SolarwattTag("FractionCPULoadUser");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_KERNEL = new SolarwattTag("FractionCPULoadKernel");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_MINUTE = new SolarwattTag(
"FractionCPULoadAverageLastMinute");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_FIVE_MINUTES = new SolarwattTag(
"FractionCPULoadAverageLastFiveMinutes");
public static final SolarwattTag CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_FIFTEEN_MINUTES = new SolarwattTag(
"FractionCPULoadAverageLastFifteenMinutes");
public static final SolarwattTag CHANNEL_MODE_CONVERTER = new SolarwattTag("ModeConverter");
public static final SolarwattTag CHANNEL_STATE_OF_CHARGE = new SolarwattTag("StateOfCharge");
public static final SolarwattTag CHANNEL_STATE_OF_HEALTH = new SolarwattTag("StateOfHealth");
public static final SolarwattTag CHANNEL_TEMPERATURE_BATTERY = new SolarwattTag("TemperatureBattery");
public static final SolarwattTag CHANNEL_POWER_BUFFERED = new SolarwattTag("PowerBuffered");
public static final SolarwattTag CHANNEL_POWER_BUFFERED_FROM_GRID = new SolarwattTag("PowerBufferedFromGrid");
public static final SolarwattTag CHANNEL_POWER_BUFFERED_FROM_PRODUCERS = new SolarwattTag(
"PowerBufferedFromProducers");
public static final SolarwattTag CHANNEL_POWER_CONSUMED = new SolarwattTag("PowerConsumed");
public static final SolarwattTag CHANNEL_POWER_CONSUMED_UNMETERED = new SolarwattTag("PowerConsumedUnmetered");
public static final SolarwattTag CHANNEL_POWER_CONSUMED_FROM_GRID = new SolarwattTag("PowerConsumedFromGrid");
public static final SolarwattTag CHANNEL_POWER_CONSUMED_FROM_STORAGE = new SolarwattTag("PowerConsumedFromStorage");
public static final SolarwattTag CHANNEL_POWER_CONSUMED_FROM_PRODUCERS = new SolarwattTag(
"PowerConsumedFromProducers");
public static final SolarwattTag CHANNEL_POWER_PRODUCED = new SolarwattTag("PowerProduced");
public static final SolarwattTag CHANNEL_POWER_OUT_FROM_PRODUCERS = new SolarwattTag("PowerOutFromProducers");
public static final SolarwattTag CHANNEL_POWER_OUT_FROM_STORAGE = new SolarwattTag("PowerOutFromStorage");
public static final SolarwattTag CHANNEL_POWER_RELEASED = new SolarwattTag("PowerReleased");
public static final SolarwattTag CHANNEL_POWER_SELF_CONSUMED = new SolarwattTag("PowerSelfConsumed");
public static final SolarwattTag CHANNEL_POWER_DIRECT_CONSUMED = new SolarwattTag("PowerDirectConsumed");
public static final SolarwattTag CHANNEL_POWER_SELF_SUPPLIED = new SolarwattTag("PowerSelfSupplied");
public static final SolarwattTag CHANNEL_WORK_BUFFERED = new SolarwattTag("WorkBuffered");
public static final SolarwattTag CHANNEL_WORK_BUFFERED_FROM_GRID = new SolarwattTag("WorkBufferedFromGrid");
public static final SolarwattTag CHANNEL_WORK_BUFFERED_FROM_PRODUCERS = new SolarwattTag(
"WorkBufferedFromProducers");
public static final SolarwattTag CHANNEL_WORK_CONSUMED = new SolarwattTag("WorkConsumed");
public static final SolarwattTag CHANNEL_WORK_CONSUMED_UNMETERED = new SolarwattTag("WorkConsumedUnmetered");
public static final SolarwattTag CHANNEL_WORK_CONSUMED_FROM_GRID = new SolarwattTag("WorkConsumedFromGrid");
public static final SolarwattTag CHANNEL_WORK_CONSUMED_FROM_STORAGE = new SolarwattTag("WorkConsumedFromStorage");
public static final SolarwattTag CHANNEL_WORK_CONSUMED_FROM_PRODUCERS = new SolarwattTag(
"WorkConsumedFromProducers");
public static final SolarwattTag CHANNEL_WORK_PRODUCED = new SolarwattTag("WorkProduced");
public static final SolarwattTag CHANNEL_WORK_OUT_FROM_PRODUCERS = new SolarwattTag("WorkOutFromProducers");
public static final SolarwattTag CHANNEL_WORK_OUT_FROM_STORAGE = new SolarwattTag("WorkOutFromStorage");
public static final SolarwattTag CHANNEL_WORK_RELEASED = new SolarwattTag("WorkReleased");
public static final SolarwattTag CHANNEL_WORK_SELF_CONSUMED = new SolarwattTag("WorkSelfConsumed");
public static final SolarwattTag CHANNEL_WORK_DIRECT_CONSUMED = new SolarwattTag("WorkDirectConsumed");
public static final SolarwattTag CHANNEL_WORK_SELF_SUPPLIED = new SolarwattTag("WorkSelfSupplied");
public static final SolarwattTag CHANNEL_CURRENT_LIMIT = new SolarwattTag("CurrentLimit");
public static final SolarwattTag CHANNEL_FEED_IN_LIMIT = new SolarwattTag("FeedInLimit");
public static final SolarwattTag CHANNEL_VOLTAGE_BATTERY_CELL_MAX = new SolarwattTag("VoltageBatteryCellMax");
public static final SolarwattTag CHANNEL_VOLTAGE_BATTERY_CELL_MIN = new SolarwattTag("VoltageBatteryCellMin");
public static final SolarwattTag CHANNEL_VOLTAGE_BATTERY_CELL_MEAN = new SolarwattTag("VoltageBatteryCellMean");
// thing configuration and properties keys
public static final String THING_PROPERTIES_GUID = "guid";
}

View File

@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
import org.openhab.binding.solarwatt.internal.handler.EnergyManagerHandler;
import org.openhab.binding.solarwatt.internal.handler.LocationHandler;
import org.openhab.binding.solarwatt.internal.handler.SimpleDeviceHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
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 SolarwattHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.solarwatt", service = ThingHandlerFactory.class)
public class SolarwattHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ENERGY_MANAGER,
THING_TYPE_INVERTER, THING_TYPE_POWERMETER, THING_TYPE_EVSTATION, THING_TYPE_BATTERYCONVERTER,
THING_TYPE_LOCATION, THING_TYPE_PVPLANT, THING_TYPE_GRIDFLOW);
private final HttpClient commonHttpClient;
private final SolarwattChannelTypeProvider channelTypeProvider;
@Activate
public SolarwattHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
final @Reference SolarwattChannelTypeProvider channelTypeProvider) {
this.commonHttpClient = httpClientFactory.getCommonHttpClient();
this.channelTypeProvider = channelTypeProvider;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_ENERGY_MANAGER.equals(thingTypeUID)) {
// energy manager is a separate device as it is the bridge
return new EnergyManagerHandler((Bridge) thing, this.channelTypeProvider, this.commonHttpClient);
} else if (THING_TYPE_LOCATION.equals(thingTypeUID)) {
return new LocationHandler(thing, this.channelTypeProvider);
} else if (this.supportsThingType(thingTypeUID)) {
// standard device handling
return new SimpleDeviceHandler(thing, this.channelTypeProvider);
}
return null;
}
}

View File

@ -0,0 +1,120 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.channel;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.SolarwattBindingConstants;
import org.openhab.binding.solarwatt.internal.domain.SolarwattChannel;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeBuilder;
import org.openhab.core.thing.type.ChannelTypeProvider;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.thing.type.StateChannelTypeBuilder;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.openhab.core.types.util.UnitUtils;
import org.osgi.service.component.annotations.Component;
/**
* A {@link ChannelTypeProvider} that creates {@link ChannelType}s according to
* the requested tags. It creates one {@link ChannelType} per tag value.
*
* @author Matthias Steigenberger - Initial contribution
* @author Sven Carstens - Adapted to solarwatt binding
*
*/
@NonNullByDefault
@Component(service = { ChannelTypeProvider.class, SolarwattChannelTypeProvider.class })
public class SolarwattChannelTypeProvider implements ChannelTypeProvider {
private final Map<String, ChannelType> channelMap = new ConcurrentHashMap<>();
@Override
public Collection<ChannelType> getChannelTypes(@Nullable Locale locale) {
return this.channelMap.values();
}
@Override
public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
return this.channelMap.values().stream().filter(channelType -> channelType.getUID().equals(channelTypeUID))
.findFirst().orElse(null);
}
/**
* Assert that the {@link ChannelType} matching our requirements exists.
*
* Only create once for each tagname supplied via {@link SolarwattChannel}.
*
* @param solarwattChannel channeltype requirements
* @return UID of existing channeltype
*/
public ChannelTypeUID assertChannelType(SolarwattChannel solarwattChannel) {
ChannelType existingChannel = this.channelMap.get(solarwattChannel.getChannelName());
if (existingChannel == null) {
ChannelType createdChannel = this.getChannelType(solarwattChannel);
this.channelMap.put(solarwattChannel.getChannelName(), createdChannel);
return createdChannel.getUID();
} else {
return existingChannel.getUID();
}
}
private ChannelType getChannelType(SolarwattChannel solarwattChannel) {
StateChannelTypeBuilder stateDescriptionBuilder;
Unit<?> unit = solarwattChannel.getUnit();
if (unit != null) {
if ("switch".equals(solarwattChannel.getCategory())) {
stateDescriptionBuilder = ChannelTypeBuilder
.state(new ChannelTypeUID(SolarwattBindingConstants.BINDING_ID,
solarwattChannel.getChannelName()), solarwattChannel.getChannelName(),
CoreItemFactory.SWITCH)
.withCategory(solarwattChannel.getCategory()).isAdvanced(solarwattChannel.getAdvanced())
.withStateDescriptionFragment(
StateDescriptionFragmentBuilder.create().withReadOnly(true).build());
} else {
String dimension = ":" + UnitUtils.getDimensionName(unit);
String unitString = unit.toString();
if (Units.PERCENT.equals(unit)) {
// strangely it is Angle
dimension = ":Dimensionless";
unitString = "%%";
}
stateDescriptionBuilder = ChannelTypeBuilder
.state(new ChannelTypeUID(SolarwattBindingConstants.BINDING_ID,
solarwattChannel.getChannelName()), solarwattChannel.getChannelName(),
CoreItemFactory.NUMBER + dimension)
.withCategory(solarwattChannel.getCategory()).isAdvanced(solarwattChannel.getAdvanced())
.withStateDescriptionFragment(StateDescriptionFragmentBuilder.create().withReadOnly(true)
.withPattern("%.2f " + unitString).build());
}
} else {
stateDescriptionBuilder = ChannelTypeBuilder
.state(new ChannelTypeUID(SolarwattBindingConstants.BINDING_ID, solarwattChannel.getChannelName()),
solarwattChannel.getChannelName(), CoreItemFactory.STRING)
.withCategory(solarwattChannel.getCategory()).isAdvanced(solarwattChannel.getAdvanced())
.withStateDescriptionFragment(StateDescriptionFragmentBuilder.create().withReadOnly(true).build());
}
return stateDescriptionBuilder.build();
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link SolarwattBridgeConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattBridgeConfiguration {
private static final int DEFAULT_RESCAN_MINUTES = 5;
private static final int DEFAULT_REFRESH_SECONDS = 30;
/**
* Hostname or ip where the solarwatt energymanager is reachable.
*
* Energy manager does not set a default name via DHCP.
*/
public String hostname = "";
/**
* Refresh interval for updating devices data
*/
public int refresh = DEFAULT_REFRESH_SECONDS;
/**
* Refresh interval for reading of devices (not the devices data)
*/
public int rescan = DEFAULT_RESCAN_MINUTES;
}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link SolarwattThingConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattThingConfiguration {
/**
* Guid for the thing that is used by the energy manager
*/
public String guid = "";
}

View File

@ -0,0 +1,194 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.discovery;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.model.BatteryConverter;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.binding.solarwatt.internal.domain.model.EVStation;
import org.openhab.binding.solarwatt.internal.domain.model.GridFlow;
import org.openhab.binding.solarwatt.internal.domain.model.Inverter;
import org.openhab.binding.solarwatt.internal.domain.model.Location;
import org.openhab.binding.solarwatt.internal.domain.model.PVPlant;
import org.openhab.binding.solarwatt.internal.domain.model.PowerMeter;
import org.openhab.binding.solarwatt.internal.handler.EnergyManagerHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovery service to discover devices attached to the energy manager.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattDevicesDiscoveryService extends AbstractDiscoveryService
implements ThingHandlerService, DiscoveryService {
private static final int TIMEOUT_SECONDS = 20;
private final Logger logger = LoggerFactory.getLogger(SolarwattDevicesDiscoveryService.class);
private @Nullable EnergyManagerHandler energyManagerHandler;
/**
* Job which will do the background scanning
*/
private final EnergymanagerScan scanningRunnable;
/**
* Schedule for scanning
*/
private @Nullable ScheduledFuture<?> scanningJob;
public SolarwattDevicesDiscoveryService() {
super(TIMEOUT_SECONDS);
this.scanningRunnable = new EnergymanagerScan();
this.activate(null);
}
@Override
public void setThingHandler(final @Nullable ThingHandler handler) {
if (handler instanceof EnergyManagerHandler) {
this.energyManagerHandler = (EnergyManagerHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return this.energyManagerHandler;
}
@Override
public void deactivate() {
this.stopBackgroundDiscovery();
}
@Override
protected void startBackgroundDiscovery() {
ScheduledFuture<?> localScanningJob = this.scanningJob;
if (localScanningJob == null || localScanningJob.isCancelled()) {
this.scanningJob = this.scheduler.scheduleWithFixedDelay(this.scanningRunnable, 5, 5 * 60,
TimeUnit.SECONDS);
}
}
@Override
protected void stopBackgroundDiscovery() {
ScheduledFuture<?> localScanningJob = this.scanningJob;
if (localScanningJob != null && !localScanningJob.isCancelled()) {
localScanningJob.cancel(true);
this.scanningJob = null;
}
}
@Override
protected synchronized void startScan() {
this.removeOlderResults(this.getTimestampOfLastScan());
final EnergyManagerHandler localEnergyManagerHandler = this.energyManagerHandler;
if (localEnergyManagerHandler == null
|| localEnergyManagerHandler.getThing().getStatus() != ThingStatus.ONLINE) {
this.logger.warn("Energymanager handler not available: {}", localEnergyManagerHandler);
return;
}
this.scanForDeviceThings();
}
/**
* Scans for device things.
*
* Walks through the list of devices and adds discovery results for the supported devices.
*/
private void scanForDeviceThings() {
EnergyManagerHandler localEnergyManagerHandler = this.energyManagerHandler;
if (localEnergyManagerHandler != null) {
final Map<String, Device> devices = localEnergyManagerHandler.getDevices();
final ThingUID bridgeUID = localEnergyManagerHandler.getThing().getUID();
if (devices == null) {
this.logger.warn("No device data for solarwatt devices in discovery for energy manager {}.", bridgeUID);
} else {
devices.forEach((key, entry) -> {
if (entry instanceof BatteryConverter) {
this.discover(bridgeUID, entry, THING_TYPE_BATTERYCONVERTER);
} else if (entry instanceof Inverter) {
this.discover(bridgeUID, entry, THING_TYPE_INVERTER);
} else if (entry instanceof PowerMeter) {
this.discover(bridgeUID, entry, THING_TYPE_POWERMETER);
} else if (entry instanceof EVStation) {
this.discover(bridgeUID, entry, THING_TYPE_EVSTATION);
} else if (entry instanceof Location) {
this.discover(bridgeUID, entry, THING_TYPE_LOCATION);
} else if (entry instanceof PVPlant) {
this.discover(bridgeUID, entry, THING_TYPE_PVPLANT);
} else if (entry instanceof GridFlow) {
this.discover(bridgeUID, entry, THING_TYPE_GRIDFLOW);
}
});
}
}
}
/**
* Create a discovery result and add to result.
*
* @param bridgeID to which this device belongs
* @param entry describing the device
* @param typeUID for matching thing
*/
private void discover(final ThingUID bridgeID, final Device entry, final ThingTypeUID typeUID) {
final ThingUID thingUID = new ThingUID(typeUID, bridgeID, this.rewriteGuid(entry.getGuid()));
final Map<String, Object> properties = new HashMap<>(5);
properties.put(THING_PROPERTIES_GUID, entry.getGuid());
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeID)
.withRepresentationProperty(THING_PROPERTIES_GUID).withProperties(properties)
.withLabel("Solarwatt " + entry.getLabel()).build();
this.thingDiscovered(discoveryResult);
}
/**
* Rewrite energy manager guids to be acceptable to openhab.
*
* @param emGuid from energy manager
* @return guid for openhab
*/
private String rewriteGuid(String emGuid) {
return emGuid.replaceAll(":", "-");
}
public class EnergymanagerScan implements Runnable {
@Override
public void run() {
SolarwattDevicesDiscoveryService.this.startScan();
}
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.EnergyManagerDTO;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.binding.solarwatt.internal.factory.EnergyManagerDevicesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Collection of all devices known to the energy manager including the energy manager itself.
*
* The {@link Device}s are generated from the {@link DeviceDTO}s inside of the {@link EnergyManagerDTO}
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EnergyManagerCollection {
private Logger logger = LoggerFactory.getLogger(EnergyManagerCollection.class);
private Map<String, Device> devices;
public EnergyManagerCollection(EnergyManagerDTO energyManagerDTO) {
this.devices = new HashMap<>();
energyManagerDTO.getItems().forEach(deviceDTO -> {
try {
Device device = EnergyManagerDevicesFactory.getEnergyManagerDevice(deviceDTO);
if (device != null) {
this.devices.put(device.getGuid(), device);
}
} catch (Exception ex) {
this.logger.error("Error setting up initial device {}: {}", deviceDTO.getGuid(),
deviceDTO.getDeviceModel(), ex);
}
});
}
public Map<String, Device> getDevices() {
return this.devices;
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Aggregation of the interesting parts to write into a channel.
*
* From this the {@link ChannelType}s are created.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattChannel {
private final String channelName;
private final @Nullable Unit<?> unit;
private final String category;
private final Boolean advanced;
public SolarwattChannel(String channelName, String category) {
this(channelName, category, false);
}
public SolarwattChannel(String channelName, Unit<?> unit, String category) {
this(channelName, unit, category, false);
}
public SolarwattChannel(String channelName, String category, Boolean advanced) {
this(channelName, null, category, advanced);
}
public SolarwattChannel(String channelName, @Nullable Unit<?> unit, String category, Boolean advanced) {
this.channelName = channelName;
this.unit = unit;
this.category = category;
this.advanced = advanced;
}
public String getChannelName() {
return this.channelName;
}
public @Nullable Unit<?> getUnit() {
return this.unit;
}
public String getCategory() {
return this.category;
}
public Boolean getAdvanced() {
return this.advanced;
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Helper to handle different character cases between energy manager tagnames
* and openhab channel names.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattTag {
private final String tagName;
private final String channelName;
public SolarwattTag(String tagName) {
this.tagName = tagName;
char chars[] = tagName.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
this.channelName = new String(chars);
}
public String getTagName() {
return this.tagName;
}
public String getChannelName() {
return this.channelName;
}
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.converter;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.types.State;
import com.google.gson.JsonElement;
/**
* Interface bundling all converters from JsonElement to openhab State.
*
* We do not implement the interface but only pass closures.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public interface JsonStateConverter {
State convert(JsonElement jsonElement);
}

View File

@ -0,0 +1,226 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.dto;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.converter.JsonStateConverter;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* DTO class for the devices returned by the energy manager.
*
* Properties without setters are only filled by gson JSON parsing.
*
* @author Sven Carstens - Initial contribution
*/
public class DeviceDTO {
private final Logger logger = LoggerFactory.getLogger(DeviceDTO.class);
private String guid;
private Collection<DeviceModel> deviceModel;
private Map<String, TagValueDTO> tagValues;
public String getGuid() {
return this.guid;
}
public Collection<String> getDeviceModelStrings() {
return this.deviceModel.stream().map(DeviceModel::getDeviceClass).collect(Collectors.toList());
}
public Collection<DeviceModel> getDeviceModel() {
return this.deviceModel;
}
public Map<String, TagValueDTO> getTagValues() {
return this.tagValues;
}
/**
* Helper to get a string value from the list of tag values.
*
* @param tagName name of tag to read
* @return tag value
*/
public @Nullable String getStringTag(String tagName) {
JsonPrimitive jsonPrimitive = this.getJsonPrimitiveFromTag(tagName);
if (jsonPrimitive != null) {
return jsonPrimitive.getAsString();
}
return null;
}
/**
* Helper to get a {@link JsonPrimitive} from the list of tag values.
*
* The primitves are later converted to the desired target type.
*
* @param tagName name of tag to read
* @return raw json from tag value
*/
protected @Nullable JsonPrimitive getJsonPrimitiveFromTag(String tagName) {
Map<String, TagValueDTO> localTagValues = this.getTagValues();
if (localTagValues != null) {
TagValueDTO localTag = localTagValues.get(tagName);
if (localTag != null && localTag.getValue().isJsonPrimitive()) {
return (JsonPrimitive) localTag.getValue();
}
}
return null;
}
/**
* Helper to get a {@link JsonObject} from the list of tag values.
*
* The objects are used by the concrete devices to read interesting
* but deeply nested values.
*
* @param tagName name of tag to read
* @return raw json from tag value
*/
public @Nullable JsonObject getJsonObjectFromTag(String tagName) {
Map<String, TagValueDTO> localTagValues = this.getTagValues();
if (localTagValues != null) {
TagValueDTO localTag = localTagValues.get(tagName);
if (localTag != null && localTag.getValue().isJsonObject()) {
return (JsonObject) localTag.getValue();
}
}
return null;
}
/**
* Helper to get a {@link JsonObject} from a tag value which contains JSON.
*
* The objects are used by the concrete devices to read interesting
* but deeply nested values.
*
* @param tagName name of tag to read
* @return raw json from tag value
*/
public JsonPrimitive getJsonPrimitiveFromPath(String tagName, String path) {
JsonElement jsonElement = this.getJsonFromPath(tagName, path);
if (jsonElement != null && jsonElement.isJsonPrimitive()) {
return (JsonPrimitive) jsonElement;
}
return null;
}
/**
* Helper to get a {@link JsonObject} from a tag value which contains JSON.
*
* The json path is traversed according to the supplied path. Only simple pathes
* are supported.
*
* @param tagName name of tag to read
* @return raw json from tag value
*/
public JsonElement getJsonFromPath(String tagName, String path) {
JsonObject json = this.getJsonObjectFromTag(tagName);
if (json != null) {
String[] parts = path.split("\\.|\\[|\\]");
JsonElement result = json;
for (String key : parts) {
key = key.trim();
if (key.isEmpty()) {
continue;
}
if (result == null) {
result = JsonNull.INSTANCE;
break;
}
if (result.isJsonObject()) {
result = ((JsonObject) result).get(key);
} else if (result.isJsonArray()) {
int ix = Integer.valueOf(key) - 1;
result = ((JsonArray) result).get(ix);
} else {
break;
}
}
return result;
}
return null;
}
/**
* Transform a value from a tag to a state.
*
* @param converter applied the the {@link JsonPrimitive}
* @param tagName to find value
* @return state for channel
*/
public State getState(JsonStateConverter converter, String tagName) {
return this.getState(converter, tagName, tagName, null);
}
/**
* Transform a value specified via JSON path from a tag to a state.
*
* @param converter applied the the {@link JsonPrimitive}
* @param channelName for the state
* @param tagName to find value
* @param jsonPath to find value
* @return state for channel
*/
public State getState(JsonStateConverter converter, String channelName, String tagName, String jsonPath) {
State state = UnDefType.UNDEF;
try {
JsonPrimitive jsonPrimitive = jsonPath == null ? this.getJsonPrimitiveFromTag(tagName)
: this.getJsonPrimitiveFromPath(tagName, jsonPath);
if (jsonPrimitive != null) {
state = converter.convert(jsonPrimitive);
} else {
state = UnDefType.NULL;
}
} catch (Exception ex) {
this.logger.warn("failed getting state for {}", channelName, ex);
}
return state;
}
public static class DeviceModel {
private String deviceClass;
public String getDeviceClass() {
return this.deviceClass;
}
@Override
public String toString() {
return this.deviceClass;
}
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.dto;
import java.util.ArrayList;
import java.util.Collection;
/**
* DTO class for the complete structure delivered by the energy manager.
*
* Properties without setters are only filled by gson JSON parsing.
*
* @author Sven Carstens - Initial contribution
*/
public class EnergyManagerDTO {
private Result result;
public Collection<DeviceDTO> getItems() {
return this.result.getItems();
}
public static class Result {
public Collection<DeviceDTO> getItems() {
return this.items;
}
private Collection<DeviceDTO> items = new ArrayList<>();
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.dto;
import com.google.gson.JsonElement;
/**
* DTO class to encapsulate the tag values from the energy manager.
*
* Properties without setters are only filled by gson JSON parsing.
* The concrete tag values can be anything. {@link com.google.gson.JsonPrimitive},
* {@link com.google.gson.JsonObject} or even strings containing json structures.
*
* @author Sven Carstens - Initial contribution
*/
public class TagValueDTO {
private String tagName;
private String guid;
private JsonElement value;
public String getTagName() {
return this.tagName;
}
public String getGuid() {
return this.guid;
}
public JsonElement getValue() {
return this.value;
}
}

View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Base class for everything supplying battery base power.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.batteryconverter.BatteryConverter=[
* PowerACIn,
* UpTimePDG,
* CurrentStringDCIn,
* ResistanceBatteryMean,
* CurrentBatteryIn,
* VoltageBatteryCellMax,
* VoltageGRMOut,
* FactorForecast,
* StateOfChargeMinimum,
* StateEqualizingChargeRequiredIsSet,
* WorkCharge,
* VoltageBatteryCellMin,
* VoltageBatteryCellMean,
* StateOfHealth,
* FactorForecastCAN,
* StateOfCharge,
* IdFirmwareGRM,
* WorkCapacity,
* CurrentGRMOut,
* StatePDG,
* TemperatureBatteryCellMax,
* TemperatureGRM,
* TemperatureBatteryMin,
* ResistanceBatteryMin,
* StateOfChargeMinimumLimit,
* IdUrlPdg,
* TemperatureBattery,
* VoltageGRMIn,
* CurrentGRMIn,
* IdSerialNumberGRM,
* ResistanceBatteryString,
* VoltageBatteryString,
* IdSerialNumberBatteryModules,
* CountBatteryModules,
* ModeConverter,
* TemperatureBatteryMax,
* TemperatureBatteryCellMin,
* StateOfChargeReactivateDischarging,
* IdMyReserveSetupRole,
* ResistanceBatteryMax,
* AvailableModes,
* PowerACInMax,
* PowerACInLimit,
* StateOfChargeShutDownLimit,
* CountBatteryContactor,
* CurrentBatteryOut,
* TimeEqualizingChargeRemaining,
* WorkACIn,
* MapInstallationDetails
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class BatteryConverter extends Inverter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.batteryconverter.BatteryConverter";
public BatteryConverter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addSwitchState(CHANNEL_MODE_CONVERTER, deviceDTO);
this.addPercentQuantity(CHANNEL_STATE_OF_CHARGE, deviceDTO);
this.addPercentQuantity(CHANNEL_STATE_OF_HEALTH, deviceDTO);
this.addCelsiusQuantity(CHANNEL_TEMPERATURE_BATTERY, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_AC_IN, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_IN, deviceDTO);
this.addVoltageQuantity(CHANNEL_VOLTAGE_BATTERY_CELL_MIN, deviceDTO, true);
this.addVoltageQuantity(CHANNEL_VOLTAGE_BATTERY_CELL_MEAN, deviceDTO, true);
this.addVoltageQuantity(CHANNEL_VOLTAGE_BATTERY_CELL_MAX, deviceDTO, true);
}
@Override
protected String getSolarWattLabel() {
return "BatteryConverter";
}
}

View File

@ -0,0 +1,315 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.SolarwattChannel;
import org.openhab.binding.solarwatt.internal.domain.SolarwattTag;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.State;
/**
* Base class for all devices which are connected to the energy manager.
*
* This fields have been identified to exist:
* com.kiwigrid.lib.device.Device=[
* IdFingerPrint,
* IdInterfaceList,
* IdName,
* IdFirmware,
* PasswordLock,
* IdLabelSet,
* StateDevice,
* StateVisibleIsSet,
* IdFingerPrintVersion,
* IdDriver,
* IdModelCode,
* StateLockedIsSet,
* IdManufacturer,
* IdSerialNumber,
* StateErrorList
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.lib.device.Device";
public static final String WATT_HOUR_CATEGORY = "energy";
public static final String WATT_CATEGORY = "energy";
private final String guid;
private @Nullable String idName;
private @Nullable String idFirmware;
private @Nullable String idManufacturer;
private ThingStatus stateDevice = ThingStatus.UNINITIALIZED;
protected final Map<String, State> stateValues;
protected final Map<String, SolarwattChannel> solarwattChannelSet;
public Device(DeviceDTO deviceDTO) {
this.stateValues = new HashMap<>();
this.solarwattChannelSet = new HashMap<>();
this.guid = deviceDTO.getGuid();
this.update(deviceDTO);
}
public void update(DeviceDTO deviceDTO) {
this.idName = deviceDTO.getStringTag("IdName");
this.idFirmware = deviceDTO.getStringTag("IdFirmware");
this.idManufacturer = deviceDTO.getStringTag("IdManufacturer");
if ("OK".equals(deviceDTO.getStringTag("StateDevice"))) {
this.stateDevice = ThingStatus.ONLINE;
} else {
this.stateDevice = ThingStatus.OFFLINE;
}
}
public BigDecimal getBigDecimalFromChannel(String channelName) {
State state = this.getStateValues().get(channelName);
if (state != null) {
@SuppressWarnings("rawtypes")
QuantityType quantity = state.as(QuantityType.class);
if (quantity != null) {
return quantity.toBigDecimal();
}
}
return BigDecimal.ZERO;
}
/**
* Add a channeltype to the known channel types.
*
* {@link org.openhab.core.thing.type.ChannelType} is only created if it does noct exist.
*
* @param tagName name for the channel
* @param unit unit for channel
* @param category text category
* @param advanced wether or not to display only in advanced
*/
public void addChannel(String tagName, @Nullable Unit<?> unit, String category, Boolean advanced) {
this.solarwattChannelSet.computeIfAbsent(tagName, s -> new SolarwattChannel(tagName, unit, category, advanced));
}
/**
* Add a state with unit and BigInteger as value.
*
* @param solarwattTag combined tag and channel name
* @param deviceDTO raw device data
* @param unit unit for value
*/
public void addStateBigInteger(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Unit<?> unit) {
this.addState(solarwattTag.getChannelName(), deviceDTO.getState(
(jsonElement -> new QuantityType<>(jsonElement.getAsBigInteger(), unit)), solarwattTag.getTagName()));
}
/**
* Add a state from a json path with unit and BigInteger as value.
*
* @param channelName target channe
* @param tagName tag for value
* @param path to find value
* @param deviceDTO raw device data
* @param unit unit for value
*/
public void addStateBigInteger(String channelName, String tagName, String path, DeviceDTO deviceDTO, Unit<?> unit) {
this.addState(channelName, deviceDTO.getState(
(jsonElement -> new QuantityType<>(jsonElement.getAsBigInteger(), unit)), channelName, tagName, path));
}
/**
* Add a state with unit and BigDecimal as value.
*
* @param solarwattTag combined tag and channel name
* @param deviceDTO raw device data
* @param unit unit for value
*/
public void addStateBigDecimal(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Unit<?> unit) {
this.addState(solarwattTag.getChannelName(), deviceDTO.getState(
(jsonElement -> new QuantityType<>(jsonElement.getAsBigDecimal(), unit)), solarwattTag.getTagName()));
}
/**
* Add a state with unit and BigDecimal as value.
*
* @param solarwattTag combined tag and channel name
* @param value BigDecimal value
* @param unit unit for value
*/
public void addStateBigDecimal(SolarwattTag solarwattTag, BigDecimal value, Unit<?> unit) {
this.addState(solarwattTag.getChannelName(), new QuantityType<>(value, unit));
}
/**
* Add a string state.
*
* @param solarwattTag combined tag and channel name
* @param deviceDTO raw device data
*/
public void addStateString(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addState(solarwattTag.getChannelName(), deviceDTO
.getState((jsonElement -> new StringType(jsonElement.getAsString())), solarwattTag.getTagName()));
}
public void addStateSwitch(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addState(solarwattTag.getChannelName(), deviceDTO
.getState((jsonElement -> OnOffType.from(jsonElement.getAsString())), solarwattTag.getTagName()));
}
public ThingStatus getStateDevice() {
return this.stateDevice;
}
public Map<String, State> getStateValues() {
return this.stateValues;
}
public Map<String, SolarwattChannel> getSolarwattChannelSet() {
return this.solarwattChannelSet;
}
protected void addWattHourQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addWattHourQuantity(solarwattTag, deviceDTO, false);
}
protected void addWattHourQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Boolean advanced) {
this.addChannel(solarwattTag.getChannelName(), Units.WATT_HOUR, WATT_HOUR_CATEGORY, advanced);
this.addStateBigDecimal(solarwattTag, deviceDTO, Units.WATT_HOUR);
}
protected void addWattQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addWattQuantity(solarwattTag, deviceDTO, false);
}
protected void addWattQuantity(SolarwattTag solarwattTag, BigDecimal value, Boolean advanced) {
this.addChannel(solarwattTag.getChannelName(), Units.WATT, WATT_CATEGORY, advanced);
this.addStateBigDecimal(solarwattTag, value, Units.WATT);
}
protected void addWattQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Boolean advanced) {
this.addChannel(solarwattTag.getChannelName(), Units.WATT, WATT_CATEGORY, advanced);
this.addStateBigDecimal(solarwattTag, deviceDTO, Units.WATT);
}
protected void addSecondsQuantity(String channelName, String tagName, String path, DeviceDTO deviceDTO) {
this.addChannel(channelName, Units.SECOND, "time", false);
this.addStateBigInteger(channelName, tagName, path, deviceDTO, Units.SECOND);
}
protected void addPercentQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addChannel(solarwattTag.getChannelName(), Units.PERCENT, "status", false);
this.addStateBigDecimal(solarwattTag, deviceDTO, Units.PERCENT);
}
protected void addCelsiusQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addChannel(solarwattTag.getChannelName(), SIUnits.CELSIUS, "temperature", false);
this.addStateBigDecimal(solarwattTag, deviceDTO, SIUnits.CELSIUS);
}
protected void addAmpereQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addAmpereQuantity(solarwattTag, deviceDTO, false);
}
protected void addAmpereQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Boolean advanced) {
this.addChannel(solarwattTag.getChannelName(), Units.AMPERE, "current", advanced);
this.addStateBigDecimal(solarwattTag, deviceDTO, Units.AMPERE);
}
protected void addVoltageQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addVoltageQuantity(solarwattTag, deviceDTO, false);
}
protected void addVoltageQuantity(SolarwattTag solarwattTag, DeviceDTO deviceDTO, Boolean advanced) {
this.addChannel(solarwattTag.getChannelName(), Units.VOLT, "voltage", advanced);
this.addStateBigDecimal(solarwattTag, deviceDTO, Units.VOLT);
}
protected void addStringState(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addChannel(solarwattTag.getChannelName(), null, "status", false);
this.addStateString(solarwattTag, deviceDTO);
}
protected void addSwitchState(SolarwattTag solarwattTag, DeviceDTO deviceDTO) {
this.addChannel(solarwattTag.getChannelName(), null, "switch", false);
this.addStateSwitch(solarwattTag, deviceDTO);
}
/**
* Add state to map and return it for further usage.
*
* @param channelName where to put the state
* @param state to put
*/
public void addState(String channelName, @Nullable State state) {
if (state != null) {
this.stateValues.put(channelName, state);
}
}
/**
* Get state from map
*
* @param channelName state to return
* @return {@link State} found
*/
public @Nullable State getState(String channelName) {
return this.stateValues.get(channelName);
}
public String getGuid() {
return this.guid;
}
public @Nullable String getIdName() {
return this.idName;
}
public @Nullable String getIdFirmware() {
return this.idFirmware;
}
public @Nullable String getIdManufacturer() {
return this.idManufacturer;
}
protected String getSolarWattLabel() {
return "Device";
}
public String getLabel() {
return this.getSolarWattLabel() + " " + this.getIdName();
}
}

View File

@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Base class for a wallbox.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.evstation.EVStation=[
* PowerACOutMax,
* PowerACIn,
* VoltageACL1,
* VoltageACL2,
* VoltageACL3,
* WorkACOut,
* ConnectivityStatus,
* StateOfChargeMinimum,
* WorkACInLimitSession,
* WorkCharge,
* PowerACOut,
* PowerACOutLimit,
* AvailableModes,
* PowerACInMax,
* StateOfCharge,
* PowerACInLimit,
* CurrentACInLimit,
* StateInfo,
* ModeStation,
* VehicleId,
* CurrentACInMinimum,
* WorkCapacity,
* WorkACInSession,
* UserId,
* TemperatureBattery,
* WorkACIn,
* CurrentACInL1,
* CurrentACInL3,
* PowerACInMin,
* CurrentACInL2
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EVStation extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.evstation.EVStation";
public EVStation(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addStringState(CHANNEL_CONNECTIVITY_STATUS, deviceDTO);
this.addStringState(CHANNEL_MODE_STATION, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_IN, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_AC_IN, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_AC_IN_SESSION, deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "EVStation";
}
}

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* The energy manager itself which aggregates all other devices.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.em.EnergyManager=[
* IdInterfacesMap,
* DateCloudLastSeen,
* IdBootLoaderVersion,
* URLProxy,
* IdTimezone,
* FractionCPULoadAverageLastFifteenMinutes,
* FractionCPULoadAverageLastFiveMinutes,
* IdSystemImageVersion,
* VersionPackagesMap,
* IdNotInstalledDevicesMap,
* StatusMonitoringMap,
* FractionCPULoadUser,
* VersionExtensionsMap,
* InstallConfiguration,
* URLCloud,
* SettingsProxyMap,
* IdDevicesMap,
* SettingsMap,
* ReasonReboots,
* IdDriverList,
* TimeSinceStart,
* ExchangeDevice,
* SettingsPrivacyMap,
* FractionTopFiveProcessesMap,
* StateAction,
* FractionCPULoadAverageLastMinute,
* SettingsNetworkMap,
* SettingsDatetimeMap,
* FractionCPULoadKernel,
* FractionCPULoadTotal,
* IdRemoteAppSet,
* IdOwner,
* VersionLocalApplicationsMap,
* AddressLocation,
* Command,
* DriverMap,
* DateTagCollectorWatchdogEvents,
* LocationGeographical
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EnergyManager extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.em.EnergyManager";
public EnergyManager(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addSecondsQuantity(CHANNEL_TIMESTAMP.getChannelName(), "SettingsDatetimeMap", ".timestamp", deviceDTO);
this.addStringState(CHANNEL_IDTIMEZONE, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_TOTAL, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_USER, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_KERNEL, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_MINUTE, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_FIVE_MINUTES, deviceDTO);
this.addPercentQuantity(CHANNEL_FRACTION_CPU_LOAD_AVERAGE_LAST_FIFTEEN_MINUTES, deviceDTO);
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Class for the weather forecast used by the energy manager to predict the produced power
* and plan the battery charging.
*
* This fields have been identified to exist:
* com.solarwatt.devices.forecast.Forecast=[
* CorrectionFactors,
* DiffuseCorrectionFactors,
* DateNextYieldTrendScheduler,
* ModePreventPVForecast,
* WeatherForecast,
* ForecastProperties,
* DateNextConsumptionTrendScheduler,
* DateNextFactorScheduler,
* WeatherAPIKey,
* WeatherHistory
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class Forecast extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.solarwatt.devices.forecast.Forecast";
public Forecast(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "Forecast";
}
}

View File

@ -0,0 +1,224 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.CHANNEL_CURRENT_LIMIT;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.CHANNEL_FEED_IN_LIMIT;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
/**
* Class planning the energy flow out/into the grid.
*
* This fields have been identified to exist:
* com.kiwigrid.kiwiapp.gridflow.GridFlow=[
* ConfigTimeshifting,
* ConfigEvStationControl,
* FractionPVTestLimit,
* ConfigInverterControl,
* LogLevel,
* PowerSetpoint,
* ConfigPeakshaving,
* ToEMRequestTag,
* CurrentLimit,
* ConfigBatteryControl,
* ConfigForecastBatteryControl,
* ConfigEvStationChargingControl,
* ConfigSgReady,
* ToCloudDataTag
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class GridFlow extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.kiwiapp.gridflow.GridFlow";
private final Logger logger = LoggerFactory.getLogger(GridFlow.class);
private @Nullable ConfigInverterControl configInverterControl;
public GridFlow(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addAmpereQuantity(CHANNEL_CURRENT_LIMIT, deviceDTO, true);
try {
JsonObject rawConfigInverterControl = deviceDTO.getJsonObjectFromTag("ConfigInverterControl");
Gson gson = new GsonBuilder().create();
this.configInverterControl = gson.fromJson(rawConfigInverterControl, GridFlow.ConfigInverterControl.class);
} catch (Exception ex) {
this.configInverterControl = null;
this.logger.warn("Could not read ConfigInverterControl", ex);
}
GridFlow.ConfigInverterControl localConfigInverterControl = this.configInverterControl;
if (localConfigInverterControl != null) {
// the only interesting value is the derailing (i.e. limit power flowing into the grid)
BigDecimal feedInLimit = localConfigInverterControl.getFeedInLimit();
if (feedInLimit != null) {
QuantityType<?> state = new QuantityType<>(feedInLimit.multiply(BigDecimal.valueOf(100)),
Units.PERCENT);
this.stateValues.put(CHANNEL_FEED_IN_LIMIT.getChannelName(), state);
this.addChannel(CHANNEL_FEED_IN_LIMIT.getChannelName(), Units.PERCENT, "status", false);
}
}
}
@Override
protected String getSolarWattLabel() {
return "GridFlow";
}
public static class ConfigInverterControl {
private @Nullable BigDecimal testModeFeedInLimit;
private @Nullable ControllerParameter controllerParameter;
private @Nullable Boolean dynamicControllingOn;
private @Nullable BigDecimal feedInLimit;
private @Nullable DynamicControllerParameter dynamicControllerParameter;
private @Nullable Boolean on;
private @Nullable Boolean isErrorPVLimiting;
private @Nullable Boolean activeTestMode;
private @Nullable Boolean isPVPlantConfigured;
private @Nullable String selectedRcr;
private @Nullable Boolean limitByRcr;
public @Nullable BigDecimal getFeedInLimit() {
return this.feedInLimit;
}
public @Nullable BigDecimal getTestModeFeedInLimit() {
return this.testModeFeedInLimit;
}
public @Nullable ControllerParameter getControllerParameter() {
return this.controllerParameter;
}
public @Nullable Boolean getDynamicControllingOn() {
return this.dynamicControllingOn;
}
public @Nullable DynamicControllerParameter getDynamicControllerParameter() {
return this.dynamicControllerParameter;
}
public @Nullable Boolean getOn() {
return this.on;
}
public @Nullable Boolean getErrorPVLimiting() {
return this.isErrorPVLimiting;
}
public @Nullable Boolean getActiveTestMode() {
return this.activeTestMode;
}
public @Nullable Boolean getPVPlantConfigured() {
return this.isPVPlantConfigured;
}
public @Nullable String getSelectedRcr() {
return this.selectedRcr;
}
public @Nullable Boolean getLimitByRcr() {
return this.limitByRcr;
}
public static class ControllerParameter {
private @Nullable BigDecimal integrationRate;
private @Nullable BigInteger outputRampRateLimit;
private @Nullable BigDecimal differentialRate;
private @Nullable BigDecimal proportionalRate;
private @Nullable BigInteger outputValueLimit;
private @Nullable BigInteger controlFaultSumLimit;
public @Nullable BigDecimal getIntegrationRate() {
return this.integrationRate;
}
public @Nullable BigInteger getOutputRampRateLimit() {
return this.outputRampRateLimit;
}
public @Nullable BigDecimal getDifferentialRate() {
return this.differentialRate;
}
public @Nullable BigDecimal getProportionalRate() {
return this.proportionalRate;
}
public @Nullable BigInteger getOutputValueLimit() {
return this.outputValueLimit;
}
public @Nullable BigInteger getControlFaultSumLimit() {
return this.controlFaultSumLimit;
}
}
public static class DynamicControllerParameter {
private @Nullable BigDecimal integrationRate;
private @Nullable BigInteger outputRampRateLimit;
private @Nullable BigDecimal differentialRate;
private @Nullable BigDecimal proportionalRate;
private @Nullable BigInteger outputValueLimit;
private @Nullable BigInteger controlFaultSumLimit;
public @Nullable BigDecimal getIntegrationRate() {
return this.integrationRate;
}
public @Nullable BigInteger getOutputRampRateLimit() {
return this.outputRampRateLimit;
}
public @Nullable BigDecimal getDifferentialRate() {
return this.differentialRate;
}
public @Nullable BigDecimal getProportionalRate() {
return this.proportionalRate;
}
public @Nullable BigInteger getOutputValueLimit() {
return this.outputValueLimit;
}
public @Nullable BigInteger getControlFaultSumLimit() {
return this.controlFaultSumLimit;
}
}
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Base class for all inverters.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.inverter.Inverter=[
* WorkACOut,
* PowerInstalledPeak,
* PowerStringDCIn,
* PowerACOutMax,
* PowerACOut,
* PowerACOutLimit,
* ACPower,
* PowerYieldSum
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class Inverter extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.inverter.Inverter";
public Inverter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_AC_OUT, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_OUT_MAX, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_OUT, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_OUT_LIMIT, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_INSTALLED_PEAK, deviceDTO, true);
}
@Override
protected String getSolarWattLabel() {
return "Inverter";
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for the Keba Wallbox.
*
* No additional fields where found.
* com.kiwigrid.devices.keba.ev.KebaEv=[]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class KebaEv extends EVStation {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.keba.ev.KebaEv";
public KebaEv(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "KebaEVStation";
}
}

View File

@ -0,0 +1,208 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;
/**
* Class that aggregates all devices which are found in one location
* and are working together to produce power.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.location.Location=[
* WorkBufferedFromProducers,
* WorkOutFromProducers,
* PowerBufferedFromGrid,
* WorkProduced,
* WorkConsumedFromStorage,
* PowerSelfConsumed,
* PowerConsumedFromProducers,
* PowerConsumedFromStorage,
* PowerOutFromProducers,
* DatePowerProductionForecastStart,
* PowerOut,
* PowerProductionForecastNow,
* PowerOutFromStorage,
* IdDevicesMap,
* PowerBuffered,
* TimePowerProductionForecastGranularity,
* WorkOutFromStorage,
* CountPersons,
* DatePowerConsumptionForecastStart,
* PowerConsumptionForecastNow,
* PowerProduced,
* WorkBuffered,
* WorkConsumedFromProducers,
* PowerConsumed,
* WorkConsumedFromGrid,
* PowerConsumptionForecastValues,
* PowerSelfSupplied,
* WorkReleased,
* PowerConsumedFromGrid,
* TimePowerConsumptionForecastGranularity,
* WorkConsumed,
* AddressLocation,
* WorkIn,
* PriceWorkIn,
* PowerIn,
* WorkBufferedFromGrid,
* WorkSelfConsumed,
* PowerProductionForecastValues,
* LocationGeographical,
* PowerBufferedFromProducers,
* PowerReleased,
* WorkOut,
* WorkSelfSupplied
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class Location extends Device {
private final Logger logger = LoggerFactory.getLogger(Location.class);
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.location.Location";
private @Nullable IdDevicesMap devicesMap;
public Location(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
// values to put on a overview dashboard
this.addWattQuantity(CHANNEL_POWER_BUFFERED, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_SELF_CONSUMED, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_SELF_SUPPLIED, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_CONSUMED_FROM_GRID, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_CONSUMED_FROM_STORAGE, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_CONSUMED, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_PRODUCED, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_OUT, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_BUFFERED, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_SELF_CONSUMED, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_SELF_SUPPLIED, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_CONSUMED_FROM_GRID, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_CONSUMED_FROM_STORAGE, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_CONSUMED, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_PRODUCED, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_OUT, deviceDTO);
// not necessary for a dashboard, so marked as advanced
this.addWattQuantity(CHANNEL_POWER_BUFFERED_FROM_GRID, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_BUFFERED_FROM_PRODUCERS, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_CONSUMED_FROM_PRODUCERS, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_IN, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_OUT_FROM_PRODUCERS, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_OUT_FROM_STORAGE, deviceDTO, true);
this.addWattQuantity(CHANNEL_POWER_RELEASED, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_BUFFERED_FROM_GRID, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_BUFFERED_FROM_PRODUCERS, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_CONSUMED_FROM_PRODUCERS, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_OUT_FROM_PRODUCERS, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_OUT_FROM_STORAGE, deviceDTO, true);
this.addWattHourQuantity(CHANNEL_WORK_RELEASED, deviceDTO, true);
// read IdDevicesMap to find out which devices are located/metered where
// to get the unmetered consumption we need for {@link LocationHandler} to take Location.(Work|Power)Consumed
// and subtract the (Work|Power)(AC)In of the OUTER_CONSUMERs
try {
JsonObject rawDevicesMap = deviceDTO.getJsonObjectFromTag("IdDevicesMap");
Gson gson = new GsonBuilder().create();
this.devicesMap = gson.fromJson(rawDevicesMap, IdDevicesMap.class);
} catch (Exception ex) {
this.devicesMap = null;
this.logger.warn("Could not read IdDevicesMap", ex);
}
}
public IdDevicesMap getDevicesMap() {
IdDevicesMap returnDevicesMap = this.devicesMap;
if (returnDevicesMap != null) {
return returnDevicesMap;
}
return new IdDevicesMap();
}
@Override
protected String getSolarWattLabel() {
return "Location";
}
public static class IdDevicesMap {
@SerializedName("INNER_BUFFER")
private @Nullable Set<String> innerBuffer;
@SerializedName("INNER_CONSUMER")
private @Nullable Set<String> innerConsumer;
@SerializedName("POWERMETER_CONSUMPTION")
private @Nullable Set<String> powermeterConsumption;
@SerializedName("OUTER_CONSUMER")
private @Nullable Set<String> outerConsumer;
@SerializedName("OUTER_BUFFER")
private @Nullable Set<String> outerBuffer;
@SerializedName("POWERMETER_PRODUCTION")
private @Nullable Set<String> powermeterProduction;
@SerializedName("OUTER_PRODUCER")
private @Nullable Set<String> outerProducer;
@SerializedName("INNER_PRODUCER")
private @Nullable Set<String> innerProducer;
public @Nullable Set<String> getInnerBuffer() {
return this.innerBuffer;
}
public @Nullable Set<String> getInnerConsumer() {
return this.innerConsumer;
}
public @Nullable Set<String> getPowermeterConsumption() {
return this.powermeterConsumption;
}
public @Nullable Set<String> getOuterConsumer() {
return this.outerConsumer;
}
public @Nullable Set<String> getOuterBuffer() {
return this.outerBuffer;
}
public @Nullable Set<String> getPowermeterProduction() {
return this.powermeterProduction;
}
public @Nullable Set<String> getOuterProducer() {
return this.outerProducer;
}
public @Nullable Set<String> getInnerProducer() {
return this.innerProducer;
}
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for the MyReserve battery.
*
* The MyReserve also contains {@link MyReservePowerMeter} and {@link MyReserveInverter}.
*
* No additional fields where found.
* com.kiwigrid.devices.solarwatt.MyReserve=[]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class MyReserve extends BatteryConverter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.solarwatt.MyReserve";
public MyReserve(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "BatteryConverter";
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for the MyReserve inverter.
*
* The MyReserve also contains {@link MyReservePowerMeter} and {@link MyReserve}.
* This fields have been identified to exist:
* com.kiwigrid.devices.solarwatt.MyReserveInverter=[
* PowerBatteryDC,
* IdMyReserveSetupRole,
* IdMyReserveSetup,
* IdDcCoupledBatteriesList,
* IdInverter,
* Command
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class MyReserveInverter extends Inverter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.solarwatt.MyReserveInverter";
public MyReserveInverter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "MyReserveInverter";
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for the MyReserve powermeter.
*
* The MyReserve also contains {@link MyReserve} and {@link MyReserveInverter}.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.solarwatt.MyReservePowermeter=[
* PpmScaleFactor,
* IdAcsVersionNumber
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class MyReservePowerMeter extends PowerMeter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.powermeter.PowerMeter";
public MyReservePowerMeter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "MyReservePowerMeter";
}
}

View File

@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.CHANNEL_POWER_AC_OUT;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.CHANNEL_WORK_AC_OUT;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Class to represent the producing parts of the photovoltaic installation.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.pvplant.PVPlant=[
* PowerACOutMax,
* IdInverterList,
* TimePowerOutForecastGranularity,
* ForecastDateUpdate,
* WorkACOut,
* DegreeDirection,
* ForecastPowerOut,
* WorkAnnualYield,
* PowerACOut,
* DatePowerOutForecastStart,
* PowerLimit,
* IdMountingType,
* FractionDeratingLimit,
* DateInstallation,
* ForecastWorkOut,
* PowerOutForecastNow,
* PriceProfitFeedin,
* AddressLocation,
* PowerOutForecastValues,
* FractionConfigDeratingLimit,
* PowerInstalledPeak,
* LocationGeographical,
* DegreeInclination
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class PVPlant extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.pvplant.PVPlant";
public PVPlant(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
public void update(DeviceDTO deviceDTO) {
super.update(deviceDTO);
this.addWattQuantity(CHANNEL_POWER_AC_OUT, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_AC_OUT, deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "PVPlant";
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Base class for all powermeters.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.powermeter.PowerMeter=[
* InductiveEnergySum,
* ACCurrentConsumptionL1,
* ACCurrentConsumptionL2,
* ACCurrentN,
* ACCurrentConsumptionL3,
* ApparentPowerL2,
* ConsumptionPowerL3,
* ConsumptionPowerL2,
* ApparentPowerL3,
* ApparentPowerL1,
* ConsumptionPowerNet,
* ReactivePowerL1,
* ReactivePowerL2,
* InductiveEnergyL2,
* InductiveEnergyL1,
* ReactivePowerL3,
* CapacitiveEnergyL2,
* CapacitiveEnergyL1,
* PhaseOfMaximumImbalance,
* InjectionPowerL1,
* ReactivePowerSum,
* ActivePowerL3,
* InjectionPowerL2,
* InjectionPowerL3,
* ActivePowerL1,
* ConsumptionPowerL1,
* InjectionEnergySum,
* ActivePowerL2,
* ImbalanceL1,
* InjectionPowerNet,
* ImbalanceL2,
* ImbalanceL3,
* CapacitiveEnergyL3,
* DirectionMetering,
* ConsumptionEnergySum,
* PowerOut,
* InjectionEnergyNet,
* ACVoltageL1,
* ACVoltageL2,
* ACVoltageL3,
* MaximumImbalance,
* CosinusPhiL3,
* CosinusPhiL2,
* CosinusPhiL1,
* FrequencyL1,
* FrequencyL3,
* FrequencyL2,
* ACCurrentInjectionL3,
* InjectionEnergyL3,
* ACCurrentInjectionL2,
* ConsumptionPowerSum,
* ACCurrentInjectionL1,
* InjectionEnergyL1,
* InjectionEnergyL2,
* ActivePowerSum,
* ConsumptionEnergyL2,
* ConsumptionEnergyL1,
* PowerFactorL1,
* PowerFactorL2,
* InjectionPowerSum,
* ConsumptionEnergyL3,
* PowerFactorL3,
* ACCurrentL3,
* CapacitiveEnergySum,
* WorkIn,
* InductiveEnergyL3,
* PowerIn,
* ACCurrentL1,
* ACCurrentL2,
* FrequencyGrid,
* ConsumptionEnergyNet,
* ApparentPowerSum,
* WorkOut
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class PowerMeter extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.powermeter.PowerMeter";
public PowerMeter(DeviceDTO deviceDTO) {
super(deviceDTO);
this.addStringState(CHANNEL_DIRECTION_METERING, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_IN, deviceDTO);
this.addWattQuantity(CHANNEL_POWER_OUT, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_IN, deviceDTO);
this.addWattHourQuantity(CHANNEL_WORK_OUT, deviceDTO);
this.addWattHourQuantity(CHANNEL_CONSUMPTION_ENERGY_SUM, deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "PowerMeter";
}
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Class for things I haven't got a clue off.
*
* This fields have been identified to exist:
* com.kiwigrid.application.schedule.ProfileApp=[
* IdPriorizedDeviceList,
* ProfilesList,
* IdProfileActive
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class ProfileApp extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.application.schedule.ProfileApp";
public ProfileApp(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "ProfileApp";
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for the powermeter connected via S0 bus.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.s0counter.S0Counter=[
* SettingRateImpulses,
* CountPulses
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class S0Counter extends PowerMeter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.s0counter.S0Counter";
public S0Counter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "S0Counter";
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Class representing the scheduler of the energy manager.
*
* This fields have been identified to exist:
* com.kiwigrid.application.schedule.ScheduleApp=[
* ScheduleEvents,
* StateSchedule,
* ExcessMinSleepTime,
* ScheduleOverride,
* ExcessMinRuntime,
* ExcessMinRuntimePerDay,
* PowerInAverage,
* ExcessMinDuration,
* PowerLimitDefaults,
* SchedulesList,
* Schedule
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class ScheduleApp extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.application.schedule.ScheduleApp";
public ScheduleApp(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "ScheduleApp";
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Deprecated class.
*
* Was superseded by {@link ScheduleApp}.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.simpleswitcher.SimpleSwitcher=[
* MigratedToScheduleApp,
* ScheduleSwitchStates
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SimpleSwitcher extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.simpleswitcher.SimpleSwitcher";
public SimpleSwitcher(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "SimpleSwitcher";
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Deprecated class.
*
* Was superseded by {@link ScheduleApp}.
*
* This fields have been identified to exist:
* com.solarwatt.devices.sem.SmartEnergyManagement=[
* FractionFeedInLimit,
* FractionFeedInTestLimit,
* ModeManagement,
* ModeActive,
* IdConsumerSettingsMap,
* IdConsumerSelectionList,
* ModeTestActive,
* PowerInSwitchedOnDevices,
* MigratedToScheduleApp,
* IdDevicesMap,
* IdConsumerManagementIntervalsMap,
* IdManageableDeviceInfo
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SmartEnergyManagement extends Device {
public static final String SOLAR_WATT_CLASSNAME = "com.solarwatt.devices.sem.SmartEnergyManagement";
public SmartEnergyManagement(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "SmartEnergyManagement";
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.domain.model;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
/**
* Specialised class for inverters adhering to the sunspec modbus protocol.
*
* This fields have been identified to exist:
* com.kiwigrid.devices.sunspec.SunSpecInverter=[
* IdSunSpecBlocks
* ]
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SunSpecInverter extends Inverter {
public static final String SOLAR_WATT_CLASSNAME = "com.kiwigrid.devices.sunspec.SunSpecInverter";
public SunSpecInverter(DeviceDTO deviceDTO) {
super(deviceDTO);
}
@Override
protected String getSolarWattLabel() {
return "SunSpecInverter";
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.exception;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Exception to be used whenever anything goes wrong talking to the energy manager.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SolarwattConnectionException extends Exception {
static final long serialVersionUID = 3387516924229948L;
public SolarwattConnectionException(final @Nullable String message) {
super(message);
}
public SolarwattConnectionException(final @Nullable String message, final Exception e) {
super(message, e);
}
}

View File

@ -0,0 +1,136 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.factory;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.domain.EnergyManagerCollection;
import org.openhab.binding.solarwatt.internal.domain.dto.DeviceDTO;
import org.openhab.binding.solarwatt.internal.domain.dto.EnergyManagerDTO;
import org.openhab.binding.solarwatt.internal.domain.model.BatteryConverter;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.binding.solarwatt.internal.domain.model.EVStation;
import org.openhab.binding.solarwatt.internal.domain.model.EnergyManager;
import org.openhab.binding.solarwatt.internal.domain.model.Forecast;
import org.openhab.binding.solarwatt.internal.domain.model.GridFlow;
import org.openhab.binding.solarwatt.internal.domain.model.Inverter;
import org.openhab.binding.solarwatt.internal.domain.model.KebaEv;
import org.openhab.binding.solarwatt.internal.domain.model.Location;
import org.openhab.binding.solarwatt.internal.domain.model.MyReserve;
import org.openhab.binding.solarwatt.internal.domain.model.MyReserveInverter;
import org.openhab.binding.solarwatt.internal.domain.model.MyReservePowerMeter;
import org.openhab.binding.solarwatt.internal.domain.model.PVPlant;
import org.openhab.binding.solarwatt.internal.domain.model.PowerMeter;
import org.openhab.binding.solarwatt.internal.domain.model.ProfileApp;
import org.openhab.binding.solarwatt.internal.domain.model.S0Counter;
import org.openhab.binding.solarwatt.internal.domain.model.ScheduleApp;
import org.openhab.binding.solarwatt.internal.domain.model.SimpleSwitcher;
import org.openhab.binding.solarwatt.internal.domain.model.SmartEnergyManagement;
import org.openhab.binding.solarwatt.internal.domain.model.SunSpecInverter;
/**
* Factory to produce concrete instances which match the device structure returned by the energy manager.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EnergyManagerDevicesFactory {
/**
* Generate a concrete collection of devices which where discovered by the energy manager.
*
* @param energyManagerDTO raw transfer object generated from json
* @return collection of all devices
*/
public static EnergyManagerCollection getEnergyManagerCollection(EnergyManagerDTO energyManagerDTO) {
return new EnergyManagerCollection(energyManagerDTO);
}
/**
* Generate one concrete device instance from one raw device.
*
* Specific implementation to use is determined by looking at the
* deviceModels supported by the device and prioritising them according from the bottom up.
*
* @param deviceDTO raw transfer object for one device
* @return device fille from the {@link DeviceDTO}
*/
public static @Nullable Device getEnergyManagerDevice(DeviceDTO deviceDTO) {
// objects on level 3
if (deviceDTO.getDeviceModelStrings().contains(MyReserve.SOLAR_WATT_CLASSNAME)) {
return new MyReserve(deviceDTO);
}
// objects on level 2
if (deviceDTO.getDeviceModelStrings().contains(S0Counter.SOLAR_WATT_CLASSNAME)) {
return new S0Counter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(KebaEv.SOLAR_WATT_CLASSNAME)) {
return new KebaEv(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(MyReservePowerMeter.SOLAR_WATT_CLASSNAME)) {
return new MyReservePowerMeter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(SunSpecInverter.SOLAR_WATT_CLASSNAME)) {
return new SunSpecInverter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(MyReserveInverter.SOLAR_WATT_CLASSNAME)) {
return new MyReserveInverter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(BatteryConverter.SOLAR_WATT_CLASSNAME)) {
return new BatteryConverter(deviceDTO);
}
// objects on level 1
if (deviceDTO.getDeviceModelStrings().contains(ScheduleApp.SOLAR_WATT_CLASSNAME)) {
return new ScheduleApp(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(SmartEnergyManagement.SOLAR_WATT_CLASSNAME)) {
return new SmartEnergyManagement(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(EnergyManager.SOLAR_WATT_CLASSNAME)) {
return new EnergyManager(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(Forecast.SOLAR_WATT_CLASSNAME)) {
return new Forecast(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(Location.SOLAR_WATT_CLASSNAME)) {
return new Location(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(EVStation.SOLAR_WATT_CLASSNAME)) {
return new EVStation(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(PowerMeter.SOLAR_WATT_CLASSNAME)) {
return new PowerMeter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(SimpleSwitcher.SOLAR_WATT_CLASSNAME)) {
return new SimpleSwitcher(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(GridFlow.SOLAR_WATT_CLASSNAME)) {
return new GridFlow(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(PVPlant.SOLAR_WATT_CLASSNAME)) {
return new PVPlant(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(Inverter.SOLAR_WATT_CLASSNAME)) {
return new Inverter(deviceDTO);
}
if (deviceDTO.getDeviceModelStrings().contains(ProfileApp.SOLAR_WATT_CLASSNAME)) {
return new ProfileApp(deviceDTO);
}
// Objects on level 0
if (deviceDTO.getDeviceModelStrings().contains(Device.SOLAR_WATT_CLASSNAME)) {
return new Device(deviceDTO);
}
return null;
}
}

View File

@ -0,0 +1,122 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.handler;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.solarwatt.internal.configuration.SolarwattBridgeConfiguration;
import org.openhab.binding.solarwatt.internal.domain.EnergyManagerCollection;
import org.openhab.binding.solarwatt.internal.domain.dto.EnergyManagerDTO;
import org.openhab.binding.solarwatt.internal.exception.SolarwattConnectionException;
import org.openhab.binding.solarwatt.internal.factory.EnergyManagerDevicesFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
/**
* Class to talk to the energy anager via HTTP and return the concrete device instances.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EnergyManagerConnector {
private static final String PROTOCOL = "http://";
private static final String WIZARD_DEVICES_URL = "/rest/kiwigrid/wizard/devices";
private static final long CONNECT_TIMEOUT_SECONDS = 30;
private final Logger logger = LoggerFactory.getLogger(EnergyManagerConnector.class);
private final Gson gson = new GsonBuilder().create();
private final HttpClient httpClient;
private @Nullable URI energyManagerURI;
public EnergyManagerConnector(final HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Pass in the configuration to know which host to talk to.
*
* @param configuration containing the hostname.
*/
public void setConfiguration(final @Nullable SolarwattBridgeConfiguration configuration) {
if (configuration != null) {
String hostname = configuration.hostname;
if (!hostname.isEmpty()) {
this.energyManagerURI = URI.create(PROTOCOL + hostname + WIZARD_DEVICES_URL);
}
}
}
/**
* Get the collection of devices represented by the energy manager.
*
* Read the JSON and transform everything into concrete instances.
*
* @return wrapping the devices
* @throws SolarwattConnectionException on any communication error
*/
public EnergyManagerCollection retrieveDevices() throws SolarwattConnectionException {
try {
final Request request = this.httpClient.newRequest(this.energyManagerURI).timeout(CONNECT_TIMEOUT_SECONDS,
TimeUnit.SECONDS);
final ContentResponse response = request.send();
return this.getEnergyManagerCollectionFromJson(response);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new SolarwattConnectionException("Interrupted");
} catch (TimeoutException | ExecutionException e) {
throw new SolarwattConnectionException("Connection problem", e);
}
}
/**
* Parse body content from energy manager from json into our DTO.
*
* @param response
* @return collection containing all {@link DeviceDTO}s
* @throws SolarwattConnectionException on communication errors
*/
private EnergyManagerCollection getEnergyManagerCollectionFromJson(ContentResponse response)
throws SolarwattConnectionException {
final String content = response.getContentAsString();
try {
if (response.getStatus() == HttpStatus.OK_200) {
EnergyManagerDTO energyManagerDTO = this.gson.fromJson(content, EnergyManagerDTO.class);
if (energyManagerDTO == null) {
throw new SolarwattConnectionException("No data received");
}
return EnergyManagerDevicesFactory.getEnergyManagerCollection(energyManagerDTO);
} else {
throw new SolarwattConnectionException(response.getReason());
}
} catch (final JsonSyntaxException e) {
this.logger.warn("Error parsing json: {}", content, e);
throw new SolarwattConnectionException(e.getMessage());
}
}
}

View File

@ -0,0 +1,394 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.handler;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.math.BigDecimal;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
import org.openhab.binding.solarwatt.internal.configuration.SolarwattBridgeConfiguration;
import org.openhab.binding.solarwatt.internal.configuration.SolarwattThingConfiguration;
import org.openhab.binding.solarwatt.internal.discovery.SolarwattDevicesDiscoveryService;
import org.openhab.binding.solarwatt.internal.domain.SolarwattChannel;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.binding.solarwatt.internal.domain.model.EnergyManager;
import org.openhab.binding.solarwatt.internal.exception.SolarwattConnectionException;
import org.openhab.core.cache.ExpiringCache;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EnergyManagerHandler} is responsible for handling energy manager thing itself
* and handle data retrieval for the child things.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class EnergyManagerHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(EnergyManagerHandler.class);
private final EnergyManagerConnector connector;
private final SolarwattChannelTypeProvider channelTypeProvider;
private @Nullable ExpiringCache<Map<String, Device>> devicesCache;
private @Nullable ScheduledFuture<?> refreshJob;
private @Nullable ZoneId zoneId;
/**
* Guid of this energy manager itself.
*/
private @Nullable String energyManagerGuid;
/**
* Runner for the {@link ExpiringCache} refresh.
*
* Triggers update of all child things.
*/
private final Runnable refreshRunnable = () -> {
EnergyManagerHandler.this.updateChannels();
EnergyManagerHandler.this.updateAllChildThings();
};
/**
* Create the handler.
*
* @param thing for which the handler is responsible
* @param channelTypeProvider provider for the channels
* @param httpClient connect to energy manager via this client
*/
public EnergyManagerHandler(final Bridge thing, final SolarwattChannelTypeProvider channelTypeProvider,
final HttpClient httpClient) {
super(thing);
this.connector = new EnergyManagerConnector(httpClient);
this.channelTypeProvider = channelTypeProvider;
}
/**
* Get services which are provided by this handler.
*
* Only service discovery is provided by
*
* @return collection containing our discovery service
*/
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(SolarwattDevicesDiscoveryService.class);
}
/**
* Execute the desired commands.
*
* Only refresh is supported and relayed to all childs of this thing.
*
* @param channelUID for which the command is issued
* @param command command issued
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
this.updateChannels();
}
}
/**
* Dynnamically updates all known channel states of the energy manager.
*/
public void updateChannels() {
Map<String, Device> devices = this.getDevices();
if (devices != null) {
if (this.energyManagerGuid == null) {
try {
this.findEnergyManagerGuid(devices);
} catch (SolarwattConnectionException ex) {
this.logger.warn("Failed updating EnergyManager channels: {}", ex.getMessage());
}
}
EnergyManager energyManager = (EnergyManager) devices.get(this.energyManagerGuid);
if (energyManager != null) {
this.calculateUpdates(energyManager);
energyManager.getStateValues().forEach((stateName, stateValue) -> {
this.updateState(stateName, stateValue);
});
} else {
this.logger.warn("updateChannels failed, missing device EnergyManager {}", this.energyManagerGuid);
}
}
}
private void calculateUpdates(EnergyManager energyManager) {
State timezoneState = energyManager.getState(CHANNEL_IDTIMEZONE.getChannelName());
if (timezoneState != null) {
this.zoneId = ZoneId.of(timezoneState.toFullString());
}
BigDecimal timestamp = energyManager.getBigDecimalFromChannel(CHANNEL_TIMESTAMP.getChannelName());
if (timestamp.compareTo(BigDecimal.ONE) > 0) {
energyManager.addState(CHANNEL_DATETIME.getChannelName(),
new DateTimeType(this.getFromMilliTimestamp(timestamp)));
}
}
/**
* Initial setup of the channels available for this thing.
*
* @param device which provides the channels
*/
protected void initDeviceChannels(Device device) {
this.assertChannel(new SolarwattChannel(CHANNEL_DATETIME.getChannelName(), "time"));
device.getSolarwattChannelSet().forEach((channelTag, solarwattChannel) -> {
this.assertChannel(solarwattChannel);
});
}
/**
* Assert that all channels inside of our thing are well defined.
*
* Only channel which can not be found are created.
*
* @param solarwattChannel channel description with name and unit
*/
protected void assertChannel(SolarwattChannel solarwattChannel) {
ChannelUID channelUID = new ChannelUID(this.getThing().getUID(), solarwattChannel.getChannelName());
ChannelTypeUID channelType = this.channelTypeProvider.assertChannelType(solarwattChannel);
if (this.getThing().getChannel(channelUID) == null) {
ThingBuilder thingBuilder = this.editThing();
thingBuilder.withChannel(
SimpleDeviceHandler.getChannelBuilder(solarwattChannel, channelUID, channelType).build());
this.updateThing(thingBuilder.build());
}
}
/**
* Finds the guid of the energy manager inside of the known devices.
*
* @param devices list with known devices
* @throws SolarwattConnectionException if there is no energy manager available
*/
private void findEnergyManagerGuid(Map<String, Device> devices) throws SolarwattConnectionException {
devices.forEach((guid, device) -> {
if (device instanceof EnergyManager) {
this.energyManagerGuid = guid;
}
});
if (this.energyManagerGuid == null) {
throw new SolarwattConnectionException("unable to find energy manager");
}
}
/**
* Setup the handler and trigger initial load via {@link EnergyManagerHandler::refreshDevices}.
*
* Web request against energy manager and loading of devices is deferred and will send the ONLINE
* event after loading all devices.
*/
@Override
public void initialize() {
SolarwattBridgeConfiguration localConfig = this.getConfigAs(SolarwattBridgeConfiguration.class);
this.initRefresh(localConfig);
this.initDeviceCache(localConfig);
}
private void initDeviceCache(SolarwattBridgeConfiguration localConfig) {
if (localConfig.hostname.isEmpty()) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Hostname is not set");
} else {
this.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"Waiting to retrieve devices.");
this.connector.setConfiguration(localConfig);
this.devicesCache = new ExpiringCache<>(Duration.of(localConfig.refresh, ChronoUnit.SECONDS),
this::refreshDevices);
ExpiringCache<Map<String, Device>> localDevicesCache = this.devicesCache;
if (localDevicesCache != null) {
// trigger initial load
this.scheduler.execute(localDevicesCache::getValue);
}
}
}
/**
* Stop the refresh job and remove devices.
*/
@Override
public void dispose() {
ScheduledFuture<?> localRefreshJob = this.refreshJob;
if (localRefreshJob != null && !localRefreshJob.isCancelled()) {
localRefreshJob.cancel(true);
this.refreshJob = null;
}
this.devicesCache = null;
}
private synchronized void initRefresh(SolarwattBridgeConfiguration localConfig) {
ScheduledFuture<?> localRefreshJob = this.refreshJob;
if (localRefreshJob == null || localRefreshJob.isCancelled()) {
this.refreshJob = this.scheduler.scheduleWithFixedDelay(this.refreshRunnable, 0, localConfig.refresh,
TimeUnit.SECONDS);
}
}
/**
* Fetch the map of devices from the cache.
*
* Used by all childs to get their values.
*
* @return map with all {@link Device}s
*/
public @Nullable Map<String, Device> getDevices() {
ExpiringCache<Map<String, Device>> localDevicesCache = this.devicesCache;
if (localDevicesCache != null) {
Map<String, Device> cache = localDevicesCache.getValue();
if (cache != null) {
this.updateStatus(ThingStatus.ONLINE);
} else {
this.updateStatus(ThingStatus.OFFLINE);
}
return cache;
} else {
return new HashMap<>();
}
}
/**
* Get a device for a specific guid.
*
* @param guid to search for
* @return device belonging to guid or null if not found.
*/
public @Nullable Device getDeviceFromGuid(String guid) {
Map<String, Device> localDevices = this.getDevices();
if (localDevices != null && localDevices.containsKey(guid)) {
return localDevices.get(guid);
}
return null;
}
/**
* Convert the energy manager millisecond timestamps to {@link ZonedDateTime}
*
* The energy manager is the only point that knows about the timezone and
* it is available to all other devices. All timestamps used by all devices
* are in milliseconds since the epoch.
*
* @param timestamp milliseconds since the epoch
* @return date time in timezone
*/
public ZonedDateTime getFromMilliTimestamp(BigDecimal timestamp) {
Map<String, Device> devices = this.getDevices();
if (devices != null) {
EnergyManager energyManager = (EnergyManager) devices.get(this.energyManagerGuid);
if (energyManager != null) {
BigDecimal[] bigDecimals = timestamp.divideAndRemainder(BigDecimal.valueOf(1_000));
Instant instant = Instant.ofEpochSecond(bigDecimals[0].longValue(),
bigDecimals[1].multiply(BigDecimal.valueOf(1_000_000)).longValue());
ZoneId localZoneID = this.zoneId;
if (localZoneID != null) {
return ZonedDateTime.ofInstant(instant, localZoneID);
}
}
}
throw new DateTimeException("Timezone from energy manager missing.");
}
/**
* Reload all devices from the energy manager.
*
* This method is called via the {@link ExpiringCache}.
*
* @return map from guid to {@link Device}}
*/
private @Nullable Map<String, Device> refreshDevices() {
try {
final Map<String, Device> devicesData = this.connector.retrieveDevices().getDevices();
this.updateStatus(ThingStatus.ONLINE);
// trigger refresh of the available channels
if (devicesData.containsKey(this.energyManagerGuid)) {
Device device = devicesData.get(this.energyManagerGuid);
if (device != null) {
this.initDeviceChannels(device);
}
} else {
this.logger.warn("{}: initDeviceChannels missing energy manager {}", this, this.getThing().getUID());
}
return devicesData;
} catch (final SolarwattConnectionException e) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
return null;
}
/**
* Trigger an update on all child things of this bridge.
*/
private void updateAllChildThings() {
this.getThing().getThings().forEach(childThing -> {
try {
ThingHandler childHandler = childThing.getHandler();
if (childHandler != null) {
childHandler.handleCommand(new ChannelUID(childThing.getUID(), CHANNEL_TIMESTAMP.getChannelName()),
RefreshType.REFRESH);
} else {
this.logger.warn("no handler found for thing/device {}",
childThing.getConfiguration().as(SolarwattThingConfiguration.class).guid);
}
} catch (Exception ex) {
this.logger.warn("Error processing child with uid {}", childThing.getUID(), ex);
}
});
}
}

View File

@ -0,0 +1,164 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.handler;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.binding.solarwatt.internal.domain.model.EVStation;
import org.openhab.binding.solarwatt.internal.domain.model.Location;
import org.openhab.binding.solarwatt.internal.domain.model.PowerMeter;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.State;
/**
* The concrete device handlers process the location specific commands.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class LocationHandler extends SimpleDeviceHandler {
public LocationHandler(Thing thing, SolarwattChannelTypeProvider channelTypeProvider) {
super(thing, channelTypeProvider);
}
/**
* Add calculated states for unmetered consum.
*
* First calculate and then call super to submit the state
*/
@Override
protected void updateDeviceChannels() {
// add calculated states
this.updateCalculated();
// submits all states
super.updateDeviceChannels();
}
/**
* Add calculated channels for unmetered consum.
*
* First calculate and then call super to submit the channels
*/
@Override
protected void initDeviceChannels() {
// add calculated channels
final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
if (bridgeHandler != null) {
// update the unmetered channel via subtracting the outerconsumers
// from the powerConsumed
Location locationDevice = (Location) this.getDevice();
if (locationDevice != null) {
locationDevice.addChannel(CHANNEL_POWER_DIRECT_CONSUMED.getChannelName(), Units.WATT,
Device.WATT_CATEGORY, false);
locationDevice.addChannel(CHANNEL_WORK_DIRECT_CONSUMED.getChannelName(), Units.WATT_HOUR,
Device.WATT_HOUR_CATEGORY, false);
locationDevice.addChannel(CHANNEL_POWER_CONSUMED_UNMETERED.getChannelName(), Units.WATT,
Device.WATT_CATEGORY, false);
locationDevice.addChannel(CHANNEL_WORK_CONSUMED_UNMETERED.getChannelName(), Units.WATT_HOUR,
Device.WATT_HOUR_CATEGORY, false);
}
}
// submit all channels
super.initDeviceChannels();
}
private void updateCalculated() {
final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
final Location locationDevice = (Location) this.getDevice();
if (bridgeHandler != null && locationDevice != null) {
this.calculateDirectConsumption(locationDevice);
this.calculateUnmeteredConsumption(bridgeHandler, locationDevice);
}
}
private void calculateUnmeteredConsumption(EnergyManagerHandler bridgeHandler, Location locationDevice) {
// update the unmetered channels via subtracting
// the outerconsumers from the powerConsumed
BigDecimal powerConsumed = locationDevice.getBigDecimalFromChannel(CHANNEL_POWER_CONSUMED.getChannelName());
BigDecimal workConsumed = locationDevice.getBigDecimalFromChannel(CHANNEL_WORK_CONSUMED.getChannelName());
final List<BigDecimal> powerOuter = new ArrayList<>();
final List<BigDecimal> workOuter = new ArrayList<>();
Set<String> outerConsumers = locationDevice.getDevicesMap().getOuterConsumer();
if (outerConsumers != null) {
outerConsumers.stream().map(bridgeHandler::getDeviceFromGuid).forEach(outerDevice -> {
if (outerDevice instanceof PowerMeter) {
powerOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_POWER_IN.getChannelName()));
workOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_WORK_IN.getChannelName()));
} else if (outerDevice instanceof EVStation) {
powerOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_POWER_AC_IN.getChannelName()));
workOuter.add(outerDevice.getBigDecimalFromChannel(CHANNEL_WORK_AC_IN.getChannelName()));
}
});
BigDecimal powerConsumedUnmetered = powerOuter.stream().reduce(powerConsumed, BigDecimal::subtract);
if (powerConsumedUnmetered.compareTo(BigDecimal.ONE) > 0) {
// sometimes the powerConsumed is exactly the power of the unmetered devices
// the resulting zero for unmetered consumption is not correct
locationDevice.addStateBigDecimal(CHANNEL_POWER_CONSUMED_UNMETERED, powerConsumedUnmetered, Units.WATT);
}
locationDevice.addStateBigDecimal(CHANNEL_WORK_CONSUMED_UNMETERED,
workOuter.stream().reduce(workConsumed, BigDecimal::subtract), Units.WATT_HOUR);
}
}
private void calculateDirectConsumption(Location locationDevice) {
// calculate direct consumption for display purposes
locationDevice.addState(CHANNEL_POWER_DIRECT_CONSUMED.getChannelName(),
this.calculateQuantityDifference(locationDevice.getState(CHANNEL_POWER_SELF_CONSUMED.getChannelName()),
locationDevice.getState(CHANNEL_POWER_BUFFERED_FROM_PRODUCERS.getChannelName())));
locationDevice.addState(CHANNEL_WORK_DIRECT_CONSUMED.getChannelName(),
this.calculateQuantityDifference(locationDevice.getState(CHANNEL_WORK_SELF_CONSUMED.getChannelName()),
locationDevice.getState(CHANNEL_WORK_BUFFERED_FROM_PRODUCERS.getChannelName())));
}
/**
* Helper to generate a new state calculated from the difference between two states.
*
* channelTarget = channelValue - channelSubtract
*
* @param stateValue value from which we subtract
* @param stateSubtract value to substrct
* @return {@link State} of calculated value
*/
private @Nullable State calculateQuantityDifference(@Nullable State stateValue, @Nullable State stateSubtract) {
if (stateValue != null && stateSubtract != null) {
@SuppressWarnings("rawtypes")
QuantityType quantityValue = stateValue.as(QuantityType.class);
@SuppressWarnings("rawtypes")
QuantityType quantitySubtract = stateSubtract.as(QuantityType.class);
if (quantityValue != null && quantitySubtract != null) {
return quantityValue.subtract(quantitySubtract);
}
}
return null;
}
}

View File

@ -0,0 +1,250 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.solarwatt.internal.handler;
import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
import java.text.MessageFormat;
import java.util.Map;
import javax.measure.Unit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
import org.openhab.binding.solarwatt.internal.configuration.SolarwattThingConfiguration;
import org.openhab.binding.solarwatt.internal.domain.SolarwattChannel;
import org.openhab.binding.solarwatt.internal.domain.model.Device;
import org.openhab.core.library.CoreItemFactory;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.Bridge;
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.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.util.UnitUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SimpleDeviceHandler} bundles everything related to generic talking to devices.
*
* @author Sven Carstens - Initial contribution
*/
@NonNullByDefault
public class SimpleDeviceHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SimpleDeviceHandler.class);
private final SolarwattChannelTypeProvider channelTypeProvider;
public SimpleDeviceHandler(Thing thing, SolarwattChannelTypeProvider channelTypeProvider) {
super(thing);
this.channelTypeProvider = channelTypeProvider;
}
/**
* Bring the thing online and update state from the bridge.
*/
@Override
public void initialize() {
final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
if (bridgeHandler != null) {
this.initDeviceChannels();
this.updateDeviceProperties();
this.updateDeviceChannels();
} else {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing!");
}
}
/**
* Process the command for this thing.
*
* Only refresh is supported in this case.
*
* @param channelUID channel for which the command was issued
* @param command to execute
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
if (bridgeHandler != null) {
if (command instanceof RefreshType) {
this.updateDeviceProperties();
this.updateDeviceChannels();
}
} else {
this.logger.warn("Thing {} has no bridgeHandler for Bridge {}", this.getThing().getUID(),
this.getThing().getBridgeUID());
}
}
/**
* Update the state of all channels.
*/
protected void updateDeviceChannels() {
// find device for the thing
Device device = this.getDevice();
if (device != null) {
device.getStateValues().forEach(this::updateState);
}
}
/**
* Assert that all {@link org.openhab.core.thing.type.ChannelType}s are registered for this thing.
*/
protected void initDeviceChannels() {
// find device for the thing
Device device = this.getDevice();
if (device != null) {
device.getSolarwattChannelSet().forEach((channelTag, solarwattChannel) -> {
this.assertChannel(solarwattChannel);
});
}
}
/**
* Update the properties for this device.
*/
protected void updateDeviceProperties() {
// find device for the thing
Device device = this.getDevice();
if (device != null) {
// update properties
Map<String, String> properties = this.editProperties();
this.putProperty(properties, PROPERTY_ID_NAME, device.getIdName());
this.putProperty(properties, PROPERTY_ID_FIRMWARE, device.getIdFirmware());
this.putProperty(properties, PROPERTY_ID_MANUFACTURER, device.getIdManufacturer());
this.updateProperties(properties);
// relay state of device to status
this.updateStatus(device.getStateDevice());
}
}
private void putProperty(Map<String, String> properties, String name, @Nullable String value) {
if (value != null) {
properties.put(name, value);
}
}
/**
* Assert that all channels inside of our thing are well defined.
*
* Only channels which can not be found are created.
*
* @param solarwattChannel channel description with name and unit
*/
protected void assertChannel(SolarwattChannel solarwattChannel) {
ChannelUID channelUID = new ChannelUID(this.getThing().getUID(), solarwattChannel.getChannelName());
ChannelTypeUID channelType = this.channelTypeProvider.assertChannelType(solarwattChannel);
if (this.getThing().getChannel(channelUID) == null) {
ThingBuilder thingBuilder = this.editThing();
thingBuilder.withChannel(getChannelBuilder(solarwattChannel, channelUID, channelType).build());
this.updateThing(thingBuilder.build());
}
}
/**
* Get a builder for a channel type according to the {@link SolarwattChannel}
*
* @param solarwattChannel channel type definition
* @param channelUID uid of the channel
* @param channelType uid of the channel type
* @return builder for that channel type
*/
public static ChannelBuilder getChannelBuilder(SolarwattChannel solarwattChannel, ChannelUID channelUID,
ChannelTypeUID channelType) {
String itemType = CoreItemFactory.STRING;
Unit<?> unit = solarwattChannel.getUnit();
if (unit != null) {
String dimension = UnitUtils.getDimensionName(unit);
if (Units.PERCENT.equals(unit)) {
// strangely it is Angle
dimension = ":Dimensionless";
}
itemType = CoreItemFactory.NUMBER;
if (dimension != null && !dimension.isEmpty()) {
itemType = CoreItemFactory.NUMBER + ":" + dimension;
}
}
ChannelBuilder channelBuilder = ChannelBuilder.create(channelUID, itemType);
channelBuilder.withLabel(solarwattChannel.getChannelName()).withType(channelType).withDescription(MessageFormat
.format("Value for {0} with Unit: {1}", solarwattChannel.getChannelName(), solarwattChannel.getUnit()))
.withKind(ChannelKind.STATE);
return channelBuilder;
}
/**
* Get the {@link EnergyManagerHandler}.
*
* Only the {@link EnergyManagerHandler} has knowledge about the devices itself.
*
* @return instance responsible for this handler
*/
protected @Nullable EnergyManagerHandler getEnergyManagerHandler() {
Bridge bridge = this.getBridge();
if (bridge != null) {
BridgeHandler bridgeHandler = bridge.getHandler();
if (bridgeHandler instanceof EnergyManagerHandler) {
return (EnergyManagerHandler) bridgeHandler;
} else {
// happens while dynamically reloading the binding
this.logger.warn("BridgeHandler is not implementing EnergyManagerHandler {}", bridgeHandler);
}
} else {
// this handler can't work without a bridge
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
"Received null bridge while initializing!");
}
return null;
}
/**
* Get the {@link Device} from the {@link EnergyManagerHandler}.
*
* @return model with values
*/
protected @Nullable Device getDevice() {
final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
if (bridgeHandler != null) {
Map<String, Device> bridgeDevices = bridgeHandler.getDevices();
if (bridgeDevices != null) {
return bridgeDevices.get(this.getConfigAs(SolarwattThingConfiguration.class).guid);
}
}
this.logger.warn("Device not found for thing with guid {}",
this.getConfigAs(SolarwattThingConfiguration.class).guid);
return null;
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="solarwatt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Solarwatt Binding</name>
<description>This is the binding for Solarwatt Energymanager.</description>
</binding:binding>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:solarwatt:energymanager">
<parameter name="hostname" type="text">
<label>Host Name</label>
<description>The host name/ip address of the solarwatt energymanager.</description>
<context>network-address</context>
</parameter>
<parameter name="refresh" type="integer" unit="s">
<label>Refresh Data Period</label>
<description>Period between updates to the devices data in seconds.
</description>
<default>30</default>
<unitLabel>s</unitLabel>
<advanced>true</advanced>
</parameter>
<parameter name="rescan" type="integer" unit="min">
<label>Redetect Devices Period</label>
<description>Period between updates to the detected devices in minutes.
</description>
<default>5</default>
<unitLabel>min</unitLabel>
<advanced>true</advanced>
</parameter>
</config-description>
<config-description uri="thing-type:solarwatt:device">
<parameter name="guid" type="text">
<label>Guid of Device</label>
<description>Guid of the device as used by the solarwatt energymanager.</description>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="solarwatt"
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">
<bridge-type id="energymanager">
<label>Solarwatt Energymanager</label>
<description>Solarwatt Energymanager is the bridge to all things attached to the PV production system.
</description>
<representation-property>hostname</representation-property>
<config-description-ref uri="thing-type:solarwatt:energymanager"/>
</bridge-type>
<thing-type id="evstation">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>EV Station</label>
<description>Electric vehicle charger station</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="batteryconverter">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>Battery Converter</label>
<description>Battery converter to supply AC from battery storage.</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="location">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>Location</label>
<description>Location aggregates all things taking part in the production process.</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="pvplant">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>PV Plant</label>
<description>Photovoltaic plant generating DC from solar energy.</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="gridflow">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>Gridflow</label>
<description>Gridflow regulates interaction with the external power grid.</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="powermeter">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>Power Meter</label>
<description>Power meter for produced or consumed energy</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
<thing-type id="inverter">
<supported-bridge-type-refs>
<bridge-type-ref id="energymanager"/>
</supported-bridge-type-refs>
<label>Inverter</label>
<description>Inverter supplying AC from DC.</description>
<representation-property>IdName</representation-property>
<config-description-ref uri="thing-type:solarwatt:device"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -303,6 +303,7 @@
<module>org.openhab.binding.snmp</module>
<module>org.openhab.binding.solaredge</module>
<module>org.openhab.binding.solarlog</module>
<module>org.openhab.binding.solarwatt</module>
<module>org.openhab.binding.somfymylink</module>
<module>org.openhab.binding.somfytahoma</module>
<module>org.openhab.binding.sonos</module>