added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.digitalstrom</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

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,558 @@
# digitalSTROM Binding
This binding integrates the [digitalSTROM-System](https://www.digitalstrom.de/).
The integration happens through the digitalSTROM-Server, which acts as a gateway to connect the digitalSTROM-Devices.
The digitalSTROM-Server communicates through the digitalSTROM-Meters with the digitalSTROM-Devices, which are directly connected to the power-line.
**Note:** All was tested with digitalSTROM-Server firmware version 1.9.3 to 1.10.3.
![various_digitalSTROM_clamps](doc/DS-Clamps.jpg)
## Supported Things
### digitalSTROM-Server
The digitalSTROM-Server is required for accessing any other digitalSTROM-Devices. It acts like a *"bridge"*.
### digitalSTROM-Devices
At this point almost all available **GE**, **SW**, **GR** and **BL** digitalSTROM-Devices with a set output-mode, unequal *disabled*, are supported by this binding.
Furthermore sensor devices like the **dS-iSens200** and **SW-devices** with binary-inputs are supported.
Last but not least the **circuit** (dS-Meter) is supported, too. They will provide the power consumption and electric meter as channels.
For that there are identically named thing types. Only the *GR* type has a channel (shade), which cannot be changed.
The other types add their channels dynamically affected by the set color-group and output-mode.
They also automatically change or add the channels, if the color-group or output-mode has changed through the dSS-web-configuration or the configured sensor priorities of the thing has changed.
- The following table shows all tested digitalSTROM-Devices with their output-modes.
| HW-Type | Output-Mode | Tested color group |
|-----------------|------------------------|--------------|
| GE-KL200 | switched | yellow |
| GE-KM200 | switched, dimmed | yellow |
| GE-TKM210 | switched, dimmed | yellow |
| GE-SDM200 | switched, dimmed | yellow |
| GE-UMV200 | 1-10V dimmed | yellow |
| GR-KL200 | standard output-mode | grey |
| GR-KL210 | standard output-mode| grey |
| GR-KL220 | standard output-mode | grey |
| SW-KL200 | switch, powersave, wipe | black, yellow |
| SW-UMR200 | single switched, combined switch, combined 2 stage switch, combined 3 stage switch | yellow , black |
| SW-ZWS200 | switch, powersave, wipe | black, yellow |
| BL-KM200 | switch, pwm | blue |
- Binary-inputs were tested with SW-UMR200.
- Sensor channels were tested with dS-iSens200 and power sensor with all other supported devices, which are listed in the table above.
### digitalSTROM-Scenes
Furthermore the digitalSTROM-Scene concept is part of the digitalSTROM-Binding.
These scenes are implemented as virtual things.
The different scene thing types are listed in the following table.
| Thing-Type-ID | Label | Description |
|-----------------|------------------------|--------------|
| appScene | Apartment-Scene | Represents a digitalSTROM Apartment-Scene. |
| zoneScene | Zone-Scene | Represents a digitalSTROM Zone-Scene. |
| groupScene | Group-Scene | Represents a digitalSTROM Group-Scene. |
| namedScene | Named-Scene | Represents a digitalSTROM Scene, which has a user-defined name. |
### digitalSTROM-Zone-Temperature-Control
Last but not least, the digitalSTROM-Zone-Temperature-Control is also supported, if a zone-temerature-control is configured, as thing-type **zone_temperature_control**.
The difference between the digitalSTROM-heating-control-app is, that there are no operation-modes, like *comfort* or *eco*.
You can directly set the target temperature, in case *pid-control* is configured, otherwise you can set the value in percent of heating valves at the zone.
The needed channels will be added automatically, as it is also the case for the devices.
## Discovery
The digitalSTROM-Server is discovered by mDNS or *dss.local.* at the local network.
Once the server is added as a thing, you have to set a user name and password or insert a valid application-token to authenticate with the server.
If the binding is authorized, it automatically reads all supported devices, the dS-Meters and temperature controlled configured zones, that are set up on the digitalSTROM-System, and puts them into the *inbox*.
digitalSTROM-Scenes can be discovered, too.
The background scene-discovery is deactivated by default to not flood the inbox.
Otherwise it will discover so many scenes, that it can be difficult to find the searched devices.
Discoverable scenes are all user named scenes, group scenes that are reachable by local push-buttons, zone scenes and apartment scenes.
The discovery also will discover all called scenes, if they aren't automatically discovered yet.
Temperature control scenes, like *eco* will be ignored, so they cannot be discovered.
If you only want to discover one of the thing types, you can start a discovery scan on the thing type you want to have discovered.
You can use the command line command, e.g.: ``smarthome:discovery start digitalstrom:namedScene`` to start the scan.
Which thing types this binding supports, please have a look at **Supported Things**.
## Thing Configuration and Properties
### digitalSTROM-Server
The digitalSTROM-Server thing has the following configuration parameter groups: *Connection configuration*, *Server information* and *General configurations*.
#### Connection configuration
If the digitalSTROM-Server isnt found automatically, e.g. because the server isnt placed at the local network or the mDNS-service is deactivated, you have to insert the network address or URL and the authentication data manually through the graphical user interface or type it into the \*.thing with textual configuration.
If you use your user name and password for authentication and there is already a token for this application, it will be automatically retrieved from the digitalSTROM-Server, otherwise a new application-token will be generated.
| Parameter Label | Parameter ID | Description | Required | Advanced
|--------------|------------|--------------------------------|----------------- |------------- |
| Network address | dSSAddress | Network address of the digitalSTROM-Server.| true | false |
| User name | userName | Name of a registered user to authenticate to the digitalSTROM-Server.| user name and password or Application-Token | false |
| Password | password | Password of a registered user to authenticate to the digitalSTROM-Server. | user name and password or Application-Token | false |
| Application-Token | applicationToken | The Application-Token to authenticate to the digitalSTROM-Server. | user name and password or Application-Token| false |
#### Server information
The parameter group *Server information* only includes informative parameters, which have no special functionality.
| Parameter Label | Parameter ID | Description | Required | Advanced
|-----------------|-------------|--------------------------|---------- |------------- |
| dSID | dSID | The unique identifier of a digitalSTROM-server. | false| false |
#### General configuration:
Here you can set general binding configuration parameters, which are shown in following table:
| Parameter Label | Parameter ID| Description | Required | Advanced | default
|-----------------|------------------|-----------------------------------------|---------------- |------------- | ----------------- |
| Sensor update interval | sensorDataUpdateInterval | Sets the seconds after the digitalSTROM-Device sensor data will be updated. If the priority is higher than 'never'. | false | false | 60 |
| Total power update interval | totalPowerUpdateInterval | Sets the interval in seconds, after the digitalSTROM total power consumption and total electric meter sensor data will be updated. | false | false | 30 |
| Days to be slaked trash bin devices | defaultTrashBinDeleateTime| Sets the days after the temporary saved digitalSTROM-Device configuration from not reachable digitalSTROM-Devices get permanently deleted. | false | false | 7 |
| Wait time sensor reading | sensorWaitTime| Waiting time between the evaluation of the sensor values and the reading of the scenes in seconds. **ATTENTION:** digitalSTROM rule 8 and 9 require a waiting period of 1 minute. Values less than 60 seconds could affect the digitalSTROM system. | false | true | 60 |
At the thing file, a manual configuration looks e.g. like
```
Bridge digitalstrom:dssBridge:dssBridge1 [ dSSAddress="dss.local.", userName="dssadmin", password="dssadmin", sensorDataUpdateInterval=180]
```
#### Properties
In addition to the configuration the digitalSTROM-Server has the following properties.
| Property-Name | Description |
| ------------- | ----------- |
| serverCert | The SSL-Certificate of the digitalSTROM-Server. |
| dS-Installation-Name | The digitalSTROM-System installation name. |
| version | The digitalSTROM-Server-Application version. |
| distroVersion | The digitalSTROM-Server firmware version. |
| Hardware | The digitalSTROM-Server hardware identifier. |
| Revision | The digitalSTROM-Server hardware revision number. |
| Serial | The digitalSTROM-Server hardware serial number. |
| Ethernet | The digitalSTROM-Server IEEE mac address. |
| MachineID | The digitalSTROM-Server unique id. |
| Kernel | The digitalSTROM-Server Linux kernel release string. |
### digitalSTROM-Devices
The digitalSTROM-Device things have the following configuration parameter groups *Device information* and *Sensor setup*.
#### Device information
Each digitalSTROM-Device needs the device ID named dSID as configuration parameter.
The device ID is printed as serial number at the digitalSTROM-Device and can also be found within the web-interface of the digitalSTROM-Server.
The following table shows the parameter:
| Parameter Label | Parameter ID| Description | Required | Advanced
|-----------------|------------------------|--------------|----------------- |------------- |
| ID | dSID| The unique identifier of a digitalSTORM-device. | true | false |
#### Sensor setup
The GE, BL and SW digitalSTROM-Devices usually have sensors to capture power consumption data.
So these devices have the following parameters to read them out.
| Parameter Label | Parameter ID| Description | Required | Advanced | Default |
|-----------------|--------------------|-----------------------------|----------------- |------------- | -----------|
| Active power refresh priority | activePowerRefreshPriority | Sets the refresh priority for the active power sensor value. Can be never, low priority, medium priority or high priority. | false | false | never |
| Electric meter refresh priority | electricMeterRefreshPriority | Sets the refresh priority for the electric meter sensor value. Can be never, low priority, medium priority or high priority. | false | false | never |
| Output current refresh priority | outputCurrentRefreshPriority | Sets the refresh priority for the output current sensor value. Can be never, low priority, medium priority or high priority. | false | false | never |
#### Properties
Furthermore a supported digitalSTROM-Device has some informative properties.
The following table shows all informative properties:
| Property-Name | Description |
| ------------- | ------------------------------------- |
|dSUID | The unique identifier of a digitalSTORM-device with virtual devices. |
| deviceName | he name of a digitalSTROM-Device. |
| meterDSID | Identifier of the meter to which the device is connected. |
| hwInfo | The hardware type from this digitalSTROM-Device. |
| zoneID |The digitalSTROM-Device is part of this zone. |
| groups | The digitalSTROM-Device is part of this user-defined or functional groups. |
| output mode | The current digitalSTROM-Device output mode e.g. 22 = dimmable. |
| funcColorGroup | The current digitalSTROM-Device functional color group e.g. yellow = light. |
The device scene configurations will also be persisted in the properties. There are in the format:
| Property-Name | Description |
| ------------- | ------------------------------------- |
| scene[sceneID] | {Scene: [sceneID], dontcare: [don't care flag], localPrio: [local prio flag], specialMode: [special mode flag]}(0..1), {sceneValue: [scene value], sceneAngle: [scene angle]}(0..1) |
### digitalSTROM-Meter
A digitalSTROM-Meter needs, like the digitalSTROM-Devices, only the unique digitalSTROM device ID named dSID as configuration parameter, which has the same parameters, so please have a look at the point *Device information*.
#### Properties
In contrast to the digitalSTROM-Device there are other informal properties. The following table shows the available properties:
| Property-Name | Description |
| ------------- | ------------------------------------- |
| hwName | The hardware name of the digitalSTROM-Meter |
| swVersion | The software version of the digitalSTROM-Meter |
| apiVersion | The api version of the digitalSTROM-Meter |
| dspSwVersion | The dsp software version of the digitalSTROM-Meter |
| dSUID | The dSUID of the digitalSTROM-Meter |
| deviceName | The user defined name of the digitalSTROM-Meter |
| armSwVersion | The arm software version of the digitalSTROM-Meter |
| hwVersion | The hardware version of the digitalSTROM-Meter |
### digitalSTROM-Zone-Temperature-Control
The thing type of a digitalSTROM-Zone-Temperature-Control is **zone_temperature_control**.
As configuration only the zone ID or the zone name, to identify the controlled zone, is needed.
| Parameter Label | Parameter ID| Description | Required | Advanced |
|-----------------|------------------------|----------------------------------|----------------- |------------- |
| Zone ID or name | zoneID | The zone id or zone name of the temperature controlled zone. | true | false |
#### Properties
| Property-Name | Description |
| ------------- | ------------------------------------- |
| controlMode | The currently configured control mode. |
| controlDSUID | The dSID of the meter or service that runs the control algorithm. |
| controlState | The currently configured control state. |
### digitalSTROM-Scenes
The digitalSTROM-Scenes can be defined with following parameters.
| Parameter Label | Parameter ID| Description | Required | Advanced |
|-----------------|------------------------|----------------------------------|----------------- |------------- |
| Zone ID or name | zoneID | The zone ID or zone name of the called scene. 0 or empty is broadcast to all. | false | false |
| Group ID or name | groupID | The group ID or group name of the called scene. 0 or empty is broadcast to all. | false | false |
| Scene ID or name | sceneID |The call scene ID or scene name, e.g. preset 1 for scene ID 5. Callable scenes are from 0 to 126. | false | false |
The Scene-Thing-Type *Named-Scene* and *Group-Scene* have all parameters.
The *Apartment-Scene* only has the parameters *Scene name* and *Scene ID* an the *Zone-Scene* has all parameters without *Group ID or name*.
### Textual configuration examples
Usually the discovery works reliable, so that a manual configuration is not needed.
However, at the thing file, a manual configuration looks e.g. like
#### digitalSTROM-Devices
```
Thing digitalstrom:GE:GE-KM200 (digitalstrom:dssBridge:myDssBridge) [ dSID="3504175fe0000000000043d4", activePowerRefreshPriority="low", electricMeterRefreshPriority=“medium", outputCurrentRefreshPriority="high"]
Thing digitalstrom:GR:GR-KL200 (digitalstrom:dssBridge:myDssBridge) [ dSID="3504175fe0000000000043d5"]
```
#### digitalSTROM-Meters
```
Thing digitalstrom:circuit:circuit (digitalstrom:dssBridge:myDssBridge) [ dSID="3504175fe0000000000043d5"]
```
#### digitalSTROM-Zone-Temperature-Control
```
Thing digitalstrom:zone_temperature_control:zone_temperature_control3 (digitalstrom:dssBridge:myDssBridge) [ zoneID="3"]
```
#### digitalSTROM-Group-Scene
```
Thing digitalstrom:groupScene:preset1 (digitalstrom:dssBridge:myDssBridge) [ zoneID="3", groupID="1", sceneID="5"]
```
## Channels
All devices support some of the following channels:
### Output-Channels
digitalSTROM-Devices with an activated output mode.
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| light_dimmer | Dimmer | The *light_dimm* channel allows to dimm a light device. | GE, SW |
| light_switch | Switch | The *light_switch* channel allows to turn a light device on or off. | GE, SW |
| light_2_stage | String| The *light_2_stage* channel allows to turn both light devices on or off or switch only 1 of the both light device on or off. | SW-UMR200 |
| light_3_stage | String | The *light_3_stage* channel allows to turn both light devices on or off or switch both light devices separated from each other on or off. | SW-UMR200 |
| shade | Rollershutter | The *shade* channel allows to control shade device e.g. a roller shutter or awnings. | GR |
| shade_angle | Dimmer | The *shade_angle* channel allows to control the relative slat position in percent of blinds. | GR |
| general_dimmer | Dimmer | The *general_dimmer* channel allows to control the power of a device e.g. a ceiling fan. | SW |
| general_switch | Switch | The *general_switch* channel allows to turn a device on or off e.g. a HIFI-System. | SW |
| general_2_stage | String | The *general_2_stage* channel allows to turn both relais of the device on or off or switch only 1 of the both relais on or off. | SW-UMR200 |
| general_3_stage | String | The *general_3_stage* channel allows to turn both relais of the device on or off or switch both relais of the device separated from each other on or off. | SW-UMR200 |
| heating_switch | Switch | The *heating_switch* channel allows to turn a heating device on or off. | BL |
| heating_dimmer | Dimmer | The *heating_switch* channel allows to control the value in percent of heating valve. | BL |
digitalSTROM-Zone-Temperature-Controlled
| Channel Type ID | Item Type | Description |
|-------|---------|------------------------------------|
| heating_temperature_controled | Number | The *heating_temperature_controled* channel allows to set a target temperature of a zone. |
| heating_dimmer | Dimmer | The *heating_switch* channel allows to control the value in percent of heating valve. |
### Sensor-Channels
digitalSTROM-Devices which have sensors data.
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| active_power | Number | This channel indicates the current active power in watt (W) of the device." | GE, SW, BL |
| output_current | Number | This channel indicates the current output current in milliamper (mA) of the device." | GE, SW, BL |
| electric_meter | Number | This channel indicates the current electric meter value in killowatts hours (kWh) of the device. | GE, SW, BL |
| temperature_indoors | Number | This channel indicates the current temperature indoors in Celsius (°C) of the device. | dS-iSens200 |
| temperature_outdoors | Number | This channel indicates the current temperature outdoors in Celsius (°C) of the device. | --- |
| brightness_indoors | Number | This channel indicates the current brightness indoors in Lux (Lx) of the device. | --- |
| brightness_outdoors | Number | This channel indicates the current brightness outdoors in Lux (Lx) of the device. | --- |
| relative_humidity_indoors | Number | This channel indicates the current relative humidity indoors in percent of the device. | dS-iSens200 |
| relative_humidity_outdoors | Number | This channel indicates the current relative humidity outdoors in percent of the device. | --- |
| air_pressure | Number | This channel indicates the current relative humidity outdoors in hectopscal (hPa bzw. mbar) of the device. | --- |
| wind_speed | Number | This channel indicates the current wind speed in m/s of the device. | --- |
| wind_direction | Number | This channel indicates the current wind direction in degree of the device. | --- |
| precipitation | Number | This channel indicates the current precipitation in milliliter per square meter of the device. | --- |
| carbon_dioxide | Number | This channel indicates the current carbon dioxide in parts per million of the device. | --- |
| sound_pressure_level | Number | This channel indicates the current carbon dioxide in Dezibel (dB) of the device. | --- |
| room_temperation_set_point | Number | This channel indicates the current room temperation set point in Celsius (°C) of the device. | --- |
| room_temperation_control_variable | Number | This channel indicates the current room temperation control variable in Celsius (°C) of the device. | --- |
*If no supported device type is at the table, digitalSTROM currently does not offer a device, which support this type of sensor.
### Binary-Input-Channels
digitalSTROM-Devices which are able to set a binary-input sensor like SW-UMR200 or SW-AKM200.
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| binary_input_presence | Switch | Will be activated, if a presence is detected. | SW |
| binary_input_brightness | Switch | Will be activated, if the brightness is higher than a setted value. | SW |
| binary_input_presence_in_darkness | Switch | Will be activated, if a presence is detected. Sensor has an integrated twilight sensor. | SW |
| binary_input_twilight | Switch | Will be activated by twilight. | SW |
| binary_input_motion | Switch | Will be activated, if a motion is detected. | SW |
| binary_input_motion_in_darkness | Switch | Will be activated, if a motion is detected. Sensor has an integrated twilight sensor. | SW |
| binary_input_smoke | Switch | Will be activated, if smoke is detected. | SW |
| binary_input_wind_strenght_above_limit | Switch | Will be activated, if wind strength is above a user adjusted limit. | SW |
| binary_input_rain | Switch | Will be activated, if rain is detected. | SW |
| binary_input_sun_radiation | Switch | Will be activated, if the sun light is above threshold. | SW |
| binary_input_temperation_below_limit | Switch | Will be activated, if the temperature is below a limit. | SW |
| binary_input_battery_status_is_low | Switch | Will be activated, if the battery status is low. | SW |
| binary_input_window_is_open | Switch | Will be activated, if a window is open. | SW |
| binary_input_door_is_open | Switch | Will be activated, if a door is open. | SW |
| binary_input_window_is_tilted | Switch | Will be activated, if a window is tilted. | SW |
| binary_input_garage_door_is_open | Switch | Will be activated, if a garage door is open. | SW |
| binary_input_sun_protection | Switch | Will be activated, if the sun light is too heavy. | SW |
| binary_input_frost | Switch | Will be activated by frost. | SW |
| binary_input_heating_operation_on_off | Switch | Will be activated, if heating operation is on, otherwise it will be deactivated. | SW |
| binary_input_change_over_heating_cooling | Switch | Will be activated, if heating is activated, otherwise cooling is activated. | SW |
### Metering-Channels
The digitalSTROM-Meters
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| consumption_Wh | Number | The *consumption_Wh* channel indicates the current power consumption in watt (W) of the circuit. | circuit |
| energy_Wh | Number | The *energy_Wh* channel indicates the current electric meter value in killowatt hours of the circuit. | circuit |
The digitalSTROM-Server
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| total_consumption_Wh | Number | The *total_consumption_Wh* channel indicates the current consumption power in watt (W) of all connected circuits to the digitalSTROM-System. | dssBridge |
| total_energy_Wh | Number | The *total_energy_Wh* channel indicates the current electric meter value in killowatt hours of all connected circuits to the digitalSTROM-System. | dssBridge |
### Scenes
| Channel Type ID | Item Type | Description | supported device type |
|-------|---------|------------------------------------|----------------- |
| scene | Switch | The scene channel allows to call or undo a scene from digitalSTROM. | all scene-types |
**Notes: **
*Generally:*
* The digitalSTROM-Server only informs the binding about scene-commands. So if you set the output value of devices e.g. through the dSS-App, the binding will not be informed about the changes and you have to send a "refresh-command" to update the channel.
* If you press a physical switch at your digitalSTROM-installation and the called scene-value is not red out yet, it can take a bit time to read it out and change the state of the channel.
It the scene-value is red out, the state will change immediately.
See also *General-Informations/digitalSTROM-Scenes*.
*Channels with accepted command type increase and decrease:*
* digitalSTROM will only evaluate increase and decrease commands, if a scene was called before which turn the device on.
*Blinds:*
* Increase, decrease and up, down commands of the shade channel changes the angle in digitalSTROM, too. If you want to set only the position, you have to set the value directly.
* To protect the slats digitalSTROM changes the position by setting the angle, too, if the position is very high or low. So if you want to see the correct position, you have to send a refresh or stop command, if the blind is ready.
## Full Example
### demo.things:
```
Bridge digitalstrom:dssBridge:dSS [ dSSAddress="urlOfMyDss", userName="dssadmin", password="mySecretPassword", sensorDataUpdateInterval=180] {
GE GE-KM-200 [ dSID="3504175fe000000000010db9", activePowerRefreshPriority="low", electricMeterRefreshPriority="medium", outputCurrentRefreshPriority="high"]
SW SW-ZWS-200 [ dSID="3504175fe0000000000651c0"]
SW SW-UMR-200 [ dSID="302ed89f43f00ec0000a1034"]
dSiSens200 dS-iSens200 [ dSID="302ed89f43f026800003543d"]
zoneTemperatureControl zoneTemperatureControl [ zoneID="livingroom"]
GR GR-KL220 [ dSID="3504175fe0000000000651c1" ]
namedScene Scene1 [ zoneID="5", groupID="1", sceneID="5"]
circuit circuit1 [ dSID="3504175fe0000010000004e4" ]
GR GR-KL200 [ dSID="3504175fe0000000000651c1" ]
}
```
### demo.items:
```
//dSS
Number TotalActivePower { channel="digitalstrom:dssBridge:dSS:total_consumption_wh" }
Number TotalElectricMeter { channel="digitalstrom:dssBridge:dSS:total_energy_wh" }
//circuit (circuit1)
Number TotalActivePowerDsm { channel="digitalstrom:circuit:dSS:circuit1:energy_wh" }
Number TotalElectricMeterDsm { channel="digitalstrom:circuit:dSS:circuit1:consumption_wh" }
//Light (KM-200)
Dimmer Brightness { channel="digitalstrom:GE:dSS:GE-KM-200:light_dimmer" }
Number ActivePower { channel="digitalstrom:GE:dSS:GE-KM-200:active_power" }
Number OutputCurrent { channel="digitalstrom:GE:dSS:GE-KM-200:output_current" }
Number ElectricMeter { channel="digitalstrom:GE:dSS:GE-KM-200:electric_meter" }
//Device
Switch DeviceSwitch { channel="digitalstrom:SW:dSS:SW-ZWS-200:general_switch" }
//Rollershutter (GR-KL200)
Rollershutter Shutter { channel="digitalstrom:GR:GR-KL200:shade" }
//Blind (GR-KL220)
Rollershutter BlindPosition { channel="digitalstrom:GR:GR-KL210:shade" }
Dimmer BlindAngle { channel="digitalstrom:GR:GR-KL210:shade_angle" }
//Scene (Scene1)
Switch Scene { channel="digitalstrom:namedScene:dSS:Scene1:scene" }
//binary input device (SW-UMR-200)
Switch SensorSwitch { channel="digitalstrom:SW:dSS:SW-UMR-200:binary_input_change_over_heating_cooling" }
//indoor climate (dSiSens200)
Number TempIndoor { channel="digitalstrom:dSiSens200:dSS:dS-iSens200:temperature_indoors" }
Number HumidityIndoor { channel="digitalstrom:dSiSens200:dSS:dS-iSens200:relative_humidity_indoors" }
//target temperature (zoneTemperatureControl)
Number Temperature { channel="digitalstrom:zoneTemperatureControl:dSS:zoneTemperatureControl:heating_temperature_controlled" }
```
### demo.sitemap:
```
sitemap demo label="Main Menu"
{
Frame label="System" {
Frame label="digitalSTROM-Server"{
Text item=TotalActivePower
Text item=TotalElectricMeter
}
Frame label="digitalSTROM-Meter"{
Text item=TotalActivePowerDsm
Text item=TotalElectricMeterDsm
}
}
Frame label="Climate" {
Frame label="heating/cooling"{
Switch item=SensorSwitch
}
Frame label="iSens200"{
Text item=TempIndoor
Text item=HumidityIndoor
}
Frame label="Target temperature"{
Slider item=Temperature
Text item=Temperature
}
}
Frame label="Shade"{
Frame label="Rollerschutter"{
Slider item=Shade
Text item=Shade
}
Frame label="Blind"{
Slider item=BlindPosition
Slider item=BlindAngle
}
}
Frame label="Scenes"{
Frame label="TV scene"{
Switch item=Scene
}
}
Frame label="HiFi" {
Frame label="TV light"{
Slider item=Brightness
Switch item=Brightness
Text item=ActivePower
Text item=OutputCurrent
Text item=ElectricMeter
}
Frame label="TV"{
Switch item=DeviceSwitch
}
}
}
```
## General-Informations
### digitalSTROM-Scenes
The device scene configuration will be saved persistently to the thing properties, if the thing is not textual configured (because textual configured things will not be persisted), to update the device state faster.
For that each scene configuration of each device has to be read out first, because of the digitalSTROM-rule 9 that requires a waiting period of one minute, that take some time so that at the first start a scene call can be take some time to read it out and update the device state.
To read it out faster only the discovered or called scenes will be read out.
**Note:**
Because the digitalSTROM-Server can't inform the binding about save scene events at this time, the persistently saved scene configurations can't be updated.
The current troubleshooting to read out the new scene configuration after a save scene action at the digitalSTROM-Server is the following:
1. delete the thing to delete the persistently saved scene configuration
2. restart the server to delete the temporary saved scene configuration
3. add the thing again to read out the scene configuration again.
### Initial state of digitalSTROM-Scenes and devices
To get the device and scene state after a server start or restart, the binding uses the last called group scenes of digitalSTROM.
Because of that there are two things to be observed:
1. If a device status has changed through a device scene or a directly set output value, the status is maybe not correct.
2. If the last called group scene was not read out yet, it can takes some time until the status will be updated.
### Textual configuration notice
If you configure your system with textual configuration files, like *\*.thing*, there is one things you have to consider.
* The feature of the persisting of scene-configurations, to get the scene-configurations after a restart faster (see *digitalSTROM-Scenes* above), will not support textural configured things, because the properties cannot be persist in this case.
### Rule specific notice
If you want to create a rule, which uses things of the digitalSTROM-Binding, there are also two things you have to consider.
1. If the rule contains several digitalSTROM-Devices, which can be summarized in a digitalSTROM-Scene, e.g. some lights in a zone, please use an equivalent supported scene. That will significantly reduce the communication to the digitalSTROM-Server, increases performance and does not bypass the digitalSTROM state-machine.
2. If you implement your own temperature control algorithm for a zone/room, e.g. because you want to use other temperature sensors, and call more than one digitalSTROM-BL-KM200, please use the *zone_temperature_control* for valve value control. The *zone_temperature_control* for valve value control will call all digitalSTROM-BL-KM200 with one command and increases the performance. To get the needed channel at the *zone_temperature_control* you have to choose a control mode unequal to *pid-controlled* for the affected zone at the digitalSTROM-heating-control-app.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.digitalstrom</artifactId>
<name>openHAB Add-ons :: Bundles :: DigitalSTROM Binding</name>
</project>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.digitalstrom-${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-digitalstrom" description="digitalSTROM Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-mdns</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.digitalstrom/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneTypes;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link DigitalSTROMBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Michael Ochel - Initial contribution
* @author Mathias Siegele - Initial contribution
*/
public class DigitalSTROMBindingConstants {
public static final String BINDING_ID = "digitalstrom";
// List of all Thing Type IDs
public static final String THING_TYPE_ID_DSS_BRIDGE = "dssBridge";
public static final String THING_TYPE_ID_ZONE_TEMERATURE_CONTROL = "zoneTemperatureControl";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_DSS_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_DSS_BRIDGE);
public static final ThingTypeUID THING_TYPE_ZONE_TEMERATURE_CONTROL = new ThingTypeUID(BINDING_ID,
THING_TYPE_ID_ZONE_TEMERATURE_CONTROL);
public static final ThingTypeUID THING_TYPE_APP_SCENE = new ThingTypeUID(BINDING_ID, SceneTypes.APARTMENT_SCENE);
public static final ThingTypeUID THING_TYPE_ZONE_SCENE = new ThingTypeUID(BINDING_ID, SceneTypes.ZONE_SCENE);
public static final ThingTypeUID THING_TYPE_GROUP_SCENE = new ThingTypeUID(BINDING_ID, SceneTypes.GROUP_SCENE);
public static final ThingTypeUID THING_TYPE_NAMED_SCENE = new ThingTypeUID(BINDING_ID, SceneTypes.NAMED_SCENE);
// scene
public static final String CHANNEL_ID_SCENE = "scene";
// sensor
public static final String CHANNEL_ID_TOTAL_ACTIVE_POWER = "totalActivePower";
public static final String CHANNEL_ID_TOTAL_ELECTRIC_METER = "totalElectricMeter";
// options combined switches
public static final String OPTION_COMBINED_BOTH_OFF = "0";
public static final String OPTION_COMBINED_BOTH_ON = "200";
public static final String OPTION_COMBINED_FIRST_ON = "90";
public static final String OPTION_COMBINED_SECOND_ON = "130";
/* config URIs */
public static final String DEVICE_CONFIG = "binding:digitalstrom:device";
public static final String GRAY_DEVICE_CONFIG = "binding:digitalstrom:grayDevice";
public static final String DSS_BRIDE_CONFIG = "binding:digitalstrom:dssBridge";
/* Bridge config properties */
public static final String HOST = "dSSAddress";
public static final String USER_NAME = "userName";
public static final String PASSWORD = "password";
public static final String APPLICATION_TOKEN = "applicationToken";
public static final String DS_ID = "dSID";
public static final String DS_NAME = "dS-Installation-Name";
public static final String SENSOR_DATA_UPDATE_INTERVAL = "sensorDataUpdateInterval";
public static final String TOTAL_POWER_UPDATE_INTERVAL = "totalPowerUpdateInterval";
public static final String DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY = "defaultTrashBinDeleteTime";
public static final String SENSOR_WAIT_TIME = "sensorWaitTime";
public static final String SERVER_CERT = "serverCert";
/* Device info properties */
public static final String DEVICE_UID = "dSUID";
public static final String DEVICE_NAME = "deviceName";
public static final String DEVICE_DSID = "dSID";
public static final String DEVICE_HW_INFO = "hwInfo";
public static final String DEVICE_ZONE_ID = "zoneID";
public static final String DEVICE_GROUPS = "groups";
public static final String DEVICE_OUTPUT_MODE = "outputmode";
public static final String DEVICE_FUNCTIONAL_COLOR_GROUP = "funcColorGroup";
public static final String DEVICE_METER_ID = "meterDSID";
public static final String DEVICE_BINARAY_INPUTS = "binarayInputs";
// Device properties scene
public static final String DEVICE_SCENE = "scene"; // + number of scene
// Sensor data channel properties
public static final String ACTIVE_POWER_REFRESH_PRIORITY = "activePowerRefreshPriority";
public static final String ELECTRIC_METER_REFRESH_PRIORITY = "electricMeterRefreshPriority";
public static final String OUTPUT_CURRENT_REFRESH_PRIORITY = "outputCurrentRefreshPriority";
/* Scene config */
public static final String ZONE_ID = "zoneID";
public static final String GROUP_ID = "groupID";
public static final String SCENE_ID = "sceneID";
// circuit properties
public static final String HW_NAME = "hwName";
public static final String HW_VERSION = "hwVersion";
public static final String SW_VERSION = "swVersion";
public static final String API_VERSION = "apiVersion";
public static final String DSP_SW_VERSION = "armSwVersion";
public static final String ARM_SW_VERSION = "dspSwVersion";
}

View File

@@ -0,0 +1,265 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.discovery.DiscoveryServiceManager;
import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler;
import org.openhab.binding.digitalstrom.internal.handler.CircuitHandler;
import org.openhab.binding.digitalstrom.internal.handler.DeviceHandler;
import org.openhab.binding.digitalstrom.internal.handler.SceneHandler;
import org.openhab.binding.digitalstrom.internal.handler.ZoneTemperatureControlHandler;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.ConnectionManagerImpl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
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.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DigitalSTROMHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Michael Ochel - Initial contribution
* @author Mathias Siegele - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.digitalstrom")
public class DigitalSTROMHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(DigitalSTROMHandlerFactory.class);
private final Map<String, DiscoveryServiceManager> discoveryServiceManagers = new HashMap<>();
private Map<ThingUID, BridgeHandler> bridgeHandlers;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return BridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)
|| SceneHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)
|| DeviceHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)
|| ZoneTemperatureControlHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)
|| CircuitHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Override
public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID,
ThingUID bridgeUID) {
if (BridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID dSSUID = getBridgeThingUID(thingTypeUID, thingUID, configuration);
if (dSSUID != null) {
return super.createThing(thingTypeUID, configuration, dSSUID, null);
} else {
logger.error("Can't generate thing UID for thing type {}"
+ ", because digitalSTROM-Server is not reachable. Please check these points:\n"
+ "Are the server address and portnumber correct?\n" + "Is the server turned on?\n"
+ "Is the network configured correctly?", thingTypeUID);
return null;
}
}
if (DeviceHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID dsDeviceUID = getDeviceUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, dsDeviceUID, bridgeUID);
}
if (CircuitHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID dsDeviceUID = getDeviceUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, dsDeviceUID, bridgeUID);
}
if (ZoneTemperatureControlHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID zoneTempConUID = getZoneTemperatureControlUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, zoneTempConUID, bridgeUID);
}
if (SceneHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID dsSceneUID = getSceneUID(thingTypeUID, thingUID, configuration, bridgeUID);
return super.createThing(thingTypeUID, configuration, dsSceneUID, bridgeUID);
}
throw new IllegalArgumentException(
"The thing type " + thingTypeUID + " is not supported by the digitalSTROM binding.");
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (BridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
BridgeHandler handler = new BridgeHandler((Bridge) thing);
if (bridgeHandlers == null) {
bridgeHandlers = new HashMap<>();
}
bridgeHandlers.put(thing.getUID(), handler);
DiscoveryServiceManager discoveryServiceManager = new DiscoveryServiceManager(handler);
discoveryServiceManager.registerDiscoveryServices(bundleContext);
discoveryServiceManagers.put(handler.getThing().getUID().getAsString(), discoveryServiceManager);
return handler;
}
if (DeviceHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new DeviceHandler(thing);
}
if (CircuitHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new CircuitHandler(thing);
}
if (ZoneTemperatureControlHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new ZoneTemperatureControlHandler(thing);
}
if (SceneHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new SceneHandler(thing);
}
return null;
}
private ThingUID getDeviceUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration,
ThingUID bridgeUID) {
if (thingUID == null && StringUtils.isNotBlank((String) configuration.get(DEVICE_DSID))) {
return new ThingUID(thingTypeUID, bridgeUID, configuration.get(DEVICE_DSID).toString());
}
return thingUID;
}
private ThingUID getZoneTemperatureControlUID(ThingTypeUID thingTypeUID, ThingUID thingUID,
Configuration configuration, ThingUID bridgeUID) {
if (thingUID == null) {
Integer zoneID = ZoneTemperatureControlHandler.getZoneID(configuration, bridgeHandlers.get(bridgeUID));
if (zoneID > ZoneTemperatureControlHandler.ZONE_ID_NOT_EXISTS) {
return new ThingUID(thingTypeUID, bridgeUID, zoneID.toString());
} else {
switch (zoneID) {
case ZoneTemperatureControlHandler.ZONE_ID_NOT_EXISTS:
logger.error("Configured zone '{}' does not exist, please check your configuration.",
configuration.get(DigitalSTROMBindingConstants.ZONE_ID));
break;
case ZoneTemperatureControlHandler.ZONE_ID_NOT_SET:
logger.error("ZoneID is missing at your configuration.");
break;
case ZoneTemperatureControlHandler.BRIDGE_IS_NULL:
logger.error("Bridge is missing, can not check the zoneID.");
break;
}
}
}
return thingUID;
}
private ThingUID getSceneUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration,
ThingUID bridgeUID) {
if (thingUID != null) {
return thingUID;
}
String sceneID = SceneHandler.getSceneID(configuration, bridgeHandlers.get(bridgeUID));
switch (sceneID) {
case SceneHandler.SCENE_WRONG:
logger.error(
"Configured scene '{}' does not exist or can not be used, please check your configuration.",
configuration.get(DigitalSTROMBindingConstants.SCENE_ID));
break;
case SceneHandler.ZONE_WRONG:
logger.error("Configured zone '{}' does not exist, please check your configuration.",
configuration.get(DigitalSTROMBindingConstants.ZONE_ID));
break;
case SceneHandler.GROUP_WRONG:
logger.error("Configured group '{}' does not exist, please check your configuration.",
configuration.get(DigitalSTROMBindingConstants.GROUP_ID));
break;
case SceneHandler.NO_STRUC_MAN:
logger.error("Waiting for building digitalSTROM model.");
break;
case SceneHandler.NO_SCENE:
logger.error("No Scene-ID is set!");
break;
case SceneHandler.NO_BRIDGE:
logger.error("No related bridge found!");
default:
return new ThingUID(thingTypeUID, bridgeUID, sceneID);
}
return thingUID;
}
private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration) {
if (thingUID != null) {
return thingUID;
}
String dSID;
if (StringUtils.isBlank((String) configuration.get(DS_ID))) {
dSID = getDSSid(configuration);
if (dSID != null) {
configuration.put(DS_ID, dSID);
}
} else {
dSID = configuration.get(DS_ID).toString();
}
if (dSID != null) {
return new ThingUID(thingTypeUID, dSID);
} else {
return null;
}
}
private String getDSSid(Configuration configuration) {
String dSID = null;
if (StringUtils.isNotBlank((String) configuration.get(HOST))) {
String host = configuration.get(HOST).toString();
String applicationToken = null;
String user = null;
String pw = null;
if (StringUtils.isNotBlank((String) configuration.get(APPLICATION_TOKEN))) {
applicationToken = configuration.get(APPLICATION_TOKEN).toString();
}
if (checkUserPassword(configuration)) {
user = configuration.get(USER_NAME).toString();
pw = configuration.get(PASSWORD).toString();
}
ConnectionManager connMan = new ConnectionManagerImpl(host, user, pw, applicationToken, false, true);
Map<String, String> dsidMap = connMan.getDigitalSTROMAPI().getDSID(connMan.getSessionToken());
if (dsidMap != null) {
dSID = dsidMap.get(JSONApiResponseKeysEnum.DSID.getKey());
}
}
return dSID;
}
private boolean checkUserPassword(Configuration configuration) {
return StringUtils.isNotBlank((String) configuration.get(USER_NAME))
&& StringUtils.isNotBlank((String) configuration.get(PASSWORD));
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof BridgeHandler) {
String uid = thingHandler.getThing().getUID().getAsString();
if (discoveryServiceManagers.get(uid) != null) {
discoveryServiceManagers.get(uid).unregisterDiscoveryServices(bundleContext);
discoveryServiceManagers.remove(uid);
}
}
}
}

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl.DsAPIImpl;
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.ThingUID;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BridgeDiscoveryService} is responsible for discovering digitalSTROM-Server, if the server is in the
* local network and is reachable through "dss.local." with default port number "8080". It uses the central
* {@link AbstractDiscoveryService}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.digitalstrom")
public class BridgeDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(BridgeDiscoveryService.class);
public static final String HOST_ADDRESS = "dss.local.";
private final Runnable resultCreater = new Runnable() {
@Override
public void run() {
createResult();
}
private void createResult() {
ThingUID uid = getThingUID();
if (uid != null) {
Map<String, Object> properties = new HashMap<>(2);
properties.put(DigitalSTROMBindingConstants.HOST, HOST_ADDRESS);
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
.withLabel("digitalSTROM-Server").build();
thingDiscovered(result);
}
}
private ThingUID getThingUID() {
DsAPI digitalSTROMClient = new DsAPIImpl(HOST_ADDRESS, Config.DEFAULT_CONNECTION_TIMEOUT,
Config.DEFAULT_READ_TIMEOUT, true);
String dSID = null;
switch (digitalSTROMClient.checkConnection("123")) {
case HttpURLConnection.HTTP_OK:
case HttpURLConnection.HTTP_UNAUTHORIZED:
case HttpURLConnection.HTTP_FORBIDDEN:
Map<String, String> dsidMap = digitalSTROMClient.getDSID(null);
if (dsidMap != null) {
dSID = dsidMap.get(JSONApiResponseKeysEnum.DSID.getKey());
}
if (StringUtils.isNotBlank(dSID)) {
return new ThingUID(DigitalSTROMBindingConstants.THING_TYPE_DSS_BRIDGE, dSID);
} else {
logger.error("Can't get server dSID to generate ThingUID. Please add the server manually.");
}
}
return null;
}
};
/**
* Creates a new {@link BridgeDiscoveryService}.
*/
public BridgeDiscoveryService() {
super(new HashSet<>(Arrays.asList(DigitalSTROMBindingConstants.THING_TYPE_DSS_BRIDGE)), 10, false);
}
@Activate
@Override
protected void activate(Map<String, Object> configProperties) {
super.activate(configProperties);
}
@Deactivate
@Override
protected void deactivate() {
super.deactivate();
}
@Modified
@Override
protected void modified(Map<String, Object> configProperties) {
super.modified(configProperties);
}
@Override
protected void startScan() {
scheduler.execute(resultCreater);
}
}

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.jmdns.ServiceInfo;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl.DsAPIImpl;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BridgeMDNSDiscoveryParticipant} is responsible for discovering digitalSTROM-Server. It uses the central
* {@link MDNSDiscoveryService}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
@Component(service = MDNSDiscoveryParticipant.class, immediate = true)
public class BridgeMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(BridgeMDNSDiscoveryParticipant.class);
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return Collections.singleton(DigitalSTROMBindingConstants.THING_TYPE_DSS_BRIDGE);
}
@Override
public String getServiceType() {
return "_tcp.local.";
}
@Override
public DiscoveryResult createResult(ServiceInfo service) {
if (service.getApplication().contains("dssweb")) {
ThingUID uid = getThingUID(service);
if (uid != null) {
String hostAddress = service.getName() + "." + service.getDomain() + ".";
Map<String, Object> properties = new HashMap<>(2);
properties.put(DigitalSTROMBindingConstants.HOST, hostAddress);
return DiscoveryResultBuilder.create(uid).withProperties(properties)
.withRepresentationProperty(uid.getId()).withLabel("digitalSTROM-Server").build();
}
}
return null;
}
@Override
public ThingUID getThingUID(ServiceInfo service) {
if (service.getApplication().contains("dssweb")) {
String hostAddress = service.getName() + "." + service.getDomain() + ".";
DsAPI digitalSTROMClient = new DsAPIImpl(hostAddress, Config.DEFAULT_CONNECTION_TIMEOUT,
Config.DEFAULT_READ_TIMEOUT, true);
Map<String, String> dsidMap = digitalSTROMClient.getDSID(null);
String dSID = null;
if (dsidMap != null) {
dSID = dsidMap.get(JSONApiResponseKeysEnum.DSID.getKey());
}
if (StringUtils.isNotBlank(dSID)) {
return new ThingUID(DigitalSTROMBindingConstants.THING_TYPE_DSS_BRIDGE, dSID);
} else {
logger.error("Can't get server dSID to generate thing UID. Please add the server manually.");
}
}
return null;
}
}

View File

@@ -0,0 +1,212 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.BINDING_ID;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
import org.openhab.binding.digitalstrom.internal.providers.DsDeviceThingTypeProvider;
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.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceDiscoveryService} discovers all digitalSTROM-Devices, of one supported device-color-type. The
* device-color-type has to be given to the {@link #DeviceDiscoveryService(BridgeHandler, ThingTypeUID)} as
* {@link ThingTypeUID}. The supported {@link ThingTypeUID} can be found at
* {@link DeviceHandler#SUPPORTED_THING_TYPES}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class DeviceDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(DeviceDiscoveryService.class);
private final BridgeHandler bridgeHandler;
private final String deviceType;
private final ThingUID bridgeUID;
public static final int TIMEOUT = 10;
/**
* Creates a new {@link DeviceDiscoveryService} for the given supported {@link ThingTypeUID}.
*
* @param bridgeHandler (must not be null)
* @param supportedThingType (must not be null)
* @throws IllegalArgumentException see {@link AbstractDiscoveryService#AbstractDiscoveryService(int)}
*/
public DeviceDiscoveryService(BridgeHandler bridgeHandler, ThingTypeUID supportedThingType)
throws IllegalArgumentException {
super(new HashSet<>(Arrays.asList(supportedThingType)), TIMEOUT, true);
this.deviceType = supportedThingType.getId();
this.bridgeHandler = bridgeHandler;
bridgeUID = bridgeHandler.getThing().getUID();
}
/**
* Deactivates the {@link DeviceDiscoveryService} and removes the {@link DiscoveryResult}s.
*/
@Override
public void deactivate() {
logger.debug("deactivate discovery service for device type {} thing types are: {}", deviceType,
super.getSupportedThingTypes().toString());
removeOlderResults(new Date().getTime());
}
@Override
protected void startScan() {
if (bridgeHandler != null) {
if (!DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString().equals(deviceType)) {
List<Device> devices = bridgeHandler.getDevices();
if (devices != null) {
for (Device device : devices) {
onDeviceAddedInternal(device);
}
}
} else {
List<Circuit> circuits = bridgeHandler.getCircuits();
if (circuits != null) {
for (Circuit circuit : circuits) {
onDeviceAddedInternal(circuit);
}
}
}
}
}
@Override
protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}
private void onDeviceAddedInternal(GeneralDeviceInformation device) {
boolean isSupported = false;
if (device instanceof Device) {
Device tempDevice = (Device) device;
if ((tempDevice.isSensorDevice() && deviceType.equals(tempDevice.getHWinfo().replaceAll("-", "")))
|| (deviceType.equals(tempDevice.getHWinfo().substring(0, 2))
&& (tempDevice.isDeviceWithOutput() || tempDevice.isBinaryInputDevice())
&& tempDevice.isPresent())) {
isSupported = true;
}
} else if (device instanceof Circuit
&& DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString().equals(deviceType)) {
isSupported = true;
}
if (isSupported) {
ThingUID thingUID = getThingUID(device);
if (thingUID != null) {
Map<String, Object> properties = new HashMap<>(1);
properties.put(DigitalSTROMBindingConstants.DEVICE_DSID, device.getDSID().getValue());
String deviceName = null;
if (StringUtils.isNotBlank(device.getName())) {
deviceName = device.getName();
} else {
// if no name is set, the dSID will be used as name
deviceName = device.getDSID().getValue();
}
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(deviceName).build();
thingDiscovered(discoveryResult);
} else {
if (device instanceof Device) {
logger.debug("Discovered unsupported device hardware type '{}' with uid {}",
((Device) device).getHWinfo(), device.getDSUID());
}
}
} else {
if (device instanceof Device) {
logger.debug(
"Discovered device with disabled or no output mode. Device was not added to inbox. "
+ "Device information: hardware info: {}, dSUID: {}, device-name: {}, output value: {}",
((Device) device).getHWinfo(), device.getDSUID(), device.getName(),
((Device) device).getOutputMode());
}
}
}
private ThingUID getThingUID(GeneralDeviceInformation device) {
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
ThingTypeUID thingTypeUID = null;
if (device instanceof Device) {
Device tempDevice = (Device) device;
thingTypeUID = new ThingTypeUID(BINDING_ID, tempDevice.getHWinfo().substring(0, 2));
if (tempDevice.isSensorDevice() && deviceType.equals(tempDevice.getHWinfo().replaceAll("-", ""))) {
thingTypeUID = new ThingTypeUID(BINDING_ID, deviceType);
}
} else {
thingTypeUID = new ThingTypeUID(BINDING_ID,
DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString());
}
if (getSupportedThingTypes().contains(thingTypeUID)) {
String thingDeviceId = device.getDSID().toString();
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, thingDeviceId);
return thingUID;
} else {
return null;
}
}
/**
* Removes the {@link Thing} of the given {@link Device}.
*
* @param device (must not be null)
*/
public void onDeviceRemoved(GeneralDeviceInformation device) {
ThingUID thingUID = getThingUID(device);
if (thingUID != null) {
thingRemoved(thingUID);
}
}
/**
* Creates a {@link DiscoveryResult} for the given {@link Device}, if the {@link Device} is supported and the
* {@link Device#getOutputMode()} is unequal {@link OutputModeEnum#DISABLED}.
*
* @param device (must not be null)
*/
public void onDeviceAdded(GeneralDeviceInformation device) {
if (super.isBackgroundDiscoveryEnabled()) {
onDeviceAddedInternal(device);
}
}
/**
* Returns the ID of this {@link DeviceDiscoveryService}.
*
* @return id of the service
*/
public String getID() {
return deviceType;
}
}

View File

@@ -0,0 +1,259 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler;
import org.openhab.binding.digitalstrom.internal.handler.CircuitHandler;
import org.openhab.binding.digitalstrom.internal.handler.DeviceHandler;
import org.openhab.binding.digitalstrom.internal.handler.SceneHandler;
import org.openhab.binding.digitalstrom.internal.handler.ZoneTemperatureControlHandler;
import org.openhab.binding.digitalstrom.internal.lib.climate.TemperatureControlSensorTransmitter;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.openhab.binding.digitalstrom.internal.providers.DsDeviceThingTypeProvider;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ThingType;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
/**
* The {@link DiscoveryServiceManager} manages the different scene and device discovery services and informs them about
* new added or removed scenes and devices.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class DiscoveryServiceManager
implements SceneStatusListener, DeviceStatusListener, TemperatureControlStatusListener {
private final Map<String, AbstractDiscoveryService> discoveryServices;
private final Map<String, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
private final String bridgeUID;
/**
* Creates a new {@link DiscoveryServiceManager} and generates automatically all {@link SceneDiscoveryService}s and
* {@link DeviceDiscoveryService}s for all supported {@link ThingType}s of the {@link DeviceHandler} and
* {@link SceneHandler}.
*
* @param bridgeHandler (must not be null)
*/
public DiscoveryServiceManager(BridgeHandler bridgeHandler) {
bridgeUID = bridgeHandler.getThing().getUID().getAsString();
discoveryServices = new HashMap<>(SceneHandler.SUPPORTED_THING_TYPES.size()
+ DeviceHandler.SUPPORTED_THING_TYPES.size() + CircuitHandler.SUPPORTED_THING_TYPES.size()
+ ZoneTemperatureControlHandler.SUPPORTED_THING_TYPES.size());
for (ThingTypeUID type : SceneHandler.SUPPORTED_THING_TYPES) {
discoveryServices.put(type.getId(), new SceneDiscoveryService(bridgeHandler, type));
}
for (ThingTypeUID type : DeviceHandler.SUPPORTED_THING_TYPES) {
discoveryServices.put(type.getId(), new DeviceDiscoveryService(bridgeHandler, type));
}
for (ThingTypeUID type : CircuitHandler.SUPPORTED_THING_TYPES) {
discoveryServices.put(type.getId(), new DeviceDiscoveryService(bridgeHandler, type));
}
for (ThingTypeUID type : ZoneTemperatureControlHandler.SUPPORTED_THING_TYPES) {
discoveryServices.put(type.getId(), new ZoneTemperatureControlDiscoveryService(bridgeHandler, type));
}
bridgeHandler.registerSceneStatusListener(this);
bridgeHandler.registerDeviceStatusListener(this);
bridgeHandler.registerTemperatureControlStatusListener(this);
}
/**
* Deactivates all {@link SceneDiscoveryService}s and {@link DeviceDiscoveryService}s of this
* {@link DiscoveryServiceManager} and unregisters them from the given {@link BundleContext}.
*
* @param bundleContext (must not be null)
*/
public void unregisterDiscoveryServices(BundleContext bundleContext) {
if (discoveryServices != null) {
for (AbstractDiscoveryService service : discoveryServices.values()) {
if (service instanceof SceneDiscoveryService) {
SceneDiscoveryService sceneDisServ = (SceneDiscoveryService) service;
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(bridgeUID + sceneDisServ.getID());
sceneDisServ.deactivate();
serviceReg.unregister();
discoveryServiceRegs.remove(bridgeUID + sceneDisServ.getID());
}
if (service instanceof DeviceDiscoveryService) {
DeviceDiscoveryService devDisServ = (DeviceDiscoveryService) service;
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(bridgeUID + devDisServ.getID());
devDisServ.deactivate();
serviceReg.unregister();
discoveryServiceRegs.remove(bridgeUID + devDisServ.getID());
}
if (service instanceof ZoneTemperatureControlDiscoveryService) {
ZoneTemperatureControlDiscoveryService devDisServ = (ZoneTemperatureControlDiscoveryService) service;
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(bridgeUID + devDisServ.getID());
devDisServ.deactivate();
serviceReg.unregister();
discoveryServiceRegs.remove(bridgeUID + devDisServ.getID());
}
}
}
}
/**
* Registers all {@link SceneDiscoveryService}s and {@link DeviceDiscoveryService}s of this
* {@link DiscoveryServiceManager} to the given {@link BundleContext}.
*
* @param bundleContext (must not be null)
*/
public void registerDiscoveryServices(BundleContext bundleContext) {
if (discoveryServices != null) {
for (AbstractDiscoveryService service : discoveryServices.values()) {
if (service instanceof SceneDiscoveryService) {
this.discoveryServiceRegs.put(bridgeUID + ((SceneDiscoveryService) service).getID(), bundleContext
.registerService(DiscoveryService.class.getName(), service, new Hashtable<>()));
}
if (service instanceof DeviceDiscoveryService) {
this.discoveryServiceRegs.put(bridgeUID + ((DeviceDiscoveryService) service).getID(), bundleContext
.registerService(DiscoveryService.class.getName(), service, new Hashtable<>()));
}
if (service instanceof ZoneTemperatureControlDiscoveryService) {
this.discoveryServiceRegs
.put(bridgeUID + ((ZoneTemperatureControlDiscoveryService) service).getID(), bundleContext
.registerService(DiscoveryService.class.getName(), service, new Hashtable<>()));
}
}
}
}
@Override
public String getSceneStatusListenerID() {
return SceneStatusListener.SCENE_DISCOVERY;
}
@Override
public void onSceneStateChanged(boolean flag) {
// nothing to do
}
@Override
public void onSceneRemoved(InternalScene scene) {
if (discoveryServices.get(scene.getSceneType()) != null) {
((SceneDiscoveryService) discoveryServices.get(scene.getSceneType())).onSceneRemoved(scene);
}
}
@Override
public void onSceneAdded(InternalScene scene) {
if (discoveryServices.get(scene.getSceneType()) != null) {
((SceneDiscoveryService) discoveryServices.get(scene.getSceneType())).onSceneAdded(scene);
}
}
@Override
public void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
// nothing to do
}
@Override
public void onDeviceRemoved(GeneralDeviceInformation device) {
if (device instanceof Device) {
String id = ((Device) device).getHWinfo().substring(0, 2);
if (((Device) device).isSensorDevice()) {
id = ((Device) device).getHWinfo().replace("-", "");
}
if (discoveryServices.get(id) != null) {
((DeviceDiscoveryService) discoveryServices.get(id)).onDeviceRemoved(device);
}
}
if (device instanceof Circuit) {
if (discoveryServices.get(DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString()) != null) {
((DeviceDiscoveryService) discoveryServices
.get(DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString())).onDeviceRemoved(device);
}
}
}
@Override
public void onDeviceAdded(GeneralDeviceInformation device) {
if (device instanceof Device) {
String id = ((Device) device).getHWinfo().substring(0, 2);
if (((Device) device).isSensorDevice()) {
id = ((Device) device).getHWinfo();
}
if (discoveryServices.get(id) != null) {
((DeviceDiscoveryService) discoveryServices.get(id)).onDeviceAdded(device);
}
}
if (device instanceof Circuit) {
if (discoveryServices.get(DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString()) != null) {
((DeviceDiscoveryService) discoveryServices
.get(DsDeviceThingTypeProvider.SupportedThingTypes.circuit.toString())).onDeviceAdded(device);
}
}
}
@Override
public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whatConfig) {
// nothing to do
}
@Override
public void onSceneConfigAdded(short sceneId) {
// nothing to do
}
@Override
public String getDeviceStatusListenerID() {
return DeviceStatusListener.DEVICE_DISCOVERY;
}
@Override
public void configChanged(TemperatureControlStatus tempControlStatus) {
// currently only this thing-type exists
if (discoveryServices.get(DigitalSTROMBindingConstants.THING_TYPE_ZONE_TEMERATURE_CONTROL.toString()) != null) {
((ZoneTemperatureControlDiscoveryService) discoveryServices
.get(DigitalSTROMBindingConstants.THING_TYPE_ZONE_TEMERATURE_CONTROL.toString()))
.configChanged(tempControlStatus);
}
}
@Override
public void registerTemperatureSensorTransmitter(
TemperatureControlSensorTransmitter temperatureSensorTransreciver) {
// nothing to do
}
@Override
public Integer getTemperationControlStatusListenrID() {
return TemperatureControlStatusListener.DISCOVERY;
}
@Override
public void onTargetTemperatureChanged(Float newValue) {
// nothing to do
}
@Override
public void onControlValueChanged(Integer newValue) {
// nothing to do
}
}

View File

@@ -0,0 +1,240 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FuncNameAndColorGroupEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
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.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneDiscoveryService} discovers all digitalSTROM-scene of one supported scene-type. The scene-type has to
* be given to the {@link #SceneDiscoveryService(BridgeHandler, ThingTypeUID)} as
* {@link ThingTypeUID}. The supported {@link ThingTypeUID} can be found at {@link SceneHandler#SUPPORTED_THING_TYPES}
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class SceneDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(SceneDiscoveryService.class);
private final BridgeHandler bridgeHandler;
private final String sceneType;
public static final int TIMEOUT = 10;
/**
* Creates a new {@link SceneDiscoveryService} for the given supportedThingType.
*
* @param bridgeHandler (must not be null)
* @param supportedThingType (must not be null)
* @throws IllegalArgumentException see {@link AbstractDiscoveryService#AbstractDiscoveryService(int)}
*/
public SceneDiscoveryService(BridgeHandler bridgeHandler, ThingTypeUID supportedThingType)
throws IllegalArgumentException {
super(new HashSet<>(Arrays.asList(supportedThingType)), TIMEOUT, false);
this.sceneType = supportedThingType.getId();
this.bridgeHandler = bridgeHandler;
}
/**
* Deactivates the {@link SceneDiscoveryService} and removes the {@link DiscoveryResult}s.
*/
@Override
public void deactivate() {
logger.debug("deactivate discovery service for scene type {} remove thing tyspes {}", sceneType,
super.getSupportedThingTypes());
removeOlderResults(new Date().getTime());
}
@Override
protected void startScan() {
if (bridgeHandler != null) {
if (bridgeHandler.getScenes() != null) {
for (InternalScene scene : bridgeHandler.getScenes()) {
onSceneAddedInternal(scene);
}
}
}
}
@Override
protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}
private void onSceneAddedInternal(InternalScene scene) {
logger.debug("{}", scene.getSceneType());
if (scene.getSceneType().equals(sceneType)) {
if (!ignoredScene(scene.getSceneID()) && !ignoreGroup(scene.getGroupID())) {
ThingUID thingUID = getThingUID(scene);
if (thingUID != null) {
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
Map<String, Object> properties = new HashMap<>();
properties.put(ZONE_ID, scene.getZoneID());
properties.put(GROUP_ID, scene.getGroupID());
if (SceneEnum.containsScene(scene.getSceneID())) {
properties.put(SCENE_ID, SceneEnum.getScene(scene.getSceneID()).toString());
} else {
logger.debug("discovered scene: name '{}' with id {} have an invalid scene-ID",
scene.getSceneName(), scene.getID());
}
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(scene.getSceneName()).build();
thingDiscovered(discoveryResult);
} else {
logger.debug("discovered unsupported scene: name '{}' with id {}", scene.getSceneName(),
scene.getID());
}
}
}
}
private boolean ignoreGroup(Short groupID) {
if (FuncNameAndColorGroupEnum.getMode(groupID) != null) {
switch (FuncNameAndColorGroupEnum.getMode(groupID)) {
case TEMPERATION_CONTROL:
return true;
default:
return false;
}
}
return false;
}
private boolean ignoredScene(short sceneID) {
switch (SceneEnum.getScene(sceneID)) {
case INCREMENT:
case DECREMENT:
case STOP:
case MINIMUM:
case MAXIMUM:
case DEVICE_ON:
case DEVICE_OFF:
case DEVICE_STOP:
case AREA_1_INCREMENT:
case AREA_1_DECREMENT:
case AREA_1_STOP:
case AREA_2_INCREMENT:
case AREA_2_DECREMENT:
case AREA_2_STOP:
case AREA_3_INCREMENT:
case AREA_3_DECREMENT:
case AREA_3_STOP:
case AREA_4_INCREMENT:
case AREA_4_DECREMENT:
case AREA_4_STOP:
case AREA_STEPPING_CONTINUE:
case ENERGY_OVERLOAD:
case ALARM_SIGNAL:
case AUTO_STANDBY:
case ZONE_ACTIVE:
return true;
default:
return false;
}
}
private ThingUID getThingUID(InternalScene scene) {
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, sceneType);
if (getSupportedThingTypes().contains(thingTypeUID)) {
String thingSceneId = scene.getID();
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, thingSceneId);
return thingUID;
} else {
return null;
}
}
/**
* Returns the ID of this {@link SceneDiscoveryService}.
*
* @return id of this service
*/
public String getID() {
return sceneType;
}
/**
* Creates a {@link DiscoveryResult} of the given {@link InternalScene}, if the scene exists, if it is allowed to
* use the scene
* and if the scene is not one of the following scenes:
* <ul>
* <li>{@link SceneEnum#INCREMENT}</li>
* <li>{@link SceneEnum#DECREMENT}</li>
* <li>{@link SceneEnum#STOP}</li>
* <li>{@link SceneEnum#MINIMUM}</li>
* <li>{@link SceneEnum#MAXIMUM}</li>
* <li>{@link SceneEnum#AUTO_OFF}</li>
* <li>{@link SceneEnum#DEVICE_ON}</li>
* <li>{@link SceneEnum#DEVICE_OFF}</li>
* <li>{@link SceneEnum#DEVICE_STOP}</li>
* <li>{@link SceneEnum#AREA_1_INCREMENT}</li>
* <li>{@link SceneEnum#AREA_1_DECREMENT}</li>
* <li>{@link SceneEnum#AREA_1_STOP}</li>
* <li>{@link SceneEnum#AREA_2_INCREMENT}</li>
* <li>{@link SceneEnum#AREA_2_DECREMENT}</li>
* <li>{@link SceneEnum#AREA_2_STOP}</li>
* <li>{@link SceneEnum#AREA_3_INCREMENT}</li>
* <li>{@link SceneEnum#AREA_3_DECREMENT}</li>
* <li>{@link SceneEnum#AREA_3_STOP}</li>
* <li>{@link SceneEnum#AREA_4_INCREMENT}</li>
* <li>{@link SceneEnum#AREA_4_DECREMENT}</li>
* <li>{@link SceneEnum#AREA_4_STOP}</li>
* <li>{@link SceneEnum#AREA_STEPPING_CONTINUE}</li>
* <li>{@link SceneEnum#ENERGY_OVERLOAD}</li>
* <li>{@link SceneEnum#ALARM_SIGNAL}</li>
* <li>{@link SceneEnum#AUTO_STANDBY}</li>
* <li>{@link SceneEnum#ZONE_ACTIVE}</li>
* </ul>
*
* @param scene (must not be null)
*/
public void onSceneAdded(InternalScene scene) {
if (super.isBackgroundDiscoveryEnabled()) {
onSceneAddedInternal(scene);
}
}
/**
* Removes the {@link DiscoveryResult} of the given {@link InternalScene}.
*
* @param scene (must not be null)
*/
public void onSceneRemoved(InternalScene scene) {
ThingUID thingUID = getThingUID(scene);
if (thingUID != null) {
thingRemoved(thingUID);
}
}
}

View File

@@ -0,0 +1,136 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.discovery;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.BINDING_ID;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.handler.BridgeHandler;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
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.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ZoneTemperatureControlDiscoveryService} discovers all digitalSTROM zones which have temperature control
* configured. The thing-type has to be given to the
* {@link #ZoneTemperatureControlDiscoveryService(BridgeHandler, ThingTypeUID)} as {@link ThingTypeUID}. The supported
* {@link ThingTypeUID} can be found at {@link ZoneTemperatureControlHandler#SUPPORTED_THING_TYPES}
*
* @author Michael Ochel
* @author Matthias Siegele
*/
public class ZoneTemperatureControlDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(ZoneTemperatureControlDiscoveryService.class);
BridgeHandler bridgeHandler;
private final ThingUID bridgeUID;
private final String thingTypeID;
public static final int TIMEOUT = 10;
/**
* Creates a new {@link ZoneTemperatureControlDiscoveryService}.
*
* @param bridgeHandler must not be null
* @param supportedThingType must not be null
* @throws IllegalArgumentException see {@link AbstractDiscoveryService#AbstractDiscoveryService(int)}
*/
public ZoneTemperatureControlDiscoveryService(BridgeHandler bridgeHandler, ThingTypeUID supportedThingType)
throws IllegalArgumentException {
super(new HashSet<>(Arrays.asList(supportedThingType)), TIMEOUT, true);
bridgeUID = bridgeHandler.getThing().getUID();
this.bridgeHandler = bridgeHandler;
thingTypeID = supportedThingType.getId();
}
@Override
protected void startScan() {
for (TemperatureControlStatus tempConStat : bridgeHandler.getTemperatureControlStatusFromAllZones()) {
internalConfigChanged(tempConStat);
}
}
@Override
public void deactivate() {
logger.debug("Deactivate discovery service for zone teperature control type remove thing types {}",
super.getSupportedThingTypes());
removeOlderResults(new Date().getTime());
}
/**
* Method for the background discovery
*
* @see org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener#configChanged(TemperatureControlStatus)
* @param tempControlStatus can be null
*/
public void configChanged(TemperatureControlStatus tempControlStatus) {
if (isBackgroundDiscoveryEnabled()) {
internalConfigChanged(tempControlStatus);
}
}
private void internalConfigChanged(TemperatureControlStatus tempControlStatus) {
if (tempControlStatus == null) {
return;
}
if (tempControlStatus.isNotSetOff()) {
logger.debug("found configured zone TemperatureControlStatus = {}", tempControlStatus);
ThingUID thingUID = getThingUID(tempControlStatus);
if (thingUID != null) {
Map<String, Object> properties = new HashMap<>();
properties.put(DigitalSTROMBindingConstants.ZONE_ID, tempControlStatus.getZoneID());
String zoneName = tempControlStatus.getZoneName();
if (StringUtils.isBlank(zoneName)) {
zoneName = tempControlStatus.getZoneID().toString();
}
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
.withBridge(bridgeUID).withLabel(zoneName).build();
thingDiscovered(discoveryResult);
}
}
}
private ThingUID getThingUID(TemperatureControlStatus tempControlStatus) {
ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, thingTypeID);
if (getSupportedThingTypes().contains(thingTypeUID)) {
String thingID = tempControlStatus.getZoneID().toString();
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, thingID);
return thingUID;
} else {
return null;
}
}
/**
* Returns the ID of this {@link ZoneTemperatureControlDiscoveryService}.
*
* @return id of the service
*/
public String getID() {
return thingTypeID;
}
}

View File

@@ -0,0 +1,852 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.handler;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.event.EventListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.TotalPowerConsumptionListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.DeviceStatusManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.SceneManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.ConnectionManagerImpl;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.DeviceStatusManagerImpl;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.SceneManagerImpl;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.StructureManagerImpl;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.TemperatureControlManager;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
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.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BridgeHandler} is the handler for a digitalSTROM-Server and connects it to
* the framework.<br>
* All {@link DeviceHandler}s and {@link SceneHandler}s use the {@link BridgeHandler} to execute the actual
* commands.<br>
* <br>
* The {@link BridgeHandler} also:
* <ul>
* <li>manages the {@link DeviceStatusManager} (starts, stops, register {@link DeviceStatusListener},
* register {@link SceneStatusListener} and so on)</li>
* <li>creates and load the configurations in the {@link Config}.</li>
* <li>implements {@link ManagerStatusListener} to manage the expiration of the Thing initializations</li>
* <li>implements the {@link ConnectionListener} to manage the {@link ThingStatus} of this {@link BridgeHandler}</li>
* <li>and implements the {@link TotalPowerConsumptionListener} to update his Channels.</li>
* </ul>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class BridgeHandler extends BaseBridgeHandler
implements ConnectionListener, TotalPowerConsumptionListener, ManagerStatusListener {
private final Logger logger = LoggerFactory.getLogger(BridgeHandler.class);
/**
* Contains all supported thing types of this handler
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DSS_BRIDGE);
private static final long RECONNECT_TRACKER_INTERVAL = 15;
/* DS-Manager */
private ConnectionManager connMan;
private StructureManager structMan;
private SceneManager sceneMan;
private DeviceStatusManager devStatMan;
private TemperatureControlManager tempContMan;
private EventListener eventListener;
private ScheduledFuture<?> reconnectTracker;
private DeviceStatusListener deviceDiscovery;
private SceneStatusListener sceneDiscovery;
private TemperatureControlStatusListener temperatureControlDiscovery;
private Config config;
List<SceneStatusListener> unregisterSceneStatusListeners;
private short connectionTimeoutCounter = 0;
private final short ignoredTimeouts = 5;
private class Initializer implements Runnable {
BridgeHandler bridge;
Config config;
public Initializer(BridgeHandler bridge, Config config) {
this.bridge = bridge;
this.config = config;
}
@Override
public void run() {
logger.debug("Checking connection");
if (connMan == null) {
connMan = new ConnectionManagerImpl(config, bridge, true);
} else {
connMan.registerConnectionListener(bridge);
connMan.configHasBeenUpdated();
}
logger.debug("Initializing digitalSTROM Manager ");
if (eventListener == null) {
eventListener = new EventListener(connMan);
}
if (structMan == null) {
structMan = new StructureManagerImpl();
}
if (sceneMan == null) {
sceneMan = new SceneManagerImpl(connMan, structMan, bridge, eventListener);
}
if (devStatMan == null) {
devStatMan = new DeviceStatusManagerImpl(connMan, structMan, sceneMan, bridge, eventListener);
} else {
devStatMan.registerStatusListener(bridge);
}
devStatMan.registerTotalPowerConsumptionListener(bridge);
if (connMan.checkConnection()) {
logger.debug("connection established, start services");
if (TemperatureControlManager.isHeatingControllerInstallated(connMan)) {
if (tempContMan == null) {
tempContMan = new TemperatureControlManager(connMan, eventListener,
temperatureControlDiscovery);
temperatureControlDiscovery = null;
} else {
if (temperatureControlDiscovery != null) {
tempContMan.registerTemperatureControlStatusListener(temperatureControlDiscovery);
}
}
}
structMan.generateZoneGroupNames(connMan);
devStatMan.start();
eventListener.start();
}
boolean configChanged = false;
Configuration configuration = bridge.getConfig();
if (connMan.getApplicationToken() != null) {
configuration.remove(USER_NAME);
configuration.remove(PASSWORD);
logger.debug("Application-Token is: {}", connMan.getApplicationToken());
configuration.put(APPLICATION_TOKEN, connMan.getApplicationToken());
configChanged = true;
}
Map<String, String> properties = editProperties();
String dSSname = connMan.getDigitalSTROMAPI().getInstallationName(connMan.getSessionToken());
if (dSSname != null) {
properties.put(DS_NAME, dSSname);
}
Map<String, String> dsidMap = connMan.getDigitalSTROMAPI().getDSID(connMan.getSessionToken());
if (dsidMap != null) {
logger.debug("{}", dsidMap);
properties.putAll(dsidMap);
}
Map<String, String> versions = connMan.getDigitalSTROMAPI().getSystemVersion();
if (versions != null) {
properties.putAll(versions);
}
if (StringUtils.isBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT))
&& StringUtils.isNotBlank(config.getCert())) {
properties.put(DigitalSTROMBindingConstants.SERVER_CERT, config.getCert());
}
logger.debug("update properties");
updateProperties(properties);
if (configChanged) {
updateConfiguration(configuration);
}
}
}
/**
* Creates a new {@link BridgeHandler}.
*
* @param bridge must not be null
*/
public BridgeHandler(Bridge bridge) {
super(bridge);
}
@Override
public void initialize() {
logger.debug("Initializing digitalSTROM-BridgeHandler");
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking configuration...");
// Start an extra thread to readout the configuration and check the connection, because it takes sometimes more
// than 5000 milliseconds and the handler will suspend (ThingStatus.UNINITIALIZED).
Config config = loadAndCheckConfig();
if (config != null) {
logger.debug("{}", config.toString());
scheduler.execute(new Initializer(this, config));
}
}
private boolean checkLoginConfig(Config config) {
if ((StringUtils.isNotBlank(config.getUserName()) && StringUtils.isNotBlank(config.getPassword()))
|| StringUtils.isNotBlank(config.getAppToken())) {
return true;
}
onConnectionStateChange(CONNECTION_LOST, NO_USER_PASSWORD);
return false;
}
private Config loadAndCheckConfig() {
Configuration thingConfig = super.getConfig();
Config config = loadAndCheckConnectionData(thingConfig);
if (config == null) {
return null;
}
logger.debug("Loading configuration");
List<String> numberExc = new ArrayList<>();
// Parameters can't be null, because of an existing default value.
if (thingConfig.get(DigitalSTROMBindingConstants.SENSOR_DATA_UPDATE_INTERVAL) instanceof BigDecimal) {
config.setSensordataRefreshInterval(
((BigDecimal) thingConfig.get(DigitalSTROMBindingConstants.SENSOR_DATA_UPDATE_INTERVAL)).intValue()
* 1000);
} else {
numberExc.add("\"Sensor update interval\" ( "
+ thingConfig.get(DigitalSTROMBindingConstants.SENSOR_DATA_UPDATE_INTERVAL) + ")");
}
if (thingConfig.get(DigitalSTROMBindingConstants.TOTAL_POWER_UPDATE_INTERVAL) instanceof BigDecimal) {
config.setTotalPowerUpdateInterval(
((BigDecimal) thingConfig.get(DigitalSTROMBindingConstants.TOTAL_POWER_UPDATE_INTERVAL)).intValue()
* 1000);
} else {
numberExc.add("\"Total power update interval\" ("
+ thingConfig.get(DigitalSTROMBindingConstants.TOTAL_POWER_UPDATE_INTERVAL) + ")");
}
if (thingConfig.get(DigitalSTROMBindingConstants.SENSOR_WAIT_TIME) instanceof BigDecimal) {
config.setSensorReadingWaitTime(
((BigDecimal) thingConfig.get(DigitalSTROMBindingConstants.SENSOR_WAIT_TIME)).intValue() * 1000);
} else {
numberExc.add("\"Wait time sensor reading\" ("
+ thingConfig.get(DigitalSTROMBindingConstants.SENSOR_WAIT_TIME) + ")");
}
if (thingConfig.get(DigitalSTROMBindingConstants.DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY) instanceof BigDecimal) {
config.setTrashDeviceDeleteTime(
((BigDecimal) thingConfig.get(DigitalSTROMBindingConstants.DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY))
.intValue());
} else {
numberExc.add("\"Days to be slaked trash bin devices\" ("
+ thingConfig.get(DigitalSTROMBindingConstants.DEFAULT_TRASH_DEVICE_DELETE_TIME_KEY) + ")");
}
if (!numberExc.isEmpty()) {
String excText = "The field ";
for (int i = 0; i < numberExc.size(); i++) {
excText = excText + numberExc.get(i);
if (i < numberExc.size() - 2) {
excText = excText + ", ";
} else if (i < numberExc.size() - 1) {
excText = excText + " and ";
}
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, excText + " have to be a number.");
return null;
}
if (StringUtils.isNotBlank(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT))) {
config.setCert(getThing().getProperties().get(DigitalSTROMBindingConstants.SERVER_CERT));
}
return config;
}
private Config loadAndCheckConnectionData(Configuration thingConfig) {
if (this.config == null) {
this.config = new Config();
}
// load and check connection and authorization data
if (StringUtils.isNotBlank((String) thingConfig.get(HOST))) {
config.setHost(thingConfig.get(HOST).toString());
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"The connection to the digitalSTROM-Server can't established, because the host address is missing. Please set the host address.");
return null;
}
if (thingConfig.get(USER_NAME) != null) {
config.setUserName(thingConfig.get(USER_NAME).toString());
} else {
config.setUserName(null);
}
if (thingConfig.get(PASSWORD) != null) {
config.setPassword(thingConfig.get(PASSWORD).toString());
} else {
config.setPassword(null);
}
if (thingConfig.get(APPLICATION_TOKEN) != null) {
config.setAppToken(thingConfig.get(APPLICATION_TOKEN).toString());
} else {
config.setAppToken(null);
}
if (!checkLoginConfig(config)) {
return null;
}
return config;
}
@Override
public void dispose() {
logger.debug("Handler disposed");
if (reconnectTracker != null && !reconnectTracker.isCancelled()) {
reconnectTracker.cancel(true);
}
if (eventListener != null) {
eventListener.stop();
}
if (devStatMan != null) {
devStatMan.unregisterTotalPowerConsumptionListener();
devStatMan.unregisterStatusListener();
this.devStatMan.stop();
}
if (connMan != null) {
connMan.unregisterConnectionListener();
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
channelLinked(channelUID);
} else {
logger.debug("Command {} is not supported for channel: {}", command, channelUID.getId());
}
}
@Override
public void handleRemoval() {
for (Thing thing : getThing().getThings()) {
// Inform Thing-Child's about removed bridge.
final ThingHandler thingHandler = thing.getHandler();
if (thingHandler != null) {
thingHandler.bridgeStatusChanged(ThingStatusInfoBuilder.create(ThingStatus.REMOVED).build());
}
}
if (StringUtils.isNotBlank((String) super.getConfig().get(APPLICATION_TOKEN))) {
if (connMan == null) {
Config config = loadAndCheckConnectionData(this.getConfig());
if (config != null) {
this.connMan = new ConnectionManagerImpl(config, null, false);
} else {
updateStatus(ThingStatus.REMOVED);
return;
}
}
if (connMan.removeApplicationToken()) {
logger.debug("Application-Token deleted");
}
}
updateStatus(ThingStatus.REMOVED);
}
/* methods to store listener */
/**
* Registers a new {@link DeviceStatusListener} on the {@link DeviceStatusManager}.
*
* @param deviceStatusListener (must not be null)
*/
public synchronized void registerDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
if (this.devStatMan != null) {
if (deviceStatusListener == null) {
throw new IllegalArgumentException("It's not allowed to pass null.");
}
if (deviceStatusListener.getDeviceStatusListenerID() != null) {
if (devStatMan.getManagerState().equals(ManagerStates.RUNNING)) {
devStatMan.registerDeviceListener(deviceStatusListener);
} else if (deviceStatusListener.getDeviceStatusListenerID()
.equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
devStatMan.registerDeviceListener(deviceStatusListener);
}
} else {
throw new IllegalArgumentException("It's not allowed to pass a DeviceStatusListener with ID = null.");
}
} else {
if (deviceStatusListener.getDeviceStatusListenerID().equals(DeviceStatusListener.DEVICE_DISCOVERY)) {
deviceDiscovery = deviceStatusListener;
}
}
}
/**
* Unregisters a new {@link DeviceStatusListener} on the {@link BridgeHandler}.
*
* @param deviceStatusListener (must not be null)
*/
public void unregisterDeviceStatusListener(DeviceStatusListener deviceStatusListener) {
if (this.devStatMan != null) {
if (deviceStatusListener.getDeviceStatusListenerID() != null) {
this.devStatMan.unregisterDeviceListener(deviceStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a DeviceStatusListener with ID = null.");
}
}
}
/**
* Registers a new {@link SceneStatusListener} on the {@link BridgeHandler}.
*
* @param sceneStatusListener (must not be null)
*/
public synchronized void registerSceneStatusListener(SceneStatusListener sceneStatusListener) {
if (this.sceneMan != null) {
if (sceneStatusListener == null) {
throw new IllegalArgumentException("It's not allowed to pass null.");
}
if (sceneStatusListener.getSceneStatusListenerID() != null) {
this.sceneMan.registerSceneListener(sceneStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a SceneStatusListener with ID = null.");
}
} else {
if (sceneStatusListener.getSceneStatusListenerID().equals(SceneStatusListener.SCENE_DISCOVERY)) {
sceneDiscovery = sceneStatusListener;
}
}
}
/**
* Unregisters a new {@link SceneStatusListener} on the {@link DeviceStatusManager}.
*
* @param sceneStatusListener (must not be null)
*/
public void unregisterSceneStatusListener(SceneStatusListener sceneStatusListener) {
if (this.sceneMan != null) {
if (sceneStatusListener.getSceneStatusListenerID() != null) {
this.sceneMan.unregisterSceneListener(sceneStatusListener);
} else {
throw new IllegalArgumentException("It's not allowed to pass a SceneStatusListener with ID = null..");
}
}
}
/**
* Has to be called from a removed Thing-Child to rediscovers the Thing.
*
* @param id = scene or device id (must not be null)
*/
public void childThingRemoved(String id) {
if (id != null && id.split("-").length == 3) {
InternalScene scene = sceneMan.getInternalScene(id);
if (scene != null) {
sceneMan.removeInternalScene(id);
sceneMan.addInternalScene(scene);
}
} else {
devStatMan.removeDevice(id);
}
}
/**
* Delegate a stop command from a Thing to the {@link DeviceStatusManager#sendStopComandsToDSS(Device)}.
*
* @param device can be null
*/
public void stopOutputValue(Device device) {
this.devStatMan.sendStopComandsToDSS(device);
}
@Override
public void channelLinked(ChannelUID channelUID) {
if (devStatMan != null) {
MeteringTypeEnum meteringType = DsChannelTypeProvider.getMeteringType(channelUID.getId());
if (meteringType != null) {
if (meteringType.equals(MeteringTypeEnum.ENERGY)) {
onEnergyMeterValueChanged(devStatMan.getTotalEnergyMeterValue());
} else {
onTotalPowerConsumptionChanged(devStatMan.getTotalPowerConsumption());
}
} else {
logger.warn("Channel with id {} is not known for the thing with id {}.", channelUID.getId(),
getThing().getUID());
}
}
}
@Override
public void onTotalPowerConsumptionChanged(int newPowerConsumption) {
updateChannelState(MeteringTypeEnum.CONSUMPTION, MeteringUnitsEnum.WH, newPowerConsumption);
}
@Override
public void onEnergyMeterValueChanged(int newEnergyMeterValue) {
updateChannelState(MeteringTypeEnum.ENERGY, MeteringUnitsEnum.WH, newEnergyMeterValue * 0.001);
}
@Override
public void onEnergyMeterWsValueChanged(int newEnergyMeterValue) {
// not needed
}
private void updateChannelState(MeteringTypeEnum meteringType, MeteringUnitsEnum meteringUnit, double value) {
String channelID = DsChannelTypeProvider.getMeteringChannelID(meteringType, meteringUnit, true);
if (getThing().getChannel(channelID) != null) {
updateState(channelID, new DecimalType(value));
}
}
@Override
public void onConnectionStateChange(String newConnectionState) {
switch (newConnectionState) {
case CONNECTION_LOST:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"The connection to the digitalSTROM-Server cannot be established.");
startReconnectTracker();
return;
case CONNECTION_RESUMED:
if (connectionTimeoutCounter > 0) {
// reset connection timeout counter
connectionTimeoutCounter = 0;
if (connMan.checkConnection()) {
restartServices();
setStatus(ThingStatus.ONLINE);
}
}
return;
case APPLICATION_TOKEN_GENERATED:
if (connMan != null) {
Configuration config = this.getConfig();
config.remove(USER_NAME);
config.remove(PASSWORD);
config.put(APPLICATION_TOKEN, connMan.getApplicationToken());
this.updateConfiguration(config);
}
return;
default:
return;
}
}
private void setStatus(ThingStatus status) {
logger.debug("set status to: {}", status);
updateStatus(status);
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler != null) {
handler.bridgeStatusChanged(getThing().getStatusInfo());
}
}
}
private void startReconnectTracker() {
if (reconnectTracker == null || reconnectTracker.isCancelled()) {
logger.debug("Connection lost, stop all services and start reconnectTracker.");
stopServices();
reconnectTracker = scheduler.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
if (connMan != null) {
boolean conStat = connMan.checkConnection();
logger.debug("check connection = {}", conStat);
if (conStat) {
restartServices();
reconnectTracker.cancel(false);
}
}
}
}, RECONNECT_TRACKER_INTERVAL, RECONNECT_TRACKER_INTERVAL, TimeUnit.SECONDS);
}
}
private void stopServices() {
if (devStatMan != null && !devStatMan.getManagerState().equals(ManagerStates.STOPPED)) {
devStatMan.stop();
}
if (eventListener != null && eventListener.isStarted()) {
eventListener.stop();
}
}
private void restartServices() {
logger.debug("reconnect, stop reconnection tracker and restart services");
if (reconnectTracker != null && !reconnectTracker.isCancelled()) {
reconnectTracker.cancel(true);
}
stopServices();
if (devStatMan != null) {
devStatMan.start();
}
if (eventListener != null) {
eventListener.start();
}
}
@Override
public void onConnectionStateChange(String newConnectionState, String reason) {
if (newConnectionState.equals(NOT_AUTHENTICATED) || newConnectionState.equals(CONNECTION_LOST)) {
switch (reason) {
case WRONG_APP_TOKEN:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"User defined Application-Token is wrong. "
+ "Please set user name and password to generate an Application-Token or set an valid Application-Token.");
stopServices();
return;
case WRONG_USER_OR_PASSWORD:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"The set username or password is wrong.");
stopServices();
return;
case NO_USER_PASSWORD:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No username or password is set to generate Application-Token. Please set user name and password or Application-Token.");
stopServices();
return;
case CONNECTON_TIMEOUT:
// ignore the first connection timeout
if (connectionTimeoutCounter++ > ignoredTimeouts) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Connection lost because connection timeout to Server.");
break;
} else {
return;
}
case HOST_NOT_FOUND:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Server not found! Please check these points:\n" + " - Is digitalSTROM-Server turned on?\n"
+ " - Is the host address correct?\n"
+ " - Is the ethernet cable connection established?");
break;
case UNKNOWN_HOST:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown host name, please check the set host name!");
break;
case INVALID_URL:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid URL is set.");
break;
case CONNECTION_LOST:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"IOException / Connection lost.");
break;
case SSL_HANDSHAKE_ERROR:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"SSL Handshake error / Connection lost.");
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, reason);
}
// reset connection timeout counter
connectionTimeoutCounter = 0;
startReconnectTracker();
}
}
/**
* Returns a list of all {@link Device}'s.
*
* @return device list (cannot be null)
*/
public List<Device> getDevices() {
return this.structMan != null && this.structMan.getDeviceMap() != null
? new LinkedList<>(this.structMan.getDeviceMap().values())
: null;
}
/**
* Returns the {@link StructureManager}.
*
* @return StructureManager
*/
public StructureManager getStructureManager() {
return this.structMan;
}
/**
* Delegates a scene command of a Thing to the
* {@link DeviceStatusManager#sendSceneComandsToDSS(InternalScene, boolean)}
*
* @param scene the called scene
* @param call_undo (true = call scene | false = undo scene)
*/
public void sendSceneComandToDSS(InternalScene scene, boolean call_undo) {
if (devStatMan != null) {
devStatMan.sendSceneComandsToDSS(scene, call_undo);
}
}
/**
* Delegates a device command of a Thing to the
* {@link DeviceStatusManager#sendComandsToDSS(Device, DeviceStateUpdate)}
*
* @param device can be null
* @param deviceStateUpdate can be null
*/
public void sendComandsToDSS(Device device, DeviceStateUpdate deviceStateUpdate) {
if (devStatMan != null) {
devStatMan.sendComandsToDSS(device, deviceStateUpdate);
}
}
/**
* Returns a list of all {@link InternalScene}'s.
*
* @return Scene list (cannot be null)
*/
public List<InternalScene> getScenes() {
return sceneMan != null ? sceneMan.getScenes() : new LinkedList<>();
}
/**
* Returns the {@link ConnectionManager}.
*
* @return ConnectionManager
*/
public ConnectionManager getConnectionManager() {
return this.connMan;
}
@Override
public void onStatusChanged(ManagerTypes managerType, ManagerStates state) {
if (managerType.equals(ManagerTypes.DEVICE_STATUS_MANAGER)) {
switch (state) {
case INITIALIZING:
if (deviceDiscovery != null) {
devStatMan.registerDeviceListener(deviceDiscovery);
deviceDiscovery = null;
}
logger.debug("Building digitalSTROM model");
break;
case RUNNING:
updateStatus(ThingStatus.ONLINE);
break;
case STOPPED:
if (!getThing().getStatusInfo().getStatusDetail().equals(ThingStatusDetail.COMMUNICATION_ERROR)
&& !getThing().getStatusInfo().getStatusDetail()
.equals(ThingStatusDetail.CONFIGURATION_ERROR)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "DeviceStatusManager is stopped.");
devStatMan.start();
}
break;
default:
break;
}
}
if (managerType.equals(ManagerTypes.SCENE_MANAGER)) {
switch (state) {
case GENERATING_SCENES:
logger.debug("SceneManager reports that he is generating scenes");
if (sceneDiscovery != null) {
sceneMan.registerSceneListener(sceneDiscovery);
sceneDiscovery = null;
}
break;
case RUNNING:
logger.debug("SceneManager reports that he is running");
break;
default:
break;
}
}
}
/**
* Returns a {@link List} of all {@link Circuit}'s.
*
* @return circuit list
*/
public List<Circuit> getCircuits() {
logger.debug("circuits: {}", structMan.getCircuitMap().values().toString());
return structMan != null && structMan.getCircuitMap() != null
? new LinkedList<>(structMan.getCircuitMap().values())
: null;
}
/**
* Returns the {@link TemperatureControlManager} or null if no one exist.
*
* @return {@link TemperatureControlManager}
*/
public TemperatureControlManager getTemperatureControlManager() {
return tempContMan;
}
/**
* Registers the given {@link TemperatureControlStatusListener} to the {@link TemperatureControlManager}.
*
* @param temperatureControlStatusListener can be null
*/
public void registerTemperatureControlStatusListener(
TemperatureControlStatusListener temperatureControlStatusListener) {
if (tempContMan != null) {
tempContMan.registerTemperatureControlStatusListener(temperatureControlStatusListener);
} else if (TemperatureControlStatusListener.DISCOVERY
.equals(temperatureControlStatusListener.getTemperationControlStatusListenrID())) {
this.temperatureControlDiscovery = temperatureControlStatusListener;
}
}
/**
* Unregisters the given {@link TemperatureControlStatusListener} from the {@link TemperatureControlManager}.
*
* @param temperatureControlStatusListener can be null
*/
public void unregisterTemperatureControlStatusListener(
TemperatureControlStatusListener temperatureControlStatusListener) {
if (tempContMan != null) {
tempContMan.unregisterTemperatureControlStatusListener(temperatureControlStatusListener);
}
}
/**
* see {@link TemperatureControlManager#getTemperatureControlStatusFromAllZones()}
*
* @return all temperature control status objects
*/
public Collection<TemperatureControlStatus> getTemperatureControlStatusFromAllZones() {
return tempContMan != null ? tempContMan.getTemperatureControlStatusFromAllZones() : new LinkedList<>();
}
}

View File

@@ -0,0 +1,311 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.handler;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.CachedMeteringValue;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
import org.openhab.core.library.types.DecimalType;
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.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CircuitHandler} is responsible for handling the configuration and updating the metering channels of a
* digitalStrom circuit. <br>
* <br>
* For that it uses the {@link BridgeHandler} to register this class as a {@link DeviceStatusListener} to get informed
* about changes from the accompanying {@link Circuit}.
*
* @author Michael Ochel
* @author Matthias Siegele
*/
public class CircuitHandler extends BaseThingHandler implements DeviceStatusListener {
private final Logger logger = LoggerFactory.getLogger(CircuitHandler.class);
/**
* Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
private String dSID;
private Circuit circuit;
private BridgeHandler dssBridgeHandler;
/**
* Creates a new {@link CircuitHandler}.
*
* @param thing must not be null
*/
public CircuitHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Initializing CircuitHandler.");
if (StringUtils.isNotBlank((String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID))) {
dSID = getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID).toString();
final Bridge bridge = getBridge();
if (bridge != null) {
bridgeStatusChanged(bridge.getStatusInfo());
} else {
// Set status to OFFLINE if no bridge is available e.g. because the bridge has been removed and the
// Thing was reinitialized.
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
}
}
@Override
public void dispose() {
logger.debug("Handler disposed... unregister DeviceStatusListener");
if (dSID != null) {
if (dssBridgeHandler != null) {
dssBridgeHandler.unregisterDeviceStatusListener(this);
}
}
circuit = null;
}
private synchronized BridgeHandler getDssBridgeHandler() {
if (this.dssBridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Bride cannot be found");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof BridgeHandler) {
dssBridgeHandler = (BridgeHandler) handler;
} else {
return null;
}
}
return dssBridgeHandler;
}
@Override
public void thingUpdated(Thing thing) {
this.thing = thing;
if (circuit == null) {
initialize();
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
if (dSID != null) {
if (getDssBridgeHandler() != null) {
if (circuit == null) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"waiting for listener registration");
dssBridgeHandler.registerDeviceStatusListener(this);
} else {
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
}
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
}
logger.debug("Set status to {}", getThing().getStatusInfo());
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// the same handling like total metering values
if (dssBridgeHandler != null) {
dssBridgeHandler.handleCommand(channelUID, command);
}
}
@Override
public void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
if (deviceStateUpdate != null && DeviceStateUpdate.UPDATE_CIRCUIT_METER.equals(deviceStateUpdate.getType())) {
if (deviceStateUpdate.getValue() instanceof CachedMeteringValue) {
CachedMeteringValue cachedVal = (CachedMeteringValue) deviceStateUpdate.getValue();
if (MeteringUnitsEnum.WH.equals(cachedVal.getMeteringUnit())) {
if (cachedVal.getMeteringType().equals(MeteringTypeEnum.ENERGY)) {
updateState(getChannelID(cachedVal), new DecimalType(cachedVal.getValue() * 0.001));
} else {
updateState(getChannelID(cachedVal), new DecimalType(cachedVal.getValue()));
}
}
}
}
}
@Override
public void onDeviceRemoved(GeneralDeviceInformation device) {
if (device instanceof Circuit) {
this.circuit = (Circuit) device;
if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
if (!((Device) circuit).isPresent()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"Circuit is not present in the digitalSTROM-System.");
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"Circuit is not avaible in the digitalSTROM-System.");
}
}
logger.debug("Set status to {}", getThing().getStatus());
}
}
@Override
public void onDeviceAdded(GeneralDeviceInformation device) {
if (device instanceof Circuit) {
this.circuit = (Circuit) device;
if (this.circuit.isPresent()) {
ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
logger.debug("Set status to {}", getThing().getStatus());
checkCircuitInfoProperties(this.circuit);
// load first channel values
onCircuitStateInitial(this.circuit);
return;
}
}
onDeviceRemoved(device);
}
private void checkCircuitInfoProperties(Circuit device) {
boolean propertiesChanged = false;
Map<String, String> properties = editProperties();
// check device info
if (device.getName() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
propertiesChanged = true;
}
if (device.getDSUID() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
propertiesChanged = true;
}
if (device.getHwName() != null) {
properties.put(DigitalSTROMBindingConstants.HW_NAME, device.getHwName());
propertiesChanged = true;
}
if (device.getHwVersionString() != null) {
properties.put(DigitalSTROMBindingConstants.HW_VERSION, device.getHwVersionString());
propertiesChanged = true;
}
if (device.getSwVersion() != null) {
properties.put(DigitalSTROMBindingConstants.SW_VERSION, device.getSwVersion());
propertiesChanged = true;
}
if (device.getApiVersion() != null) {
properties.put(DigitalSTROMBindingConstants.API_VERSION, device.getApiVersion().toString());
propertiesChanged = true;
}
if (device.getDspSwVersion() != null) {
properties.put(DigitalSTROMBindingConstants.DSP_SW_VERSION, device.getDspSwVersion().toString());
propertiesChanged = true;
}
if (device.getArmSwVersion() != null) {
properties.put(DigitalSTROMBindingConstants.ARM_SW_VERSION, device.getArmSwVersion().toString());
propertiesChanged = true;
}
if (propertiesChanged) {
super.updateProperties(properties);
propertiesChanged = false;
}
}
private void onCircuitStateInitial(Circuit circuit) {
if (circuit != null) {
for (CachedMeteringValue cachedMeterValue : circuit.getAllCachedMeteringValues()) {
if (cachedMeterValue != null && MeteringUnitsEnum.WH.equals(cachedMeterValue.getMeteringUnit())) {
String channelID = getChannelID(cachedMeterValue);
if (isLinked(channelID)) {
channelLinked(new ChannelUID(getThing().getUID(), channelID));
}
}
}
}
}
private String getChannelID(CachedMeteringValue cachedMeterValue) {
return DsChannelTypeProvider.getMeteringChannelID(cachedMeterValue.getMeteringType(),
cachedMeterValue.getMeteringUnit(), false);
}
@Override
public void channelLinked(ChannelUID channelUID) {
if (circuit != null) {
MeteringTypeEnum meteringType = DsChannelTypeProvider.getMeteringType(channelUID.getId());
double val = circuit.getMeteringValue(meteringType, MeteringUnitsEnum.WH);
if (val > -1) {
if (meteringType.equals(MeteringTypeEnum.ENERGY)) {
updateState(channelUID, new DecimalType(val * 0.001));
} else {
updateState(channelUID, new DecimalType(val));
}
}
}
}
@Override
public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whatConfig) {
// nothing to do, will be registered again
}
@Override
public void onSceneConfigAdded(short sceneID) {
// nothing to do
}
@Override
public String getDeviceStatusListenerID() {
return this.dSID;
}
}

View File

@@ -0,0 +1,977 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.handler;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.GeneralLibConstance;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceHandler} is responsible for handling the configuration, load supported channels of a
* digitalSTROM device and handling commands, which are sent to one of the channels. <br>
* <br>
* For that it uses the {@link BridgeHandler} and the {@link DeviceStateUpdate} mechanism of the {@link Device} to
* execute the actual command and implements the {@link DeviceStatusListener} to get informed about changes from the
* accompanying {@link Device}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class DeviceHandler extends BaseThingHandler implements DeviceStatusListener {
private final Logger logger = LoggerFactory.getLogger(DeviceHandler.class);
/**
* Contains all supported thing types of this handler, will be filled by DsDeviceThingTypeProvider.
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>();
public static final String TWO_STAGE_SWITCH_IDENTICATOR = "2";
public static final String THREE_STAGE_SWITCH_IDENTICATOR = "3";
private String dSID;
private Device device;
private BridgeHandler dssBridgeHandler;
private Command lastComand;
private String currentChannel;
private List<String> loadedSensorChannels;
/**
* Creates a new {@link DeviceHandler}.
*
* @param thing must not be null
*/
public DeviceHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Initializing DeviceHandler.");
if (StringUtils.isNotBlank((String) getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID))) {
dSID = getConfig().get(DigitalSTROMBindingConstants.DEVICE_DSID).toString();
final Bridge bridge = getBridge();
if (bridge != null) {
bridgeStatusChanged(bridge.getStatusInfo());
} else {
// Set status to OFFLINE if no bridge is available e.g. because the bridge has been removed and the
// Thing was reinitialized.
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "dSID is missing");
}
}
@Override
public void dispose() {
logger.debug("Handler disposed... unregister DeviceStatusListener");
if (dSID != null) {
if (dssBridgeHandler != null) {
dssBridgeHandler.unregisterDeviceStatusListener(this);
}
}
if (device != null) {
device.setSensorDataRefreshPriority(Config.REFRESH_PRIORITY_NEVER, Config.REFRESH_PRIORITY_NEVER,
Config.REFRESH_PRIORITY_NEVER);
}
device = null;
}
@Override
public void handleRemoval() {
if (getDssBridgeHandler() != null) {
this.dssBridgeHandler.childThingRemoved(dSID);
}
updateStatus(ThingStatus.REMOVED);
}
@Override
public void thingUpdated(Thing thing) {
this.thing = thing;
if (device == null) {
initialize();
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
if (dSID != null) {
if (getDssBridgeHandler() != null) {
if (device == null) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"waiting for listener registration");
dssBridgeHandler.registerDeviceStatusListener(this);
} else {
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No dSID is set!");
}
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
}
logger.debug("Set status to {}", getThing().getStatusInfo());
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
BridgeHandler dssBridgeHandler = getDssBridgeHandler();
if (dssBridgeHandler == null) {
logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
return;
}
if (device == null) {
logger.debug(
"Device not known on StructureManager or DeviceStatusListener is not registerd. Cannot handle command.");
return;
}
if (command instanceof RefreshType) {
try {
SensorEnum sensorType = SensorEnum.valueOf(channelUID.getId());
dssBridgeHandler.sendComandsToDSS(device, new DeviceStateUpdateImpl(sensorType, 1));
} catch (IllegalArgumentException e) {
dssBridgeHandler.sendComandsToDSS(device,
new DeviceStateUpdateImpl(DeviceStateUpdate.REFRESH_OUTPUT, 0));
}
} else if (!device.isShade()) {
if (DsChannelTypeProvider.isOutputChannel(channelUID.getId())) {
if (command instanceof PercentType) {
device.setOutputValue(
(short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxOutputValue()));
} else if (command instanceof OnOffType) {
if (OnOffType.ON.equals(command)) {
device.setIsOn(true);
} else {
device.setIsOn(false);
}
} else if (command instanceof IncreaseDecreaseType) {
if (IncreaseDecreaseType.INCREASE.equals(command)) {
device.increase();
} else {
device.decrease();
}
} else if (command instanceof StringType) {
device.setOutputValue(Short.parseShort(((StringType) command).toString()));
}
} else {
logger.debug("Command sent to an unknown channel id: {}", channelUID);
}
} else {
if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
if (command instanceof PercentType) {
device.setAnglePosition(
(short) fromPercentToValue(((PercentType) command).intValue(), device.getMaxSlatAngle()));
} else if (command instanceof OnOffType) {
if (OnOffType.ON.equals(command)) {
device.setAnglePosition(device.getMaxSlatAngle());
} else {
device.setAnglePosition(device.getMinSlatAngle());
}
} else if (command instanceof IncreaseDecreaseType) {
if (IncreaseDecreaseType.INCREASE.equals(command)) {
device.increaseSlatAngle();
} else {
device.decreaseSlatAngle();
}
}
} else if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
if (command instanceof PercentType) {
int percent = ((PercentType) command).intValue();
if (!device.getHWinfo().equals("GR-KL200")) {
percent = 100 - percent;
}
device.setSlatPosition(fromPercentToValue(percent, device.getMaxSlatPosition()));
this.lastComand = command;
} else if (command instanceof StopMoveType) {
if (StopMoveType.MOVE.equals(command)) {
handleCommand(channelUID, this.lastComand);
} else {
dssBridgeHandler.stopOutputValue(device);
}
} else if (command instanceof UpDownType) {
if (UpDownType.UP.equals(command)) {
device.setIsOpen(true);
this.lastComand = command;
} else {
device.setIsOpen(false);
this.lastComand = command;
}
}
} else {
logger.debug("Command sent to an unknown channel id: {}", channelUID);
}
}
}
private int fromPercentToValue(int percent, int max) {
if (percent < 0 || percent == 0) {
return 0;
}
if (max < 0 || max == 0) {
return 0;
}
return (int) (max * ((float) percent / 100));
}
private synchronized BridgeHandler getDssBridgeHandler() {
if (this.dssBridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Bride cannot be found");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof BridgeHandler) {
dssBridgeHandler = (BridgeHandler) handler;
} else {
return null;
}
}
return dssBridgeHandler;
}
private boolean sensorChannelsLoaded() {
return loadedSensorChannels != null && !loadedSensorChannels.isEmpty();
}
@Override
public synchronized void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate) {
if (device != null) {
if (deviceStateUpdate != null) {
if (sensorChannelsLoaded()) {
if (deviceStateUpdate.isSensorUpdateType()) {
updateState(getSensorChannelID(deviceStateUpdate.getTypeAsSensorEnum()),
new DecimalType(deviceStateUpdate.getValueAsFloat()));
logger.debug("Update ESH-State");
return;
}
if (deviceStateUpdate.isBinarayInputType()) {
if (deviceStateUpdate.getValueAsShort() == 1) {
updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
OnOffType.ON);
} else {
updateState(getBinaryInputChannelID(deviceStateUpdate.getTypeAsDeviceBinarayInputEnum()),
OnOffType.OFF);
}
}
}
if (!device.isShade()) {
if (currentChannel != null) {
switch (deviceStateUpdate.getType()) {
case DeviceStateUpdate.OUTPUT_DECREASE:
case DeviceStateUpdate.OUTPUT_INCREASE:
case DeviceStateUpdate.OUTPUT:
if (currentChannel.contains(DsChannelTypeProvider.DIMMER)) {
if (deviceStateUpdate.getValueAsInteger() > 0) {
updateState(currentChannel, new PercentType(fromValueToPercent(
deviceStateUpdate.getValueAsInteger(), device.getMaxOutputValue())));
} else {
updateState(currentChannel, OnOffType.OFF);
}
} else if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
if (currentChannel.contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
updateState(currentChannel,
new StringType(convertStageValue((short) 2, device.getOutputValue())));
} else {
updateState(currentChannel,
new StringType(convertStageValue((short) 3, device.getOutputValue())));
}
}
break;
case DeviceStateUpdate.ON_OFF:
if (currentChannel.contains(DsChannelTypeProvider.STAGE)) {
onDeviceStateChanged(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT,
device.getOutputValue()));
}
if (deviceStateUpdate.getValueAsInteger() > 0) {
updateState(currentChannel, OnOffType.ON);
} else {
updateState(currentChannel, OnOffType.OFF);
}
break;
default:
return;
}
}
} else {
int percent = 0;
switch (deviceStateUpdate.getType()) {
case DeviceStateUpdate.SLAT_DECREASE:
case DeviceStateUpdate.SLAT_INCREASE:
case DeviceStateUpdate.SLATPOSITION:
percent = fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
device.getMaxSlatPosition());
break;
case DeviceStateUpdate.OPEN_CLOSE:
if (deviceStateUpdate.getValueAsInteger() > 0) {
percent = 100;
}
break;
case DeviceStateUpdate.OPEN_CLOSE_ANGLE:
if (device.isBlind() && currentChannel != null) {
if (deviceStateUpdate.getValueAsInteger() > 0) {
updateState(currentChannel, PercentType.HUNDRED);
} else {
updateState(currentChannel, PercentType.ZERO);
}
}
return;
case DeviceStateUpdate.SLAT_ANGLE_DECREASE:
case DeviceStateUpdate.SLAT_ANGLE_INCREASE:
case DeviceStateUpdate.SLAT_ANGLE:
if (device.isBlind() && currentChannel != null) {
updateState(currentChannel,
new PercentType(fromValueToPercent(deviceStateUpdate.getValueAsInteger(),
device.getMaxSlatAngle())));
}
return;
default:
return;
}
if (!device.getHWinfo().equals("GR-KL210")) {
percent = 100 - percent;
}
updateState(DsChannelTypeProvider.SHADE, new PercentType(percent));
}
logger.debug("Update ESH-State");
}
}
}
private int fromValueToPercent(int value, int max) {
if (value <= 0 || max <= 0) {
return 0;
}
int percentValue = new BigDecimal(value * ((float) 100 / max)).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
return percentValue < 0 ? 0 : percentValue > 100 ? 100 : percentValue;
}
@Override
public synchronized void onDeviceRemoved(GeneralDeviceInformation device) {
if (device instanceof Device) {
this.device = (Device) device;
if (this.getThing().getStatus().equals(ThingStatus.ONLINE)) {
if (!((Device) device).isPresent()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"Device is not present in the digitalSTROM-System.");
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"Device is not avaible in the digitalSTROM-System.");
}
}
logger.debug("Set status to {}", getThing().getStatus());
}
}
@Override
public synchronized void onDeviceAdded(GeneralDeviceInformation device) {
if (device instanceof Device) {
this.device = (Device) device;
if (this.device.isPresent()) {
ThingStatusInfo statusInfo = this.dssBridgeHandler.getThing().getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
logger.debug("Set status to {}", getThing().getStatus());
// load scene configurations persistently into the thing
for (Short i : this.device.getSavedScenes()) {
onSceneConfigAdded(i);
}
logger.debug("Load saved scene specification into device");
this.device.saveConfigSceneSpecificationIntoDevice(getThing().getProperties());
checkDeviceInfoProperties(this.device);
// load sensor priorities into the device and load sensor channels of the thing
if (!this.device.isShade()) {
loadSensorChannels();
// check and load output channel of the thing
checkOutputChannel();
} else if (this.device.isBlind()) {
// load channel for set the angle of jalousie devices
String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(
((Device) device).getFunctionalColorGroup(), ((Device) device).getOutputMode());
loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
DsChannelTypeProvider.getItemType(channelTypeID));
}
// load first channel values
onDeviceStateInitial(this.device);
return;
}
}
onDeviceRemoved(device);
}
/**
* Updates device info properties.
*
* @param device (must not be null)
*/
private void checkDeviceInfoProperties(Device device) {
boolean propertiesChanged = false;
Map<String, String> properties = editProperties();
// check device info
if (device.getName() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_NAME, device.getName());
propertiesChanged = true;
}
if (device.getDSUID() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_UID, device.getDSUID());
propertiesChanged = true;
}
if (device.getHWinfo() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_HW_INFO, device.getHWinfo());
propertiesChanged = true;
}
if (device.getZoneId() != -1) {
properties.put(DigitalSTROMBindingConstants.DEVICE_ZONE_ID, device.getZoneId() + "");
propertiesChanged = true;
}
if (device.getGroups() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_GROUPS, device.getGroups().toString());
propertiesChanged = true;
}
if (device.getOutputMode() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
propertiesChanged = true;
}
if (device.getFunctionalColorGroup() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_FUNCTIONAL_COLOR_GROUP,
device.getFunctionalColorGroup().toString());
propertiesChanged = true;
}
if (device.getMeterDSID() != null) {
properties.put(DigitalSTROMBindingConstants.DEVICE_METER_ID, device.getMeterDSID().toString());
propertiesChanged = true;
}
if (!device.getBinaryInputs().isEmpty()) {
properties.put(DigitalSTROMBindingConstants.DEVICE_BINARAY_INPUTS, getBinarayInputList());
propertiesChanged = true;
}
if (propertiesChanged) {
super.updateProperties(properties);
propertiesChanged = false;
}
}
private String getBinarayInputList() {
List<String> binarayInputs = new ArrayList<>(device.getBinaryInputs().size());
for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
DeviceBinarayInputEnum devBinInp = DeviceBinarayInputEnum.getdeviceBinarayInput(binInput.getInputType());
if (devBinInp != null) {
binarayInputs.add(devBinInp.toString().toLowerCase());
}
}
return binarayInputs.toString();
}
private void loadSensorChannels() {
if (device != null && device.isPresent()) {
// load sensor priorities into the device
boolean configChanged = false;
Configuration config = getThing().getConfiguration();
logger.debug("Add sensor priorities to the device");
String activePowerPrio = Config.REFRESH_PRIORITY_NEVER;
if (config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY) != null) {
activePowerPrio = config.get(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY).toString();
} else {
config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
configChanged = true;
}
// By devices with output mode WIPE the active power always will be read out to check, if the device is not
// in standby any more.
if (OutputModeEnum.WIPE.equals(device.getOutputMode())
&& activePowerPrio.equals(Config.REFRESH_PRIORITY_NEVER)) {
config.put(DigitalSTROMBindingConstants.ACTIVE_POWER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_LOW);
configChanged = true;
}
String outputCurrentPrio = Config.REFRESH_PRIORITY_NEVER;
if (config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY) != null) {
outputCurrentPrio = config.get(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY).toString();
} else {
config.put(DigitalSTROMBindingConstants.OUTPUT_CURRENT_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
configChanged = true;
}
String electricMeterPrio = Config.REFRESH_PRIORITY_NEVER;
if (config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY) != null) {
electricMeterPrio = config.get(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY).toString();
} else {
config.put(DigitalSTROMBindingConstants.ELECTRIC_METER_REFRESH_PRIORITY, Config.REFRESH_PRIORITY_NEVER);
configChanged = true;
}
if (configChanged) {
super.updateConfiguration(config);
configChanged = false;
}
device.setSensorDataRefreshPriority(activePowerPrio, electricMeterPrio, outputCurrentPrio);
logger.debug(
"add sensor prioritys: active power = {}, output current = {}, electric meter = {} to device with id {}",
activePowerPrio, outputCurrentPrio, electricMeterPrio, device.getDSID());
// check and load sensor channels of the thing
checkSensorChannel();
}
}
private boolean addLoadedSensorChannel(String sensorChannelType) {
if (loadedSensorChannels == null) {
loadedSensorChannels = new LinkedList<>();
}
if (!loadedSensorChannels.contains(sensorChannelType.toString())) {
return loadedSensorChannels.add(sensorChannelType.toString());
}
return false;
}
private boolean removeLoadedSensorChannel(String sensorChannelType) {
if (loadedSensorChannels == null) {
return false;
}
return loadedSensorChannels.remove(sensorChannelType);
}
private boolean isSensorChannelLoaded(String sensorChannelType) {
if (loadedSensorChannels == null) {
return false;
}
return loadedSensorChannels.contains(sensorChannelType);
}
private void checkSensorChannel() {
List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
boolean channelListChanged = false;
// if sensor channels with priority never are loaded delete these channels
if (!channelList.isEmpty()) {
Iterator<Channel> channelInter = channelList.iterator();
while (channelInter.hasNext()) {
Channel channel = channelInter.next();
String channelID = channel.getUID().getId();
if (channelID.startsWith(DsChannelTypeProvider.BINARY_INPUT_PRE)) {
DeviceBinarayInputEnum devBinInput = getBinaryInput(channelID);
if (device.getBinaryInput(devBinInput) != null) {
addLoadedSensorChannel(channelID);
} else {
logger.debug("remove {} binary input channel", channelID);
channelInter.remove();
channelListChanged = removeLoadedSensorChannel(channelID);
}
} else {
SensorEnum sensorType = getSensorEnum(channelID);
if (sensorType != null) {
if (SensorEnum.isPowerSensor(sensorType)) {
if (device.checkPowerSensorRefreshPriorityNever(sensorType)) {
logger.debug("remove {} sensor channel", channelID);
channelInter.remove();
channelListChanged = removeLoadedSensorChannel(channelID);
} else {
addLoadedSensorChannel(channelID);
}
} else {
if (device.supportsSensorType(sensorType)) {
addLoadedSensorChannel(channelID);
} else {
logger.debug("remove {} sensor channel", channelID);
channelInter.remove();
removeLoadedSensorChannel(channelID);
channelListChanged = true;
}
}
}
}
}
}
for (SensorEnum sensorType : device.getPowerSensorTypes()) {
if (!device.checkPowerSensorRefreshPriorityNever(sensorType)
&& !isSensorChannelLoaded(getSensorChannelID(sensorType))) {
logger.debug("create {} sensor channel", sensorType.toString());
channelList.add(getSensorChannel(sensorType));
channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
}
}
if (device.hasClimateSensors()) {
for (SensorEnum sensorType : device.getClimateSensorTypes()) {
if (!isSensorChannelLoaded(getSensorChannelID(sensorType))) {
logger.debug("create {} sensor channel", sensorType.toString());
channelList.add(getSensorChannel(sensorType));
channelListChanged = addLoadedSensorChannel(getSensorChannelID(sensorType));
}
}
}
if (device.isBinaryInputDevice()) {
for (DeviceBinaryInput binInput : device.getBinaryInputs()) {
DeviceBinarayInputEnum binInputType = DeviceBinarayInputEnum
.getdeviceBinarayInput(binInput.getInputType());
if (binInputType != null && !isSensorChannelLoaded(getBinaryInputChannelID(binInputType))) {
logger.debug("create {} sensor channel", binInputType.toString());
channelList.add(getBinaryChannel(binInputType));
channelListChanged = addLoadedSensorChannel(getBinaryInputChannelID(binInputType));
}
}
}
if (channelListChanged) {
logger.debug("load new channel list");
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(channelList);
updateThing(thingBuilder.build());
}
}
private Channel getSensorChannel(SensorEnum sensorType) {
return ChannelBuilder.create(getSensorChannelUID(sensorType), "Number")
.withType(DsChannelTypeProvider.getSensorChannelUID(sensorType)).build();
}
private Channel getBinaryChannel(DeviceBinarayInputEnum binaryInputType) {
return ChannelBuilder.create(getBinaryInputChannelUID(binaryInputType), "Switch")
.withType(DsChannelTypeProvider.getBinaryInputChannelUID(binaryInputType)).build();
}
private void checkOutputChannel() {
if (device == null) {
logger.debug("Can not load a channel without a device!");
return;
}
// if the device have no output channel or it is disabled all output channels will be deleted
if (!device.isDeviceWithOutput()) {
loadOutputChannel(null, null);
}
String channelTypeID = DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(),
device.getOutputMode());
logger.debug("load channel: typeID={}, itemType={}",
DsChannelTypeProvider.getOutputChannelTypeID(device.getFunctionalColorGroup(), device.getOutputMode()),
DsChannelTypeProvider.getItemType(channelTypeID));
if (channelTypeID != null && (currentChannel == null || !currentChannel.equals(channelTypeID))) {
loadOutputChannel(new ChannelTypeUID(BINDING_ID, channelTypeID),
DsChannelTypeProvider.getItemType(channelTypeID));
}
}
private void loadOutputChannel(ChannelTypeUID channelTypeUID, String acceptedItemType) {
if (channelTypeUID == null || acceptedItemType == null) {
return;
}
currentChannel = channelTypeUID.getId();
List<Channel> channelList = new LinkedList<>(this.getThing().getChannels());
boolean channelIsAlreadyLoaded = false;
boolean channelListChanged = false;
if (!channelList.isEmpty()) {
Iterator<Channel> channelInter = channelList.iterator();
while (channelInter.hasNext()) {
Channel eshChannel = channelInter.next();
if (DsChannelTypeProvider.isOutputChannel(eshChannel.getUID().getId())) {
if (!eshChannel.getUID().getId().equals(currentChannel)
&& !(device.isShade() && eshChannel.getUID().getId().equals(DsChannelTypeProvider.SHADE))) {
channelInter.remove();
channelListChanged = true;
} else {
if (!eshChannel.getUID().getId().equals(DsChannelTypeProvider.SHADE)) {
channelIsAlreadyLoaded = true;
}
}
}
}
}
if (!channelIsAlreadyLoaded && currentChannel != null) {
Channel channel = ChannelBuilder
.create(new ChannelUID(this.getThing().getUID(), channelTypeUID.getId()), acceptedItemType)
.withType(channelTypeUID).build();
channelList.add(channel);
channelListChanged = true;
}
if (channelListChanged) {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(channelList);
updateThing(thingBuilder.build());
logger.debug("load channel: {} with item: {}", channelTypeUID.getAsString(), acceptedItemType);
}
}
private ChannelUID getSensorChannelUID(SensorEnum sensorType) {
return new ChannelUID(getThing().getUID(), getSensorChannelID(sensorType));
}
private String getSensorChannelID(SensorEnum sensorType) {
return sensorType.toString().toLowerCase();
}
private ChannelUID getBinaryInputChannelUID(DeviceBinarayInputEnum binaryInputType) {
return new ChannelUID(getThing().getUID(), getBinaryInputChannelID(binaryInputType));
}
private String getBinaryInputChannelID(DeviceBinarayInputEnum binaryInputType) {
return DsChannelTypeProvider.BINARY_INPUT_PRE + binaryInputType.toString().toLowerCase();
}
private DeviceBinarayInputEnum getBinaryInput(String channelID) {
try {
return DeviceBinarayInputEnum
.valueOf(channelID.replace(DsChannelTypeProvider.BINARY_INPUT_PRE, "").toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
private SensorEnum getSensorEnum(String channelID) {
try {
return SensorEnum.valueOf(channelID.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
@Override
public void channelLinked(ChannelUID channelUID) {
if (device != null) {
SensorEnum sensorType = getSensorEnum(channelUID.getId());
if (sensorType != null) {
Float val = device.getFloatSensorValue(sensorType);
if (val != null) {
updateState(channelUID, new DecimalType(val));
}
}
Short val = device.getBinaryInputState(getBinaryInput(channelUID.getId()));
if (val != null) {
if (val == 1) {
updateState(channelUID, OnOffType.ON);
} else {
updateState(channelUID, OnOffType.OFF);
}
}
if (channelUID.getId().contains(DsChannelTypeProvider.DIMMER)) {
if (device.isOn()) {
updateState(channelUID,
new PercentType(fromValueToPercent(device.getOutputValue(), device.getMaxOutputValue())));
} else {
updateState(channelUID, new PercentType(0));
}
return;
}
if (channelUID.getId().contains(DsChannelTypeProvider.SWITCH)) {
if (device.isOn()) {
updateState(channelUID, OnOffType.ON);
} else {
updateState(channelUID, OnOffType.OFF);
}
return;
}
if (channelUID.getId().contains(DsChannelTypeProvider.SHADE)) {
updateState(channelUID,
new PercentType(fromValueToPercent(device.getSlatPosition(), device.getMaxSlatPosition())));
return;
}
if (channelUID.getId().contains(DsChannelTypeProvider.ANGLE)) {
updateState(channelUID,
new PercentType(fromValueToPercent(device.getAnglePosition(), device.getMaxSlatAngle())));
return;
}
if (channelUID.getId().contains(DsChannelTypeProvider.STAGE)) {
if (channelUID.getId().contains(TWO_STAGE_SWITCH_IDENTICATOR)) {
updateState(channelUID, new StringType(convertStageValue((short) 2, device.getOutputValue())));
return;
}
if (channelUID.getId().contains(THREE_STAGE_SWITCH_IDENTICATOR)) {
updateState(channelUID, new StringType(convertStageValue((short) 3, device.getOutputValue())));
return;
}
}
}
}
private String convertStageValue(short stage, short value) {
switch (stage) {
case 2:
if (value < 85) {
return OPTION_COMBINED_BOTH_OFF;
} else if (value >= 85 && value < 170) {
return OPTION_COMBINED_FIRST_ON;
} else if (value >= 170 && value <= 255) {
return OPTION_COMBINED_BOTH_ON;
}
case 3:
if (value < 64) {
return OPTION_COMBINED_BOTH_OFF;
} else if (value >= 64 && value < 128) {
return OPTION_COMBINED_FIRST_ON;
} else if (value >= 128 && value < 192) {
return OPTION_COMBINED_SECOND_ON;
} else if (value >= 192 && value <= 255) {
return OPTION_COMBINED_BOTH_ON;
}
}
return null;
}
private void onDeviceStateInitial(Device device) {
if (device != null) {
if (currentChannel != null) {
if (isLinked(currentChannel)) {
channelLinked(new ChannelUID(getThing().getUID(), currentChannel));
}
}
if (!device.isShade()) {
if (loadedSensorChannels != null) {
for (String sensor : loadedSensorChannels) {
Channel channel = getThing().getChannel(sensor);
if (channel != null && isLinked(sensor)) {
channelLinked(channel.getUID());
}
}
}
} else {
if (isLinked(DsChannelTypeProvider.SHADE)) {
channelLinked(new ChannelUID(getThing().getUID(), DsChannelTypeProvider.SHADE));
}
}
}
}
@Override
public synchronized void onSceneConfigAdded(short sceneId) {
if (device != null) {
String saveScene = "";
DeviceSceneSpec sceneSpec = device.getSceneConfig(sceneId);
if (sceneSpec != null) {
saveScene = sceneSpec.toString();
}
Integer[] sceneValue = device.getSceneOutputValue(sceneId);
if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_VALUE] != -1) {
saveScene = saveScene + ", sceneValue: " + sceneValue[0];
}
if (sceneValue[GeneralLibConstance.SCENE_ARRAY_INDEX_ANGLE] != -1) {
saveScene = saveScene + ", sceneAngle: " + sceneValue[1];
}
String key = DigitalSTROMBindingConstants.DEVICE_SCENE + sceneId;
if (!saveScene.isEmpty()) {
logger.debug("Save scene configuration: [{}] to thing with UID {}", saveScene, getThing().getUID());
super.updateProperty(key, saveScene);
// persist the new property
// super.updateThing(editThing().build());
}
}
}
@Override
public void onDeviceConfigChanged(ChangeableDeviceConfigEnum whichConfig) {
if (whichConfig != null) {
switch (whichConfig) {
case DEVICE_NAME:
super.updateProperty(DEVICE_NAME, device.getName());
break;
case METER_DSID:
super.updateProperty(DEVICE_METER_ID, device.getMeterDSID().getValue());
break;
case ZONE_ID:
super.updateProperty(DEVICE_ZONE_ID, device.getZoneId() + "");
break;
case GROUPS:
super.updateProperty(DEVICE_GROUPS, device.getGroups().toString());
break;
case FUNCTIONAL_GROUP:
super.updateProperty(DEVICE_FUNCTIONAL_COLOR_GROUP, device.getFunctionalColorGroup().toString());
checkOutputChannel();
break;
case OUTPUT_MODE:
super.updateProperty(DEVICE_OUTPUT_MODE, device.getOutputMode().toString());
checkOutputChannel();
break;
case BINARY_INPUTS:
super.updateProperty(DEVICE_BINARAY_INPUTS, getBinarayInputList());
checkSensorChannel();
break;
default:
break;
}
}
}
@Override
public String getDeviceStatusListenerID() {
return this.dSID;
}
}

View File

@@ -0,0 +1,367 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.handler;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
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.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneHandler} is responsible for handling commands, which are sent to the channel of an
* DigitalSTROM-Scene.<br>
* For that it uses the {@link BridgeHandler} to execute the actual command and implements the
* {@link SceneStatusListener} to get informed about changes from the accompanying {@link InternalScene}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public class SceneHandler extends BaseThingHandler implements SceneStatusListener {
private final Logger logger = LoggerFactory.getLogger(SceneHandler.class);
/**
* Contains all supported thing types of this handler.
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(Arrays.asList(
DigitalSTROMBindingConstants.THING_TYPE_APP_SCENE, DigitalSTROMBindingConstants.THING_TYPE_GROUP_SCENE,
DigitalSTROMBindingConstants.THING_TYPE_ZONE_SCENE, DigitalSTROMBindingConstants.THING_TYPE_NAMED_SCENE));
/**
* Configured scene does not exist or cannot be used.
*/
public static final String SCENE_WRONG = "sceneWrong";
/**
* Configured zone does not exist.
*/
public static final String ZONE_WRONG = "zoneWrong";
/**
* Configured group does not exist.
*/
public static final String GROUP_WRONG = "groupWrong";
/**
* StructureManager in BridgeHandler is null
*/
public static final String NO_STRUC_MAN = "noStrucMan";
/**
* Configured scene is null.
*/
public static final String NO_SCENE = "noScene";
/**
* BridgeHandler is null.
*/
public static final String NO_BRIDGE = "noBridge";
private BridgeHandler bridgeHandler;
private InternalScene scene;
private String sceneThingID;
/**
* Creates a new {@link SceneHandler}.
*
* @param thing must not be null
*/
public SceneHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Initializing SceneHandler");
final Bridge bridge = getBridge();
if (bridge != null) {
bridgeStatusChanged(bridge.getStatusInfo());
}
}
@Override
public void dispose() {
logger.debug("Handler disposed... unregistering SceneStatusListener");
if (sceneThingID != null) {
BridgeHandler dssBridgeHandler = getBridgeHandler();
if (dssBridgeHandler != null) {
getBridgeHandler().unregisterSceneStatusListener(this);
}
sceneThingID = null;
scene = null;
}
}
@Override
public void handleRemoval() {
if (getBridgeHandler() != null) {
this.bridgeHandler.childThingRemoved(sceneThingID);
}
updateStatus(ThingStatus.REMOVED);
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
if (getBridgeHandler() != null) {
if (scene == null) {
String sceneID = getSceneID(getConfig(), bridgeHandler);
switch (sceneID) {
case SCENE_WRONG:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Configured scene '" + getConfig().get(DigitalSTROMBindingConstants.SCENE_ID)
+ "' does not exist or cannot be used, please check the configuration.");
break;
case ZONE_WRONG:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.ZONE_ID)
+ "' does not exist, please check the configuration.");
break;
case GROUP_WRONG:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Configured group '" + getConfig().get(DigitalSTROMBindingConstants.GROUP_ID)
+ "' does not exist, please check the configuration.");
break;
case NO_STRUC_MAN:
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"Waiting for building digitalSTROM model.");
break;
case NO_SCENE:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No Scene-ID is set!");
break;
default:
this.sceneThingID = sceneID;
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"Waiting for listener registration");
logger.debug("Set status on {}", getThing().getStatus());
this.bridgeHandler.registerSceneStatusListener(this);
}
} else {
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE);
}
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
}
logger.debug("Set status to {}", getThing().getStatusInfo());
}
/**
* Checks the configuration and returns a unique Scene-ID or error string.<br>
* The {@link StructureManager} of the {@link BridgeHandler} is used for checking the existing configured zone and
* group. The {@link SceneEnum} will be used to check, if the configured scene exists and is allowed to use.<br>
* If the check succeed the scene-ID will be returned in format "[zoneID]-[groupID]-[SceneID]", otherwise one of the
* following errors {@link String}s will returned:
* <ul>
* <li>{@link #SCENE_WRONG}: Configured scene does not exist or cannot be used.</li>
* <li>{@link #ZONE_WRONG} Configured zone does not exist.</li>
* <li>{@link #GROUP_WRONG}: Configured group does not exist.</li>
* <li>{@link #NO_STRUC_MAN}: StructureManager in BridgeHandler is null.</li>
* <li>{@link #NO_SCENE}: Configured scene is null.</li>
* <li>{@link #NO_BRIDGE}: BridgeHandler is null.</li>
* </ul>
*
* @param configuration (must not be null)
* @param bridgeHandler (can be null)
* @return unique Scene-ID or error string
*/
public static String getSceneID(Configuration configuration, BridgeHandler bridgeHandler) {
if (configuration == null) {
throw new IllegalArgumentException("configuration cannot be null");
}
if (bridgeHandler == null) {
return NO_BRIDGE;
}
String configZoneID;
String configGroupID;
String configSceneID;
short sceneID;
int zoneID;
short groupID;
if (configuration.get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
configZoneID = configuration.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
} else {
configZoneID = "";
}
if (configuration.get(DigitalSTROMBindingConstants.GROUP_ID) != null) {
configGroupID = configuration.get(DigitalSTROMBindingConstants.GROUP_ID).toString();
} else {
configGroupID = "";
}
if (configuration.get(DigitalSTROMBindingConstants.SCENE_ID) != null) {
configSceneID = configuration.get(DigitalSTROMBindingConstants.SCENE_ID).toString();
} else {
configSceneID = "";
}
if (!configSceneID.isEmpty()) {
try {
sceneID = Short.parseShort(configSceneID);
if (!SceneEnum.containsScene(sceneID)) {
return SCENE_WRONG;
}
} catch (NumberFormatException e) {
try {
sceneID = SceneEnum.valueOf(configSceneID.replace(" ", "_").toUpperCase()).getSceneNumber();
} catch (IllegalArgumentException e1) {
return SCENE_WRONG;
}
}
StructureManager strucMan = bridgeHandler.getStructureManager();
if (strucMan != null) {
if (configZoneID.isEmpty()) {
zoneID = 0;
} else {
try {
zoneID = Integer.parseInt(configZoneID);
if (!strucMan.checkZoneID(zoneID)) {
return ZONE_WRONG;
}
} catch (NumberFormatException e) {
zoneID = strucMan.getZoneId(configZoneID);
if (zoneID == -1) {
return ZONE_WRONG;
}
}
}
if (configGroupID.isEmpty()) {
groupID = 0;
} else {
try {
groupID = Short.parseShort(configGroupID);
if (!strucMan.checkZoneGroupID(zoneID, groupID)) {
return GROUP_WRONG;
}
} catch (NumberFormatException e) {
String zoneName = strucMan.getZoneName(zoneID);
groupID = strucMan.getZoneGroupId(zoneName, configGroupID);
}
if (groupID == -1) {
return GROUP_WRONG;
}
}
return zoneID + "-" + groupID + "-" + sceneID;
} else {
return NO_STRUC_MAN;
}
} else {
return NO_SCENE;
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
BridgeHandler dssBridgeHandler = getBridgeHandler();
if (dssBridgeHandler == null) {
logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
return;
}
if (channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
if (command instanceof OnOffType) {
if (OnOffType.ON.equals(command)) {
this.bridgeHandler.sendSceneComandToDSS(scene, true);
} else {
this.bridgeHandler.sendSceneComandToDSS(scene, false);
}
}
} else {
logger.warn("Command sent to an unknown channel id: {}", channelUID);
}
}
private synchronized BridgeHandler getBridgeHandler() {
if (this.bridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Bridge cannot be found");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof BridgeHandler) {
this.bridgeHandler = (BridgeHandler) handler;
} else {
logger.debug("BridgeHandler cannot be found");
return null;
}
}
return this.bridgeHandler;
}
@Override
public void onSceneStateChanged(boolean flag) {
if (flag) {
updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.ON);
} else {
updateState(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE, OnOffType.OFF);
}
}
@Override
public void channelLinked(ChannelUID channelUID) {
if (scene != null && channelUID.getId().equals(DigitalSTROMBindingConstants.CHANNEL_ID_SCENE)) {
onSceneStateChanged(scene.isActive());
}
}
@Override
public void onSceneRemoved(InternalScene scene) {
this.scene = scene;
updateStatus(ThingStatus.OFFLINE);
logger.debug("Set status on {}", getThing().getStatus());
}
@Override
public void onSceneAdded(InternalScene scene) {
logger.debug("Scene {} added", scene.getID());
if (this.bridgeHandler != null) {
ThingStatusInfo statusInfo = this.bridgeHandler.getThing().getStatusInfo();
updateStatus(statusInfo.getStatus(), statusInfo.getStatusDetail(), statusInfo.getDescription());
logger.debug("Set status on {}", getThing().getStatus());
}
this.scene = scene;
onSceneStateChanged(scene.isActive());
}
@Override
public String getSceneStatusListenerID() {
return this.sceneThingID;
}
}

View File

@@ -0,0 +1,358 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.handler;
import static org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants.BINDING_ID;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.DigitalSTROMBindingConstants;
import org.openhab.binding.digitalstrom.internal.lib.climate.TemperatureControlSensorTransmitter;
import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlModes;
import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlStates;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.impl.TemperatureControlManager;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FunctionalColorGroupEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
import org.openhab.binding.digitalstrom.internal.providers.DsChannelTypeProvider;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ZoneTemperatureControlHandler} is responsible for handling the configuration, to load the supported
* channel of a
* digitalSTROM zone, which has a temperature control configured, and handling commands, which are sent to the channel.
* <br>
* <br>
* For that it uses the {@link BridgeHandler} to register itself as {@link TemperatureControlStatusListener} at the
* {@link TemperatureControlManager} to get informed by status changes. Through the registration as
* {@link TemperatureControlStatusListener} a {@link TemperatureControlSensorTransmitter} will be registered to this
* {@link ZoneTemperatureControlHandler}, which is needed to set the temperature or the control value of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class ZoneTemperatureControlHandler extends BaseThingHandler implements TemperatureControlStatusListener {
/**
* Contains all supported thing types of this handler
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(
Arrays.asList(DigitalSTROMBindingConstants.THING_TYPE_ZONE_TEMERATURE_CONTROL));
private final Logger logger = LoggerFactory.getLogger(ZoneTemperatureControlHandler.class);
private TemperatureControlSensorTransmitter temperatureSensorTransmitter;
private BridgeHandler dssBridgeHandler;
private Integer zoneID;
private String currentChannelID;
private Float currentValue = 0f;
private final Float step = 1f;
// check zoneID error codes
public static final int ZONE_ID_NOT_EXISTS = -1;
public static final int ZONE_ID_NOT_SET = -2;
public static final int BRIDGE_IS_NULL = -3;
/**
* Creates a new {@link ZoneTemperatureControlHandler}.
*
* @param thing must not be null
*/
public ZoneTemperatureControlHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Initializing DeviceHandler.");
if (getConfig().get(DigitalSTROMBindingConstants.ZONE_ID) != null) {
final Bridge bridge = getBridge();
if (bridge != null) {
bridgeStatusChanged(bridge.getStatusInfo());
} else {
// Set status to OFFLINE, if no bridge is available e.g. because the bridge has been removed and the
// Thing was reinitialized.
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge is missing!");
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "zoneID is missing");
}
}
/**
* Returns the configured zoneID of the given {@link Configuration}. If the zoneID does't exist or can't be checked
* {@link #ZONE_ID_NOT_EXISTS}, {@link #ZONE_ID_NOT_SET} or {@link #BRIDGE_IS_NULL} will be returned.
*
* @param config the {@link Configuration} to be checked
* @param bridge the responsible {@link BridgeHandler}
* @return zoneID the existing dS zoneID or a error constant
*/
public static int getZoneID(Configuration config, BridgeHandler bridge) {
if (config == null || config.get(DigitalSTROMBindingConstants.ZONE_ID) == null) {
return ZONE_ID_NOT_SET;
}
if (bridge == null) {
return BRIDGE_IS_NULL;
}
String configZoneID = config.get(DigitalSTROMBindingConstants.ZONE_ID).toString();
int zoneID;
StructureManager strucMan = bridge.getStructureManager();
if (strucMan != null) {
try {
zoneID = Integer.parseInt(configZoneID);
if (!strucMan.checkZoneID(zoneID)) {
zoneID = ZONE_ID_NOT_EXISTS;
}
} catch (NumberFormatException e) {
zoneID = strucMan.getZoneId(configZoneID);
}
return zoneID;
}
return ZONE_ID_NOT_EXISTS;
}
@Override
public void dispose() {
logger.debug("Handler disposed... unregister DeviceStatusListener");
if (zoneID != null) {
if (dssBridgeHandler != null) {
dssBridgeHandler.unregisterTemperatureControlStatusListener(this);
}
temperatureSensorTransmitter = null;
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
int tempZoneID = getZoneID(getConfig(), getDssBridgeHandler());
if (tempZoneID == ZONE_ID_NOT_EXISTS) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Configured zone '" + getConfig().get(DigitalSTROMBindingConstants.ZONE_ID)
+ "' does not exist, please check the configuration.");
} else {
this.zoneID = tempZoneID;
}
if (zoneID != null) {
if (getDssBridgeHandler() != null && temperatureSensorTransmitter == null) {
dssBridgeHandler.registerTemperatureControlStatusListener(this);
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.CONFIGURATION_PENDING,
"waiting for listener registration");
} else {
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No zoneID is set!");
}
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.OFFLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatusInfo.getStatus().equals(ThingStatus.REMOVED)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Bridge has been removed.");
}
logger.debug("Set status to {}", getThing().getStatusInfo());
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
BridgeHandler dssBridgeHandler = getDssBridgeHandler();
if (dssBridgeHandler == null) {
logger.debug("BridgeHandler not found. Cannot handle command without bridge.");
return;
}
if (temperatureSensorTransmitter == null && zoneID != null) {
logger.debug(
"Device not known on TemperationControlManager or temperatureSensorTransreciver is not registerd. Cannot handle command.");
return;
}
if (channelUID.getId().equals(currentChannelID)) {
if (command instanceof PercentType || command instanceof DecimalType) {
sendCommandAndUpdateChannel(((DecimalType) command).floatValue());
} else if (command instanceof OnOffType) {
if (OnOffType.ON.equals(command)) {
if (isTemperature()) {
sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MAX_TEMP);
} else {
sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MAX_CONTROLL_VALUE);
}
} else {
if (isTemperature()) {
sendCommandAndUpdateChannel(0f);
} else {
sendCommandAndUpdateChannel(TemperatureControlSensorTransmitter.MIN_CONTROLL_VALUE);
}
}
} else if (command instanceof IncreaseDecreaseType) {
if (IncreaseDecreaseType.INCREASE.equals(command)) {
sendCommandAndUpdateChannel(currentValue + step);
} else {
sendCommandAndUpdateChannel(currentValue - step);
}
}
} else {
logger.debug("Command sent to an unknown channel id: {}", channelUID);
}
}
private boolean isTemperature() {
return currentChannelID.contains(DsChannelTypeProvider.TEMPERATURE_CONTROLLED);
}
private void sendCommandAndUpdateChannel(Float newValue) {
if (isTemperature()) {
if (temperatureSensorTransmitter.pushTargetTemperature(zoneID, newValue)) {
currentValue = newValue;
updateState(currentChannelID, new DecimalType(newValue));
}
} else {
if (temperatureSensorTransmitter.pushControlValue(zoneID, newValue)) {
currentValue = newValue;
updateState(currentChannelID, new PercentType(newValue.intValue()));
}
}
}
private synchronized BridgeHandler getDssBridgeHandler() {
if (this.dssBridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Bride cannot be found");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof BridgeHandler) {
dssBridgeHandler = (BridgeHandler) handler;
} else {
return null;
}
}
return dssBridgeHandler;
}
@Override
public synchronized void configChanged(TemperatureControlStatus tempControlStatus) {
if (tempControlStatus != null && tempControlStatus.isNotSetOff()) {
ControlModes controlMode = ControlModes.getControlMode(tempControlStatus.getControlMode());
ControlStates controlState = ControlStates.getControlState(tempControlStatus.getControlState());
if (controlMode != null && controlState != null) {
logger.debug("config changed: {}", tempControlStatus.toString());
if (controlMode.equals(ControlModes.OFF) && currentChannelID != null) {
currentChannelID = null;
loadChannel();
} else if (controlMode.equals(ControlModes.PID_CONTROL)
&& (currentChannelID == null
|| !currentChannelID.contains(DsChannelTypeProvider.TEMPERATURE_CONTROLLED))
&& !controlState.equals(ControlStates.EMERGENCY)) {
currentChannelID = DsChannelTypeProvider.getOutputChannelTypeID(FunctionalColorGroupEnum.BLUE,
OutputModeEnum.TEMPRETURE_PWM);
loadChannel();
currentValue = tempControlStatus.getNominalValue();
updateState(currentChannelID, new DecimalType(currentValue.doubleValue()));
} else if (!controlMode.equals(ControlModes.PID_CONTROL) && !controlMode.equals(ControlModes.OFF)) {
currentChannelID = DsChannelTypeProvider.getOutputChannelTypeID(FunctionalColorGroupEnum.BLUE,
OutputModeEnum.HEATING_PWM);
loadChannel();
currentValue = tempControlStatus.getControlValue();
updateState(currentChannelID, new PercentType(fixPercent(currentValue.intValue())));
if (controlState.equals(ControlStates.EMERGENCY)) {
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"The communication with temperation sensor fails. Temperature control state emergency (temperature control though the control value) is active.");
}
}
Map<String, String> properties = editProperties();
properties.put("controlDSUID", tempControlStatus.getControlDSUID());
properties.put("controlMode", controlMode.getKey());
properties.put("controlState", controlState.getKey());
updateProperties(properties);
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"digitalSTROM temperature control is for this zone not configured in.");
}
}
private synchronized void loadChannel() {
List<Channel> newChannelList = new ArrayList<>(1);
if (currentChannelID != null) {
newChannelList.add(ChannelBuilder
.create(new ChannelUID(this.getThing().getUID(), currentChannelID),
DsChannelTypeProvider.getItemType(currentChannelID))
.withType(new ChannelTypeUID(BINDING_ID, currentChannelID)).build());
}
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(newChannelList);
updateThing(thingBuilder.build());
logger.debug("load channel: {} with item: {}", currentChannelID,
DsChannelTypeProvider.getItemType(currentChannelID));
}
@Override
public synchronized void onTargetTemperatureChanged(Float newValue) {
if (isTemperature()) {
updateState(currentChannelID, new DecimalType(newValue));
}
}
@Override
public synchronized void onControlValueChanged(Integer newValue) {
if (!isTemperature()) {
updateState(currentChannelID, new PercentType(fixPercent(newValue)));
}
}
private int fixPercent(int value) {
return value < 0 ? 0 : value > 100 ? 100 : value;
}
@Override
public void registerTemperatureSensorTransmitter(
TemperatureControlSensorTransmitter temperatureSensorTransreciver) {
updateStatus(ThingStatus.ONLINE);
this.temperatureSensorTransmitter = temperatureSensorTransreciver;
}
@Override
public Integer getTemperationControlStatusListenrID() {
return zoneID;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib;
/**
* The {@link GeneralLibConstance} contains all relevant library constants.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class GeneralLibConstance {
/**
* digitalSTROMs broadcast zone id.
*/
public static final int BROADCAST_ZONE_GROUP_ID = 0;
/**
* digitalSTROMs broadcast zone string by query response.
*/
public static final String QUERY_BROADCAST_ZONE_STRING = "zone0";
/**
* Scene-Array index for the scene value.
*/
public static final int SCENE_ARRAY_INDEX_VALUE = 0;
/**
* Scene-Array index for the scene value.
*/
public static final int SCENE_ARRAY_INDEX_ANGLE = 1;
/**
* The highest read out priority for
* {@link org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob}s.
*/
public static final int HIGHEST_READ_OUT_PRIORITY = 0;
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate;
/**
* The {@link TemperatureControlSensorTransmitter} can be implement by subclasses to implement a
* transmitter which can be used to push the target temperature or control value to a digitalSTROM zone.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public interface TemperatureControlSensorTransmitter {
/**
* Maximal temperature, which can be set as target temperature in digitalSTROM.
*/
static float MAX_TEMP = 50f;
/**
* Minimal temperature, which can be set as target temperature in digitalSTROM.
*/
static float MIN_TEMP = -43.15f;
/**
* Maximal control value, which can be set as target temperature in digitalSTROM.
*/
static float MAX_CONTROLL_VALUE = 100f;
/**
* Minimal control value, which can be set as target temperature in digitalSTROM.
*/
static float MIN_CONTROLL_VALUE = 0f;
/**
* Pushes a new target temperature to a digitalSTROM zone.
*
* @param zoneID (must not be null)
* @param newValue (must not be null)
* @return true, if the push was successfully
*/
boolean pushTargetTemperature(Integer zoneID, Float newValue);
/**
* Pushes a new control value to a digitalSTROM zone.
*
* @param zoneID (must not be null)
* @param newValue (must not be null)
* @return true, if the push was successfully
*/
boolean pushControlValue(Integer zoneID, Float newValue);
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.constants;
/**
* The {@link ControlModes} contains all digitalSTROM heating control modes.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public enum ControlModes {
OFF((short) 0, "off"),
PID_CONTROL((short) 1, "pid-control"),
ZONE_FOLLOWER((short) 2, "zone-follower"),
FIXED_VALUE((short) 3, "fixed-value"),
MANUAL((short) 4, "manual");
private final Short id;
private final String key;
private static final ControlModes[] CONTROL_MODES = new ControlModes[ControlModes.values().length];
static {
for (ControlModes controlMode : ControlModes.values()) {
CONTROL_MODES[controlMode.id] = controlMode;
}
}
private ControlModes(short id, String key) {
this.id = id;
this.key = key;
}
/**
* Returns the key of the operation mode.
*
* @return key
*/
public String getKey() {
return key;
}
/**
* Returns the ID of the operation mode.
*
* @return ID
*/
public Short getID() {
return id;
}
/**
* Returns the {@link ControlModes} of the given control mode id.
*
* @param id of the control mode
* @return control mode
*/
public static ControlModes getControlMode(short id) {
try {
return CONTROL_MODES[id];
} catch (IndexOutOfBoundsException e) {
return null;
}
}
}

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.constants;
/**
* The {@link ControlStates} contains all digitalSTROM heating control states.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public enum ControlStates {
INTERNAL((short) 0, "internal"),
EXTERNAL((short) 1, "external"),
EXBACKUP((short) 2, "exbackup"),
EMERGENCY((short) 3, "emergency");
private final Short id;
private final String key;
private static final ControlStates[] CONTROL_STATES = new ControlStates[ControlStates.values().length];
static {
for (ControlStates controlState : ControlStates.values()) {
CONTROL_STATES[controlState.id] = controlState;
}
}
private ControlStates(short id, String key) {
this.id = id;
this.key = key;
}
/**
* Returns the key of the operation mode.
*
* @return key
*/
public String getKey() {
return key;
}
/**
* Returns the ID of the operation mode.
*
* @return ID
*/
public Short getID() {
return id;
}
/**
* Returns the {@link ControlStates} of the given control state id.
*
* @param id of the control state
* @return control state
*/
public static ControlStates getControlState(short id) {
try {
return CONTROL_STATES[id];
} catch (IndexOutOfBoundsException e) {
return null;
}
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.constants;
/**
* The {@link OperationModes} contains all digitalSTROM heating operation states.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public enum OperationModes {
OFF((short) 0, "Off"),
COMFORT((short) 1, "Comfort"),
ECONEMY((short) 2, "Econemy"),
NOT_USED((short) 3, "NotUsed"),
NIGHT((short) 4, "Night"),
HOLLYDAY((short) 5, "Holliday"),
COOLING((short) 6, "Cooling"),
COOLING_OFF((short) 7, "CoolingOff");
private final Short id;
private final String key;
private static final OperationModes[] OPERATION_MODES = new OperationModes[OperationModes.values().length];
static {
for (OperationModes operationMode : OperationModes.values()) {
OPERATION_MODES[operationMode.id] = operationMode;
}
}
/**
* Returns the {@link OperationModes} of the given operation mode id.
*
* @param id of the operation mode
* @return operation mode
*/
public static OperationModes getOperationMode(short id) {
try {
return OPERATION_MODES[id];
} catch (IndexOutOfBoundsException e) {
return null;
}
}
private OperationModes(short id, String key) {
this.id = id;
this.key = key;
}
/**
* Returns the key of the operation mode.
*
* @return key
*/
public String getKey() {
return key;
}
/**
* Returns the ID of the operation mode.
*
* @return ID
*/
public Short getID() {
return id;
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.datatypes;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
/**
* The {@link AssignSensorType} assigns a sensor type of a zone to the dSUID of the sensor-device.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class AssignSensorType {
private final SensorEnum sensorType;
private final String dsuid;
/**
* Create a new {@link AssignSensorType}.
*
* @param sensorType must not be null
* @param dSUID must not be null
*/
public AssignSensorType(SensorEnum sensorType, String dSUID) {
this.sensorType = sensorType;
dsuid = dSUID;
}
/**
* Returns the sensor type as {@link SensorEnum}.
*
* @return the sensor type
*/
public SensorEnum getSensorType() {
return sensorType;
}
/**
* Returns the dSUID of the assign sensor-device.
*
* @return the dSUID
*/
public String getDSUID() {
return dsuid;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "AssignSensorType [SENSOR_TYPE=" + sensorType + ", dSUID=" + dsuid + "]";
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dsuid == null) ? 0 : dsuid.hashCode());
result = prime * result + ((sensorType == null) ? 0 : sensorType.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof AssignSensorType)) {
return false;
}
AssignSensorType other = (AssignSensorType) obj;
if (dsuid == null) {
if (other.dsuid != null) {
return false;
}
} else if (!dsuid.equals(other.dsuid)) {
return false;
}
if (sensorType != other.sensorType) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.datatypes;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link CachedSensorValue} holds a read sensor value. For that the {@link CachedSensorValue} includes the sensor
* type, sensor value and a the timestamp.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class CachedSensorValue {
private final Logger logger = LoggerFactory.getLogger(CachedSensorValue.class);
private final SensorEnum sensorType;
private final Float sensorValue;
private final String timestamp;
/**
* Create a new {@link CachedSensorValue}.
*
* @param sensorType must not be null
* @param sensorValue must not be null
* @param timestamp must not be null
*/
public CachedSensorValue(SensorEnum sensorType, Float sensorValue, String timestamp) {
this.sensorType = sensorType;
this.sensorValue = sensorValue;
this.timestamp = timestamp;
}
/**
* Returns the sensor type as {@link SensorEnum}.
*
* @return the sensorType
*/
public SensorEnum getSensorType() {
return sensorType;
}
/**
* Returns the sensor value.
*
* @return the sensorValue
*/
public Float getSensorValue() {
return sensorValue;
}
/**
* Returns the timestamp as {@link String}.
*
* @return the timestamp
*/
public String getTimestamp() {
return timestamp;
}
/**
* Returns the time stamp as {@link Date}.
*
* @return the timeStamp
*/
public Date getTimestampAsDate() {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SS");
try {
return formatter.parse(timestamp);
} catch (ParseException e) {
logger.error("A ParseException occurred by parsing date string: {}", timestamp, e);
}
return null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "CachedSensorValue [SENSOR_TYPE=" + sensorType + ", SENSOR_VALUE=" + sensorValue + ", TIMESTAMP="
+ timestamp + "]";
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((sensorType == null) ? 0 : sensorType.hashCode());
result = prime * result + ((sensorValue == null) ? 0 : sensorValue.hashCode());
result = prime * result + ((timestamp == null) ? 0 : timestamp.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof CachedSensorValue)) {
return false;
}
CachedSensorValue other = (CachedSensorValue) obj;
if (sensorType != other.sensorType) {
return false;
}
if (sensorValue == null) {
if (other.sensorValue != null) {
return false;
}
} else if (!sensorValue.equals(other.sensorValue)) {
return false;
}
if (timestamp == null) {
if (other.timestamp != null) {
return false;
}
} else if (!timestamp.equals(other.timestamp)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer;
import java.util.LinkedList;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.climate.datatypes.CachedSensorValue;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import com.google.gson.JsonObject;
/**
* The {@link BaseSensorValues} is a base implementation of sensor response of the digitalSTROM-json-API.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public abstract class BaseSensorValues {
private List<CachedSensorValue> sensorValues;
/**
* Adds a sensor value through the digitalSTROM-API response as {@link JsonObject}. The boolean outdoor has to be
* set to indicate that it is an outdoor or indoor value (needed to choose the right sensor type).
*
* @param jObject must not be null
* @param outdoor (true = outdoor; false = indoor)
*/
protected void addSensorValue(JsonObject jObject, boolean outdoor) {
if (jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE.getKey()) != null) {
SensorEnum sensorType = SensorEnum.TEMPERATURE_INDOORS;
if (outdoor) {
sensorType = SensorEnum.TEMPERATURE_OUTDOORS;
}
addSensorValue(new CachedSensorValue(sensorType,
jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE.getKey()).getAsFloat(),
jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE_TIME.getKey()).getAsString()));
}
if (jObject.get(JSONApiResponseKeysEnum.HUMIDITY_VALUE.getKey()) != null) {
SensorEnum sensorType = SensorEnum.RELATIVE_HUMIDITY_INDOORS;
if (outdoor) {
sensorType = SensorEnum.RELATIVE_HUMIDITY_OUTDOORS;
}
addSensorValue(new CachedSensorValue(sensorType,
jObject.get(JSONApiResponseKeysEnum.HUMIDITY_VALUE.getKey()).getAsFloat(),
jObject.get(JSONApiResponseKeysEnum.HUMIDITY_VALUE_TIME.getKey()).getAsString()));
}
if (jObject.get(JSONApiResponseKeysEnum.CO2_CONCENTRATION_VALUE.getKey()) != null) {
addSensorValue(new CachedSensorValue(SensorEnum.CARBON_DIOXIDE,
jObject.get(JSONApiResponseKeysEnum.CO2_CONCENTRATION_VALUE.getKey()).getAsFloat(),
jObject.get(JSONApiResponseKeysEnum.CO2_CONCENTRATION_VALUE_TIME.getKey()).getAsString()));
}
if (jObject.get(JSONApiResponseKeysEnum.BRIGHTNESS_VALUE.getKey()) != null) {
SensorEnum sensorType = SensorEnum.BRIGHTNESS_INDOORS;
if (outdoor) {
sensorType = SensorEnum.BRIGHTNESS_OUTDOORS;
}
addSensorValue(new CachedSensorValue(sensorType,
jObject.get(JSONApiResponseKeysEnum.BRIGHTNESS_VALUE.getKey()).getAsFloat(),
jObject.get(JSONApiResponseKeysEnum.BRIGHTNESS_VALUE_TIME.getKey()).getAsString()));
}
}
private void addSensorValue(CachedSensorValue cachedSensorValue) {
if (sensorValues == null) {
sensorValues = new LinkedList<>();
sensorValues.add(cachedSensorValue);
} else {
sensorValues.add(cachedSensorValue);
}
}
/**
* Returns the available sensor types.
*
* @return available sensor types
*/
public List<SensorEnum> getAvailableSensorTypes() {
List<SensorEnum> sensorTypes = new LinkedList<>();
if (sensorValues != null) {
for (CachedSensorValue cSensorValue : sensorValues) {
sensorTypes.add(cSensorValue.getSensorType());
}
}
return sensorTypes;
}
/**
* Returns the {@link CachedSensorValue}'s as {@link List}.
*
* @return list of {@link CachedSensorValue}'s
*/
public List<CachedSensorValue> getCachedSensorValues() {
return sensorValues;
}
/**
* Returns the {@link CachedSensorValue} of the given sensor type or null, if no {@link CachedSensorValue} for the
* given sensor type exists.
*
* @param sensorType can be null
* @return the {@link CachedSensorValue} of the given sensorType or null
*/
public CachedSensorValue getCachedSensorValue(SensorEnum sensorType) {
if (sensorType != null && sensorValues != null) {
for (CachedSensorValue cSensorValue : sensorValues) {
if (cSensorValue.getSensorType().equals(sensorType)) {
return cSensorValue;
}
}
}
return null;
}
/**
* Returns true, if the given sensor type exist, otherwise false.
*
* @param sensorType can be null
* @return true, if the given sensor type exist, otherwise false
*/
public boolean existSensorValue(SensorEnum sensorType) {
return getCachedSensorValue(sensorType) != null;
}
/**
* Returns true, is sensor values exists, otherwise false.
*
* @return true, if sensor values exists, otherwise false
*/
public boolean existSensorValues() {
return sensorValues != null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "SensorValues [sensorValues=" + sensorValues + "]";
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer;
import org.openhab.binding.digitalstrom.internal.lib.climate.constants.ControlModes;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link BaseTemperatureControl} is a base implementation for temperature controls status and configurations. For
* that it extends the {@link BaseZoneIdentifier}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public abstract class BaseTemperatureControl extends BaseZoneIdentifier {
protected String controlDSUID;
protected Short controlMode;
/**
* Creates a new {@link BaseTemperatureControl} through the {@link JsonObject} which will be returned by an zone
* call.<br>
* Because zone calls do not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public BaseTemperatureControl(JsonObject jObject, Integer zoneID, String zoneName) {
super(zoneID, zoneName);
init(jObject);
}
/**
* Creates a new {@link BaseTemperatureControl} through the {@link JsonObject} which will be returned by an
* apartment call.
*
* @param jObject must not be null
*/
public BaseTemperatureControl(JsonObject jObject) {
super(jObject);
init(jObject);
}
private void init(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_MODE.getKey()) != null) {
this.controlMode = jObject.get(JSONApiResponseKeysEnum.CONTROL_MODE.getKey()).getAsShort();
}
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_DSUID.getKey()) != null) {
this.controlDSUID = jObject.get(JSONApiResponseKeysEnum.CONTROL_DSUID.getKey()).getAsString();
}
}
/**
* Returns the dSUID of the control sensor for heating of the zone.
*
* @return the controlDSUID
*/
public String getControlDSUID() {
return controlDSUID;
}
/**
* Returns controlMode for heating of the zone.
*
* @return the controlMode
*/
public Short getControlMode() {
return controlMode;
}
/**
* Returns true, if heating for this zone is not set off (set {@link ControlModes} = {@link ControlModes#OFF}),
* otherwise false.
*
* @return true, if the set {@link ControlModes} is not {@link ControlModes#OFF}
*/
public Boolean isNotSetOff() {
return !ControlModes.OFF.getID().equals(controlMode);
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link BaseZoneIdentifier} is a base implementation of the {@link ZoneIdentifier}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public abstract class BaseZoneIdentifier implements ZoneIdentifier {
protected Integer zoneID;
protected String zoneName;
/**
* Creates a new {@link BaseZoneIdentifier} with an zone id and zone name.
*
* @param zoneID must not be null
* @param zoneName can be null
*/
public BaseZoneIdentifier(Integer zoneID, String zoneName) {
this.zoneID = zoneID;
this.zoneName = zoneName;
}
/**
* Creates a new {@link BaseZoneIdentifier} through the {@link JsonObject} of the response of an digitalSTROM-API
* apartment call.
*
* @param jObject must not be null
*/
public BaseZoneIdentifier(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.ID.getKey()) != null) {
this.zoneID = jObject.get(JSONApiResponseKeysEnum.ID.getKey()).getAsInt();
}
if (jObject.get(JSONApiResponseKeysEnum.NAME.getKey()) != null) {
this.zoneName = jObject.get(JSONApiResponseKeysEnum.NAME.getKey()).getAsString();
}
}
@Override
public Integer getZoneID() {
return zoneID;
}
@Override
public String getZoneName() {
return zoneName;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer;
/**
* The {@link ZoneIdentifier} can be implement to identify a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface ZoneIdentifier {
/**
* Returns the zoneID of this zone.
*
* @return the zoneID
*/
Integer getZoneID();
/**
* Returns the zoneName of this zone.
*
* @return the zoneName
*/
String getZoneName();
}

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.climate.datatypes.AssignSensorType;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseZoneIdentifier;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link AssignedSensors} acts as container for the digitalSTROM json-method <i>getAssignedSensors</i>. So the
* {@link AssignedSensors} contains all {@link AssignSensorType}s of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class AssignedSensors extends BaseZoneIdentifier {
private List<AssignSensorType> sensors;
/**
* Creates a new {@link AssignedSensors} through the {@link JsonObject} that will be returned by an apartment call.
*
* @param jObject must not be null
*/
public AssignedSensors(JsonObject jObject) {
super(jObject);
init(jObject);
}
/**
* Creates a new {@link AssignedSensors} through the {@link JsonObject} which will be returned by an zone call.
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public AssignedSensors(JsonObject jObject, Integer zoneID, String zoneName) {
super(zoneID, zoneName);
init(jObject);
}
private void init(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.SENSORS.getKey()) != null
&& jObject.get(JSONApiResponseKeysEnum.SENSORS.getKey()).isJsonArray()) {
JsonArray jArray = jObject.get(JSONApiResponseKeysEnum.SENSORS.getKey()).getAsJsonArray();
if (jArray.size() != 0) {
sensors = new LinkedList<>();
Iterator<JsonElement> iter = jArray.iterator();
while (iter.hasNext()) {
JsonObject assignedSensor = iter.next().getAsJsonObject();
Short sensorType = null;
String meterDSUID = null;
if (assignedSensor.get(JSONApiResponseKeysEnum.SENSOR_TYPE.getKey()) != null) {
sensorType = assignedSensor.get(JSONApiResponseKeysEnum.SENSOR_TYPE.getKey()).getAsShort();
}
if (assignedSensor.get(JSONApiResponseKeysEnum.DSUID_LOWER_CASE.getKey()) != null) {
meterDSUID = assignedSensor.get(JSONApiResponseKeysEnum.DSUID_LOWER_CASE.getKey())
.getAsString();
}
sensors.add(new AssignSensorType(SensorEnum.getSensor(sensorType), meterDSUID));
}
}
}
}
/**
* Returns a {@link List} of all assigned sensor types as {@link SensorEnum} of a zone.
*
* @return list of all assigned sensor types
*/
public List<SensorEnum> getAssignedZoneSensorTypes() {
List<SensorEnum> sensorTypes = new LinkedList<>();
if (sensors != null) {
for (AssignSensorType aSensorValue : sensors) {
sensorTypes.add(aSensorValue.getSensorType());
}
}
return sensorTypes;
}
/**
* Returns a {@link List} of all {@link AssignSensorType} of a zone.
*
* @return list of {@link AssignSensorType}s
*/
public List<AssignSensorType> getAssignedSensorTypes() {
return sensors;
}
/**
* Returns the {@link AssignSensorType} of the given sensorType or null, if the given sensorType does not exist.
*
* @param sensorType can be null
* @return the {@link AssignSensorType} of the given sensorType or null
*/
public AssignSensorType getAssignedSensorType(SensorEnum sensorType) {
if (sensorType != null && sensors != null) {
for (AssignSensorType aSensorValue : sensors) {
if (aSensorValue.getSensorType().equals(sensorType)) {
return aSensorValue;
}
}
}
return null;
}
/**
* Returns true, if the given sensorType exists at the zone, otherwise false.
*
* @param sensorType can be null
* @return true, if sensorType exists, otherwise false
*/
public boolean existSensorType(SensorEnum sensorType) {
return getAssignedSensorType(sensorType) != null;
}
/**
* Returns true, if sensors exists at the zone, otherwise false.
*
* @return true, if sensors exists, otherwise false
*/
public boolean existsAssignedSensors() {
return sensors != null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "AssignedSensors [sensors=" + sensors + "]";
}
}

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import java.util.Iterator;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseSensorValues;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.ZoneIdentifier;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link SensorValues} acts as container for the digitalSTROM json-method <i>getSensorValues</i>. So the
* {@link SensorValues} contains all {@link CachedSensorValue}s of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class SensorValues extends BaseSensorValues implements ZoneIdentifier {
private Integer zoneID;
private String zoneName;
/**
* Creates a new {@link SensorValues} through the {@link JsonObject} that will be returned by an apartment call.
*
* @param jObject must not be null
*/
public SensorValues(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.ID.getKey()) != null) {
this.zoneID = jObject.get(JSONApiResponseKeysEnum.ID.getKey()).getAsInt();
}
if (jObject.get(JSONApiResponseKeysEnum.NAME.getKey()) != null) {
this.zoneName = jObject.get(JSONApiResponseKeysEnum.NAME.getKey()).getAsString();
}
init(jObject);
}
/**
* Creates a new {@link SensorValues} through the {@link JsonObject} which will be returned by an zone call.
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public SensorValues(JsonObject jObject, Integer zoneID, String zoneName) {
this.zoneID = zoneID;
this.zoneName = zoneName;
init(jObject);
}
private void init(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.VALUES.getKey()).isJsonArray()) {
JsonArray jArray = jObject.get(JSONApiResponseKeysEnum.VALUES.getKey()).getAsJsonArray();
if (jArray.size() != 0) {
Iterator<JsonElement> iter = jArray.iterator();
while (iter.hasNext()) {
JsonObject cachedSensorValue = iter.next().getAsJsonObject();
super.addSensorValue(cachedSensorValue, false);
}
}
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "SensorValues [zoneID=" + zoneID + ", zoneName=" + zoneName + ", " + super.toString() + "]";
}
@Override
public Integer getZoneID() {
return zoneID;
}
@Override
public String getZoneName() {
return zoneName;
}
}

View File

@@ -0,0 +1,260 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseTemperatureControl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link TemperatureControlConfig} acts as container for the digitalSTROM json-method
* <i>getTemperatureControlConfig</i>. So the {@link TemperatureControlConfig} contains all heating control
* configurations of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class TemperatureControlConfig extends BaseTemperatureControl {
private Integer referenceZone;
private Float ctrlOffset;
private Float manualValue;
private Float emergencyValue;
private Float ctrlKp;
private Float ctrlTs;
private Float ctrlTi;
private Float ctrlKd;
private Float ctrlImin;
private Float ctrlImax;
private Float ctrlYmin;
private Float ctrlYmax;
private Boolean ctrlAntiWindUp;
private Boolean ctrlKeepFloorWarm;
/**
* Creates a new {@link TemperatureControlConfig} through the {@link JsonObject} which will be returned by an
* apartment call.
*
* @param jObject must not be null
*/
public TemperatureControlConfig(JsonObject jObject) {
super(jObject);
init(jObject);
}
/**
* Creates a new {@link TemperatureControlConfig} through the {@link JsonObject} which will be returned by an zone
* call.<br>
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public TemperatureControlConfig(JsonObject jObject, Integer zoneID, String zoneName) {
super(jObject, zoneID, zoneName);
init(jObject);
}
private void init(JsonObject jObject) {
if (isNotSetOff()) {
if (controlMode == 1) {
if (jObject.get(JSONApiResponseKeysEnum.EMERGENCY_VALUE.getKey()) != null) {
this.emergencyValue = jObject.get(JSONApiResponseKeysEnum.EMERGENCY_VALUE.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_KP.getKey()) != null) {
this.ctrlKp = jObject.get(JSONApiResponseKeysEnum.CTRL_KP.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_TS.getKey()) != null) {
this.ctrlTs = jObject.get(JSONApiResponseKeysEnum.CTRL_TS.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_TI.getKey()) != null) {
this.ctrlTi = jObject.get(JSONApiResponseKeysEnum.CTRL_TI.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_KD.getKey()) != null) {
this.ctrlKd = jObject.get(JSONApiResponseKeysEnum.CTRL_KD.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_MIN.getKey()) != null) {
this.ctrlImin = jObject.get(JSONApiResponseKeysEnum.CTRL_MIN.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_MAX.getKey()) != null) {
this.ctrlImax = jObject.get(JSONApiResponseKeysEnum.CTRL_MAX.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_Y_MIN.getKey()) != null) {
this.ctrlYmin = jObject.get(JSONApiResponseKeysEnum.CTRL_Y_MIN.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_Y_MAX.getKey()) != null) {
this.ctrlYmax = jObject.get(JSONApiResponseKeysEnum.CTRL_Y_MAX.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_KEEP_FLOOR_WARM.getKey()) != null) {
this.ctrlKeepFloorWarm = jObject.get(JSONApiResponseKeysEnum.CTRL_KEEP_FLOOR_WARM.getKey())
.getAsBoolean();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_ANTI_WIND_UP.getKey()) != null) {
this.ctrlAntiWindUp = jObject.get(JSONApiResponseKeysEnum.CTRL_ANTI_WIND_UP.getKey())
.getAsBoolean();
}
}
if (controlMode == 2) {
if (jObject.get(JSONApiResponseKeysEnum.REFERENCE_ZONE.getKey()) != null) {
this.referenceZone = jObject.get(JSONApiResponseKeysEnum.REFERENCE_ZONE.getKey()).getAsInt();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_OFFSET.getKey()) != null) {
this.ctrlOffset = jObject.get(JSONApiResponseKeysEnum.CTRL_OFFSET.getKey()).getAsFloat();
}
}
}
}
/**
* Returns the refenceZone, if control-mode is {@link ControlModes#ZONE_FOLLOWER}, otherwise null.
*
* @return the referenceZone
*/
public Integer getReferenceZone() {
return referenceZone;
}
/**
* Returns the ctrlOffset, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlOffset
*/
public Float getCtrlOffset() {
return ctrlOffset;
}
/**
* Returns the manualValue, if control-mode is {@link ControlModes#MANUAL}, otherwise null.
*
* @return the manualValue
*/
public Float getManualValue() {
return manualValue;
}
/**
* Returns the emergencyValue, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the emergencyValue
*/
public Float getEmergencyValue() {
return emergencyValue;
}
/**
* Returns the ctrlKp, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlKp
*/
public Float getCtrlKp() {
return ctrlKp;
}
/**
* Returns the ctrlTs, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlTs
*/
public Float getCtrlTs() {
return ctrlTs;
}
/**
* Returns the ctrlTi, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlTi
*/
public Float getCtrlTi() {
return ctrlTi;
}
/**
* Returns the ctrlKd, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlKd
*/
public Float getCtrlKd() {
return ctrlKd;
}
/**
* Returns the ctrlImin, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlImin
*/
public Float getCtrlImin() {
return ctrlImin;
}
/**
* Returns the ctrlImax, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlImax
*/
public Float getCtrlImax() {
return ctrlImax;
}
/**
* Returns the ctrlYmin, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlYmin
*/
public Float getCtrlYmin() {
return ctrlYmin;
}
/**
* Returns the ctrlYmax, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlYmax
*/
public Float getCtrlYmax() {
return ctrlYmax;
}
/**
* Returns the ctrlAntiWindUp, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlAntiWindUp
*/
public Boolean getCtrlAntiWindUp() {
return ctrlAntiWindUp;
}
/**
* Returns the ctrlKeepFloorWarm, if control-mode is {@link ControlModes#PID_CONTROL}, otherwise null.
*
* @return the ctrlKeepFloorWarm
*/
public Boolean getCtrlKeepFloorWarm() {
return ctrlKeepFloorWarm;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TemperatureControlConfig [referenceZone=" + referenceZone + ", ctrlOffset=" + ctrlOffset
+ ", manualValue=" + manualValue + ", emergencyValue=" + emergencyValue + ", ctrlKp=" + ctrlKp
+ ", ctrlTs=" + ctrlTs + ", ctrlTi=" + ctrlTi + ", ctrlKd=" + ctrlKd + ", ctrlImin=" + ctrlImin
+ ", ctrlImax=" + ctrlImax + ", ctrlYmin=" + ctrlYmin + ", ctrlYmax=" + ctrlYmax + ", ctrlAntiWindUp="
+ ctrlAntiWindUp + ", ctrlKeepFloorWarm=" + ctrlKeepFloorWarm + "]";
}
}

View File

@@ -0,0 +1,203 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseTemperatureControl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link TemperatureControlInternals} acts as container for the digitalSTROM json-method
* <i>getTemperatureControlInternals</i>. So the {@link TemperatureControlInternals} contains all internal heating
* control configurations of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class TemperatureControlInternals extends BaseTemperatureControl {
private Short controlState;
private Float ctrlTRecent;
private Float ctrlTReference;
private Float ctrlTError;
private Float ctrlTErrorPrev;
private Float ctrlIntegral;
private Float ctrlYp;
private Float ctrlYi;
private Float ctrlYd;
private Float ctrlY;
private Short ctrlAntiWindUp;
/**
* Creates a new {@link TemperatureControlInternals} through the {@link JsonObject} which will be returned by an
* zone
* call.<br>
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public TemperatureControlInternals(JsonObject jObject, Integer zoneID, String zoneName) {
super(jObject, zoneID, zoneName);
if (isNotSetOff()) {
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_STATE.getKey()) != null) {
this.controlState = jObject.get(JSONApiResponseKeysEnum.CONTROL_STATE.getKey()).getAsShort();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_T_RECENT.getKey()) != null) {
this.ctrlTRecent = jObject.get(JSONApiResponseKeysEnum.CTRL_T_RECENT.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_T_REFERENCE.getKey()) != null) {
this.ctrlTReference = jObject.get(JSONApiResponseKeysEnum.CTRL_T_REFERENCE.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_T_ERROR.getKey()) != null) {
this.ctrlTError = jObject.get(JSONApiResponseKeysEnum.CTRL_T_ERROR.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_T_ERROR_PREV.getKey()) != null) {
this.ctrlTErrorPrev = jObject.get(JSONApiResponseKeysEnum.CTRL_T_ERROR_PREV.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_INTEGRAL.getKey()) != null) {
this.ctrlIntegral = jObject.get(JSONApiResponseKeysEnum.CTRL_INTEGRAL.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_YP.getKey()) != null) {
this.ctrlY = jObject.get(JSONApiResponseKeysEnum.CTRL_YP.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_YI.getKey()) != null) {
this.ctrlYi = jObject.get(JSONApiResponseKeysEnum.CTRL_YI.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_YD.getKey()) != null) {
this.ctrlYd = jObject.get(JSONApiResponseKeysEnum.CTRL_YD.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_Y.getKey()) != null) {
this.ctrlY = jObject.get(JSONApiResponseKeysEnum.CTRL_Y.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CTRL_ANTI_WIND_UP.getKey()) != null) {
this.ctrlAntiWindUp = jObject.get(JSONApiResponseKeysEnum.CTRL_ANTI_WIND_UP.getKey()).getAsShort();
}
}
}
/**
* Returns the controleState for heating of the zone.
*
* @return the controlState
*/
public Short getControlState() {
return controlState;
}
/**
* Returns the ctrlTRecent for heating of the zone.
*
* @return the ctrlTRecent
*/
public Float getCtrlTRecent() {
return ctrlTRecent;
}
/**
* Returns the ctrlTReference for heating of the zone.
*
* @return the ctrlTReference
*/
public Float getCtrlTReference() {
return ctrlTReference;
}
/**
* Returns the ctrlTError for heating of the zone.
*
* @return the ctrlTError
*/
public Float getCtrlTError() {
return ctrlTError;
}
/**
* Returns the ctrlTErrorPrev for heating of the zone.
*
* @return the ctrlTErrorPrev
*/
public Float getCtrlTErrorPrev() {
return ctrlTErrorPrev;
}
/**
* Returns the ctrlIntegral for heating of the zone.
*
* @return the ctrlIntegral
*/
public Float getCtrlIntegral() {
return ctrlIntegral;
}
/**
* Returns the ctrlYp for heating of the zone.
*
* @return the ctrlYp
*/
public Float getCtrlYp() {
return ctrlYp;
}
/**
* Returns the ctrlYi for heating of the zone.
*
* @return the ctrlYi
*/
public Float getCtrlYi() {
return ctrlYi;
}
/**
* Returns the ctrlYd for heating of the zone.
*
* @return the ctrlYd
*/
public Float getCtrlYd() {
return ctrlYd;
}
/**
* Returns the ctrlY for heating of the zone.
*
* @return the ctrlY
*/
public Float getCtrlY() {
return ctrlY;
}
/**
* Returns the ctrlAntiWindUp for heating of the zone.
*
* @return the ctrlAntiWindUp
*/
public Short getCtrlAntiWindUp() {
return ctrlAntiWindUp;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TemperatureControlInternals [controlState=" + controlState + ", ctrlTRecent=" + ctrlTRecent
+ ", ctrlTReference=" + ctrlTReference + ", ctrlTError=" + ctrlTError + ", ctrlTErrorPrev="
+ ctrlTErrorPrev + ", ctrlIntegral=" + ctrlIntegral + ", ctrlYp=" + ctrlYp + ", ctrlYi=" + ctrlYi
+ ", ctrlYd=" + ctrlYd + ", ctrlY=" + ctrlY + ", ctrlAntiWindUp=" + ctrlAntiWindUp + "]";
}
}

View File

@@ -0,0 +1,216 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseTemperatureControl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link TemperatureControlStatus} acts as container for the digitalSTROM json-method
* <i>getTemperatureControlStatus</i>. So the {@link TemperatureControlStatus} contains all heating
* control status information of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class TemperatureControlStatus extends BaseTemperatureControl {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
private Short controlState;
private Short operationMode;
private Float temperature;
private String temperatureTime;
private Float nominalValue;
private String nominalValueTime;
private Float controlValue;
private String controlValueTime;
/**
* Creates a new {@link TemperatureControlStatus} through the {@link JsonObject} which will be returned by an
* apartment call.
*
* @param jObject must not be null
*/
public TemperatureControlStatus(JsonObject jObject) {
super(jObject);
init(jObject);
}
/**
* Creates a new {@link TemperatureControlStatus} through the {@link JsonObject} which will be returned by an zone
* call.<br>
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public TemperatureControlStatus(JsonObject jObject, Integer zoneID, String zoneName) {
super(jObject, zoneID, zoneName);
init(jObject);
}
private void init(JsonObject jObject) {
if (isNotSetOff()) {
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_STATE.getKey()) != null) {
this.controlState = jObject.get(JSONApiResponseKeysEnum.CONTROL_STATE.getKey()).getAsShort();
}
if (jObject.get(JSONApiResponseKeysEnum.OPERATION_MODE.getKey()) != null) {
this.operationMode = jObject.get(JSONApiResponseKeysEnum.OPERATION_MODE.getKey()).getAsShort();
}
if (jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE.getKey()) != null) {
this.temperature = jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.NOMINAL_VALUE.getKey()) != null) {
this.nominalValue = jObject.get(JSONApiResponseKeysEnum.NOMINAL_VALUE.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_VALUE.getKey()) != null) {
this.controlValue = jObject.get(JSONApiResponseKeysEnum.CONTROL_VALUE.getKey()).getAsFloat();
}
if (jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE_TIME.getKey()) != null) {
this.temperatureTime = jObject.get(JSONApiResponseKeysEnum.TEMPERATION_VALUE_TIME.getKey())
.getAsString();
}
if (jObject.get(JSONApiResponseKeysEnum.NOMINAL_VALUE_TIME.getKey()) != null) {
this.nominalValueTime = jObject.get(JSONApiResponseKeysEnum.NOMINAL_VALUE_TIME.getKey()).getAsString();
}
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_VALUE_TIME.getKey()) != null) {
this.controlValueTime = jObject.get(JSONApiResponseKeysEnum.CONTROL_VALUE_TIME.getKey()).getAsString();
}
}
}
/**
* Returns the controleState for heating of the zone.
*
* @return the controlState
*/
public Short getControlState() {
return controlState;
}
/**
* Returns the operationMode for heating of the zone.
*
* @return the operationMode
*/
public Short getOperationMode() {
return operationMode;
}
/**
* Returns the current temperature of the zone.
*
* @return the temperature
*/
public Float getTemperature() {
return temperature;
}
/**
* Returns the timestamp when the temperature was read out as {@link Date}.
*
* @return the temperatureTime
* @throws ParseException see {@link DateFormat#parse(String)}
*/
public Date getTemperatureTimeAsDate() throws ParseException {
return formatter.parse(temperatureTime);
}
/**
* Returns the timestamp when the temperature was read out as {@link String}.
*
* @return the temperatureTime
*/
public String getTemperatureTimeAsString() {
return temperatureTime;
}
/**
* Returns the nominal value for heating of the zone.
*
* @return the nominalValue
*/
public Float getNominalValue() {
return nominalValue;
}
/**
* Returns the timestamp as {@link Date} for the nominal value of the zone.
*
* @return the nominalValueTime
* @throws ParseException see {@link DateFormat#parse(String)}
*/
public Date getNominalValueTimeAsDate() throws ParseException {
return formatter.parse(nominalValueTime);
}
/**
* Returns the timestamp as {@link String} for the nominal value of the zone.
*
* @return the nominalValueTime
*/
public String getNominalValueTimeAsString() {
return nominalValueTime;
}
/**
* Returns the control value for heating of the zone.
*
* @return the controlValue
*/
public Float getControlValue() {
return controlValue;
}
/**
* Returns timestamp as {@link Date} for the control value for heating of the zone.
*
* @return the controlValueTime
* @throws ParseException see {@link DateFormat#parse(String)}
*/
public Date getControlValueTimeAsDate() throws ParseException {
return formatter.parse(controlValueTime);
}
/**
* Returns timestamp as {@link String} for the control value for heating of the zone.
*
* @return the controlValueTime
*/
public String getControlValueTimeAsString() {
return controlValueTime;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TemperatureControlStatus [controlMode=" + controlMode + ", controlState=" + controlState
+ ", operationMode=" + operationMode + ", temperature=" + temperature + ", temperatureTime="
+ temperatureTime + ", nominalValue=" + nominalValue + ", nominalValueTime=" + nominalValueTime
+ ", controlValue=" + controlValue + ", controlValueTime=" + controlValueTime + "]";
}
}

View File

@@ -0,0 +1,136 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.lib.climate.constants.OperationModes;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseZoneIdentifier;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonObject;
/**
* The {@link TemperatureControlValues} acts as container for the digitalSTROM json-method
* <i>getTemperatureControlValues</i>. So the {@link TemperatureControlValues} contains all temperature
* control values information of a zone.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class TemperatureControlValues extends BaseZoneIdentifier {
private Map<OperationModes, Float> temperatureControlValues;
private String controlDSUID;
private Boolean isConfigured;
/**
* Creates a new {@link TemperatureControlValues} through the {@link JsonObject} which will be returned by an
* apartment call.
*
* @param jObject must not be null
*/
public TemperatureControlValues(JsonObject jObject) {
super(jObject);
init(jObject);
}
/**
* Creates a new {@link TemperatureControlValues} through the {@link JsonObject} which will be returned by an zone
* call.<br>
* Because of zone calls does not include a zoneID or zoneName in the json response, the zoneID and zoneName have to
* be handed over the constructor.
*
* @param jObject must not be null
* @param zoneID must not be null
* @param zoneName can be null
*/
public TemperatureControlValues(JsonObject jObject, Integer zoneID, String zoneName) {
super(zoneID, zoneName);
init(jObject);
}
private void init(JsonObject jObject) {
if (jObject.get(JSONApiResponseKeysEnum.IS_CONFIGURED.getKey()) != null) {
this.isConfigured = jObject.get(JSONApiResponseKeysEnum.IS_CONFIGURED.getKey()).getAsBoolean();
}
if (isConfigured) {
if (jObject.get(JSONApiResponseKeysEnum.CONTROL_DSUID.getKey()) != null) {
this.controlDSUID = jObject.get(JSONApiResponseKeysEnum.CONTROL_DSUID.getKey()).getAsString();
}
temperatureControlValues = new HashMap<>(OperationModes.values().length);
for (OperationModes opMode : OperationModes.values()) {
if (jObject.get(opMode.getKey()) != null) {
temperatureControlValues.put(opMode, jObject.get(opMode.getKey()).getAsFloat());
}
}
}
}
/**
* @see TemperatureControlStatus#getControlDSUID()
* @return the controlDSUID
*/
public String getControlDSUID() {
return controlDSUID;
}
/**
* @see TemperatureControlStatus#getIsConfigured()
* @return the isConfigured
*/
public Boolean getIsConfigured() {
return isConfigured;
}
/**
* Returns the set temperature of the given operation mode.
*
* @param operationMode must not be null
* @return temperature of the operation mode
*/
public Float getTemperation(OperationModes operationMode) {
return temperatureControlValues.get(operationMode);
}
/**
* Returns the available operation modes as {@link Set}.
*
* @return available operation modes
*/
public Set<OperationModes> getOperationModes() {
return temperatureControlValues.keySet();
}
/**
* Returns a {@link Map} that maps the available operation modes to the set values.
*
* @return Map with operation modes and their values
*/
public Map<OperationModes, Float> getTemperatureControlValues() {
return temperatureControlValues;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "TemperatureControlValues [temperatureControlValues=" + temperatureControlValues + ", controlDSUID="
+ controlDSUID + ", isConfigured=" + isConfigured + "]";
}
}

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.BaseSensorValues;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
/**
* The {@link WeatherSensorData} acts as container for the digitalSTROM json-method <i>getSensorValues</i>. The
* {@link WeatherSensorData} contains all {@link CachedSensorValue}s and weather service information of the
* digitalSTROM-server, if a weather service is set.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class WeatherSensorData extends BaseSensorValues {
private final Logger logger = LoggerFactory.getLogger(WeatherSensorData.class);
private String weatherIconId;
private String weatherConditionId;
private String weatherServiceId;
private String weatherServiceTime;
/**
* Creates a new {@link SensorValues} through the {@link JsonObject} that will be returned by an apartment call.
*
* @param jObject must not be null
*/
public WeatherSensorData(JsonObject jObject) {
super.addSensorValue(jObject, true);
if (jObject.get(JSONApiResponseKeysEnum.WEATHER_ICON_ID.getKey()) != null) {
weatherIconId = jObject.get(JSONApiResponseKeysEnum.WEATHER_ICON_ID.getKey()).getAsString();
}
if (jObject.get(JSONApiResponseKeysEnum.WEATHER_CONDITION_ID.getKey()) != null) {
weatherConditionId = jObject.get(JSONApiResponseKeysEnum.WEATHER_CONDITION_ID.getKey()).getAsString();
}
if (jObject.get(JSONApiResponseKeysEnum.WEATHER_SERVICE_ID.getKey()) != null) {
weatherServiceId = jObject.get(JSONApiResponseKeysEnum.WEATHER_SERVICE_ID.getKey()).getAsString();
}
if (jObject.get(JSONApiResponseKeysEnum.WEATHER_SERVICE_TIME.getKey()) != null) {
weatherServiceTime = jObject.get(JSONApiResponseKeysEnum.WEATHER_SERVICE_TIME.getKey()).getAsString();
}
}
/**
* Returns the weather icon id of the set weather service.
*
* @return the weatherIconId
*/
public String getWeatherIconId() {
return weatherIconId;
}
/**
* Returns the weather condition id of the set weather service.
*
* @return the weatherConditionId
*/
public String getWeatherConditionId() {
return weatherConditionId;
}
/**
* Returns the weather service id of the set weather service.
*
* @return the weatherServiceId
*/
public String getWeatherServiceId() {
return weatherServiceId;
}
/**
* Returns the weather service time as {@link String} of the set weather service.
*
* @return the weatherServiceTime as {@link String}
*/
public String getWeatherServiceTimeAsSting() {
return weatherServiceTime;
}
/**
* Returns the weather service time as {@link Date} of the set weather service.
*
* @return the weatherServiceTime as {@link Date}
*/
public Date getWeatherServiceTimeAsDate() {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SS");
try {
return formatter.parse(weatherServiceTime);
} catch (ParseException e) {
logger.error("A ParseException occurred by parsing date string: {}", weatherServiceTime, e);
}
return null;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "WeatherSensorData [weatherIconId=" + weatherIconId + ", weatherConditionId=" + weatherConditionId
+ ", weatherServiceId=" + weatherServiceId + ", weatherServiceTime=" + weatherServiceTime + ", "
+ super.toString() + "]";
}
}

View File

@@ -0,0 +1,622 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.config;
/**
* The {@link Config} contains all configurations for the digitalSTROM-Library.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class Config {
/* Client configuration */
// connection configuration
/**
* The default application name to generate the application token.
*/
public static final String DEFAULT_APPLICATION_NAME = "openHAB";
/**
* Defines the used tread pool name to get a thread pool from {@link ThreadPoolManager}.
*/
public static final String THREADPOOL_NAME = "digitalSTROM";
private String applicationName = DEFAULT_APPLICATION_NAME;
private String host;
private String userName;
private String password;
private String appToken;
private String trustCertPath;
private String cert;
// Timeouts
/**
* Default connection timeout
*/
public static final int DEFAULT_CONNECTION_TIMEOUT = 4000;
/**
* High connection timeout for requests that take some time.
*/
public static final int HIGH_CONNECTION_TIMEOUT = 60000;
/**
* Default read timeout
*/
public static final int DEFAULT_READ_TIMEOUT = 10000;
/**
* High read timeout for requests that take some time.
*/
public static final int HIGH_READ_TIMEOUT = 60000;
/**
* Default connection timeout for sensor readings from devices
*/
public static final int DEFAULT_SENSORDATA_CONNECTION_TIMEOUT = 4000;
/**
* Default read timeout for sensor readings from devices
*/
public static final int DEFAULT_SENSORDATA_READ_TIMEOUT = 20000;
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
private int readTimeout = DEFAULT_READ_TIMEOUT;
private int sensordataConnectionTimeout = DEFAULT_SENSORDATA_CONNECTION_TIMEOUT;
private int sensordataReadTimeout = DEFAULT_SENSORDATA_READ_TIMEOUT;
/* Internal Configurations */
// Trash Bin Config
/**
* The default number of days after the trash devices is deleted.
*/
public static final int DEFAULT_TRASH_DEVICE_DELETE_TIME = 7;
private int trashDeviceDeleteTime = DEFAULT_TRASH_DEVICE_DELETE_TIME;
/**
* The default milliseconds after the trash devices will be checked if its time to delete.
*/
public static final int DEFAULT_BIN_CHECK_TIME = 360000; // in milliseconds
private int binCheckTime = DEFAULT_BIN_CHECK_TIME; // in milliseconds
// Device update config
/**
* Default interval of the polling frequency in milliseconds. The digitalSTROM-rules state that the
* polling interval must to be at least 1 second.
*/
public static final int DEFAULT_POLLING_FREQUENCY = 1000; // in milliseconds
private int pollingFrequency = DEFAULT_POLLING_FREQUENCY; // in milliseconds
/* Sensordata */
// Sensodata read config
/**
* The default interval to refresh the sensor data.
*/
public static final int DEFAULT_SENSORDATA_REFRESH_INTERVAL = 60000;
private int sensordataRefreshInterval = DEFAULT_SENSORDATA_REFRESH_INTERVAL;
/**
* The default interval to refresh the total power sensor data.
*/
public static final int DEFAULT_TOTAL_POWER_UPDATE_INTERVAL = 30000;
private int totalPowerUpdateInterval = DEFAULT_TOTAL_POWER_UPDATE_INTERVAL;
/**
* Default time to wait between another {@link SensorJob} can be executed on a circuit.
*/
public static final int DEFAULT_SENSOR_READING_WAIT_TIME = 60000;
private int sensorReadingWaitTime = DEFAULT_SENSOR_READING_WAIT_TIME;
// sensor data Prioritys
/**
* Priority for never refresh the sensor value.
*/
public static final String REFRESH_PRIORITY_NEVER = "never";
/**
* Priority for refresh the sensor value with low priority.
*/
public static final String REFRESH_PRIORITY_LOW = "low";
/**
* Priority for refresh the sensor value with medium priority.
*/
public static final String REFRESH_PRIORITY_MEDIUM = "medium";
/**
* Priority for refresh the sensor value with high priority.
*/
public static final String REFRESH_PRIORITY_HIGH = "high";
// max sensor reading cyclic to wait
/**
* The default factor to prioritize medium {@link SensorJob}s down.
*/
public static final long DEFAULT_MEDIUM_PRIORITY_FACTOR = 5;
/**
* The default factor to prioritize low {@link SensorJob}s down.
*/
public static final long DEFAULT_LOW_PRIORITY_FACTOR = 10;
private long mediumPriorityFactor = DEFAULT_MEDIUM_PRIORITY_FACTOR;
private long lowPriorityFactor = DEFAULT_LOW_PRIORITY_FACTOR;
/**
* Defines the event polling interval of the {@link EventListener} in milliseconds.
*/
private int eventListenerRefreshinterval = DEFAULT_POLLING_FREQUENCY;
/**
* The default max standby active power for a device. It's needed to set a {@link Device} with output mode
* {@link OutputModeEnum#WIPE} on if it isen't any more in standby mode.
*/
public static final int DEFAULT_STANDBY_ACTIVE_POWER = 2;
private int standbyActivePower = DEFAULT_STANDBY_ACTIVE_POWER;
/**
* Creates a new {@link Config} and set the given hostAddress, userName, password and applicationToken. The other
* configurations will be set to default.
*
* @param hostAddress of the digitalSTROM-Server, must not be null
* @param userName to login, can be null
* @param password to login, can be null
* @param applicationToken to login , can be null
*/
public Config(String hostAddress, String userName, String password, String applicationToken) {
this.host = hostAddress;
this.userName = userName;
this.password = password;
this.appToken = applicationToken;
}
/**
* Creates a {@link Config} with default values.
*/
public Config() {
// config with default values
}
/**
* Returns the host name from the server.
*
* @return the host address
*/
public String getHost() {
return host;
}
/**
* Sets the host name of the Server.
* <br>
* <br>
* <b>Note:</b><br>
* If the host dosen't use the default port (8080), the port has to be set after the host name. e.g.
* <i>my-digitalSTROM-Server.com:58080</i>
*
* @param hostAddress of the digitalSTROM-Server
*/
public void setHost(String hostAddress) {
this.host = hostAddress;
}
/**
* Returns the username.
*
* @return the username
*/
public String getUserName() {
return userName;
}
/**
* Sets the username.
*
* @param userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* Returns the password.
*
* @return the password
*/
public String getPassword() {
return password;
}
/**
* Sets the password.
*
* @param password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns the Application-Token.
*
* @return the Application-Token
*/
public String getAppToken() {
return appToken;
}
/**
* Sets the Application-Token.
*
* @param applicationToken to set
*/
public void setAppToken(String applicationToken) {
this.appToken = applicationToken;
}
/**
* Returns the connection timeout.
*
* @return the connection timeout
*/
public int getConnectionTimeout() {
return connectionTimeout;
}
/**
* Sets the connection timeout.
*
* @param connectionTimeout to set
*/
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
/**
* Returns the read timeout.
*
* @return the read timeout
*/
public int getReadTimeout() {
return readTimeout;
}
/**
* Sets the read timeout.
*
* @param readTimeout the readTimeout to set
*/
public void setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
}
/**
* Returns the connection timeout for sensor readings from devices.
*
* @return the connection sensor data timeout
*/
public int getSensordataConnectionTimeout() {
return sensordataConnectionTimeout;
}
/**
* Sets the connection timeout for sensor readings from devices.
*
* @param sensordataConnectionTimeout to set
*/
public void setSensordataConnectionTimeout(int sensordataConnectionTimeout) {
this.sensordataConnectionTimeout = sensordataConnectionTimeout;
}
/**
* Returns the read timeout for sensor readings from devices.
*
* @return the read sensor data timeout
*/
public int getSensordataReadTimeout() {
return sensordataReadTimeout;
}
/**
* Sets the connection timeout for sensor readings from devices.
*
* @param sensordataReadTimeout to set
*/
public void setSensordataReadTimeout(int sensordataReadTimeout) {
this.sensordataReadTimeout = sensordataReadTimeout;
}
/**
* Returns the path to the SSL-Certificate.
*
* @return the path to the trust certification
*/
public String getTrustCertPath() {
return trustCertPath;
}
/**
* Return the SSL-Certificate of the server.
*
* @return the SSL-Certificate of the server
*/
public String getCert() {
return cert;
}
/**
* Sets the SSL-Certificate of the server, will be set automatically by the {@link HttpTransportImpl} if no
* SSL-Certification path is set.
*
* @param cert of the digitalSTROM-Server to set
*/
public void setCert(String cert) {
this.cert = cert;
}
/**
* Sets the path to the SSL-Certificate. It can be a absolute or relative path.
*
* @param trustCertPath path to a SSL-Certificate
*/
public void setTrustCertPath(String trustCertPath) {
this.trustCertPath = trustCertPath;
}
/**
* Returns the number of days after the trash devices is deleted.
*
* @return the trash-device delete time in days
*/
public int getTrashDeviceDeleteTime() {
return trashDeviceDeleteTime;
}
/**
* Sets the number of days after the trash devices is deleted.
*
* @param trashDeviceDeleteTime in days
*/
public void setTrashDeviceDeleteTime(int trashDeviceDeleteTime) {
this.trashDeviceDeleteTime = trashDeviceDeleteTime;
}
/**
* Sets the milliseconds after the trash devices will be checked, if its time to delete.
*
* @return the bin check time in milliseconds
*/
public int getBinCheckTime() {
return binCheckTime;
}
/**
* Returns the milliseconds after the trash devices will be checked, if its time to delete.
*
* @param binCheckTime in milliseconds
*/
public void setBinCheckTime(int binCheckTime) {
this.binCheckTime = binCheckTime;
}
/**
* Returns the interval of the polling frequency in milliseconds. The digitalSTROM-rules state that the
* polling interval must to be at least 1 second.
*
* @return the pollingFrequency in milliseconds
*/
public int getPollingFrequency() {
return pollingFrequency;
}
/**
* Sets the interval of the polling frequency in milliseconds. The digitalSTROM-rules state that the
* polling interval must to be at least 1 second.
*
* @param pollingFrequency to set
*/
public void setPollingFrequency(int pollingFrequency) {
this.pollingFrequency = pollingFrequency;
}
/**
* Returns the interval in milliseconds to refresh the sensor data.
*
* @return the sensor data refresh interval in milliseconds
*/
public int getSensordataRefreshInterval() {
return sensordataRefreshInterval;
}
/**
* Sets the interval in milliseconds to refresh the sensor data.
*
* @param sensordataRefreshInterval in milliseconds.
*/
public void setSensordataRefreshInterval(int sensordataRefreshInterval) {
this.sensordataRefreshInterval = sensordataRefreshInterval;
}
/**
* Returns the interval to refresh the total power sensor data.
*
* @return the total power update interval in milliseconds.
*/
public int getTotalPowerUpdateInterval() {
return totalPowerUpdateInterval;
}
/**
* Sets the interval in milliseconds to refresh the total power sensor data.
*
* @param totalPowerUpdateInterval in milliseconds
*/
public void setTotalPowerUpdateInterval(int totalPowerUpdateInterval) {
this.totalPowerUpdateInterval = totalPowerUpdateInterval;
}
/**
* Returns the time in milliseconds to wait between another {@link SensorJob} can be executed on a circuit.
*
* @return the sensor reading wait time in milliseconds
*/
public int getSensorReadingWaitTime() {
return sensorReadingWaitTime;
}
/**
* Sets the time in milliseconds to wait between another {@link SensorJob} can be executed on a circuit.
*
* @param sensorReadingWaitTime in milliseconds
*/
public void setSensorReadingWaitTime(int sensorReadingWaitTime) {
this.sensorReadingWaitTime = sensorReadingWaitTime;
}
/**
* Returns the factor to prioritize medium {@link SensorJob}s in the {@link SensorJobExecutor} down.
*
* @return the medium priority factor
*/
public long getMediumPriorityFactor() {
return mediumPriorityFactor;
}
/**
* Sets the factor to prioritize medium {@link SensorJob}s in the {@link SensorJobExecutor} down.
*
* @param mediumPriorityFactor to set
*/
public void setMediumPriorityFactor(long mediumPriorityFactor) {
this.mediumPriorityFactor = mediumPriorityFactor;
}
/**
* Returns the factor to prioritize low {@link SensorJob}s in the {@link SensorJobExecutor} down.
*
* @return the low priority factor
*/
public long getLowPriorityFactor() {
return lowPriorityFactor;
}
/**
* Sets the factor to prioritize low {@link SensorJob}s in the {@link SensorJobExecutor}down.
*
* @param lowPriorityFactor to set
*/
public void setLowPriorityFactor(long lowPriorityFactor) {
this.lowPriorityFactor = lowPriorityFactor;
}
/**
* Returns the polling interval in milliseconds to poll the {@link Event}s in the {@link EventListener}.
*
* @return the EventListener refresh interval in milliseconds
*/
public int getEventListenerRefreshinterval() {
return eventListenerRefreshinterval;
}
/**
* Sets the polling interval in milliseconds to poll the {@link Event}s in the {@link EventListener}.
*
* @param eventListenerRefreshinterval to set
*/
public void setEventListenerRefreshinterval(int eventListenerRefreshinterval) {
this.eventListenerRefreshinterval = eventListenerRefreshinterval;
}
/**
* Returns the max standby active power for a device. It's needed to set a {@link Device} with output mode
* {@link OutputModeEnum#WIPE} on if it isen't any more in standby mode.
*
* @return the standby active power
*/
public int getStandbyActivePower() {
return standbyActivePower;
}
/**
* Sets the max standby active power for a device. It's needed to set a {@link Device} with output mode
* {@link OutputModeEnum#WIPE} on if it isen't any more in standby mode.
*
* @param standbyActivePower to set
*/
public void setStandbyActivePower(int standbyActivePower) {
this.standbyActivePower = standbyActivePower;
}
/**
* Returns the application name to generate the application token.
*
* @return the applicationName
*/
public String getApplicationName() {
return applicationName;
}
/**
* Sets the application name to generate the application token.
*
* @param applicationName to set
*/
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
/**
* Removes the configured username and password from this {@link Config}.
*/
public void removeUsernameAndPassword() {
this.userName = null;
this.password = null;
}
/**
* Updates this {@link Config} with the configuration of given {@link Config}.
*
* @param config new config
*/
public void updateConfig(Config config) {
setHost(config.getHost());
setUserName(config.getUserName());
setPassword(config.getPassword());
setAppToken(config.getAppToken());
setConnectionTimeout(config.getConnectionTimeout());
setReadTimeout(config.getReadTimeout());
setSensordataConnectionTimeout(config.getSensordataConnectionTimeout());
setSensordataReadTimeout(config.getSensordataReadTimeout());
setTrustCertPath(config.getTrustCertPath());
setTrashDeviceDeleteTime(config.getTrashDeviceDeleteTime());
setBinCheckTime(config.getBinCheckTime());
setPollingFrequency(config.getPollingFrequency());
setSensordataRefreshInterval(config.getSensordataRefreshInterval());
setTotalPowerUpdateInterval(config.getTotalPowerUpdateInterval());
setSensorReadingWaitTime(config.getSensorReadingWaitTime());
setMediumPriorityFactor(config.getMediumPriorityFactor());
setLowPriorityFactor(config.getLowPriorityFactor());
setEventListenerRefreshinterval(config.getEventListenerRefreshinterval());
setStandbyActivePower(config.getStandbyActivePower());
setApplicationName(config.getApplicationName());
}
@Override
public String toString() {
return "Config [applicationName=" + applicationName + ", host=" + host + ", userName=" + userName
+ ", password=" + password + ", appToken=" + appToken + ", connectionTimeout=" + connectionTimeout
+ ", readTimeout=" + readTimeout + ", sensordataConnectionTimeout=" + sensordataConnectionTimeout
+ ", sensordataReadTimeout=" + sensordataReadTimeout + ", trustCertPath=" + trustCertPath
+ ", trashDeviceDeleteTime=" + trashDeviceDeleteTime + ", binCheckTime=" + binCheckTime
+ ", pollingFrequency=" + pollingFrequency + ", sensordataRefreshInterval=" + sensordataRefreshInterval
+ ", totalPowerUpdateInterval=" + totalPowerUpdateInterval + ", sensorReadingWaitTime="
+ sensorReadingWaitTime + ", mediumPriorityFactor=" + mediumPriorityFactor + ", lowPriorityFactor="
+ lowPriorityFactor + ", eventListenerRefreshinterval=" + eventListenerRefreshinterval
+ ", standbyActivePower=" + standbyActivePower + "]";
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
/**
* The {@link EventHandler} can be implemented to get informed by {@link EventItem}'s through the {@link EventListener}.
* <br>
* For that the {@link #getSupportetEvents()} and {@link #supportsEvent(String)} methods have to be implemented, so that
* the {@link EventListener} knows whitch events it has to subscribe at the digitalSTROM-server and which handler has
* to be informed. <br>
* The implementation of the {@link EventHandler} also has to be registered through
* {@link EventListener#addEventHandler(EventHandler)} to the {@link EventListener} and the {@link EventListener} has to
* be started.<br>
* <br>
* To handle the {@link EventItem} the method {@link #handleEvent(EventItem)} has to be implemented.
*
* @author Michael Ochel
* @author Matthias Siegele
*/
public interface EventHandler {
/**
* Handles a {@link EventItem} e.g. which was detected by the {@link EventListener}.
*
* @param eventItem to handle
*/
void handleEvent(EventItem eventItem);
/**
* Returns a {@link List} that contains the supported events.
*
* @return supported events
*/
List<String> getSupportedEvents();
/**
* Returns true, if the {@link EventHandler} supports the given event.
*
* @param eventName to check
* @return true, if event is supported, otherwise false
*/
boolean supportsEvent(String eventName);
/**
* Returns the unique id of the {@link EventHandler}.
*
* @return uid of the EventHandler
*/
String getUID();
/**
* Sets a {@link EventListener} to this {@link EventHandler}.
*
* @param eventListener to set
*/
void setEventListener(EventListener eventListener);
/**
* Unsets a {@link EventListener} to this {@link EventHandler}.
*
* @param eventListener to unset
*/
void unsetEventListener(EventListener eventListener);
}

View File

@@ -0,0 +1,419 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.event.types.Event;
import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
import org.openhab.binding.digitalstrom.internal.lib.event.types.JSONEventImpl;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl.JSONResponseHandler;
import org.openhab.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link EventListener} listens for events which will be thrown by the digitalSTROM-Server and it notifies the
* added {@link EventHandler} about the detected events, if it supports the event-type.<br>
* You can add {@link EventHandler}'s through the constructors or the methods {@link #addEventHandler(EventHandler)} and
* {@link #addEventHandlers(List)}.<br>
* You can also delete a {@link EventHandler} though the method {@link #removeEventHandler(EventHandler)}.<br>
* If the {@link EventListener} is started, both methods subscribe respectively unsubscribe the event-types of the
* {@link EventHandler}/s automatically.<br>
* If you want to dynamically subscribe event-types, e.g. because a configuration has changed and a
* {@link EventHandler} needs to be informed of another event-type, you can use the methods
* {@link #addSubscribe(String)} or {@link #addSubscribeEvents(List)} to add more than one event-type. To remove a
* subscribed event you can use the method {@link #removeSubscribe(String, String)}, you also have to change the return
* of the {@link EventHandler} methods {@link EventHandler#getSupportetEvents()} and
* {@link EventHandler#supportsEvent(String)}.
* <br>
* To start and stop the listening you have to call the methods {@link #start()} and {@link #stop()}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class EventListener {
private final Logger logger = LoggerFactory.getLogger(EventListener.class);
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
public static final int SUBSCRIBE_DELAY = 500;
private ScheduledFuture<?> pollingScheduler;
private ScheduledFuture<?> subscriptionScheduler;
private int subscriptionID = 15;
private final int timeout = 500;
private final List<String> subscribedEvents = Collections.synchronizedList(new LinkedList<>());
private boolean subscribed = false;
// error message
public static final String INVALID_SESSION = "Invalid session!";
public static final String TOKEN_NOT_FOUND = "token not found."; // Complete text: "Event subscription token not
// found."
private final ConnectionManager connManager;
private final List<EventHandler> eventHandlers = Collections.synchronizedList(new LinkedList<>());
private final Config config;
private boolean isStarted = false;
/**
* Creates a new {@link EventListener} to listen to the supported event-types of the given eventHandler and notify
* about a detected event.<br>
* <br>
* To get notified by events you have to call {@link #start()}.
*
* @param connectionManager must not be null
* @param eventHandler must not be null
*/
public EventListener(ConnectionManager connectionManager, EventHandler eventHandler) {
this.connManager = connectionManager;
this.config = connectionManager.getConfig();
addEventHandler(eventHandler);
}
/**
* This constructor can add more than one {@link EventHandler} as a list of {@link EventHandler}'s.
*
* @param connectionManager must not be null
* @param eventHandlers list of {@link EventHandler}'s must not be null
* @see #EventListener(ConnectionManager, EventHandler)
*/
public EventListener(ConnectionManager connectionManager, List<EventHandler> eventHandlers) {
this.connManager = connectionManager;
this.config = connectionManager.getConfig();
addEventHandlers(eventHandlers);
}
/**
* Creates a new {@link EventListener} without a {@link EventHandler}<br>
* <br>
* To get notified by events you have to call {@link #start()} and {@link #addEventHandler(EventHandler)} or
* {@link #addEventHandlers(List)}.
*
* @param connectionManager must not be null
*/
public EventListener(ConnectionManager connectionManager) {
this.connManager = connectionManager;
this.config = connectionManager.getConfig();
}
/**
* Stops this {@link EventListener} and unsubscribe events.
*/
public synchronized void stop() {
logger.debug("Stop EventListener");
isStarted = false;
internalStop();
}
/**
* Returns true, if the {@link EventListener} is started.
*
* @return true, if is started
*/
public boolean isStarted() {
return isStarted;
}
private void internalStop() {
if (subscriptionScheduler != null && !subscriptionScheduler.isCancelled()) {
subscriptionScheduler.cancel(true);
subscriptionScheduler = null;
}
if (pollingScheduler != null && !pollingScheduler.isCancelled()) {
pollingScheduler.cancel(true);
pollingScheduler = null;
unsubscribe();
logger.debug("internal stop EventListener");
}
}
/**
* Starts this {@link EventListener} and subscribe events.
*/
public synchronized void start() {
logger.debug("Start EventListener");
isStarted = true;
internalStart();
}
private void internalStart() {
if (!eventHandlers.isEmpty() && (pollingScheduler == null || pollingScheduler.isCancelled())) {
pollingScheduler = scheduler.scheduleWithFixedDelay(runableListener, 0,
config.getEventListenerRefreshinterval(), TimeUnit.MILLISECONDS);
logger.debug("internal start EventListener");
}
}
/**
* Adds a {@link List} of {@link EventHandler}'s and subscribe the supported event-types, if the
* {@link EventListener} is started and the event-types are not already subscribed.
*
* @param eventHandlers to add
*/
public void addEventHandlers(List<EventHandler> eventHandlers) {
if (eventHandlers != null) {
for (EventHandler eventHandler : eventHandlers) {
addEventHandler(eventHandler);
}
}
}
/**
* Adds a {@link EventHandler}'s and subscribe the supported event-types, if the
* {@link EventListener} is started and the event-types are not already subscribed.<br>
* <br>
* <b>Note:</b><br>
* If {@link #start()} was called before the {@link EventListener} will start now, otherwise you have to call
* {@link #start()} to get notified by events.
*
* @param eventHandler to add
*/
public void addEventHandler(EventHandler eventHandler) {
if (eventHandler != null) {
boolean handlerExist = false;
for (EventHandler handler : eventHandlers) {
if (handler.getUID().equals(eventHandler.getUID())) {
handlerExist = true;
}
}
if (!handlerExist) {
eventHandlers.add(eventHandler);
addSubscribeEvents(eventHandler.getSupportedEvents());
logger.debug("eventHandler: {} added", eventHandler.getUID());
if (isStarted) {
internalStart();
}
}
}
}
/**
* Remove a {@link EventHandler} and unsubscribes the supported event-types, if the
* {@link EventListener} is started and no other {@link EventHandler} needed the event-types.
*
* @param eventHandler to remove
*/
public void removeEventHandler(EventHandler eventHandler) {
if (eventHandler != null && eventHandlers.contains(eventHandler)) {
List<String> tempSubsList = new ArrayList<>();
int index = -1;
EventHandler intEventHandler = null;
boolean subscribedEventsChanged = false;
for (int i = 0; i < eventHandlers.size(); i++) {
intEventHandler = eventHandlers.get(i);
if (intEventHandler.getUID().equals(eventHandler.getUID())) {
index = i;
} else {
tempSubsList.addAll(intEventHandler.getSupportedEvents());
}
}
if (index != -1) {
intEventHandler = eventHandlers.remove(index);
for (String eventName : intEventHandler.getSupportedEvents()) {
if (!tempSubsList.contains(eventName)) {
subscribedEvents.remove(eventName);
subscribedEventsChanged = true;
}
}
}
if (subscribedEventsChanged) {
// Because of the json-call unsubscribe?eventName=XY&subscriptionID=Z doesn't work like it is explained
// in the dS-JSON-API, the whole EventListener will be restarted. The problem is, that not only the
// given eventName, rather all events of the subscitionID will be deleted.
restartListener();
}
}
}
/**
* Removes a subscribed event and unsubscibe it, if it is not needed by other {@link EventHandler}'s.
*
* @param unsubscribeEvent event name to unsubscibe
* @param eventHandlerID EventHandler-ID of the EventHandler that unsubscibe a event
*/
public void removeSubscribe(String unsubscribeEvent, String eventHandlerID) {
if (subscribedEvents != null && !subscribedEvents.contains(unsubscribeEvent)) {
boolean eventNeededByAnotherHandler = false;
for (EventHandler handler : eventHandlers) {
if (!handler.getUID().equals(eventHandlerID)) {
eventNeededByAnotherHandler = handler.getSupportedEvents().contains(unsubscribeEvent);
}
}
if (!eventNeededByAnotherHandler) {
logger.debug("unsubscribeEvent: {} is not needed by other EventHandler's... unsubscribe it",
unsubscribeEvent);
subscribedEvents.remove(unsubscribeEvent);
restartListener();
} else {
logger.debug("unsubscribeEvent: {} is needed by other EventHandler's... dosen't unsubscribe it",
unsubscribeEvent);
}
}
}
private void restartListener() {
internalStop();
if (!eventHandlers.isEmpty() && isStarted) {
logger.debug("Min one subscribed events was deleted, EventListener will be restarted");
internalStart();
}
}
/**
* Adds a event and subscribed it, if it is not subscribed already.
*
* @param subscribeEvent event name to subscribe
*/
public void addSubscribe(String subscribeEvent) {
if (!subscribedEvents.contains(subscribeEvent)) {
subscribedEvents.add(subscribeEvent);
logger.debug("subscibeEvent: {} added", subscribeEvent);
if (subscribed) {
subscribe(subscribeEvent);
}
}
}
/**
* Adds the events of the {@link List} and subscribe them, if a event is not subscribed already.
*
* @param subscribeEvents event name to subscribe
*/
public void addSubscribeEvents(List<String> subscribeEvents) {
for (String eventName : subscribeEvents) {
subscribe(eventName);
}
}
private void getSubscriptionID() {
boolean subscriptionIDavailable = false;
while (!subscriptionIDavailable) {
String response = connManager.getDigitalSTROMAPI().getEvent(connManager.getSessionToken(), subscriptionID,
timeout);
JsonObject responseObj = JSONResponseHandler.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responseObj)) {
subscriptionID++;
} else {
String errorStr = null;
if (responseObj != null && responseObj.get(JSONApiResponseKeysEnum.MESSAGE.getKey()) != null) {
errorStr = responseObj.get(JSONApiResponseKeysEnum.MESSAGE.getKey()).getAsString();
}
if (errorStr != null && errorStr.contains(TOKEN_NOT_FOUND)) {
subscriptionIDavailable = true;
}
}
}
}
private boolean subscribe(String eventName) {
if (!subscribed) {
getSubscriptionID();
}
subscribed = connManager.getDigitalSTROMAPI().subscribeEvent(connManager.getSessionToken(), eventName,
subscriptionID, config.getConnectionTimeout(), config.getReadTimeout());
if (subscribed) {
logger.debug("subscribed event: {} to subscriptionID: {}", eventName, subscriptionID);
} else {
logger.error(
"Couldn't subscribe event {} ... maybe timeout because system is too busy ... event will be tried to subscribe later again ... ",
eventName);
}
return subscribed;
}
private void subscribe(final List<String> evetNames) {
final Iterator<String> eventNameIter = evetNames.iterator();
subscriptionScheduler = scheduler.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
while (eventNameIter.hasNext()) {
subscribe(eventNameIter.next());
}
subscriptionScheduler.cancel(true);
}
}, 0, SUBSCRIBE_DELAY, TimeUnit.MILLISECONDS);
}
private final Runnable runableListener = new Runnable() {
@Override
public void run() {
if (subscribed) {
String response = connManager.getDigitalSTROMAPI().getEvent(connManager.getSessionToken(),
subscriptionID, timeout);
JsonObject responseObj = JSONResponseHandler.toJsonObject(response);
if (JSONResponseHandler.checkResponse(responseObj)) {
JsonObject obj = JSONResponseHandler.getResultJsonObject(responseObj);
if (obj != null && obj.get(JSONApiResponseKeysEnum.EVENTS.getKey()).isJsonArray()) {
JsonArray array = obj.get(JSONApiResponseKeysEnum.EVENTS.getKey()).getAsJsonArray();
handleEvent(array);
}
} else {
String errorStr = null;
if (responseObj != null && responseObj.get(JSONApiResponseKeysEnum.MESSAGE.getKey()) != null) {
errorStr = responseObj.get(JSONApiResponseKeysEnum.MESSAGE.getKey()).getAsString();
}
if (errorStr != null && (errorStr.equals(INVALID_SESSION) || errorStr.contains(TOKEN_NOT_FOUND))) {
unsubscribe();
subscribe(subscribedEvents);
} else if (errorStr != null) {
pollingScheduler.cancel(true);
logger.error("Unknown error message at event response: {}", errorStr);
}
}
} else {
subscribe(subscribedEvents);
}
}
};
private void unsubscribe() {
for (String eventName : this.subscribedEvents) {
connManager.getDigitalSTROMAPI().unsubscribeEvent(this.connManager.getSessionToken(), eventName,
this.subscriptionID, config.getConnectionTimeout(), config.getReadTimeout());
}
}
private void handleEvent(JsonArray array) {
if (array.size() > 0) {
Event event = new JSONEventImpl(array);
for (EventItem item : event.getEventItems()) {
logger.debug("detect event {}", item.toString());
for (EventHandler handler : eventHandlers) {
if (handler.supportsEvent(item.getName())) {
logger.debug("inform handler with id {} about event {}", handler.getUID(), item.toString());
handler.handleEvent(item);
}
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.constants;
/**
* The {@link EventNames} contains all needed event names to subscribe at the digitalSTROM server.
*
* @author Michael Ochel
* @author Matthias Siegele
*/
public class EventNames {
public static final String ZONE_SENSOR_VALUE = "zoneSensorValue";
public static final String HEATING_CONTROL_OPERATION_MODE = "heating-controller.operation-mode";
public static final String STATE_CHANGED = "stateChange";
public static final String CALL_SCENE = "callScene";
public static final String UNDO_SCENE = "undoScene";
public static final String DEVICE_SENSOR_VALUE = "deviceSensorValue";
public static final String DEVICE_BINARY_INPUT_EVENT = "deviceBinaryInputEvent";
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.constants;
import java.util.HashMap;
import java.util.Map;
/**
* The {@link EventResponseEnum} contains digitalSTROM-Event properties of the events at {@link EventNames}.
*
* @author Michael Ochel
* @author Mathias Siegele
*/
public enum EventResponseEnum {
// general
NAME("Name"),
PROPERTIES("properties"),
SOURCE("source"),
SET("set"),
DSID("dsid"),
ZONEID("zoneID"),
GROUPID("groupID"),
IS_APARTMENT("isApartment"),
IS_GROUP("isGroup"),
IS_DEVICE("isDevice"),
IS_SERVICE("isService"),
// scene event
FORCED("forced"),
ORIGEN_TOKEN("originToken"),
CALL_ORIGEN("callOrigin"),
ORIGEN_DSUID("originDSUID"),
SCENEID("sceneID"),
ORIGIN_DEVICEID("originDeviceID"),
// device/zone sensor value
SENSOR_VALUE_FLOAT("sensorValueFloat"),
SENSOR_TYPE("sensorType"),
SENSOR_VALUE("sensorValue"),
SENSOR_INDEX("sensorIndex"),
// state changed
OLD_VALUE("oldvalue"),
STATE_NAME("statename"),
STATE("state"),
VALUE("value"),
// operation mode
ACTIONS("actions"),
OPERATION_MODE("operationMode"),
FORCED_UPDATE("forceUpdate"),
// binary input
INPUT_TYPE("inputType"),
INPUT_STATE("inputState"),
INPUT_INDEX("inputIndex");
private final String id;
static final Map<String, EventResponseEnum> EVENT_RESPONSE_FIELDS = new HashMap<>();
static {
for (EventResponseEnum ev : EventResponseEnum.values()) {
EVENT_RESPONSE_FIELDS.put(ev.getId(), ev);
}
}
/**
* Returns true, if the given property exists at the ESH event properties, otherwise false.
*
* @param property to check
* @return contains property (true = yes | false = no)
*/
public static boolean containsId(String property) {
return EVENT_RESPONSE_FIELDS.keySet().contains(property);
}
/**
* Returns the {@link EventResponseEnum} to the given property.
*
* @param property to get
* @return EventPropertyEnum
*/
public static EventResponseEnum getProperty(String property) {
return EVENT_RESPONSE_FIELDS.get(property);
}
private EventResponseEnum(String id) {
this.id = id;
}
/**
* Returns the id of this {@link EventResponseEnum}.
*
* @return id of this {@link EventResponseEnum}
*/
public String getId() {
return id;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.types;
import java.util.List;
/**
* The {@link Event} represents a digitalSTROM-Event.
*
* @author Alexander Betker
*/
public interface Event {
/**
* Returns a list of the {@link EventItem}s of this Event.
*
* @return List of {@link EventItem}s
*/
List<EventItem> getEventItems();
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.types;
import java.util.Map;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
/**
* The {@link EventItem} represents a event item of an digitalSTROM-Event.
*
* @author Alexander Betker
* @author Michael Ochel - add getSource()
* @author Matthias Siegele - add getSource()
*/
public interface EventItem {
/**
* Returns the name of this {@link EventItem}.
*
* @return name of this {@link EventItem}
*/
String getName();
/**
* Returns {@link HashMap} with the properties fiels of this {@link EventItem}.
* The key is a {@link EventResponseEnum} and represents the property name
* and the value is the property value.
*
* @return the properties of this {@link EventItem}
*/
Map<EventResponseEnum, String> getProperties();
/**
* Returns {@link HashMap} with the source fields of this {@link EventItem}.
* The key is a {@link EventResponseEnum} and represents the property name
* and the value is the property value.
*
* @return the properties of this {@link EventItem}
*/
Map<EventResponseEnum, String> getSource();
}

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.types;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* The {@link EventItemImpl} is the implementation of the {@link EventItem}.
*
* @author Michael Ochel
* @author Mathias Siegele
*/
public class EventItemImpl implements EventItem {
private final String name;
private Map<EventResponseEnum, String> properties;
private Map<EventResponseEnum, String> source;
/**
* Creates a new {@link EventItemImpl} from the given digitalSTROM-Event-Item {@link JsonObject}.
*
* @param jsonEventItem must not be null
*/
public EventItemImpl(JsonObject jsonEventItem) {
name = jsonEventItem.get(JSONApiResponseKeysEnum.NAME.getKey()).getAsString();
if (jsonEventItem.get(JSONApiResponseKeysEnum.PROPERTIES.getKey()).isJsonObject()) {
Set<Entry<String, JsonElement>> propObjEntrySet = jsonEventItem
.get(JSONApiResponseKeysEnum.PROPERTIES.getKey()).getAsJsonObject().entrySet();
properties = new HashMap<>(propObjEntrySet.size());
for (Entry<String, JsonElement> entry : propObjEntrySet) {
if (EventResponseEnum.containsId(entry.getKey())) {
addProperty(EventResponseEnum.getProperty(entry.getKey()), entry.getValue().getAsString());
}
}
}
if (jsonEventItem.get(JSONApiResponseKeysEnum.SOURCE.getKey()).isJsonObject()) {
Set<Entry<String, JsonElement>> sourceObjEntrySet = jsonEventItem
.get(JSONApiResponseKeysEnum.SOURCE.getKey()).getAsJsonObject().entrySet();
source = new HashMap<>(sourceObjEntrySet.size());
for (Entry<String, JsonElement> entry : sourceObjEntrySet) {
if (EventResponseEnum.containsId(entry.getKey())) {
addSource(EventResponseEnum.getProperty(entry.getKey()), entry.getValue().getAsString());
}
}
}
}
private void addProperty(EventResponseEnum propertieKey, String value) {
properties.put(propertieKey, value);
}
private void addSource(EventResponseEnum sourceKey, String value) {
source.put(sourceKey, value);
}
@Override
public String getName() {
return name;
}
@Override
public Map<EventResponseEnum, String> getProperties() {
return properties;
}
@Override
public Map<EventResponseEnum, String> getSource() {
return source;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "EventItemImpl [name=" + name + ", properties=" + properties + ", source=" + source + "]";
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.event.types;
import java.util.LinkedList;
import java.util.List;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link JSONEventImpl} is the implementation of the {@link Event}.
*
* @author Alexander Betker
*/
public class JSONEventImpl implements Event {
private List<EventItem> eventItemList;
/**
* Creates a new {@link JSONEventImpl} from the given digitalSTROM-Event {@link JsonArray}.
*
* @param jsonEventArray must not be null
*/
public JSONEventImpl(JsonArray jsonEventArray) {
this.eventItemList = new LinkedList<>();
for (int i = 0; i < jsonEventArray.size(); i++) {
if (jsonEventArray.get(i) instanceof JsonObject) {
this.eventItemList.add(new EventItemImpl((JsonObject) jsonEventArray.get(i)));
}
}
}
@Override
public List<EventItem> getEventItems() {
return eventItemList;
}
}

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
/**
* The {@link ConnectionListener} is notified if the connection state of digitalSTROM-Server has changed.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public interface ConnectionListener {
/* Connection-States */
/**
* State, if you're not authenticated on the digitalSTROM-Server.
*/
final String NOT_AUTHENTICATED = "notAuth";
/**
* State, if the connection to the digitalSTROM-Server is lost.
*/
final String CONNECTION_LOST = "connLost";
/**
* State, if a ssl handshake problem occured while communicating with the digitalSTROM-Server.
*/
final String SSL_HANDSHAKE_ERROR = "sslHandshakeError";
/**
* State, if the connection to the digitalSTROM-Server is resumed.
*/
final String CONNECTION_RESUMED = "connResumed";
/**
* State, if the Application-Token is generated.
*/
final String APPLICATION_TOKEN_GENERATED = "appGen";
/* Not authentication reasons */
/**
* State, if the given Application-Token cannot be used.
*/
final String WRONG_APP_TOKEN = "wrongAppT";
/**
* State, if the given username or password cannot be used.
*/
final String WRONG_USER_OR_PASSWORD = "wrongUserOrPasswd";
/**
* State, if no username or password is set and the given application-token cannot be used or is null.
*/
final String NO_USER_PASSWORD = "noUserPasswd";
/**
* State, if the connection timed out.
*/
final String CONNECTON_TIMEOUT = "connTimeout";
/**
* State, if the host address cannot be found.
*/
final String HOST_NOT_FOUND = "hostNotFound";
/**
* State, if the host address is unknown.
*/
final String UNKNOWN_HOST = "unknownHost";
/**
* State, if the the URL is invalid.
*/
final String INVALID_URL = "invalideURL";
/**
* This method is called whenever the connection state has changed from {@link #CONNECTION_LOST}
* to {@link #CONNECTION_RESUMED} and vice versa. It also will be called if the application-token is generated over
* {@link #APPLICATION_TOKEN_GENERATED}.
*
* @param newConnectionState of the connection
*/
void onConnectionStateChange(String newConnectionState);
/**
* This method is called whenever the connection state has changed to {@link #NOT_AUTHENTICATED} or
* {@link #CONNECTION_LOST}
* and also passes the reason why. Reason can be:
* <ul>
* <li>{@link #WRONG_APP_TOKEN} if the given application-token can't be used.</li>
* <li>{@link #WRONG_USER_OR_PASSWORD} if the given user name or password can't be used.</li>
* <li>{@link #NO_USER_PASSWORD} if no user name or password is set and the given application-token can't be used.
* <li>{@link #HOST_NOT_FOUND} if the host can't be found.
* <li>{@link #INVALID_URL} if the the URL is invalid.
* </li>
* </ul>
*
* @param newConnectionState of the connection
* @param reason why the connection is failed
*/
void onConnectionStateChange(String newConnectionState, String reason);
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.GeneralDeviceInformation;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
/**
* The {@link DeviceStatusListener} is notified, if a {@link Device} status or configuration has changed, if a scene
* configuration is added to a {@link Device} or if a device has been added or removed. The {@link DeviceStatusListener}
* can be also registered by a {@link Circuit} to get informed by configuration or status changes.
* <p>
* By implementation with the id {@link #DEVICE_DISCOVERY} this listener can be used as a device discovery to get
* informed, if a new {@link Device} or {@link Circuit} is added or removed from the digitalSTROM-System.<br>
* For that the {@link DeviceStatusListener} has to be registered on the
* {@link DeviceStatusManager#registerDeviceListener(DeviceStatusListener)}. Then the {@link DeviceStatusListener} gets
* informed by the methods {@link #onDeviceAdded(Object)} and {@link #onDeviceRemoved(Object)}.
* </p>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public interface DeviceStatusListener {
/**
* ID of the device discovery listener.
*/
static final String DEVICE_DISCOVERY = "DeviceDiscovery";
/**
* This method is called whenever the state of the {@link Device} has changed and passes the new device state as an
* {@link DeviceStateUpdate} object.
*
* @param deviceStateUpdate new device status
*/
void onDeviceStateChanged(DeviceStateUpdate deviceStateUpdate);
/**
* This method is called whenever a device is removed.
*
* @param device which is removed
*/
void onDeviceRemoved(GeneralDeviceInformation device);
/**
* This method is called whenever a device is added.
*
* @param device which is added
*/
void onDeviceAdded(GeneralDeviceInformation device);
/**
* This method is called whenever a configuration of an device has changed. What configuration has changed
* can be see by the given parameter whatConfig to handle the change.<br>
* Please have a look at {@link ChangeableDeviceConfigEnum} to see what configuration are changeable.
*
* @param whatConfig has changed
*/
void onDeviceConfigChanged(ChangeableDeviceConfigEnum whatConfig);
/**
* This method is called whenever a scene configuration is added to a device.
*
* @param sceneID of a read scene configuration
*/
void onSceneConfigAdded(short sceneID);
/**
* Returns the id of this {@link DeviceStatusListener}.
*
* @return the device listener id
*/
String getDeviceStatusListenerID();
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
/**
* The {@link ManagerStatusListener} is notified, if the state of digitalSTROM-Manager has changed.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface ManagerStatusListener {
/**
* This method is called whenever the state of an digitalSTROM-Manager has changed.<br>
* For that it passes the {@link ManagerTypes} and the new {@link ManagerStates}.
*
* @param managerType of the digitalSTROM-Manager
* @param newState of the digitalSTROM-Manager
*/
void onStatusChanged(ManagerTypes managerType, ManagerStates newState);
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
/**
* The {@link SceneStatusListener} is notified, if a {@link InternalScene} status has changed or a
* {@link InternalScene} has been removed or added.
*
* <p>
* By implementation with the id {@link #SCENE_DISCOVERY} this listener can be used as a scene discovery to get
* informed, if a new {@link InternalScene}s is added or removed from the digitalSTROM-System.<br>
* For that the {@link SceneStatusListener} has to be registered on the
* {@link SceneManager#registerSceneListener(SceneStatusListener)}. Then the {@link SceneStatusListener} gets
* informed by the methods {@link #onSceneAdded(InternalScene)} and {@link #onSceneRemoved(InternalScene)}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface SceneStatusListener {
/**
* The {@link SceneStatusListener} id for the discovery implementation.
*/
static final String SCENE_DISCOVERY = "SceneDiscovery";
/**
* This method is called whenever the state of the {@link InternalScene} has changed.
*
* @param newState (true = call | false = undo)
*/
void onSceneStateChanged(boolean newState);
/**
* This method is called whenever a {@link InternalScene} is removed.
*
* @param scene that was removed
*/
void onSceneRemoved(InternalScene scene);
/**
* This method is called whenever a {@link InternalScene} is added.
*
* @param scene that was added
*/
void onSceneAdded(InternalScene scene);
/**
* Return the id of this {@link SceneStatusListener}.
*
* @return the scene listener id
*/
String getSceneStatusListenerID();
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
/**
* The {@link SystemStateChangeListener} can be implemented to get informed by digitalSTROM system state changes. It
* has to be registered by supported classes, e.g. the {@link TemperatureControlManager} or self implemented classes.
*
* @author Michael Ochel
* @author Matthias Siegele
*/
public interface SystemStateChangeListener {
/**
* Will be called, if a digitalSTROM system state has changed.
*
* @param stateType of the digitalSTROM system state
* @param newState of the digitalSTROM system state
*/
void onSystemStateChanged(String stateType, String newState);
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
import org.openhab.binding.digitalstrom.internal.lib.climate.TemperatureControlSensorTransmitter;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
/**
* The {@link TemperatureControlStatusListener} can be implemented to get informed by configuration and status changes.
* <br>
* It also can be implemented as discovery, than the id have to be {@link #DISCOVERY}.
*
* @author Michael Ochel
* @author Matthias Siegele
*
*/
public interface TemperatureControlStatusListener {
/**
* The id for discovery.
*/
static Integer DISCOVERY = -2;
/**
* Will be called, if the configuration of the {@link TemperatureControlStatus} has changed.
*
* @param tempControlStatus that has changed
*/
void configChanged(TemperatureControlStatus tempControlStatus);
/**
* Will be called, if the target temperature has changed.
*
* @param newValue of the target temperature
*/
void onTargetTemperatureChanged(Float newValue);
/**
* Will be called, if the control value has changed.
*
* @param newValue of the control value
*/
void onControlValueChanged(Integer newValue);
/**
* Registers a {@link TemperatureControlSensorTransmitter}.
*
* @param temperatureSensorTransmitter to register
*/
void registerTemperatureSensorTransmitter(TemperatureControlSensorTransmitter temperatureSensorTransmitter);
/**
* Returns the id of this {@link TemperatureControlStatusListener}.
*
* @return id
*/
Integer getTemperationControlStatusListenrID();
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener;
/**
* The {@link TotalPowerConsumptionListener} is notified, if the total power consumption or the total electric meter
* value has changed.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public interface TotalPowerConsumptionListener {
/**
* This method is called whenever the total power consumption of the digitalSTROM-System has changed.
*
* @param newPowerConsumption of the digitalSTROM-System
*/
void onTotalPowerConsumptionChanged(int newPowerConsumption);
/**
* This method is called whenever the total energy meter value in Wh of the digitalSTROM-System has changed.
*
* @param newEnergyMeterValue of the digitalSTROM-System
*/
void onEnergyMeterValueChanged(int newEnergyMeterValue);
/**
* This method is called whenever the total energy meter value in Ws of the digitalSTROM-System has changed.
*
* @param newEnergyMeterValue of the digitalSTROM-System
*/
void onEnergyMeterWsValueChanged(int newEnergyMeterValue);
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener.stateenums;
/**
* The {@link ManagerStates} contains all reachable states of the digitalSTROM-Manager in {@link ManagerTypes}
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public enum ManagerStates {
RUNNING,
STOPPED,
INITIALIZING,
GENERATING_SCENES
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.listener.stateenums;
/**
* The {@link ManagerTypes} contains all reachable digitalSTROM-Managers, which have states.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public enum ManagerTypes {
DEVICE_STATUS_MANAGER,
SCENE_MANAGER,
CONNECTION_MANAGER
}

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport;
/**
* The {@link ConnectionManager} manages the connection to a digitalSTROM-Server.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface ConnectionManager {
public static final int GENERAL_EXCEPTION = -1;
public static final int MALFORMED_URL_EXCEPTION = -2;
public static final int CONNECTION_EXCEPTION = -3;
public static final int SOCKET_TIMEOUT_EXCEPTION = -4;
public static final int UNKNOWN_HOST_EXCEPTION = -5;
public static final int AUTHENTIFICATION_PROBLEM = -6;
public static final int SSL_HANDSHAKE_EXCEPTION = -7;
/**
* Returns the {@link HttpTransport} to execute queries or special commands on the digitalSTROM-Server.
*
* @return the HttpTransport
*/
HttpTransport getHttpTransport();
/**
* Returns the {@link DsAPI} to execute commands on the digitalSTROM-Server.
*
* @return the DsAPI
*/
DsAPI getDigitalSTROMAPI();
/**
* This method has to be called before each command to check the connection to the digitalSTROM-Server.
* It examines the connection to the server, sets a new Session-Token, if it is expired and sets a new
* Application-Token, if none it set at the digitalSTROM-Server. It also outputs the specific connection failure.
*
* @return true if the connection is established and false if not
*/
boolean checkConnection();
/**
* Returns the current Session-Token.
*
* @return Session-Token
*/
String getSessionToken();
/**
* Returns the auto-generated or user defined Application-Token.
*
* @return Application-Token
*/
String getApplicationToken();
/**
* Registers a {@link ConnectionListener} to this {@link ConnectionManager}.
*
* @param connectionListener to register
*/
void registerConnectionListener(ConnectionListener connectionListener);
/**
* Unregisters the {@link ConnectionListener} from this {@link ConnectionManager}.
*/
void unregisterConnectionListener();
/**
* Revokes the saved Application-Token from the digitalSTROM-Server and returns true if the Application-Token was
* revoke successful, otherwise false.
*
* @return successful = true, otherwise false
*/
boolean removeApplicationToken();
/**
* Updates the login configuration.
*
* @param hostAddress of the digitalSTROM-Server
* @param username to login
* @param password to login
* @param applicationToken to login
*/
void updateConfig(String hostAddress, String username, String password, String applicationToken);
/**
* Updates the {@link Config} with the given config.
*
* @param config to update
*/
void updateConfig(Config config);
/**
* Returns the {@link Config}.
*
* @return the config
*/
Config getConfig();
/**
* Informs this {@link ConnectionManager} that the {@link Config} has been updated.
*/
void configHasBeenUpdated();
/**
* Generates and returns a new session token.
*
* @return new session token
*/
String getNewSessionToken();
/**
* Checks the connection through the given HTTP-Response-Code or exception code. If a {@link ConnectionListener} is
* registered this method also informs the registered {@link ConnectionListener} if the connection state has
* changed. <br>
* <br>
* <b>Exception-Codes:</b><br>
* <b>{@link #GENERAL_EXCEPTION}</b> general exception<br>
* <b>{@link #MALFORMED_URL_EXCEPTION}</b> MalformedURLException<br>
* <b>{@link #CONNECTION_EXCEPTION}</b> java.net.ConnectException<br>
* <b>{@link #SOCKET_TIMEOUT_EXCEPTION}</b> SocketTimeoutException<br>
* <b>{@link #UNKNOWN_HOST_EXCEPTION}</b> java.net.UnknownHostException<br>
* <br>
* <b>Code for authentication problems:</b> {@link #AUTHENTIFICATION_PROBLEM}<br>
*
*
* @param code exception or HTTP-Response-Code
* @return true, if connection is valid
*/
boolean checkConnection(int code);
/**
* Returns true, if connection is established, otherwise false.
*
* @return true, if connection is established, otherwise false
*/
boolean connectionEstablished();
}

View File

@@ -0,0 +1,242 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager;
import org.openhab.binding.digitalstrom.internal.lib.event.EventHandler;
import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.TotalPowerConsumptionListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
/**
* <p>
* The {@link DeviceStatusManager} is responsible for the synchronization between the internal model of the
* digitalSTROM-devices and the real existing digitalSTROM-devices. You can change the state of an device by sending a
* direct command to the devices or by calling a scene. Furthermore the {@link DeviceStatusManager} get informed over
* the {@link SceneManager} by the {@link EventListener} if scenes are called by external sources. All
* configurations of the physically device will be synchronized to the internally managed model and updated as required.
* The {@link DeviceStatusManager} also initializes {@link SensorJob}'s with the {@link SensorJobExecutor} and
* {@link SceneReadingJobExecutor} to update required sensor and scene data.
*
* <p>
* Therefore the {@link DeviceStatusManager} uses the {@link StructureManager} for the internal management of the
* structure of the digitalSTROM-system, {@link ConnectionManager} to check the connectivity,
* {@link SceneManager} to identify externally called scenes and to update the affected devices of these
* called scenes to the internal model. The most methods of the above-named managers will be directly called over the
* {@link DeviceStatusManager}, because they are linked to the affected managers.
*
* <p>
* To get informed by all relevant information you can register some useful listeners. Here the list of all listeners
* which can registered or unregistered:
* </p>
* <ul>
* <li>{@link DeviceStatusListener} over {@link #registerDeviceListener(DeviceStatusListener)} respectively
* {@link #unregisterDeviceListener(DeviceStatusListener)}</li>
* <li>{@link SceneStatusListener} over {@link #registerSceneListener(SceneStatusListener)} respectively
* {@link #unregisterSceneListener(SceneStatusListener)}</li>
* <li>{@link TotalPowerConsumptionListener} over
* {@link #registerTotalPowerConsumptionListener(TotalPowerConsumptionListener)} respectively
* {@link #unregisterTotalPowerConsumptionListener()}</li>
* <li>{@link ManagerStatusListener} over {@link #registerStatusListener(ManagerStatusListener)}
* respectively {@link #unregisterStatusListener()}</li>
* <li>{@link ConnectionListener} over {@link #registerDeviceListener(DeviceStatusListener)} respectively
* {@link #unregisterDeviceListener(DeviceStatusListener)}</li>
* </ul>
* For what the listener can be used please have a look at the listener.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface DeviceStatusManager extends EventHandler {
/**
* Starts the working process for device synchronization. It also starts the {@link SensorJobExecutor} and the
* {@link SceneReadingJobExecutor} and the {@link SceneManager}.
*/
void start();
/**
* Stops the working process for device synchronization. It also stops the {@link SensorJobExecutor} and the
* {@link SceneReadingJobExecutor} and the {@link SceneManager}.
*/
void stop();
/**
* This method sends a call scene command for the given {@link InternalScene}, if call_undo is true otherwise it
* sends a undo command.
* <br>
* It also updates the scene state, if the command was send successful.
*
* @param scene to call
* @param call_undo (true = call | false = undo)
*/
void sendSceneComandsToDSS(InternalScene scene, boolean call_undo);
/**
* This method sends a stop command for the given {@link Device}.
* <br>
* It also reads out the current output value of the device and updates it, if the command was send successful.
*
* @param device which will send a stop command
*/
void sendStopComandsToDSS(Device device);
/**
* This method sends the command for the given {@link DeviceStateUpdate} for the given {@link Device}. Please have a
* look at {@link DeviceStateUpdate} to see what types is there and how the value {@link DeviceStateUpdate} will be
* evaluated.
* <br>
* It also updates the device if the command was send successful.
*
* @param device device which will send a command
* @param deviceStateUpdate command to send
*/
void sendComandsToDSS(Device device, DeviceStateUpdate deviceStateUpdate);
/**
* This method adds a {@link SensorJob} with the appropriate priority to the {@link SensorJob}.
*
* @param sensorJob to update sensor data
* @param priority to update
*/
void updateSensorData(SensorJob sensorJob, String priority);
/**
* This method adds a {@link SensorJob} with the appropriate priority to the {@link SceneReadingJobExecutor}.
*
* @param device device which will update scene data
* @param deviceStateUpdate scene data to update
*/
void updateSceneData(Device device, DeviceStateUpdate deviceStateUpdate);
/**
* Registers the given {@link DeviceStatusListener} to the {@link Device}, if it exists or registers it as a
* device discovery, if the id of the {@link DeviceStatusListener} is {@link DeviceStatusListener#DEVICE_DISCOVERY}.
*
* @param deviceListener to rigister
*/
void registerDeviceListener(DeviceStatusListener deviceListener);
/**
* Unregisters the given {@link DeviceStatusListener} from the {@link Device}, if it exists or unregisters the
* device discovery, if the id of the {@link DeviceStatusListener} is {@link DeviceStatusListener#DEVICE_DISCOVERY}.
*
* @param deviceListener to unregister
*/
void unregisterDeviceListener(DeviceStatusListener deviceListener);
/**
* Registers the given {@link TotalPowerConsumptionListener} to this {@link DeviceStatusManager}.
*
* @param totalPowerConsumptionListener to register
*/
void registerTotalPowerConsumptionListener(TotalPowerConsumptionListener totalPowerConsumptionListener);
/**
* Unregisters the {@link TotalPowerConsumptionListener} from this {@link DeviceStatusManager}.
*/
void unregisterTotalPowerConsumptionListener();
/**
* Registers the given {@link SceneStatusListener} to the {@link InternalScene}, if it exists or registers it as a
* scene discovery to the {@link SceneManager}, if the id of the {@link SceneStatusListener} is
* {@link SceneStatusListener#SCENE_DISCOVERY}.
*
* @param sceneListener to register
*/
void registerSceneListener(SceneStatusListener sceneListener);
/**
* Unregisters the given {@link SceneStatusListener} from the {@link InternalScene} if it exist or unregisters the
* scene discovery from the {@link SceneManager}, if the id of the {@link SceneStatusListener} is
* {@link SceneStatusListener#SCENE_DISCOVERY}.
*
* @param sceneListener to unregister
*/
void unregisterSceneListener(SceneStatusListener sceneListener);
/**
* Registers the given {@link ConnectionListener} to the {@link ConnectionManager}.
*
* @param connectionListener to register
*/
void registerConnectionListener(ConnectionListener connectionListener);
/**
* Unregisters the {@link ConnectionListener} from the {@link ConnectionManager}.
*/
void unregisterConnectionListener();
/**
* Removes the {@link Device} with the given dSID from the internal model, if it exists.
*
* @param dSID of the {@link Device} which will be removed
*/
void removeDevice(String dSID);
/**
* Registers the given {@link ManagerStatusListener} to all available managers. What manager are available please
* have a look at {@link ManagerTypes}.
*
* @param statusListener to register
*/
void registerStatusListener(ManagerStatusListener statusListener);
/**
* Unregisters the {@link ManagerStatusListener} from all available managers. What manager are available please have
* a look at {@link ManagerTypes}.
*/
void unregisterStatusListener();
/**
* Returns the {@link ManagerTypes} of this class.
*
* @return these {@link ManagerTypes}
*/
ManagerTypes getManagerType();
/**
* Returns the current {@link ManagerStates}.
*
* @return current {@link ManagerStates}
*/
ManagerStates getManagerState();
/**
* Reads the current total power consumption out and returns it.
*
* @return the current total power consumption
*/
int getTotalPowerConsumption();
/**
* Reads the current total energy meter value in Wh out and returns it.
*
* @return the current total energy meter value Wh
*/
int getTotalEnergyMeterValue();
/**
* Reads the current total energy meter value in Ws out and returns it.
*
* @return the current total energy meter value in Ws
*/
int getTotalEnergyMeterWsValue();
}

View File

@@ -0,0 +1,252 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.event.EventHandler;
import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
/**
* The {@link SceneManager} manages all functions concerning scenes without sending the commands itself.
*
* <p>
* So it manages a list of all {@link InternalScene} they called in the past or was generated by calling
* {@link #generateScenes()}.<br>
* Through this class you can also register {@link SceneStatusListener}'s to the {@link InternalScene}'s or register a
* scene discovery. With {@link #addEcho(String)} or {@link #addEcho(String, short)} scene calls form the library can be
* ignored. To update the state of an {@link InternalScene} or {@link Device} the methods
* {@link #callInternalScene(InternalScene)}, {@link #callInternalScene(String)},
* {@link #callDeviceScene(Device, Short)}
* , {@link #callDeviceScene(String, Short)} etc. can be used.
*
* <p>
* If you call the {@link #start()} method a {@link EventListener} will be started to handle scene calls and undos from
* the outside.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public interface SceneManager extends EventHandler {
/**
* Activates the given {@link InternalScene}, if it exists. Otherwise it will be added to the scene list and
* activated, if it is a callable scene.
*
* @param scene to call
*/
void callInternalScene(InternalScene scene);
/**
* Activates a {@link InternalScene} with the given id, if it exists. Otherwise a new
* {@link InternalScene} will be created and activated, if it is a callable scene.
*
* @param sceneID of the scene to call
*/
void callInternalScene(String sceneID);
/**
* Call the given sceneID on the {@link Device} with the given dSID, if the {@link Device} exists.
*
* @param dSID of the {@link Device} to call
* @param sceneID of the scene to call
*/
void callDeviceScene(String dSID, Short sceneID);
/**
* Call the given sceneID on the given {@link Device}, if the {@link Device} exists.
*
* @param device to call
* @param sceneID to call
*/
void callDeviceScene(Device device, Short sceneID);
/**
* Deactivates the given {@link InternalScene}, if it exists. Otherwise it will added to the scene list and
* deactivated, if it is a callable scene.
*
* @param scene to undo
*/
void undoInternalScene(InternalScene scene);
/**
* Deactivates a {@link InternalScene} with the given sceneID, if it exists. Otherwise a new
* {@link InternalScene} will be created and deactivated, if it is a callable scene.
*
* @param sceneID of the scene to undo
*/
void undoInternalScene(String sceneID);
/**
* Undo the last scene on the {@link Device} with the given dSID, if the {@link Device} exists.
*
* @param dSID of the {@link Device} to undo
*/
void undoDeviceScene(String dSID);
/**
* Undo the last scene on the {@link Device}, if the {@link Device} exists.
*
* @param device the {@link Device} to undo
*/
void undoDeviceScene(Device device);
/**
* Registers the given {@link SceneStatusListener} to the {@link InternalScene}, if it exists or registers it as a
* Scene-Discovery if the id of the {@link SceneStatusListener} is {@link SceneStatusListener#SCENE_DISCOVERY}.
*
* @param sceneListener to register
*/
void registerSceneListener(SceneStatusListener sceneListener);
/**
* Unregisters the given {@link SceneStatusListener} from the {@link InternalScene}, if it exists or unregisters the
* Scene-Discovery, if the id of the {@link SceneStatusListener} is {@link SceneStatusListener#SCENE_DISCOVERY}.
*
* @param sceneListener to register
*/
void unregisterSceneListener(SceneStatusListener sceneListener);
/**
* Adds the given {@link InternalScene} to the scene list, if it is a callable scene.
*
* @param intScene to add
*/
void addInternalScene(InternalScene intScene);
/**
* Adds the scene call with the given dSID and sceneId as an echo to ignore them by detecting the {@link EventItem}.
*
* @param dSID of the {@link Device} that will be ignored
* @param sceneId of the scene that will be ignored
*/
void addEcho(String dSID, short sceneId);
/**
* Adds the scene call with the given internal scene id as an echo to ignore them by detecting the {@link EventItem}
* .
*
* @param internalSceneID to ignore
*/
void addEcho(String internalSceneID);
/**
* Returns the list of all {@link InternalScene}.
*
* @return list of all scenes
*/
List<InternalScene> getScenes();
/**
* Returns true, if all reachable scenes are already generated, otherwise false.
*
* @return true = reachable scenes generated, otherwise false
*/
boolean scenesGenerated();
/**
* Generates all reachable scenes.
*
*/
void generateScenes();
/**
* Will be called from the {@link SceneDiscovery}, if a scene type is generated or is fail.<br>
* For that the scenesGenerated char array has four chars. Each char represents one scene type in the following
* direction:
* <ul>
* <li><b>first:</b> named scenes</li>
* <li><b>second:</b> apartment scenes</li>
* <li><b>third:</b> zone scenes</li>
* <li><b>fourth</b>: group scenes, if they can call by push buttons</li>
* </ul>
* If a scene type is not generated the char is "0". If a scene type is generated the char is "1" and, if it is fail
* the char is "2".
*
* @param scenesGenerated array
*/
void scenesGenerated(char[] scenesGenerated);
/**
* Returns true, if a discovery is registered, otherwise false.
*
* @return true discovery is registered, otherwise false
*/
boolean isDiscoveryRegistrated();
/**
* Starts the {@link EventListener}.
*/
void start();
/**
* Stops the {@link EventListener}.
*/
void stop();
/**
* Removes the {@link InternalScene} with the given sceneID.
*
* @param sceneID of the {@link InternalScene} to remove
*/
void removeInternalScene(String sceneID);
/**
* Returns the {@link InternalScene} with the given sceneID.
*
* @param sceneID of the {@link InternalScene}
* @return internal scenes
*/
InternalScene getInternalScene(String sceneID);
/**
* Registers the given {@link ManagerStatusListener} to this class.
*
* @param statusListener to register
*/
void registerStatusListener(ManagerStatusListener statusListener);
/**
* Unregisters the {@link ManagerStatusListener} from this class.
*/
void unregisterStatusListener();
/**
* Returns the {@link ManagerTypes} of this class.
*
* @return these {@link ManagerTypes}
*/
ManagerTypes getManagerType();
/**
* Returns the current {@link ManagerStates}.
*
* @return current {@link ManagerStates}
*/
ManagerStates getManagerState();
/**
* Calls a scene without inform the scene discovery about the conceivably new {@link InternalScene}.
*
* @param zoneID to call
* @param groupID to call
* @param sceneID to call
*/
void callInternalSceneWithoutDiscovery(Integer zoneID, Short groupID, Short sceneID);
}

View File

@@ -0,0 +1,267 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
/**
* The {@link StructureManager} builds the internal model of the digitalSTROM-System.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface StructureManager {
/**
* Generates the zone- and group-names.
*
* @param connectionManager must not be null
* @return true, if it's generated, otherwise false
*/
boolean generateZoneGroupNames(ConnectionManager connectionManager);
/**
* Returns the name of a zone or null, if the given zoneID dose not exists.<br>
* Note: Zone-names have to be generated over {@link #generateZoneGroupNames(ConnectionManager)}.
*
* @param zoneID of the zone
* @return zone-name
*/
String getZoneName(int zoneID);
/**
* Returns the id of a given zone-name or -1, if the given zone-name dose not exists.<br>
* Note: Zone-names have to be generated over {@link #generateZoneGroupNames(ConnectionManager)}.
*
* @param zoneName of the zone
* @return zoneID
*/
int getZoneId(String zoneName);
/**
* Returns the name of the given groupID from the given zoneID or null, if the zoneID or groupID dose not exists.
* <br>
* Note: Zone-group-names have to be generated over {@link #generateZoneGroupNames(ConnectionManager)}.
*
* @param zoneID of the group
* @param groupID of the group
* @return group-name
*/
String getZoneGroupName(int zoneID, short groupID);
/**
* Returns the groupID of the given group-name from the given zone name or -1, if the zone-name or group name dose
* not exists.<br>
* Note: Zone-group-names have to be generated over {@link #generateZoneGroupNames(ConnectionManager)}.
*
* @param zoneName of the group
* @param groupName of the group
* @return group-id
*/
short getZoneGroupId(String zoneName, String groupName);
/**
* Returns a new {@link Map} of all {@link Device}'s with the {@link DSID} as key and the {@link Device} as value.
* If no devices are found, an empty {@link Map} will be returned.
*
* @return device-map (cannot be null)
*/
Map<DSID, Device> getDeviceMap();
/**
* Returns a reference to the {@link Map} of all {@link Device}'s with the {@link DSID} as key and the
* {@link Device} as value. If no devices are found, an empty {@link Map} will be returned.
*
* @return reference device-map
*/
Map<DSID, Device> getDeviceHashMapReference();
/**
* Returns the reference of the structure as {@link Map}[zoneID, {@link Map}[groupID,
* {@link List}[{@link Device}]]].
*
* @return structure reference
*/
Map<Integer, Map<Short, List<Device>>> getStructureReference();
/**
* Returns the Map of all groups as format HashMap[Short, List[Device]].
*
* @param zoneID of the zone
* @return groups
*/
Map<Short, List<Device>> getGroupsFromZoneX(int zoneID);
/**
* Returns the reference {@link List} of the {@link Device}'s of an zone-group.
*
* @param zoneID of the zone
* @param groupID of the group
* @return reference device-list
*/
List<Device> getReferenceDeviceListFromZoneXGroupX(int zoneID, short groupID);
/**
* Returns the {@link Device} of the given dSID as {@link String} or null if no {@link Device} exists.
*
* @param dSID of the device
* @return device
*/
Device getDeviceByDSID(String dSID);
/**
* Returns the {@link Device} of the given dSID as {@link DSID} or null if no {@link Device} exists.
*
* @param dSID of the device
* @return device
*/
Device getDeviceByDSID(DSID dSID);
/**
* Returns the {@link Device} of the given dSUID or null if no {@link Device} exists.
*
* @param dSUID of the device
* @return the {@link Device} with the given dSUID
*/
Device getDeviceByDSUID(String dSUID);
/**
* Updates a {@link Device} of the structure.
*
* @param oldZone ID
* @param oldGroups ID's
* @param device new {@link Device}
*/
void updateDevice(int oldZone, List<Short> oldGroups, Device device);
/**
* Updates a {@link Device} of the structure.
*
* @param device to update
*/
void updateDevice(Device device);
/**
* Deletes a {@link Device} from the structure.
*
* @param device to delete
*/
void deleteDevice(Device device);
/**
* Adds a {@link Device} to the structure.
*
* @param device to add
*/
void addDeviceToStructure(Device device);
/**
* Returns a {@link Set} of all zoneID's
*
* @return zoneID's
*/
Set<Integer> getZoneIDs();
/**
* Returns true, if a zone with the given zoneID exists, otherwise false.
*
* @param zoneID to check
* @return true = zoneID exists | false = zoneID not exists
*/
boolean checkZoneID(int zoneID);
/**
* Returns true, if a zone-group with the given zoneID and groupID exists, otherwise false.
*
* @param zoneID to check
* @param groupID to check
* @return true = zoneID or groupID exists | false = zoneID or groupID not exists
*/
boolean checkZoneGroupID(int zoneID, short groupID);
/**
* Adds the given {@link List} of {@link Circuit}'s to this {@link StructureManager}.
*
* @param referenceCircuitList to add
*/
void addCircuitList(List<Circuit> referenceCircuitList);
/**
* Adds the given {@link Circuit} to this {@link StructureManager}.
*
* @param circuit to add
* @return the old {@link Circuit}, if the given {@link Circuit} was already added.
*/
Circuit addCircuit(Circuit circuit);
/**
* Returns the {@link Circuit} with the given {@link DSID}.
*
* @param dSID of the {@link Circuit} to get
* @return the {@link Circuit} with the given {@link DSID}
*/
Circuit getCircuitByDSID(DSID dSID);
/**
* Returns the {@link Circuit} with the given dSID as {@link String}.
*
* @param dSID of the {@link Circuit} to get
* @return the {@link Circuit} with the given dSID
*/
Circuit getCircuitByDSID(String dSID);
/**
* Returns the {@link Circuit} with the given dSUID as {@link String}.
*
* @param dSUID of the {@link Circuit} to get
* @return the {@link Circuit} with the given dSUID
*/
Circuit getCircuitByDSUID(String dSUID);
/**
* Updates the configuration of an added {@link Circuit} through a new {@link Circuit} object.
*
* @param newCircuit to update
* @return {@link Circuit} with the old configuration
*/
Circuit updateCircuitConfig(Circuit newCircuit);
/**
* Deletes the {@link Circuit} with the given {@link DSID}.
*
* @param dSID of the {@link Circuit} to remove
* @return the removed {@link Circuit}
*/
Circuit deleteCircuit(DSID dSID);
/**
* Deletes the {@link Circuit} with the given dSUID.
*
* @param dSUID of the {@link Circuit} to remove
* @return the removed {@link Circuit}
*/
Circuit deleteCircuit(String dSUID);
/**
* Returns a {@link Map} of all {@link Circuit}'s which are added to this {@link StructureManager}.
*
* @return {@link Map} of all added {@link Circuit}'s
*/
Map<DSID, Circuit> getCircuitMap();
}

View File

@@ -0,0 +1,542 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager.impl;
import java.net.HttpURLConnection;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.listener.ConnectionListener;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl.DsAPIImpl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.impl.HttpTransportImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link ConnectionManagerImpl} is the implementation of the {@link ConnectionManager}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class ConnectionManagerImpl implements ConnectionManager {
/**
* Query to get all enabled application tokens. Can be executed with {@link DsAPI#query(String, String)} or
* {@link DsAPI#query2(String, String)}.
*/
public final String QUERY_GET_ENABLED_APPLICATION_TOKENS = "/system/security/applicationTokens/enabled/*(*)";
private final Logger logger = LoggerFactory.getLogger(ConnectionManagerImpl.class);
private Config config;
private ConnectionListener connListener;
private HttpTransport transport;
private String sessionToken;
private Boolean connectionEstablished = false;
private boolean genAppToken;
private DsAPI digitalSTROMClient;
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String)}, but the connection
* timeout and read timeout can be set, too.
*
* @param hostArddress (must not be null)
* @param connectTimeout (if connectTimeout is lower than 0 the {@link Config#DEFAULT_CONNECTION_TIMEOUT} will be
* set)
* @param readTimeout (if readTimeout is lower than 0 the {@link Config#DEFAULT_CONNECTION_TIMEOUT} will be set)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set
* @param applicationToken (can be null, if username and password is set)
* @see #ConnectionManagerImpl(String, String, String, String)
*/
public ConnectionManagerImpl(String hostArddress, int connectTimeout, int readTimeout, String username,
String password, String applicationToken) {
init(hostArddress, connectTimeout, readTimeout, username, password, applicationToken, false);
}
/**
* Creates a new {@link ConnectionManagerImpl} through a {@link Config} object, which has all configurations set.
*
* @param config (must not be null)
*/
public ConnectionManagerImpl(Config config) {
init(config, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(Config)}, but a {@link ConnectionListener} can be
* registered, too.
*
* @param config (must not be null)
* @param connectionListener (can be null)
* @see #ConnectionManagerImpl(Config)
*/
public ConnectionManagerImpl(Config config, ConnectionListener connectionListener) {
this.connListener = connectionListener;
init(config, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(Config, ConnectionListener)}, but through genApToken it
* can be set, if a application token will be automatically generated.
*
* @param config (must not be null)
* @param connectionListener (can be null)
* @param genAppToken (true = application token will be generated, otherwise false)
* @see #ConnectionManagerImpl(Config, ConnectionListener)
*/
public ConnectionManagerImpl(Config config, ConnectionListener connectionListener, boolean genAppToken) {
this.connListener = connectionListener;
this.genAppToken = genAppToken;
init(config, false);
}
/**
* Creates a new {@link ConnectionManagerImpl} with the given parameters, which are needed to create the
* {@link HttpTransport} and to login into the digitalSTROM server. If the application token is null and the
* username and password are valid, a application token will be automatically generated or a existing application
* token for the at {@link Config#getApplicationName()} set application name will be set.
*
* @param hostAddress (must not be null)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set
* @param applicationToken (can be null, if username and password is set)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, String applicationToken) {
init(hostAddress, -1, -1, username, password, applicationToken, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String)}, but without username
* and password.
*
* @param hostAddress (must not be null)
* @param applicationToken (must not be null)
*/
public ConnectionManagerImpl(String hostAddress, String applicationToken) {
init(hostAddress, -1, -1, null, null, applicationToken, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String)}, but without application
* token.
*
* @param hostAddress (must not be null)
* @param username (must not be null)
* @param password (must not be null)
* @see #ConnectionManagerImpl(String, String, String, String)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password) {
init(hostAddress, -1, -1, username, password, null, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String)}, but a
* {@link ConnectionListener} can be set, too.
*
* @param hostAddress (must not be null)
* @param username (must not be null)
* @param password (must not be null)
* @param connectionListener (can be null)
* @see #ConnectionManagerImpl(String, String, String)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password,
ConnectionListener connectionListener) {
this.connListener = connectionListener;
init(hostAddress, -1, -1, username, password, null, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String)}, but a
* {@link ConnectionListener} can be set, too.
*
* @param hostAddress (must not be null)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set)
* @param applicationToken (can be null, if username and password is set)
* @param connectionListener (can be null)
* @see #ConnectionManagerImpl(String, String, String, String)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, String applicationToken,
ConnectionListener connectionListener) {
this.connListener = connectionListener;
init(hostAddress, -1, -1, username, password, null, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String)}, but through genApToken it
* can be set, if a application token will be automatically generated.
*
* @param hostAddress (must not be null)
* @param username (must not be null)
* @param password (must not be null)
* @param genAppToken (true = application token will be generated, otherwise false)
* @see #ConnectionManagerImpl(String, String, String, String)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, boolean genAppToken) {
this.genAppToken = genAppToken;
init(hostAddress, -1, -1, username, password, null, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String)}, but through genApToken
* it can be set, if a application token will be automatically generated.
*
* @param hostAddress (must not be null)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set)
* @param applicationToken (can be null, if username and password is set)
* @param genAppToken (true = application token will be generated, otherwise false)
* @see #ConnectionManagerImpl(String, String, String, String)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, String applicationToken,
boolean genAppToken) {
this.genAppToken = genAppToken;
init(hostAddress, -1, -1, username, password, applicationToken, false);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String, boolean)}, but through
* acceptAllCerts it can be set, if all SSL-Certificates will be accept.
*
* @param hostAddress (must not be null)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set)
* @param applicationToken (can be null, if username and password is set)
* @param genAppToken (true = application token will be generated, otherwise false)
* @param acceptAllCerts (true = all SSL-Certificates will be accept, otherwise false)
* @see #ConnectionManagerImpl(String, String, String, String, boolean)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, String applicationToken,
boolean genAppToken, boolean acceptAllCerts) {
this.genAppToken = genAppToken;
init(hostAddress, -1, -1, username, password, applicationToken, acceptAllCerts);
}
/**
* The same constructor like {@link #ConnectionManagerImpl(String, String, String, String, boolean)}, but a
* {@link ConnectionListener} can be set, too.
*
* @param hostAddress (must not be null)
* @param username (can be null, if application token is set)
* @param password (can be null, if application token is set)
* @param applicationToken (can be null, if username and password is set)
* @param genAppToken (true = application token will be generated, otherwise false)
* @param connectionListener (can be null)
* @see #ConnectionManagerImpl(String, String, String, String, boolean)
*/
public ConnectionManagerImpl(String hostAddress, String username, String password, String applicationToken,
boolean genAppToken, ConnectionListener connectionListener) {
this.connListener = connectionListener;
this.genAppToken = genAppToken;
init(hostAddress, -1, -1, username, password, applicationToken, false);
}
private void init(String hostAddress, int connectionTimeout, int readTimeout, String username, String password,
String applicationToken, boolean acceptAllCerts) {
config = new Config(hostAddress, username, password, applicationToken);
if (connectionTimeout >= 0) {
config.setConnectionTimeout(connectionTimeout);
}
if (readTimeout >= 0) {
config.setReadTimeout(readTimeout);
}
init(config, acceptAllCerts);
}
private void init(Config config, boolean acceptAllCerts) {
this.config = config;
this.transport = new HttpTransportImpl(this, acceptAllCerts);
this.digitalSTROMClient = new DsAPIImpl(transport);
if (this.genAppToken) {
this.onNotAuthenticated();
}
}
@Override
public HttpTransport getHttpTransport() {
return transport;
}
@Override
public DsAPI getDigitalSTROMAPI() {
return this.digitalSTROMClient;
}
@Override
public String getSessionToken() {
return this.sessionToken;
}
@Override
public String getNewSessionToken() {
if (this.genAppToken) {
if (StringUtils.isNotBlank(config.getAppToken())) {
sessionToken = this.digitalSTROMClient.loginApplication(config.getAppToken());
} else if (codeIsAuthentificationFaild()) {
onNotAuthenticated();
}
} else {
sessionToken = this.digitalSTROMClient.login(this.config.getUserName(), this.config.getPassword());
}
return sessionToken;
}
@Override
public synchronized boolean checkConnection() {
return checkConnection(this.digitalSTROMClient.checkConnection(null));
}
private final short code = HttpURLConnection.HTTP_OK;
private boolean codeIsAuthentificationFaild() {
return this.code == HttpURLConnection.HTTP_FORBIDDEN;
}
@Override
public boolean checkConnection(int code) {
switch (code) {
case HttpURLConnection.HTTP_INTERNAL_ERROR:
case HttpURLConnection.HTTP_OK:
if (!connectionEstablished) {
onConnectionResumed();
}
break;
case HttpURLConnection.HTTP_UNAUTHORIZED:
connectionEstablished = false;
break;
case HttpURLConnection.HTTP_FORBIDDEN:
getNewSessionToken();
if (sessionToken != null) {
if (!connectionEstablished) {
onConnectionResumed();
}
} else {
if (this.genAppToken) {
onNotAuthenticated();
}
connectionEstablished = false;
}
break;
case ConnectionManager.MALFORMED_URL_EXCEPTION:
onConnectionLost(ConnectionListener.INVALID_URL);
break;
case ConnectionManager.CONNECTION_EXCEPTION:
case ConnectionManager.SOCKET_TIMEOUT_EXCEPTION:
onConnectionLost(ConnectionListener.CONNECTON_TIMEOUT);
break;
case ConnectionManager.SSL_HANDSHAKE_EXCEPTION:
onConnectionLost(ConnectionListener.SSL_HANDSHAKE_ERROR);
break;
case ConnectionManager.GENERAL_EXCEPTION:
onConnectionLost(ConnectionListener.CONNECTION_LOST);
break;
case ConnectionManager.UNKNOWN_HOST_EXCEPTION:
onConnectionLost(ConnectionListener.UNKNOWN_HOST);
break;
case ConnectionManager.AUTHENTIFICATION_PROBLEM:
if (connListener != null) {
if (config.getAppToken() != null) {
connListener.onConnectionStateChange(ConnectionListener.NOT_AUTHENTICATED,
ConnectionListener.WRONG_APP_TOKEN);
} else {
connListener.onConnectionStateChange(ConnectionListener.NOT_AUTHENTICATED,
ConnectionListener.WRONG_USER_OR_PASSWORD);
}
}
break;
case HttpURLConnection.HTTP_NOT_FOUND:
onConnectionLost(ConnectionListener.HOST_NOT_FOUND);
break;
}
return connectionEstablished;
}
@Override
public boolean connectionEstablished() {
return connectionEstablished;
}
/**
* This method is called whenever the connection to the digitalSTROM-Server is available,
* but requests are not allowed due to a missing or invalid authentication.
*/
private void onNotAuthenticated() {
String applicationToken = null;
boolean isAuthenticated = false;
if (StringUtils.isNotBlank(config.getAppToken())) {
sessionToken = digitalSTROMClient.loginApplication(config.getAppToken());
if (sessionToken != null) {
isAuthenticated = true;
} else {
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.NOT_AUTHENTICATED,
ConnectionListener.WRONG_APP_TOKEN);
if (!checkUserPassword()) {
return;
}
}
}
}
if (checkUserPassword()) {
if (!isAuthenticated) {
// if an application-token for the application exists, use this application-token and test host is
// reachable
logger.debug("check existing application-tokens");
sessionToken = digitalSTROMClient.login(config.getUserName(), config.getPassword());
if (sessionToken != null) {
JsonObject jObj = digitalSTROMClient.query(sessionToken, QUERY_GET_ENABLED_APPLICATION_TOKENS);
if (jObj != null) {
if (jObj.get("enabled") != null && jObj.get("enabled").isJsonArray()) {
JsonArray jArray = jObj.get("enabled").getAsJsonArray();
// application-token check
for (int i = 0; i < jArray.size(); i++) {
JsonObject appToken = jArray.get(i).getAsJsonObject();
if (appToken.get("applicationName") != null && appToken.get("applicationName")
.getAsString().equals(config.getApplicationName())) {
// found application-token, set as application-token
applicationToken = appToken.get("token").getAsString();
logger.debug("found application-token {} for application {}", applicationToken,
config.getApplicationName());
break;
}
}
}
if (applicationToken == null) {
// no token found, generate applicationToken
applicationToken = this.digitalSTROMClient
.requestAppplicationToken(config.getApplicationName());
logger.debug(
"no application-token for application {} found, generate a application-token {}",
config.getApplicationName(), applicationToken);
if (StringUtils.isNotBlank(applicationToken)) {
// enable applicationToken
if (!digitalSTROMClient.enableApplicationToken(applicationToken,
digitalSTROMClient.login(config.getUserName(), config.getPassword()))) {
// if enable failed set application-token = null so thats not will be set
applicationToken = null;
}
}
}
if (applicationToken != null) {
logger.debug("application-token can be used");
config.setAppToken(applicationToken);
isAuthenticated = true;
}
}
} else {
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.NOT_AUTHENTICATED,
ConnectionListener.WRONG_USER_OR_PASSWORD);
return;
}
}
}
// remove password and username, to don't store them persistently
if (isAuthenticated) {
config.removeUsernameAndPassword();
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.APPLICATION_TOKEN_GENERATED);
}
}
} else if (!isAuthenticated) {
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.NOT_AUTHENTICATED,
ConnectionListener.NO_USER_PASSWORD);
}
}
}
private boolean checkUserPassword() {
if (StringUtils.isNotBlank(config.getUserName()) && StringUtils.isNotBlank(config.getPassword())) {
return true;
}
return false;
}
/**
* This method is called whenever the connection to the digitalSTROM-Server is lost.
*
* @param reason
*/
private void onConnectionLost(String reason) {
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.CONNECTION_LOST, reason);
}
connectionEstablished = false;
}
/**
* This method is called whenever the connection to the digitalSTROM-Server is resumed.
*/
private void onConnectionResumed() {
if (connListener != null) {
connListener.onConnectionStateChange(ConnectionListener.CONNECTION_RESUMED);
}
connectionEstablished = true;
}
@Override
public void registerConnectionListener(ConnectionListener listener) {
this.connListener = listener;
}
@Override
public void unregisterConnectionListener() {
this.connListener = null;
}
@Override
public String getApplicationToken() {
return config.getAppToken();
}
@Override
public boolean removeApplicationToken() {
if (StringUtils.isNotBlank(config.getAppToken())) {
return digitalSTROMClient.revokeToken(config.getAppToken(), null);
}
return true;
}
@Override
public void updateConfig(String host, String username, String password, String applicationToken) {
init(host, -1, -1, username, password, applicationToken, false);
}
@Override
public void updateConfig(Config config) {
if (this.config != null) {
this.config.updateConfig(config);
} else {
this.config = config;
}
init(this.config, false);
}
@Override
public void configHasBeenUpdated() {
init(this.config, false);
}
@Override
public Config getConfig() {
return this.config;
}
}

View File

@@ -0,0 +1,556 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.openhab.binding.digitalstrom.internal.lib.event.EventListener;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventNames;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
import org.openhab.binding.digitalstrom.internal.lib.listener.ManagerStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.SceneStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerStates;
import org.openhab.binding.digitalstrom.internal.lib.listener.stateenums.ManagerTypes;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.SceneManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.SceneDiscovery;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.SceneEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneManagerImpl} is the implementation of the {@link SceneManager}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public class SceneManagerImpl implements SceneManager {
/**
* Contains all supported event-types.
*/
public static final List<String> SUPPORTED_EVENTS = Arrays.asList(EventNames.CALL_SCENE, EventNames.UNDO_SCENE);
private final Logger logger = LoggerFactory.getLogger(SceneManagerImpl.class);
private final List<String> echoBox = Collections.synchronizedList(new LinkedList<>());
private final Map<String, InternalScene> internalSceneMap = Collections.synchronizedMap(new HashMap<>());
private EventListener eventListener;
private final StructureManager structureManager;
private final ConnectionManager connectionManager;
private final SceneDiscovery discovery;
private ManagerStatusListener statusListener;
private ManagerStates state = ManagerStates.STOPPED;
private boolean scenesGenerated = false;
/**
* Creates a new {@link SceneManagerImpl} through the given managers.
*
* @param connectionManager (must not be null)
* @param structureManager (must not be null)
*/
public SceneManagerImpl(ConnectionManager connectionManager, StructureManager structureManager) {
this.structureManager = structureManager;
this.connectionManager = connectionManager;
this.discovery = new SceneDiscovery(this);
}
/**
* Same constructor like {@link #SceneManagerImpl(ConnectionManager, StructureManager)}, but a
* {@link ManagerStatusListener} can be set, too.
*
* @param connectionManager (must not be null)
* @param structureManager (must not be null)
* @param statusListener (can be null)
* @see #SceneManagerImpl(ConnectionManager, StructureManager)
*/
public SceneManagerImpl(ConnectionManager connectionManager, StructureManager structureManager,
ManagerStatusListener statusListener) {
this.structureManager = structureManager;
this.connectionManager = connectionManager;
this.discovery = new SceneDiscovery(this);
this.statusListener = statusListener;
}
/**
* Same constructor like {@link #SceneManagerImpl(ConnectionManager, StructureManager, ManagerStatusListener)}, but
* a {@link EventListener} can be set, too.
*
* @param connectionManager (must not be null)
* @param structureManager (must not be null)
* @param statusListener (can be null)
* @param eventListener (can be null)
* @see #SceneManagerImpl(ConnectionManager, StructureManager, ManagerStatusListener)
*/
public SceneManagerImpl(ConnectionManager connectionManager, StructureManager structureManager,
ManagerStatusListener statusListener, EventListener eventListener) {
this.structureManager = structureManager;
this.connectionManager = connectionManager;
this.discovery = new SceneDiscovery(this);
this.statusListener = statusListener;
this.eventListener = eventListener;
}
@Override
public void start() {
logger.debug("start SceneManager");
if (eventListener == null) {
logger.debug("no EventListener is set, create a new EventListener");
eventListener = new EventListener(connectionManager, this);
} else {
logger.debug("EventListener is set, add this SceneManager as EventHandler");
eventListener.addEventHandler(this);
}
eventListener.start();
logger.debug("start SceneManager");
stateChanged(ManagerStates.RUNNING);
}
@Override
public void stop() {
logger.debug("stop SceneManager");
if (eventListener != null) {
eventListener.removeEventHandler(this);
}
this.discovery.stop();
this.stateChanged(ManagerStates.STOPPED);
}
@Override
public void handleEvent(EventItem eventItem) {
if (eventItem == null) {
return;
}
boolean isCallScene = true;
String isCallStr = eventItem.getName();
if (isCallStr != null) {
isCallScene = isCallStr.equals(EventNames.CALL_SCENE);
}
boolean isDeviceCall = false;
String deviceCallStr = eventItem.getSource().get(EventResponseEnum.IS_DEVICE);
if (deviceCallStr != null) {
isDeviceCall = Boolean.parseBoolean(deviceCallStr);
}
if (isDeviceCall) {
String dsidStr = null;
dsidStr = eventItem.getSource().get(EventResponseEnum.DSID);
short sceneId = -1;
String sceneStr = eventItem.getProperties().get(EventResponseEnum.SCENEID);
if (sceneStr != null) {
try {
sceneId = Short.parseShort(sceneStr);
} catch (java.lang.NumberFormatException e) {
logger.error("An exception occurred, while handling event at parsing sceneID: {}", sceneStr, e);
}
}
if (!isEcho(dsidStr, sceneId)) {
logger.debug("{} event for device: {}", eventItem.getName(), dsidStr);
if (isCallScene) {
this.callDeviceScene(dsidStr, sceneId);
} else {
this.undoDeviceScene(dsidStr);
}
}
} else {
String intSceneID = null;
String zoneIDStr = eventItem.getSource().get(EventResponseEnum.ZONEID);
String groupIDStr = eventItem.getSource().get(EventResponseEnum.GROUPID);
String sceneIDStr = eventItem.getProperties().get(EventResponseEnum.SCENEID);
if (zoneIDStr != null && sceneIDStr != null && groupIDStr != null) {
intSceneID = zoneIDStr + "-" + groupIDStr + "-" + sceneIDStr;
if (!isEcho(intSceneID)) {
logger.debug("{} event for scene: {}-{}-{}", eventItem.getName(), zoneIDStr, groupIDStr,
sceneIDStr);
if (isCallScene) {
this.callInternalScene(intSceneID);
} else {
this.undoInternalScene(intSceneID);
}
}
}
}
}
private boolean isEcho(String dsid, short sceneId) {
// sometimes the dS-event has a dSUID saved in the dSID
String echo = (structureManager.getDeviceByDSUID(dsid) != null
? structureManager.getDeviceByDSUID(dsid).getDSID().getValue()
: dsid) + "-" + sceneId;
logger.debug("An echo scene event was detected: {}", echo);
return isEcho(echo);
}
private boolean isEcho(String echoID) {
if (echoBox.contains(echoID)) {
echoBox.remove(echoID);
return true;
}
return false;
}
// ... we want to ignore own 'command-echos'
@Override
public void addEcho(String dsid, short sceneId) {
addEcho(dsid + "-" + sceneId);
}
// ... we want to ignore own 'command-echos'
@Override
public void addEcho(String internalSceneID) {
echoBox.add(internalSceneID);
}
@Override
public void callInternalScene(InternalScene scene) {
InternalScene intScene = this.internalSceneMap.get(scene.getID());
if (intScene != null) {
intScene.activateScene();
} else {
if (SceneEnum.getScene(scene.getSceneID()) != null
&& structureManager.checkZoneGroupID(scene.getZoneID(), scene.getGroupID())) {
scene.addReferenceDevices(this.structureManager.getReferenceDeviceListFromZoneXGroupX(scene.getZoneID(),
scene.getGroupID()));
this.internalSceneMap.put(scene.getID(), scene);
scene.activateScene();
}
}
}
@Override
public void callInternalSceneWithoutDiscovery(Integer zoneID, Short groupID, Short sceneID) {
InternalScene intScene = this.internalSceneMap.get(zoneID + "-" + groupID + "-" + sceneID);
if (intScene != null) {
intScene.activateScene();
} else {
InternalScene scene = new InternalScene(zoneID, groupID, sceneID, null);
if (structureManager.checkZoneGroupID(scene.getZoneID(), scene.getGroupID())) {
scene.addReferenceDevices(this.structureManager.getReferenceDeviceListFromZoneXGroupX(scene.getZoneID(),
scene.getGroupID()));
scene.activateScene();
}
}
}
@Override
public void callInternalScene(String sceneID) {
InternalScene intScene = this.internalSceneMap.get(sceneID);
if (intScene != null) {
logger.debug("activating existing scene {}", intScene.getSceneName());
intScene.activateScene();
} else {
intScene = createNewScene(sceneID);
if (intScene != null) {
logger.debug("created new scene, activating it: {}", intScene.getSceneName());
discovery.sceneDiscoverd(intScene);
intScene.activateScene();
}
}
}
@Override
public void addInternalScene(InternalScene intScene) {
if (!this.internalSceneMap.containsKey(intScene.getID())) {
if (SceneEnum.getScene(intScene.getSceneID()) != null
&& structureManager.checkZoneGroupID(intScene.getZoneID(), intScene.getGroupID())) {
intScene.addReferenceDevices(this.structureManager
.getReferenceDeviceListFromZoneXGroupX(intScene.getZoneID(), intScene.getGroupID()));
this.internalSceneMap.put(intScene.getID(), intScene);
}
} else {
InternalScene oldScene = this.internalSceneMap.get(intScene.getID());
String oldSceneName = this.internalSceneMap.get(intScene.getID()).getSceneName();
String newSceneName = intScene.getSceneName();
if ((oldSceneName.contains("Zone:") && oldSceneName.contains("Group:") && oldSceneName.contains("Scene:"))
&& !(newSceneName.contains("Zone:") && newSceneName.contains("Group:")
&& newSceneName.contains("Scene:"))) {
oldScene.setSceneName(newSceneName);
this.discovery.sceneDiscoverd(oldScene);
}
}
}
@Override
public void removeInternalScene(String sceneID) {
this.internalSceneMap.remove(sceneID);
}
@Override
public InternalScene getInternalScene(String sceneID) {
return this.internalSceneMap.get(sceneID);
}
private InternalScene createNewScene(String sceneID) {
String[] sceneData = sceneID.split("-");
if (sceneData.length == 3) {
int zoneID = Integer.parseInt(sceneData[0]);
short groupID = Short.parseShort(sceneData[1]);
short sceneNumber = Short.parseShort(sceneData[2]);
String sceneName = null;
sceneName = connectionManager.getDigitalSTROMAPI().getSceneName(connectionManager.getSessionToken(), zoneID,
null, groupID, sceneNumber);
InternalScene intScene = null;
if (SceneEnum.getScene(sceneNumber) != null && structureManager.checkZoneGroupID(zoneID, groupID)) {
if (sceneName == null) {
if (structureManager.getZoneName(zoneID) != null) {
sceneName = "Zone: " + structureManager.getZoneName(zoneID);
if (structureManager.getZoneGroupName(zoneID, groupID) != null) {
sceneName = sceneName + " Group: " + structureManager.getZoneGroupName(zoneID, groupID);
} else {
sceneName = sceneName + " Group: " + groupID;
}
} else {
if (structureManager.getZoneGroupName(zoneID, groupID) != null) {
sceneName = "Zone: " + zoneID + " Group: "
+ structureManager.getZoneGroupName(zoneID, groupID);
} else {
sceneName = "Zone: " + zoneID + " Group: " + groupID;
}
}
sceneName = sceneName + " Scene: "
+ SceneEnum.getScene(sceneNumber).toString().toLowerCase().replace("_", " ");
}
intScene = new InternalScene(zoneID, groupID, sceneNumber, sceneName);
}
return intScene;
}
return null;
}
@Override
public void callDeviceScene(String dSID, Short sceneID) {
Device device = this.structureManager.getDeviceByDSID(new DSID(dSID));
if (device != null) {
device.internalCallScene(sceneID);
} else {
device = this.structureManager.getDeviceByDSUID(dSID);
if (device != null) {
device.internalCallScene(sceneID);
}
}
}
@Override
public void callDeviceScene(Device device, Short sceneID) {
if (device != null) {
callDeviceScene(device.getDSID().toString(), sceneID);
}
}
@Override
public void undoInternalScene(InternalScene scene) {
if (scene != null) {
undoInternalScene(scene.getID());
}
}
@Override
public void undoInternalScene(String sceneID) {
InternalScene intScene = this.internalSceneMap.get(sceneID);
if (intScene != null) {
logger.debug("deactivating existing scene {}", intScene.getSceneName());
intScene.deactivateScene();
} else {
intScene = createNewScene(sceneID);
if (intScene != null) {
logger.debug("created new scene, deactivating it: {}", intScene.getSceneName());
intScene.deactivateScene();
}
}
}
@Override
public void undoDeviceScene(String dSID) {
Device device = this.structureManager.getDeviceByDSID(new DSID(dSID));
if (device != null) {
device.internalUndoScene();
} else {
device = this.structureManager.getDeviceByDSUID(dSID);
if (device != null) {
device.internalUndoScene();
}
}
}
@Override
public void undoDeviceScene(Device device) {
if (device != null) {
undoDeviceScene(device.getDSID().toString());
}
}
@Override
public void registerSceneListener(SceneStatusListener sceneListener) {
if (sceneListener != null) {
String id = sceneListener.getSceneStatusListenerID();
if (id.equals(SceneStatusListener.SCENE_DISCOVERY)) {
discovery.registerSceneDiscovery(sceneListener);
logger.debug("Scene-Discovery registrated");
for (InternalScene scene : internalSceneMap.values()) {
discovery.sceneDiscoverd(scene);
}
} else {
InternalScene intScene = internalSceneMap.get(sceneListener.getSceneStatusListenerID());
if (intScene != null) {
intScene.registerSceneListener(sceneListener);
} else {
addInternalScene(createNewScene(id));
registerSceneListener(sceneListener);
}
logger.debug("SceneStatusListener with id {} is registrated", sceneListener.getSceneStatusListenerID());
}
}
}
@Override
public void unregisterSceneListener(SceneStatusListener sceneListener) {
if (sceneListener != null) {
String id = sceneListener.getSceneStatusListenerID();
if (id.equals(SceneStatusListener.SCENE_DISCOVERY)) {
this.discovery.unRegisterDiscovery();
logger.debug("Scene-Discovery unregistrated");
} else {
InternalScene intScene = this.internalSceneMap.get(sceneListener.getSceneStatusListenerID());
if (intScene != null) {
intScene.unregisterSceneListener();
}
logger.debug("SceneStatusListener with id {} is unregistrated",
sceneListener.getSceneStatusListenerID());
}
}
}
@Override
public synchronized boolean scenesGenerated() {
return scenesGenerated;
}
@Override
public void generateScenes() {
stateChanged(ManagerStates.GENERATING_SCENES);
logger.debug("start generating scenes");
discovery.generateAllScenes(connectionManager, structureManager);
}
@Override
public void scenesGenerated(char[] scenesGenerated) {
if (String.valueOf(scenesGenerated).equals("1111")) {
this.scenesGenerated = true;
stateChanged(ManagerStates.RUNNING);
}
if (String.valueOf(scenesGenerated).contains("2")) {
String type = "nan";
switch (String.valueOf(scenesGenerated).indexOf("2")) {
case 0:
type = "namedScens";
break;
case 1:
type = "appScenes";
break;
case 2:
type = "zoneScenes";
break;
case 3:
type = "reachableScenes";
break;
}
logger.debug("Not all scenes are generated, try it again. Scene type {} is not generated.", type);
stateChanged(ManagerStates.RUNNING);
}
}
@Override
public boolean isDiscoveryRegistrated() {
return this.discovery != null;
}
private void stateChanged(ManagerStates state) {
this.state = state;
if (statusListener != null) {
statusListener.onStatusChanged(ManagerTypes.SCENE_MANAGER, state);
}
}
@Override
public ManagerTypes getManagerType() {
return ManagerTypes.SCENE_MANAGER;
}
@Override
public synchronized ManagerStates getManagerState() {
return state;
}
@Override
public List<InternalScene> getScenes() {
return this.internalSceneMap != null ? new LinkedList<>(this.internalSceneMap.values()) : null;
}
@Override
public void registerStatusListener(ManagerStatusListener statusListener) {
this.statusListener = statusListener;
}
@Override
public void unregisterStatusListener() {
this.statusListener = null;
}
@Override
public String getUID() {
return this.getClass().getSimpleName() + "-" + SUPPORTED_EVENTS.toString();
}
@Override
public List<String> getSupportedEvents() {
return SUPPORTED_EVENTS;
}
@Override
public boolean supportsEvent(String eventName) {
return SUPPORTED_EVENTS.contains(eventName);
}
@Override
public void setEventListener(EventListener eventListener) {
if (this.eventListener != null) {
this.eventListener.removeEventHandler(this);
}
this.eventListener = eventListener;
}
@Override
public void unsetEventListener(EventListener eventListener) {
if (this.eventListener != null) {
this.eventListener.removeEventHandler(this);
}
this.eventListener = null;
}
}

View File

@@ -0,0 +1,424 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager.impl;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.manager.StructureManager;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.AbstractGeneralDeviceInformations;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Circuit;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.CachedMeteringValue;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
/**
* The {@link StructureManagerImpl} is the implementation of the {@link StructureManager}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class StructureManagerImpl implements StructureManager {
private class ZoneGroupsNameAndIDMap {
public final String zoneName;
public final int zoneID;
private final Map<Short, String> groupIdNames;
private final Map<String, Short> groupNameIds;
public ZoneGroupsNameAndIDMap(final int zoneID, final String zoneName, JsonArray groups) {
this.zoneID = zoneID;
this.zoneName = zoneName;
groupIdNames = new HashMap<>(groups.size());
groupNameIds = new HashMap<>(groups.size());
for (int k = 0; k < groups.size(); k++) {
short groupID = ((JsonObject) groups.get(k)).get("group").getAsShort();
String groupName = ((JsonObject) groups.get(k)).get("name").getAsString();
groupIdNames.put(groupID, groupName);
groupNameIds.put(groupName, groupID);
}
}
public String getGroupName(Short groupID) {
return groupIdNames.get(groupID);
}
public short getGroupID(String groupName) {
final Short tmp = groupNameIds.get(groupName);
return tmp != null ? tmp : -1;
}
}
/**
* Query to get all zone and group names. Can be executed with {@link DsAPI#query(String, String)} or
* {@link DsAPI#query2(String, String)}.
*/
public static final String ZONE_GROUP_NAMES = "/apartment/zones/*(ZoneID,name)/groups/*(group,name)";
private final Map<Integer, Map<Short, List<Device>>> zoneGroupDeviceMap = Collections
.synchronizedMap(new HashMap<>());
private final Map<DSID, Device> deviceMap = Collections.synchronizedMap(new HashMap<>());
private final Map<DSID, Circuit> circuitMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String, DSID> dSUIDToDSIDMap = Collections.synchronizedMap(new HashMap<>());
private Map<Integer, ZoneGroupsNameAndIDMap> zoneGroupIdNameMap;
private Map<String, ZoneGroupsNameAndIDMap> zoneGroupNameIdMap;
/**
* Creates a new {@link StructureManagerImpl} with the {@link Device}s of the given referenceDeviceList.
*
* @param referenceDeviceList to add
*/
public StructureManagerImpl(List<Device> referenceDeviceList) {
handleStructure(referenceDeviceList);
}
/**
* Creates a new {@link StructureManagerImpl} with the {@link Device}s of the given referenceDeviceList.
*
* @param referenceDeviceList to add
* @param referenceCircuitList to add
*/
public StructureManagerImpl(List<Device> referenceDeviceList, List<Circuit> referenceCircuitList) {
handleStructure(referenceDeviceList);
addCircuitList(referenceCircuitList);
}
/**
* Creates a new {@link StructureManagerImpl} without {@link Device}s.
*/
public StructureManagerImpl() {
}
@Override
public boolean generateZoneGroupNames(ConnectionManager connectionManager) {
JsonObject resultJsonObj = connectionManager.getDigitalSTROMAPI().query(connectionManager.getSessionToken(),
ZONE_GROUP_NAMES);
if (resultJsonObj != null && resultJsonObj.get("zones") instanceof JsonArray) {
JsonArray zones = (JsonArray) resultJsonObj.get("zones");
if (zoneGroupIdNameMap == null) {
zoneGroupIdNameMap = new HashMap<>(zones.size());
zoneGroupNameIdMap = new HashMap<>(zones.size());
}
if (zones != null) {
for (int i = 0; i < zones.size(); i++) {
if (((JsonObject) zones.get(i)).get("groups") instanceof JsonArray) {
JsonArray groups = (JsonArray) ((JsonObject) zones.get(i)).get("groups");
ZoneGroupsNameAndIDMap zoneGoupIdNameMap = new ZoneGroupsNameAndIDMap(
((JsonObject) zones.get(i)).get("ZoneID").getAsInt(),
((JsonObject) zones.get(i)).get("name").getAsString(), groups);
zoneGroupIdNameMap.put(zoneGoupIdNameMap.zoneID, zoneGoupIdNameMap);
zoneGroupNameIdMap.put(zoneGoupIdNameMap.zoneName, zoneGoupIdNameMap);
}
}
}
}
return true;
}
@Override
public String getZoneName(int zoneID) {
if (zoneGroupIdNameMap == null) {
return null;
}
final ZoneGroupsNameAndIDMap tmp = zoneGroupIdNameMap.get(zoneID);
return tmp != null ? tmp.zoneName : null;
}
@Override
public String getZoneGroupName(int zoneID, short groupID) {
if (zoneGroupIdNameMap == null) {
return null;
}
final ZoneGroupsNameAndIDMap tmp = zoneGroupIdNameMap.get(zoneID);
return tmp != null ? tmp.getGroupName(groupID) : null;
}
@Override
public int getZoneId(String zoneName) {
if (zoneGroupNameIdMap == null) {
return -1;
}
final ZoneGroupsNameAndIDMap tmp = zoneGroupNameIdMap.get(zoneName);
return tmp != null ? tmp.zoneID : -1;
}
@Override
public boolean checkZoneID(int zoneID) {
return getGroupsFromZoneX(zoneID) != null;
}
@Override
public boolean checkZoneGroupID(int zoneID, short groupID) {
final Map<Short, List<Device>> tmp = getGroupsFromZoneX(zoneID);
return tmp != null ? tmp.get(groupID) != null : false;
}
@Override
public short getZoneGroupId(String zoneName, String groupName) {
if (zoneGroupNameIdMap == null) {
return -1;
}
final ZoneGroupsNameAndIDMap tmp = zoneGroupNameIdMap.get(zoneName);
return tmp != null ? tmp.getGroupID(groupName) : -1;
}
@Override
public Map<DSID, Device> getDeviceMap() {
return new HashMap<>(deviceMap);
}
private void putDeviceToHashMap(Device device) {
if (device.getDSID() != null) {
deviceMap.put(device.getDSID(), device);
addDSIDtoDSUID((AbstractGeneralDeviceInformations) device);
}
}
/**
* This method build the digitalSTROM structure as an {@link HashMap} with the zone id as key
* and an {@link HashMap} as value. This {@link HashMap} has the group id as key and a {@link List}
* with all digitalSTROM {@link Device}s.<br>
* <br>
* <b>Note:</b> the zone id 0 is the broadcast address and the group id 0, too.
*/
private void handleStructure(List<Device> deviceList) {
Map<Short, List<Device>> groupXHashMap = new HashMap<>();
groupXHashMap.put((short) 0, deviceList);
zoneGroupDeviceMap.put(0, groupXHashMap);
for (Device device : deviceList) {
addDeviceToStructure(device);
}
}
@Override
public Map<DSID, Device> getDeviceHashMapReference() {
return deviceMap;
}
@Override
public Map<Integer, Map<Short, List<Device>>> getStructureReference() {
return zoneGroupDeviceMap;
}
@Override
public Map<Short, List<Device>> getGroupsFromZoneX(int zoneID) {
return zoneGroupDeviceMap.get(zoneID);
}
@Override
public List<Device> getReferenceDeviceListFromZoneXGroupX(int zoneID, short groupID) {
final Map<Short, List<Device>> tmp = getGroupsFromZoneX(zoneID);
return tmp != null ? tmp.get(groupID) : null;
}
@Override
public Device getDeviceByDSID(String dSID) {
return getDeviceByDSID(new DSID(dSID));
}
@Override
public Device getDeviceByDSID(DSID dSID) {
return deviceMap.get(dSID);
}
@Override
public Device getDeviceByDSUID(String dSUID) {
final DSID tmp = dSUIDToDSIDMap.get(dSUID);
return tmp != null ? getDeviceByDSID(tmp) : null;
}
@Override
public void updateDevice(int oldZone, List<Short> oldGroups, Device device) {
int intOldZoneID = oldZone;
if (intOldZoneID == -1) {
intOldZoneID = device.getZoneId();
}
deleteDevice(intOldZoneID, oldGroups, device);
addDeviceToStructure(device);
}
@Override
public void updateDevice(Device device) {
if (device != null) {
int oldZoneID = -1;
List<Short> oldGroups = null;
Device internalDevice = this.getDeviceByDSID(device.getDSID());
if (internalDevice != null) {
if (device.getZoneId() != internalDevice.getZoneId()) {
oldZoneID = internalDevice.getZoneId();
internalDevice.setZoneId(device.getZoneId());
}
if (!internalDevice.getGroups().equals(device.getGroups())) {
oldGroups = internalDevice.getGroups();
internalDevice.setGroups(device.getGroups());
}
if (deleteDevice(oldZoneID, oldGroups, internalDevice)) {
addDeviceToStructure(internalDevice);
}
}
}
}
@Override
public void deleteDevice(Device device) {
dSUIDToDSIDMap.remove(device.getDSUID());
deviceMap.remove(device.getDSID());
deleteDevice(device.getZoneId(), device.getGroups(), device);
}
private boolean deleteDevice(int zoneID, List<Short> groups, Device device) {
List<Short> intGroups = groups;
int intZoneID = zoneID;
if (intGroups != null || intZoneID >= 0) {
if (intGroups == null) {
intGroups = device.getGroups();
}
if (intZoneID == -1) {
intZoneID = device.getZoneId();
}
for (Short groupID : intGroups) {
List<Device> deviceList = getReferenceDeviceListFromZoneXGroupX(intZoneID, groupID);
if (deviceList != null) {
deviceList.remove(device);
}
deviceList = getReferenceDeviceListFromZoneXGroupX(0, groupID);
if (deviceList != null) {
deviceList.remove(device);
}
}
return true;
}
return false;
}
@Override
public void addDeviceToStructure(Device device) {
putDeviceToHashMap(device);
addDevicetoZoneXGroupX(0, (short) 0, device);
int zoneID = device.getZoneId();
addDevicetoZoneXGroupX(zoneID, (short) 0, device);
for (Short groupID : device.getGroups()) {
addDevicetoZoneXGroupX(zoneID, groupID, device);
if (groupID <= 16) {
addDevicetoZoneXGroupX(0, groupID, device);
}
}
}
private void addDevicetoZoneXGroupX(int zoneID, short groupID, Device device) {
Map<Short, List<Device>> groupXHashMap = zoneGroupDeviceMap.get(zoneID);
if (groupXHashMap == null) {
groupXHashMap = new HashMap<>();
zoneGroupDeviceMap.put(zoneID, groupXHashMap);
}
List<Device> groupDeviceList = groupXHashMap.get(groupID);
if (groupDeviceList == null) {
groupDeviceList = new LinkedList<>();
groupDeviceList.add(device);
groupXHashMap.put(groupID, groupDeviceList);
} else {
if (!groupDeviceList.contains(device)) {
groupDeviceList.add(device);
}
}
}
@Override
public Set<Integer> getZoneIDs() {
return zoneGroupDeviceMap.keySet();
}
@Override
public void addCircuitList(List<Circuit> referenceCircuitList) {
for (Circuit circuit : referenceCircuitList) {
addCircuit(circuit);
}
}
@Override
public Circuit addCircuit(Circuit circuit) {
addDSIDtoDSUID((AbstractGeneralDeviceInformations) circuit);
return circuitMap.put(circuit.getDSID(), circuit);
}
private void addDSIDtoDSUID(AbstractGeneralDeviceInformations deviceInfo) {
if (deviceInfo.getDSID() != null) {
dSUIDToDSIDMap.put(deviceInfo.getDSUID(), deviceInfo.getDSID());
}
}
@Override
public Circuit getCircuitByDSID(DSID dSID) {
return circuitMap.get(dSID);
}
@Override
public Circuit getCircuitByDSUID(String dSUID) {
final DSID tmp = dSUIDToDSIDMap.get(dSUID);
return tmp != null ? getCircuitByDSID(tmp) : null;
}
@Override
public Circuit getCircuitByDSID(String dSID) {
return getCircuitByDSID(new DSID(dSID));
}
@Override
public Circuit updateCircuitConfig(Circuit newCircuit) {
Circuit intCircuit = circuitMap.get(newCircuit.getDSID());
if (intCircuit != null && !intCircuit.equals(newCircuit)) {
for (CachedMeteringValue meteringValue : intCircuit.getAllCachedMeteringValues()) {
newCircuit.addMeteringValue(meteringValue);
}
if (intCircuit.isListenerRegisterd()) {
newCircuit.registerDeviceStatusListener(intCircuit.getDeviceStatusListener());
}
}
return addCircuit(newCircuit);
}
@Override
public Circuit deleteCircuit(DSID dSID) {
return circuitMap.remove(dSID);
}
@Override
public Circuit deleteCircuit(String dSUID) {
return deleteCircuit(dSUIDToDSIDMap.get(dSUID));
}
@Override
public Map<DSID, Circuit> getCircuitMap() {
return new HashMap<>(circuitMap);
}
}

View File

@@ -0,0 +1,447 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.manager.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.climate.TemperatureControlSensorTransmitter;
import org.openhab.binding.digitalstrom.internal.lib.climate.jsonresponsecontainer.impl.TemperatureControlStatus;
import org.openhab.binding.digitalstrom.internal.lib.event.EventHandler;
import org.openhab.binding.digitalstrom.internal.lib.event.EventListener;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventNames;
import org.openhab.binding.digitalstrom.internal.lib.event.constants.EventResponseEnum;
import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
import org.openhab.binding.digitalstrom.internal.lib.listener.SystemStateChangeListener;
import org.openhab.binding.digitalstrom.internal.lib.listener.TemperatureControlStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FuncNameAndColorGroupEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link TemperatureControlManager} is responsible for handling the zone temperature control of the digitalSTROM
* zones. For that it implements a {@link EventHandler} to get informed by control changes, like the target temperature.
* It also implement the {@link TemperatureControlSensorTransmitter}, so the zone temperature can be set through this
* class. <br>
* <br>
* To check, if the heating-control-app is installed at the digitalSTROM server the static method
* {@link #isHeatingControllerInstallated(ConnectionManager)} can be used.<br>
* <br>
* To get informed by status changes tow listener types can be registered to the {@link TemperatureControlManager}:<br>
* {@link TemperatureControlStatusListener}, to get informed by configuration and status changes or as discovery.<br>
* {@link SystemStateChangeListener}, to get informed by heating water system changes. The heating system states are
* {@link #STATE_HEATING_WATER_SYSTEM_OFF}, {@link #STATE_HEATING_WATER_SYSTEM_COLD_WATER} and
* {@link #STATE_HEATING_WATER_SYSTEM_COLD_WATER}<br>
* <br>
* The {@link TemperatureControlManager} also contains some helpful static constants, like
* {@link #GET_HEATING_WATER_SYSTEM_STATE_PATH} to get the current heating water system state through
* {@link DsAPI#propertyTreeGetString(String, String)}.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class TemperatureControlManager implements EventHandler, TemperatureControlSensorTransmitter {
private final List<String> SUPPORTED_EVENTS = Arrays.asList(EventNames.HEATING_CONTROL_OPERATION_MODE);
private final Logger logger = LoggerFactory.getLogger(TemperatureControlManager.class);
private final ConnectionManager connectionMananager;
private final DsAPI dSapi;
private final EventListener eventListener;
private boolean isConfigured = false;
private HashMap<Integer, TemperatureControlStatusListener> zoneTemperationControlListenerMap;
private HashMap<Integer, TemperatureControlStatus> temperationControlStatus;
private TemperatureControlStatusListener discovery;
private SystemStateChangeListener systemStateChangeListener;
/**
* Name of the digitalSTROM heating water system state.
*/
public static final String STATE_NAME_HEATING_WATER_SYSTEM = "heating_water_system";
/**
* digitalSTROM heating water system state as string for off.
*/
public static final String STATE_HEATING_WATER_SYSTEM_OFF = "off"; // val=0
/**
* digitalSTROM heating water system state as string for hot water.
*/
public static final String STATE_HEATING_WATER_SYSTEM_HOT_WATER = "hot water"; // val=1
/**
* digitalSTROM heating water system state as string for cold water.
*/
public static final String STATE_HEATING_WATER_SYSTEM_COLD_WATER = "cold water"; // val=2
/**
* Path to get the current digitalSTROM heating water system state through
* {@link DsAPI#propertyTreeGetString(String, String)}.
*/
public static final String GET_HEATING_WATER_SYSTEM_STATE_PATH = "/usr/states/heating_water_system/state";
/**
* Path to get the current digitalSTROM heating controller nodes through
* {@link DsAPI#propertyTreeGetString(String, String)}.
* Can be used e.g. to check, if the digitalSTROM heating controller app is installed at the digitalSTROM server.
*/
public static final String GET_HEATING_HEATING_CONTROLLER_CHILDREN_PATH = "/scripts/heating-controller/";
/**
* Action for set operation mode at {@link EventNames#HEATING_CONTROL_OPERATION_MODE}.
*/
public static final String SET_OPERATION_MODE = "setOperationMode";
/**
* Action for evaluate real active mode at {@link EventNames#HEATING_CONTROL_OPERATION_MODE}. Will be called after
* {@link #SET_OPERATION_MODE} or if the configuration of a zone temperature control status has changed.
*/
public static final String EVALUATE_REAL_ACTIVE_MODE = "evaluateRealActiveMode";
private String currentHeatingWaterSystemStage;
private final List<String> echoBox = Collections.synchronizedList(new LinkedList<>());
/**
* Creates a new {@link TemperatureControlManager}. The {@link ConnectionManager} is needed. The other fields are
* only needed, if you want to get automatically informed by status changes through the {@link EventListener} and/or
* get informed by new configured zones as discovery.
*
* @param connectionMananager (must not be null)
* @param eventListener (can be null)
* @param discovery (can be null)
*/
public TemperatureControlManager(ConnectionManager connectionMananager, EventListener eventListener,
TemperatureControlStatusListener discovery) {
this(connectionMananager, eventListener, discovery, null);
}
/**
* Same constructor like
* {@link #TemperatureControlManager(ConnectionManager, EventListener, TemperatureControlStatusListener)}, but it
* can be set a {@link SystemStateChangeListener}, too.
*
* @param connectionMananager (must not be null)
* @param eventListener (can be null)
* @param discovery (can be null)
* @param systemStateChangeListener (can be null)
* @see #TemperatureControlManager(ConnectionManager, EventListener, TemperatureControlStatusListener)
*/
public TemperatureControlManager(ConnectionManager connectionMananager, EventListener eventListener,
TemperatureControlStatusListener discovery, SystemStateChangeListener systemStateChangeListener) {
this.connectionMananager = connectionMananager;
this.dSapi = connectionMananager.getDigitalSTROMAPI();
this.systemStateChangeListener = systemStateChangeListener;
this.discovery = discovery;
this.eventListener = eventListener;
checkZones();
if (eventListener != null) {
if (isConfigured) {
SUPPORTED_EVENTS.add(EventNames.ZONE_SENSOR_VALUE);
if (systemStateChangeListener != null) {
SUPPORTED_EVENTS.add(EventNames.STATE_CHANGED);
}
}
eventListener.addEventHandler(this);
}
}
/**
* Checks all digitalSTROM zones, if temperature control is configured. If a zone with configured temperature
* control is found, it will be stored, the flag for {@link #isConfigured()} will be set to true and the discovery
* will be informed, if a discovery is registered.
*/
public void checkZones() {
List<TemperatureControlStatus> temperationControlStatus = dSapi
.getApartmentTemperatureControlStatus(connectionMananager.getSessionToken());
if (!temperationControlStatus.isEmpty()) {
for (TemperatureControlStatus tempConStat : temperationControlStatus) {
addTemperatureControlStatus(tempConStat);
}
if (isConfigured && systemStateChangeListener != null) {
currentHeatingWaterSystemStage = dSapi.propertyTreeGetString(connectionMananager.getSessionToken(),
GET_HEATING_WATER_SYSTEM_STATE_PATH);
}
}
}
/**
* Returns true, if the digitalSTROM heating controller app is installed.
*
* @param connectionManager (must not be null)
* @return true, if heating controller app is installed, otherwise false
*/
public static boolean isHeatingControllerInstallated(ConnectionManager connectionManager) {
return connectionManager.getDigitalSTROMAPI().propertyTreeGetChildren(connectionManager.getSessionToken(),
GET_HEATING_HEATING_CONTROLLER_CHILDREN_PATH) != null;
}
/**
* Returns all zone which have temperature controlled configured.
*
* @return all temperature controlled zones
*/
public Collection<TemperatureControlStatus> getTemperatureControlStatusFromAllZones() {
return temperationControlStatus != null ? this.temperationControlStatus.values() : new LinkedList<>();
}
/**
* Registers a {@link TemperatureControlStatusListener} for a zone, if the temperation control for this zone is
* configured. It can be also register a {@link TemperatureControlStatusListener} as discovery, if the
* {@link TemperatureControlStatusListener#getTemperationControlStatusListenrID()} returns
* {@link TemperatureControlStatusListener#DISCOVERY}.
*
* @param temperatureControlStatusListener to register
*/
public void registerTemperatureControlStatusListener(
TemperatureControlStatusListener temperatureControlStatusListener) {
if (temperatureControlStatusListener != null) {
if (temperatureControlStatusListener.getTemperationControlStatusListenrID()
.equals(TemperatureControlStatusListener.DISCOVERY)) {
logger.debug("discovery is registered");
this.discovery = temperatureControlStatusListener;
if (temperationControlStatus != null) {
for (TemperatureControlStatus tempConStat : temperationControlStatus.values()) {
discovery.configChanged(tempConStat);
}
}
} else {
if (zoneTemperationControlListenerMap == null) {
zoneTemperationControlListenerMap = new HashMap<>();
}
TemperatureControlStatus tempConStat = checkAndGetTemperatureControlStatus(
temperatureControlStatusListener.getTemperationControlStatusListenrID());
if (tempConStat != null) {
logger.debug("register listener with id {}",
temperatureControlStatusListener.getTemperationControlStatusListenrID());
zoneTemperationControlListenerMap.put(
temperatureControlStatusListener.getTemperationControlStatusListenrID(),
temperatureControlStatusListener);
temperatureControlStatusListener.registerTemperatureSensorTransmitter(this);
}
temperatureControlStatusListener.configChanged(tempConStat);
}
}
}
/**
* Unregisters a {@link TemperatureControlStatusListener}, if it exist.
*
* @param temperatureControlStatusListener to unregister
*/
public void unregisterTemperatureControlStatusListener(
TemperatureControlStatusListener temperatureControlStatusListener) {
if (temperatureControlStatusListener != null) {
if (temperatureControlStatusListener.getTemperationControlStatusListenrID()
.equals(TemperatureControlStatusListener.DISCOVERY)) {
this.discovery = null;
return;
}
if (discovery != null && zoneTemperationControlListenerMap
.remove(temperatureControlStatusListener.getTemperationControlStatusListenrID()) != null) {
discovery.configChanged(temperationControlStatus
.get(temperatureControlStatusListener.getTemperationControlStatusListenrID()));
}
}
}
/**
* Returns the {@link TemperatureControlStatus} for the given zone, if the temperature control is configured,
* otherwise it will be returned null.
*
* @param zoneID to check
* @return {@link TemperatureControlStatus} if the temperature control is configured, otherwise null
*/
public TemperatureControlStatus checkAndGetTemperatureControlStatus(Integer zoneID) {
TemperatureControlStatus tempConStat = this.temperationControlStatus.get(zoneID);
if (tempConStat.isNotSetOff()) {
return tempConStat;
}
return null;
}
private boolean isEcho(Integer zoneID, SensorEnum sensorType, Float value) {
return echoBox.remove(zoneID + "-" + sensorType.getSensorType() + "-" + value);
}
private void addEcho(Integer zoneID, SensorEnum sensorType, Float value) {
echoBox.add(zoneID + "-" + sensorType.getSensorType() + "-" + value);
}
@Override
public void handleEvent(EventItem eventItem) {
logger.debug("detect event: {}", eventItem.toString());
if (eventItem.getName().equals(EventNames.ZONE_SENSOR_VALUE)) {
if (zoneTemperationControlListenerMap != null) {
if (SensorEnum.ROOM_TEMPERATURE_SET_POINT.getSensorType().toString()
.equals(eventItem.getProperties().get(EventResponseEnum.SENSOR_TYPE))) {
Integer zoneID = Integer.parseInt(eventItem.getSource().get(EventResponseEnum.ZONEID));
if (zoneTemperationControlListenerMap.get(zoneID) != null) {
Float newValue = Float
.parseFloat(eventItem.getProperties().get(EventResponseEnum.SENSOR_VALUE_FLOAT));
if (!isEcho(zoneID, SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE, newValue)) {
zoneTemperationControlListenerMap.get(zoneID).onTargetTemperatureChanged(newValue);
}
}
}
if (SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE.getSensorType().toString()
.equals(eventItem.getProperties().get(EventResponseEnum.SENSOR_TYPE))) {
Integer zoneID = Integer.parseInt(eventItem.getSource().get(EventResponseEnum.ZONEID));
if (zoneTemperationControlListenerMap.get(zoneID) != null) {
Float newValue = Float
.parseFloat(eventItem.getProperties().get(EventResponseEnum.SENSOR_VALUE_FLOAT));
if (!isEcho(zoneID, SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE, newValue)) {
zoneTemperationControlListenerMap.get(zoneID).onControlValueChanged(newValue.intValue());
}
}
}
}
}
if (eventItem.getName().equals(EventNames.HEATING_CONTROL_OPERATION_MODE)) {
if (EVALUATE_REAL_ACTIVE_MODE.equals(eventItem.getProperties().get(EventResponseEnum.ACTIONS))) {
Integer zoneID = Integer.parseInt(eventItem.getProperties().get(EventResponseEnum.ZONEID));
TemperatureControlStatus temperationControlStatus = dSapi
.getZoneTemperatureControlStatus(connectionMananager.getSessionToken(), zoneID, null);
if (temperationControlStatus != null) {
addTemperatureControlStatus(temperationControlStatus);
}
}
}
if (eventItem.getName().equals(EventNames.STATE_CHANGED)) {
if (STATE_NAME_HEATING_WATER_SYSTEM.equals(eventItem.getProperties().get(EventResponseEnum.STATE_NAME))) {
currentHeatingWaterSystemStage = eventItem.getProperties().get(EventResponseEnum.STATE);
logger.debug("heating water system state changed to {}", currentHeatingWaterSystemStage);
if (systemStateChangeListener != null) {
systemStateChangeListener.onSystemStateChanged(STATE_NAME_HEATING_WATER_SYSTEM,
currentHeatingWaterSystemStage);
}
}
}
}
private void addTemperatureControlStatus(TemperatureControlStatus temperationControlStatus) {
if (temperationControlStatus.isNotSetOff()) {
if (this.temperationControlStatus == null) {
this.temperationControlStatus = new HashMap<>();
}
if (this.temperationControlStatus.get(temperationControlStatus.getZoneID()) == null && discovery != null) {
discovery.configChanged(temperationControlStatus);
if (!isConfigured) {
isConfigured = true;
}
}
this.temperationControlStatus.put(temperationControlStatus.getZoneID(), temperationControlStatus);
if (zoneTemperationControlListenerMap != null
&& zoneTemperationControlListenerMap.get(temperationControlStatus.getZoneID()) != null) {
zoneTemperationControlListenerMap.get(temperationControlStatus.getZoneID())
.configChanged(temperationControlStatus);
}
}
}
@Override
public List<String> getSupportedEvents() {
return SUPPORTED_EVENTS;
}
@Override
public boolean supportsEvent(String eventName) {
return SUPPORTED_EVENTS.contains(eventName);
}
@Override
public String getUID() {
return getClass().getSimpleName();
}
@Override
public void setEventListener(EventListener eventListener) {
eventListener.addEventHandler(this);
}
@Override
public void unsetEventListener(EventListener eventListener) {
eventListener.removeEventHandler(this);
}
@Override
public boolean pushTargetTemperature(Integer zoneID, Float newValue) {
if (checkAndGetTemperatureControlStatus(zoneID) != null) {
if (dSapi.pushZoneSensorValue(connectionMananager.getSessionToken(), zoneID, null, (short) 0, null,
newValue, SensorEnum.ROOM_TEMPERATURE_SET_POINT)) {
addEcho(zoneID, SensorEnum.ROOM_TEMPERATURE_SET_POINT, newValue);
return true;
}
}
return false;
}
@Override
public boolean pushControlValue(Integer zoneID, Float newValue) {
if (checkAndGetTemperatureControlStatus(zoneID) != null) {
if (dSapi.pushZoneSensorValue(connectionMananager.getSessionToken(), zoneID, null,
FuncNameAndColorGroupEnum.TEMPERATION_CONTROL.getFunctionalColorGroup(), null, newValue,
SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE)) {
addEcho(zoneID, SensorEnum.ROOM_TEMPERATURE_CONTROL_VARIABLE, newValue);
return true;
}
}
return false;
}
/**
* Returns true, if minimum one zone has temperature control configured.
*
* @return true, if minimum one zone has temperature control configured, otherwise false
*/
public boolean isConfigured() {
return isConfigured;
}
/**
* Returns the current heating water system state, if a {@link SystemStateChangeListener} is registered, otherwise
* null.
*
* @return the current heating water system state or null, if no {@link SystemStateChangeListener}
*/
public String getHeatingWaterSystemState() {
return currentHeatingWaterSystemStage;
}
/**
* Registers the given {@link SystemStateChangeListener}, which will be informed about heating system water state
* changes.
*
* @param systemStateChangeListener to register
*/
public void registerSystemStateChangeListener(SystemStateChangeListener systemStateChangeListener) {
if (eventListener != null) {
SUPPORTED_EVENTS.add(EventNames.STATE_CHANGED);
eventListener.addSubscribe(EventNames.STATE_CHANGED);
}
this.systemStateChangeListener = systemStateChangeListener;
}
/**
* Unregisters a registered {@link SystemStateChangeListener}.
*/
public void unregisterSystemStateChangeListener() {
this.systemStateChangeListener = null;
}
}

View File

@@ -0,0 +1,241 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.openhab.core.common.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractSensorJobExecutor} provides the working process to execute implementations of {@link SensorJob}'s
* in the time interval set at the {@link Config}.
* <p>
* The following methods can be overridden by subclasses to implement a execution priority:
* </p>
* <ul>
* <li>{@link #addLowPriorityJob(SensorJob)}</li>
* <li>{@link #addMediumPriorityJob(SensorJob)}</li>
* <li>{@link #addHighPriorityJob(SensorJob)}</li>
* </ul>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public abstract class AbstractSensorJobExecutor {
private final Logger logger = LoggerFactory.getLogger(AbstractSensorJobExecutor.class);
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool(Config.THREADPOOL_NAME);
private Map<DSID, ScheduledFuture<?>> pollingSchedulers;
private final DsAPI dSAPI;
protected Config config;
private final ConnectionManager connectionManager;
private final List<CircuitScheduler> circuitSchedulerList = new LinkedList<>();
private class ExecutorRunnable implements Runnable {
private final CircuitScheduler circuit;
public ExecutorRunnable(CircuitScheduler circuit) {
this.circuit = circuit;
}
@Override
public void run() {
// pollingSchedulers is not final and might be set to null by another thread. See #8214
Map<DSID, ScheduledFuture<?>> pollingSchedulers = AbstractSensorJobExecutor.this.pollingSchedulers;
SensorJob sensorJob = circuit.getNextSensorJob();
DSID meter = circuit.getMeterDSID();
if (sensorJob != null) {
sensorJob.execute(dSAPI, connectionManager.getSessionToken());
}
if (circuit.noMoreJobs() && pollingSchedulers != null) {
logger.debug("no more jobs... stop circuit schedduler with id = {}", meter);
ScheduledFuture<?> scheduler = pollingSchedulers.get(meter);
if (scheduler != null) {
scheduler.cancel(true);
}
}
}
}
/**
* Creates a new {@link AbstractSensorJobExecutor}.
*
* @param connectionManager must not be null
*/
public AbstractSensorJobExecutor(ConnectionManager connectionManager) {
this.connectionManager = connectionManager;
config = connectionManager.getConfig();
this.dSAPI = connectionManager.getDigitalSTROMAPI();
}
/**
* Stops all circuit schedulers.
*/
public synchronized void shutdown() {
if (pollingSchedulers != null) {
for (ScheduledFuture<?> scheduledExecutor : pollingSchedulers.values()) {
scheduledExecutor.cancel(true);
}
pollingSchedulers = null;
logger.debug("stop all circuit schedulers.");
}
}
/**
* Starts all circuit schedulers.
*/
public synchronized void startExecutor() {
logger.debug("start all circuit schedulers.");
if (pollingSchedulers == null) {
pollingSchedulers = new HashMap<>();
}
if (circuitSchedulerList != null && !circuitSchedulerList.isEmpty()) {
for (CircuitScheduler circuit : circuitSchedulerList) {
startSchedduler(circuit);
}
}
}
private void startSchedduler(CircuitScheduler circuit) {
if (pollingSchedulers != null) {
if (pollingSchedulers.get(circuit.getMeterDSID()) == null
|| pollingSchedulers.get(circuit.getMeterDSID()).isCancelled()) {
pollingSchedulers.put(circuit.getMeterDSID(),
scheduler.scheduleWithFixedDelay(new ExecutorRunnable(circuit), circuit.getNextExecutionDelay(),
config.getSensorReadingWaitTime(), TimeUnit.MILLISECONDS));
}
}
}
/**
* Adds a high priority {@link SensorJob}.
*
* @param sensorJob to add
*/
public void addHighPriorityJob(SensorJob sensorJob) {
// can be Overridden to implement a priority
addSensorJobToCircuitScheduler(sensorJob);
}
/**
* Adds a medium priority {@link SensorJob}.
*
* @param sensorJob to add
*/
public void addMediumPriorityJob(SensorJob sensorJob) {
// can be overridden to implement a priority
addSensorJobToCircuitScheduler(sensorJob);
}
/**
* Adds a low priority {@link SensorJob}.
*
* @param sensorJob to add
*/
public void addLowPriorityJob(SensorJob sensorJob) {
// can be overridden to implement a priority
addSensorJobToCircuitScheduler(sensorJob);
}
/**
* Adds a {@link SensorJob} with a given priority .
*
* @param sensorJob to add
* @param priority to update
*/
public void addPriorityJob(SensorJob sensorJob, long priority) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(priority);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SensorJob from device with dSID {} and priority {} to AbstractJobExecutor",
sensorJob.getDSID(), priority);
}
/**
* Adds the given {@link SensorJob}.
*
* @param sensorJob to add
*/
protected void addSensorJobToCircuitScheduler(SensorJob sensorJob) {
synchronized (this.circuitSchedulerList) {
CircuitScheduler circuit = getCircuitScheduler(sensorJob.getMeterDSID());
if (circuit != null) {
circuit.addSensorJob(sensorJob);
} else {
circuit = new CircuitScheduler(sensorJob, config);
this.circuitSchedulerList.add(circuit);
}
startSchedduler(circuit);
}
}
private CircuitScheduler getCircuitScheduler(DSID dsid) {
for (CircuitScheduler circuit : this.circuitSchedulerList) {
if (circuit.getMeterDSID().equals(dsid)) {
return circuit;
}
}
return null;
}
/**
* Removes all SensorJobs of a specific {@link Device}.
*
* @param device to remove
*/
public void removeSensorJobs(Device device) {
if (device != null) {
CircuitScheduler circuit = getCircuitScheduler(device.getMeterDSID());
if (circuit != null) {
circuit.removeSensorJob(device.getDSID());
}
}
}
/**
* Removes the {@link SensorJob} with the given ID.
*
* @param device needed for the meterDSID
* @param ID of the {@link SensorJob} to remove
*/
public void removeSensorJob(Device device, String ID) {
if (device != null && ID != null) {
CircuitScheduler circuit = getCircuitScheduler(device.getMeterDSID());
if (circuit != null) {
circuit.removeSensorJob(ID);
}
}
}
}

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor;
import java.util.Comparator;
import java.util.Iterator;
import java.util.PriorityQueue;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This {@link CircuitScheduler} represents a circuit in the digitalSTROM-System and manages the priorities and
* execution times for the {@link SensorJob}s on this circuit.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class CircuitScheduler {
private final Logger logger = LoggerFactory.getLogger(CircuitScheduler.class);
private class SensorJobComparator implements Comparator<SensorJob> {
@Override
public int compare(SensorJob job1, SensorJob job2) {
return ((Long) job1.getInitalisationTime()).compareTo(job2.getInitalisationTime());
}
}
private final DSID meterDSID;
private long nextExecutionTime = System.currentTimeMillis();
private final PriorityQueue<SensorJob> sensorJobQueue = new PriorityQueue<>(10, new SensorJobComparator());
private final Config config;
/**
* Creates a new {@link CircuitScheduler}.
*
* @param meterDSID must not be null
* @param config must not be null
* @throws IllegalArgumentException if the meterDSID is null
*/
public CircuitScheduler(DSID meterDSID, Config config) {
if (meterDSID == null) {
throw new IllegalArgumentException("The meterDSID must not be null!");
}
this.meterDSID = meterDSID;
this.config = config;
}
/**
* Creates a new {@link CircuitScheduler} and add the first {@link SensorJob} to this {@link CircuitScheduler}.
*
* @param sensorJob to add, must not be null
* @param config must not be null
*/
public CircuitScheduler(SensorJob sensorJob, Config config) {
this.meterDSID = sensorJob.getMeterDSID();
this.sensorJobQueue.add(sensorJob);
this.config = config;
logger.debug("create circuitScheduler: {} and add sensorJob: {}", this.getMeterDSID(),
sensorJob.getDSID().toString());
}
/**
* Returns the meterDSID of the dS-Meter in which the {@link SensorJob}s will be executed.
*
* @return meterDSID
*/
public DSID getMeterDSID() {
return this.meterDSID;
}
/**
* Adds a new SensorJob to this {@link CircuitScheduler}, if no {@link SensorJob} with a higher priority exists.
*
* @param sensorJob to add
*/
public void addSensorJob(SensorJob sensorJob) {
synchronized (sensorJobQueue) {
if (!this.sensorJobQueue.contains(sensorJob)) {
sensorJobQueue.add(sensorJob);
logger.debug("Add sensorJob: {} to circuitScheduler: {}", sensorJob.toString(), this.getMeterDSID());
} else if (checkSensorJobPrio(sensorJob)) {
logger.debug("add sensorJob: {} with higher priority to circuitScheduler: {}", sensorJob.toString(),
this.getMeterDSID());
} else {
logger.debug("sensorJob: {} allready exist with a higher priority", sensorJob.getDSID());
}
}
}
private boolean checkSensorJobPrio(SensorJob sensorJob) {
synchronized (sensorJobQueue) {
for (Iterator<SensorJob> iter = sensorJobQueue.iterator(); iter.hasNext();) {
SensorJob existSensorJob = iter.next();
if (existSensorJob.equals(sensorJob)) {
if (sensorJob.getInitalisationTime() < existSensorJob.getInitalisationTime()) {
iter.remove();
sensorJobQueue.add(sensorJob);
return true;
}
}
}
}
return false;
}
/**
* Returns the next {@link SensorJob} which can be executed or null, if there are no more {@link SensorJob} to
* execute or the wait time between the {@link SensorJob}s executions has not expired yet.
*
* @return next SensorJob or null
*/
public SensorJob getNextSensorJob() {
synchronized (sensorJobQueue) {
if (sensorJobQueue.peek() != null && this.nextExecutionTime <= System.currentTimeMillis()) {
nextExecutionTime = System.currentTimeMillis() + config.getSensorReadingWaitTime();
return sensorJobQueue.poll();
} else {
return null;
}
}
}
/**
* Returns the time when the next {@link SensorJob} can be executed.
*
* @return next SesnorJob execution time
*/
public Long getNextExecutionTime() {
return this.nextExecutionTime;
}
/**
* Returns the delay when the next {@link SensorJob} can be executed.
*
* @return next SesnorJob execution delay
*/
public Long getNextExecutionDelay() {
long delay = this.nextExecutionTime - System.currentTimeMillis();
return delay > 0 ? delay : 0;
}
/**
* Removes all {@link SensorJob} of a specific {@link Device} with the given {@link DSID}.
*
* @param dSID of the device
*/
public void removeSensorJob(DSID dSID) {
synchronized (sensorJobQueue) {
for (Iterator<SensorJob> iter = sensorJobQueue.iterator(); iter.hasNext();) {
SensorJob job = iter.next();
if (job.getDSID().equals(dSID)) {
iter.remove();
logger.debug("Remove SensorJob with ID {}.", job.getID());
}
}
}
}
/**
* Removes the {@link SensorJob} with the given ID .
*
* @param id of the {@link SensorJob}
*/
public void removeSensorJob(String id) {
synchronized (sensorJobQueue) {
for (Iterator<SensorJob> iter = sensorJobQueue.iterator(); iter.hasNext();) {
SensorJob job = iter.next();
if (job.getID().equals(id)) {
iter.remove();
logger.debug("Remove SensorJob with ID {}.", id);
return;
}
}
logger.debug("No SensorJob with ID {} found, cannot remove a not existing SensorJob.", id);
}
}
/**
* Returns true, if there are no more {@link SensorJob}s to execute, otherwise false.
*
* @return no more SensorJobs? (true | false)
*/
public boolean noMoreJobs() {
synchronized (sensorJobQueue) {
return this.sensorJobQueue.isEmpty();
}
}
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneReadingJobExecutor} is the implementation of the {@link AbstractSensorJobExecutor} to execute
* digitalSTROM-Device scene configuration {@link SensorJob}'s e.g. {@link SceneConfigReadingJob} and
* {@link SceneOutputValueReadingJob}.
* <p>
* In addition priorities can be assigned to jobs therefore the {@link SceneReadingJobExecutor} offers the methods
* {@link #addHighPriorityJob(SensorJob)}, {@link #addMediumPriorityJob(SensorJob)} and
* {@link #addLowPriorityJob(SensorJob)}.
* </p>
* <p>
* <b>NOTE:</b><br>
* In contrast to the {@link SensorJobExecutor} the {@link SceneReadingJobExecutor} will execute {@link SensorJob}'s
* with high priority always before medium priority {@link SensorJob}s and so on.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public class SceneReadingJobExecutor extends AbstractSensorJobExecutor {
private Logger logger = LoggerFactory.getLogger(SceneReadingJobExecutor.class);
/**
* Creates a new {@link SceneReadingJobExecutor}.
*
* @param connectionManager must not be null
*/
public SceneReadingJobExecutor(ConnectionManager connectionManager) {
super(connectionManager);
}
@Override
public void addHighPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(0);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SceneReadingJob from device with dSID {} and high-priority to SceneReadingSobExecutor",
sensorJob.getDSID());
}
@Override
public void addMediumPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(1);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SceneReadingJob from device with dSID {} and medium-priority to SceneReadingJobExecutor",
sensorJob.getDSID());
}
@Override
public void addLowPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(2);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SceneReadingJob from device with dSID {} and low-priority to SceneReadingJobExecutor",
sensorJob.getDSID());
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SensorJobExecutor} is the implementation of the {@link AbstractSensorJobExecutor} to execute
* digitalSTROM-Device {@link SensorJob}'s e.g. {@link DeviceConsumptionSensorJob} and
* {@link DeviceOutputValueSensorJob}.
* <p>
* In addition priorities can be assigned to jobs, but the following list shows the maximum evaluation of a
* {@link SensorJob} per priority.
* </p>
* <ul>
* <li>low priority: read cycles before execution is set in {@link Config}</li>
* <li>medium priority: read cycles before execution is set in {@link Config}</li>
* <li>high priority: read cycles before execution 0</li>
* </ul>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public class SensorJobExecutor extends AbstractSensorJobExecutor {
private final Logger logger = LoggerFactory.getLogger(SensorJobExecutor.class);
private final long mediumFactor = super.config.getSensorReadingWaitTime() * super.config.getMediumPriorityFactor();
private final long lowFactor = super.config.getSensorReadingWaitTime() * super.config.getLowPriorityFactor();
/**
* Creates a new {@link SensorJobExecutor}.
*
* @param connectionManager must not be null
*/
public SensorJobExecutor(ConnectionManager connectionManager) {
super(connectionManager);
}
@Override
public void addHighPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SensorJob from device with dSID {} and high-priority to SensorJobExecutor",
sensorJob.getDSID());
}
@Override
public void addMediumPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(sensorJob.getInitalisationTime() + this.mediumFactor);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SensorJob from device with dSID {} and medium-priority to SensorJobExecutor",
sensorJob.getDSID());
}
@Override
public void addLowPriorityJob(SensorJob sensorJob) {
if (sensorJob == null) {
return;
}
sensorJob.setInitalisationTime(sensorJob.getInitalisationTime() + this.lowFactor);
addSensorJobToCircuitScheduler(sensorJob);
logger.debug("Add SensorJob from device with dSID {} and low-priority to SensorJobExecutor",
sensorJob.getDSID());
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor.sensorjob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
/**
* The {@link SensorJob} represents an executable job to read out digitalSTROM-Sensors or device configurations like
* scene values.<br>
* It can be added to an implementation of the {@link AbstractSensorJobExecutor} e.g. {@link SceneReadingJobExecutor} or
* {@link SensorJobExecutor}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface SensorJob {
/**
* Returns the dSID of the {@link Device} for which this job is to be created.
*
* @return dSID from the device
*/
DSID getDSID();
/**
* Returns the dSID of the digitalSTROM-Meter on which this job is to be created.
*
* @return dSID from the device meter
*/
DSID getMeterDSID();
/**
* Executes the SensorJob.
*
* @param dSAPI must not be null
* @param sessionToken to login
*/
void execute(DsAPI dSAPI, String sessionToken);
/**
* Returns the time when the {@link SensorJob} was initialized.
*
* @return the initialization time
*/
long getInitalisationTime();
/**
* Sets the time when the {@link SensorJob} was initialized e.g. to manages the priority of this {@link SensorJob}.
*
* @param time to set
*/
void setInitalisationTime(long time);
/**
* Returns the id of this {@link SensorJob}.
*
* @return id
*/
String getID();
}

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceConsumptionSensorJob} is the implementation of a {@link SensorJob}
* for reading out the current value of the of a digitalSTROM-Device sensor and updates the {@link Device}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class DeviceConsumptionSensorJob implements SensorJob {
private final Logger logger = LoggerFactory.getLogger(DeviceConsumptionSensorJob.class);
private final Device device;
private final SensorEnum sensorType;
private final DSID meterDSID;
private long initalisationTime = 0;
private boolean updateDevice = true;
/**
* Creates a new {@link DeviceConsumptionSensorJob}. Through updateDevice you can set, if the {@link Device} will be
* updates automatically.
*
* @param device to update
* @param type to update
* @param updateDevice (true = automatically device, otherwise false)
* @see #DeviceConsumptionSensorJob(Device, SensorEnum)
*/
public DeviceConsumptionSensorJob(Device device, SensorEnum type, boolean updateDevice) {
this.device = device;
this.sensorType = type;
this.meterDSID = device.getMeterDSID();
this.initalisationTime = System.currentTimeMillis();
this.updateDevice = updateDevice;
}
/**
* Creates a new {@link DeviceConsumptionSensorJob} with the given {@link SensorEnum} for the given {@link Device}
* and automatically {@link Device} update.
*
* @param device to update
* @param type to update
*/
public DeviceConsumptionSensorJob(Device device, SensorEnum type) {
this.device = device;
this.sensorType = type;
this.meterDSID = device.getMeterDSID();
this.initalisationTime = System.currentTimeMillis();
}
@Override
public void execute(DsAPI digitalSTROM, String token) {
int consumption = digitalSTROM.getDeviceSensorValue(token, this.device.getDSID(), null, null,
device.getSensorIndex(sensorType));
logger.debug("Executes {} new device consumption is {}", this.toString(), consumption);
if (updateDevice) {
device.setDeviceSensorDsValueBySensorJob(sensorType, consumption);
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DeviceConsumptionSensorJob) {
DeviceConsumptionSensorJob other = (DeviceConsumptionSensorJob) obj;
String device = this.device.getDSID().getValue() + this.sensorType.getSensorType();
return device.equals(other.device.getDSID().getValue() + other.sensorType.getSensorType());
}
return false;
}
@Override
public int hashCode() {
return new String(this.device.getDSID().getValue() + this.sensorType.getSensorType()).hashCode();
}
@Override
public DSID getDSID() {
return device.getDSID();
}
@Override
public DSID getMeterDSID() {
return this.meterDSID;
}
@Override
public long getInitalisationTime() {
return this.initalisationTime;
}
@Override
public void setInitalisationTime(long time) {
this.initalisationTime = time;
}
@Override
public String toString() {
return "DeviceConsumptionSensorJob [sensorType=" + sensorType + ", sensorIndex="
+ device.getSensorIndex(sensorType) + ", deviceDSID : " + device.getDSID().getValue() + ", meterDSID="
+ meterDSID + ", initalisationTime=" + initalisationTime + "]";
}
@Override
public String getID() {
return getID(device, sensorType);
}
/**
* Returns the id for a {@link DeviceConsumptionSensorJob} with the given {@link Device} and {@link SensorEnum}.
*
* @param device to update
* @param sensorType to update
* @return id
*/
public static String getID(Device device, SensorEnum sensorType) {
return DeviceConsumptionSensorJob.class.getSimpleName() + "-" + device.getDSID().getValue() + "-"
+ sensorType.toString();
}
}

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceConstants;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceStateUpdateImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceOutputValueSensorJob} is the implementation of a {@link SensorJob}
* for reading out the current device output value of a digitalSTROM-Device and update the {@link Device}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class DeviceOutputValueSensorJob implements SensorJob {
private final Logger logger = LoggerFactory.getLogger(DeviceOutputValueSensorJob.class);
private final Device device;
private short index = 0;
private final DSID meterDSID;
private long initalisationTime = 0;
/**
* Creates a new {@link DeviceOutputValueSensorJob} for the given {@link Device}.
*
* @param device to update
*/
public DeviceOutputValueSensorJob(Device device) {
this.device = device;
if (device.isShade()) {
this.index = DeviceConstants.DEVICE_SENSOR_SLAT_POSITION_OUTPUT;
} else {
this.index = DeviceConstants.DEVICE_SENSOR_OUTPUT;
}
this.meterDSID = device.getMeterDSID();
this.initalisationTime = System.currentTimeMillis();
}
@Override
public void execute(DsAPI digitalSTROM, String token) {
int value = digitalSTROM.getDeviceOutputValue(token, this.device.getDSID(), null, null, index);
logger.debug("Device output value on Demand : {}, dSID: {}", value, this.device.getDSID().getValue());
if (value != 1) {
switch (this.index) {
case 0:
this.device.updateInternalDeviceState(new DeviceStateUpdateImpl(DeviceStateUpdate.OUTPUT, value));
return;
case 2:
this.device.updateInternalDeviceState(
new DeviceStateUpdateImpl(DeviceStateUpdate.SLATPOSITION, value));
if (device.isBlind()) {
value = digitalSTROM.getDeviceOutputValue(token, this.device.getDSID(), null, null,
DeviceConstants.DEVICE_SENSOR_SLAT_ANGLE_OUTPUT);
logger.debug("Device angle output value on Demand : {}, dSID: {}", value,
this.device.getDSID().getValue());
if (value != 1) {
this.device.updateInternalDeviceState(
new DeviceStateUpdateImpl(DeviceStateUpdate.SLAT_ANGLE, value));
}
}
return;
default:
return;
}
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DeviceOutputValueSensorJob) {
DeviceOutputValueSensorJob other = (DeviceOutputValueSensorJob) obj;
String key = this.device.getDSID().getValue() + this.index;
return key.equals((other.device.getDSID().getValue() + other.index));
}
return false;
}
@Override
public int hashCode() {
return new String(this.device.getDSID().getValue() + this.index).hashCode();
}
@Override
public DSID getDSID() {
return device.getDSID();
}
@Override
public DSID getMeterDSID() {
return this.meterDSID;
}
@Override
public long getInitalisationTime() {
return this.initalisationTime;
}
@Override
public void setInitalisationTime(long time) {
this.initalisationTime = time;
}
@Override
public String toString() {
return "DeviceOutputValueSensorJob [deviceDSID : " + device.getDSID().getValue() + ", meterDSID=" + meterDSID
+ ", initalisationTime=" + initalisationTime + "]";
}
@Override
public String getID() {
return getID(device);
}
/**
* Returns the id for a {@link DeviceOutputValueSensorJob} with the given {@link Device}.
*
* @param device to update
* @return id
*/
public static String getID(Device device) {
return DeviceOutputValueSensorJob.class.getSimpleName() + "-" + device.getDSID().getValue();
}
}

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneConfigReadingJob} is the implementation of a {@link SensorJob}
* for reading out a scene output value of a digitalSTROM-Device and store it into the {@link Device}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class SceneConfigReadingJob implements SensorJob {
private final Logger logger = LoggerFactory.getLogger(SceneConfigReadingJob.class);
private final Device device;
private short sceneID = 0;
private final DSID meterDSID;
private long initalisationTime = 0;
/**
* Creates a new {@link SceneConfigReadingJob} for the given {@link Device} and the given sceneID.
*
* @param device to update
* @param sceneID to update
*/
public SceneConfigReadingJob(Device device, short sceneID) {
this.device = device;
this.sceneID = sceneID;
this.meterDSID = device.getMeterDSID();
this.initalisationTime = System.currentTimeMillis();
}
@Override
public void execute(DsAPI digitalSTROM, String token) {
DeviceSceneSpec sceneConfig = digitalSTROM.getDeviceSceneMode(token, device.getDSID(), null, null, sceneID);
if (sceneConfig != null) {
device.addSceneConfig(sceneID, sceneConfig);
logger.debug("UPDATED scene configuration for dSID: {}, sceneID: {}, configuration: {}",
this.device.getDSID(), sceneID, sceneConfig);
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SceneConfigReadingJob) {
SceneConfigReadingJob other = (SceneConfigReadingJob) obj;
String str = other.device.getDSID().getValue() + "-" + other.sceneID;
return (this.device.getDSID().getValue() + "-" + this.sceneID).equals(str);
}
return false;
}
@Override
public int hashCode() {
return new String(this.device.getDSID().getValue() + this.sceneID).hashCode();
}
@Override
public DSID getDSID() {
return device.getDSID();
}
@Override
public DSID getMeterDSID() {
return this.meterDSID;
}
@Override
public long getInitalisationTime() {
return this.initalisationTime;
}
@Override
public void setInitalisationTime(long time) {
this.initalisationTime = time;
}
@Override
public String toString() {
return "SceneConfigReadingJob [sceneID: " + sceneID + ", deviceDSID : " + device.getDSID().getValue()
+ ", meterDSID=" + meterDSID + ", initalisationTime=" + initalisationTime + "]";
}
@Override
public String getID() {
return getID(device, sceneID);
}
/**
* Returns the id for a {@link SceneConfigReadingJob} with the given {@link Device} and sceneID.
*
* @param device to update
* @param sceneID to update
* @return id
*/
public static String getID(Device device, Short sceneID) {
return SceneConfigReadingJob.class.getSimpleName() + "-" + device.getDSID().getValue() + "-" + sceneID;
}
}

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.impl;
import org.openhab.binding.digitalstrom.internal.lib.sensorjobexecutor.sensorjob.SensorJob;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.DsAPI;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SceneOutputValueReadingJob} is the implementation of a {@link SensorJob}
* for reading out a scene configuration of a digitalSTROM-Device and store it into the {@link Device}.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class SceneOutputValueReadingJob implements SensorJob {
private final Logger logger = LoggerFactory.getLogger(SceneOutputValueReadingJob.class);
private final Device device;
private short sceneID = 0;
private final DSID meterDSID;
private long initalisationTime = 0;
/**
* Creates a new {@link SceneOutputValueReadingJob} for the given {@link Device} and the given sceneID.
*
* @param device to update
* @param sceneID to update
*/
public SceneOutputValueReadingJob(Device device, short sceneID) {
this.device = device;
this.sceneID = sceneID;
this.meterDSID = device.getMeterDSID();
this.initalisationTime = System.currentTimeMillis();
}
@Override
public void execute(DsAPI digitalSTROM, String token) {
int[] sceneValue = digitalSTROM.getSceneValue(token, this.device.getDSID(), null, null, this.sceneID);
if (sceneValue[0] != -1) {
if (device.isBlind()) {
device.setSceneOutputValue(this.sceneID, sceneValue[0], sceneValue[1]);
} else {
device.setSceneOutputValue(this.sceneID, sceneValue[0]);
}
logger.debug("UPDATED sceneOutputValue for dsid: {}, sceneID: {}, value: {}, angle: {}",
this.device.getDSID(), sceneID, sceneValue[0], sceneValue[1]);
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SceneOutputValueReadingJob) {
SceneOutputValueReadingJob other = (SceneOutputValueReadingJob) obj;
String str = other.device.getDSID().getValue() + "-" + other.sceneID;
return (this.device.getDSID().getValue() + "-" + this.sceneID).equals(str);
}
return false;
}
@Override
public int hashCode() {
return new String(this.device.getDSID().getValue() + this.sceneID).hashCode();
}
@Override
public DSID getDSID() {
return device.getDSID();
}
@Override
public DSID getMeterDSID() {
return this.meterDSID;
}
@Override
public long getInitalisationTime() {
return this.initalisationTime;
}
@Override
public void setInitalisationTime(long time) {
this.initalisationTime = time;
}
@Override
public String toString() {
return "SceneOutputValueReadingJob [sceneID: " + sceneID + ", deviceDSID : " + device.getDSID().getValue()
+ ", meterDSID=" + meterDSID + ", initalisationTime=" + initalisationTime + "]";
}
@Override
public String getID() {
return getID(device, sceneID);
}
/**
* Returns the id for a {@link SceneOutputValueReadingJob} with the given {@link Device} and sceneID.
*
* @param device to update
* @param sceneID to update
* @return id
*/
public static String getID(Device device, Short sceneID) {
return DeviceOutputValueSensorJob.class.getSimpleName() + "-" + device.getDSID().getValue() + "-" + sceneID;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection;
/**
* The {@link HttpTransport} executes an request to the DigitalSTROM-Server.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface HttpTransport {
/**
* Executes a digitalSTROM-request through calling {@link #execute(String, int, int)} with default connection time
* out and read timeout.
*
* @param request to execute
* @return response
*/
String execute(String request);
/**
* Executes a digitalSTROM-request.
*
* @param request to execute
* @param connectTimeout of execution
* @param readTimeout of execution
* @return response
*/
String execute(String request, int connectTimeout, int readTimeout);
/**
* Executes a digitalSTROM test request and returns the HTTP-Code.
*
* @param testRequest to execute
* @return HTTP-Code
*/
int checkConnection(String testRequest);
/**
* Returns the connection timeout for sensor data readings.
*
* @return sensor data connection timeout
*/
int getSensordataConnectionTimeout();
/**
* Returns the read timeout for sensor data readings.
*
* @return sensor data read timeout
*/
int getSensordataReadTimeout();
/**
* Saves the SSL-Certificate in a file at the given path.
*
* @param path to save
* @return absolute path
*/
String writePEMCertFile(String path);
}

View File

@@ -0,0 +1,236 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.constants;
/**
* The {@link JSONApiResponseKeysEnum} contains digitalSTROM-JSON response keys.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel completely changed and updated only methods remained
* @author Matthias Siegele completely changed and updated only methods remained
*/
public enum JSONApiResponseKeysEnum {
// GENERAL
OK("ok"),
MESSAGE("message"),
RESULT("result"),
// STRUCTURE
APARTMENT("apartment"),
DS_METERS("dSMeters"),
ZONES("zones"),
CIRCUITS("circuits"),
DEVICES("devices"),
GROUPS("groups"),
REACHABLE_SCENES("reachableScenes"),
// SENSORS
// device
CONSUMPTION("consumption"),
SENSOR_VALUE("sensorValue"),
SENSOR_INDEX("sensorIndex"),
METER_VALUE("meterValue"),
TYPE("type"),
// meter sensors
POWER_CONSUMPTION("powerConsumption"),
ENERGY_METER_VALUE("energyMeterValue"),
ENERGY_METER_VALUE_WS("energyMeterValueWs"),
RESOLUTIONS("resolutions"),
RESOLUTION("resolution"),
SERIES("series"),
UNIT("unit"),
VALUES("values"),
DATE("date"),
// zone/apartment sensors
SENSOR_TYPE("sensorType"),
TEMPERATION_VALUE("TemperatureValue"),
TEMPERATION_VALUE_TIME("TemperatureValueTime"),
HUMIDITY_VALUE("HumidityValue"),
HUMIDITY_VALUE_TIME("HumidityValueTime"),
BRIGHTNESS_VALUE("BrightnessValue"),
BRIGHTNESS_VALUE_TIME("BrightnessValueTime"),
CO2_CONCENTRATION_VALUE("CO2ConcentrationValue"),
CO2_CONCENTRATION_VALUE_TIME("CO2ConcentrationValueTime"),
SENSORS("sensors"),
WEATHER_ICON_ID("WeatherIconId"),
WEATHER_CONDITION_ID("WeatherConditionId"),
WEATHER_SERVICE_ID("WeatherServiceId"),
WEATHER_SERVICE_TIME("WeatherServiceTime"),
// IDs
DSID("dSID"),
DSUID("dSUID"),
DSID_LOWER_CASE("dsid"),
METER_DSID("meterDSID"),
ZONE_ID("ZoneID"),
ZONE_ID_Lower_Z("zoneID"),
DSUID_LOWER_CASE("dsuid"),
GROUP_ID("groupID"),
METER_ID("meterID"),
ID("id"),
SCENE_ID("sceneID"),
NAME("name"),
DISPLAY_ID("DisplayID"),
// DEVICE
// status
IS_PRESENT("isPresent"),
IS_VALID("isValid"),
IS_ON("isOn"),
PRESENT("present"),
ON("on"),
// descriptions
FUNCTION_ID("functionID"),
PRODUCT_REVISION("productRevision"),
PRODUCT_ID("productID"),
HW_INFO("hwInfo"),
OUTPUT_MODE("outputMode"),
BUTTON_ID("buttonID"),
HAS_TAG("hasTag"),
TAGS("tags"),
REVISION_ID("revisionID"),
// config
CLASS("class"),
INDEX("index"),
VALUE("value"),
DONT_CARE("dontCare"),
LOCAL_PRIO("localPrio"),
SPECIAL_MODE("specialMode"),
FLASH_MODE("flashMode"),
LEDCON_INDEX("ledconIndex"),
DIM_TIME_INDEX("dimtimeIndex"),
UP("up"),
DOWN("down"),
// event table
TEST("test"),
ACTION("action"),
HYSTERSIS("hysteresis"),
VALIDITY("validity"),
// EVENTS
EVENTS("events"),
PROPERTIES("properties"),
EVENT_INDEX("eventIndex"),
EVENT_NAME("eventName"),
// SYSTEM & LOGIN
VERSION("version"),
TIME("time"),
TOKEN("token"),
APPLICATION_TOKEN("applicationToken"),
SELF("self"),
// CLIMATE
IS_CONFIGURED("IsConfigured"),
CONTROL_MODE("ControlMode"),
CONTROL_STATE("ControlState"),
CONTROL_DSUID("ControlDSUID"),
OPERATION_MODE("OperationMode"),
TEMPERATURE_VALUE("TemperatureValue"),
NOMINAL_VALUE("NominalValue"),
CONTROL_VALUE("ControlValue"),
TEMPERATURE_VALUE_TIME("TemperatureValueTime"),
NOMINAL_VALUE_TIME("NominalValueTime"),
CONTROL_VALUE_TIME("ControlValueTime"),
CTRL_T_RECENT("CtrlTRecent"),
CTRL_T_REFERENCE("CtrlTReference"),
CTRL_T_ERROR("CtrlTError"),
CTRL_T_ERROR_PREV("CtrlTErrorPrev"),
CTRL_INTEGRAL("CtrlIntegral"),
CTRL_YP("CtrlYp"),
CTRL_YI("CtrlYi"),
CTRL_YD("CtrlYd"),
CTRL_Y("CtrlY"),
CTRL_ANTI_WIND_UP("CtrlAntiWindUp"),
REFERENCE_ZONE("ReferenceZone"),
CTRL_OFFSET("CtrlOffset"),
EMERGENCY_VALUE("EmergencyValue"),
CTRL_KP("CtrlKp"),
CTRL_TS("CtrlTs"),
CTRL_TI("CtrlTi"),
CTRL_KD("CtrlKd"),
CTRL_MIN("CtrlImin"),
CTRL_MAX("CtrlImax"),
CTRL_Y_MIN("CtrlYmin"),
CTRL_Y_MAX("CtrlYmax"),
CTRL_KEEP_FLOOR_WARM("CtrlKeepFloorWarm"),
// UNDEF
COLOR_SELECT("colorSelect"),
MODE_SELECT("modeSelect"),
DIM_MODE("dimMode"),
RGB_MODE("rgbMode"),
GROUP_COLOR_MODE("groupColorMode"),
SOURCE("source"),
IS_SCENE_DEVICE("isSceneDevice"),
// Circuit
HW_VERSION("hwVersion"),
HW_VERSION_STRING("hwVersionString"),
SW_VERSION("swVersion"),
ARM_SW_VERSION("armSwVersion"),
DSP_SW_VERSION("dspSwVersion"),
API_VERSION("apiVersion"),
HW_NAME("hwName"),
BUS_MEMBER_TYPE("busMemberType"),
HAS_DEVICES("hasDevices"),
HAS_METERING("hasMetering"),
VDC_CONFIG_URL("VdcConfigURL"),
VDC_MODEL_UID("VdcModelUID"),
VDC_HARDWARE_GUID("VdcHardwareGuid"),
VDC_HARDWARE_MODEL_GUID("VdcHardwareModelGuid"),
VDC_VENDOR_GUID("VdcVendorGuid"),
VDC_OEM_GUID("VdcOemGuid"),
IGNORE_ACTIONS_FROM_NEW_DEVICES("ignoreActionsFromNewDevices"),
DS_METER_DSID("DSMeterDSID"),
HW_INFO_UPPER_HW("HWInfo"),
VALID("valid"),
VALUE_DS("valueDS"),
TIMESTAMP("timestamp"),
SENSOR_INPUTS("sensorInputs"),
GROUP("group"),
LAST_CALL_SCENE("lastCalledScene"),
ANGLE("angle"),
// Binary inputs
BINARY_INPUTS("binaryInputs"),
STATE("state"),
STATE_VALUE("stateValue"),
TARGET_GROUP_TYPE("targetGroupType"),
TARGET_GROUP("targetGroup"),
INPUT_TYPE("inputType"),
INPUT_ID("inputId");
private final String key;
private JSONApiResponseKeysEnum(String key) {
this.key = key;
}
/**
* Returns the key.
*
* @return key
*/
public String getKey() {
return key;
}
}

View File

@@ -0,0 +1,605 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.manager.ConnectionManager;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.HttpTransport;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ParameterKeys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link HttpTransportImpl} executes an request to the digitalSTROM-Server.
* <p>
* If a {@link Config} is given at the constructor. It sets the SSL-Certificate what is set in
* {@link Config#getCert()}. If there is no SSL-Certificate, but an path to an external SSL-Certificate file what is set
* in {@link Config#getTrustCertPath()} this will be set. If no SSL-Certificate is set in the {@link Config} it will be
* red out from the server and set in {@link Config#setCert(String)}.
*
* <p>
* If no {@link Config} is given the SSL-Certificate will be stored locally.
*
* <p>
* The method {@link #writePEMCertFile(String)} saves the SSL-Certificate in a file at the given path. If all
* SSL-Certificates shout be ignored the flag <i>exeptAllCerts</i> have to be true at the constructor
* </p>
* <p>
* If a {@link ConnectionManager} is given at the constructor, the session-token is not needed by requests and the
* {@link ConnectionListener}, which is registered at the {@link ConnectionManager}, will be automatically informed
* about
* connection state changes through the {@link #execute(String, int, int)} method.
* </p>
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public class HttpTransportImpl implements HttpTransport {
private static final String LINE_SEPERATOR = System.getProperty("line.separator");
private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----" + LINE_SEPERATOR;
private static final String END_CERT = LINE_SEPERATOR + "-----END CERTIFICATE-----" + LINE_SEPERATOR;
private final Logger logger = LoggerFactory.getLogger(HttpTransportImpl.class);
private static final short MAY_A_NEW_SESSION_TOKEN_IS_NEEDED = 1;
private String uri;
private int connectTimeout;
private int readTimeout;
private Config config;
private ConnectionManager connectionManager;
private String cert;
private SSLSocketFactory sslSocketFactory;
private final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return arg0.equals(arg1.getPeerHost()) || arg0.contains("dss.local.");
}
};
/**
* Creates a new {@link HttpTransportImpl} with registration of the given {@link ConnectionManager} and set ignore
* all SSL-Certificates. The {@link Config} will be automatically added from the configurations of the given
* {@link ConnectionManager}.
*
* @param connectionManager to check connection, can be null
* @param exeptAllCerts (true = all will ignore)
*/
public HttpTransportImpl(ConnectionManager connectionManager, boolean exeptAllCerts) {
this.connectionManager = connectionManager;
this.config = connectionManager.getConfig();
init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), exeptAllCerts);
}
/**
* Creates a new {@link HttpTransportImpl} with configurations of the given {@link Config} and set ignore all
* SSL-Certificates.
*
* @param config to get configurations, must not be null
* @param exeptAllCerts (true = all will ignore)
*/
public HttpTransportImpl(Config config, boolean exeptAllCerts) {
this.config = config;
init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), exeptAllCerts);
}
/**
* Creates a new {@link HttpTransportImpl} with configurations of the given {@link Config}.
*
* @param config to get configurations, must not be null
*/
public HttpTransportImpl(Config config) {
this.config = config;
init(config.getHost(), config.getConnectionTimeout(), config.getReadTimeout(), false);
}
/**
* Creates a new {@link HttpTransportImpl}.
*
* @param uri of the server, must not be null
*/
public HttpTransportImpl(String uri) {
init(uri, Config.DEFAULT_CONNECTION_TIMEOUT, Config.DEFAULT_READ_TIMEOUT, false);
}
/**
* Creates a new {@link HttpTransportImpl} and set ignore all SSL-Certificates.
*
* @param uri of the server, must not be null
* @param exeptAllCerts (true = all will ignore)
*/
public HttpTransportImpl(String uri, boolean exeptAllCerts) {
init(uri, Config.DEFAULT_CONNECTION_TIMEOUT, Config.DEFAULT_READ_TIMEOUT, exeptAllCerts);
}
/**
* Creates a new {@link HttpTransportImpl}.
*
* @param uri of the server, must not be null
* @param connectTimeout to set
* @param readTimeout to set
*/
public HttpTransportImpl(String uri, int connectTimeout, int readTimeout) {
init(uri, connectTimeout, readTimeout, false);
}
/**
* Creates a new {@link HttpTransportImpl} and set ignore all SSL-Certificates..
*
* @param uri of the server, must not be null
* @param connectTimeout to set
* @param readTimeout to set
* @param exeptAllCerts (true = all will ignore)
*/
public HttpTransportImpl(String uri, int connectTimeout, int readTimeout, boolean exeptAllCerts) {
init(uri, connectTimeout, readTimeout, exeptAllCerts);
}
private void init(String uri, int connectTimeout, int readTimeout, boolean exeptAllCerts) {
logger.debug("init HttpTransportImpl");
this.uri = fixURI(uri);
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
// Check SSL Certificate
if (exeptAllCerts) {
sslSocketFactory = generateSSLContextWhichAcceptAllSSLCertificats();
} else {
if (config != null) {
cert = config.getCert();
logger.debug("generate SSLcontext from config cert");
if (StringUtils.isNotBlank(cert)) {
sslSocketFactory = generateSSLContextFromPEMCertString(cert);
} else {
if (StringUtils.isNotBlank(config.getTrustCertPath())) {
logger.debug("generate SSLcontext from config cert path");
cert = readPEMCertificateStringFromFile(config.getTrustCertPath());
if (StringUtils.isNotBlank(cert)) {
sslSocketFactory = generateSSLContextFromPEMCertString(cert);
}
} else {
logger.debug("generate SSLcontext from server");
cert = getPEMCertificateFromServer(this.uri);
sslSocketFactory = generateSSLContextFromPEMCertString(cert);
if (sslSocketFactory != null) {
config.setCert(cert);
}
}
}
} else {
logger.debug("generate SSLcontext from server");
cert = getPEMCertificateFromServer(this.uri);
sslSocketFactory = generateSSLContextFromPEMCertString(cert);
}
}
}
private String fixURI(String uri) {
String fixedURI = uri;
if (!fixedURI.startsWith("https://")) {
fixedURI = "https://" + fixedURI;
}
if (fixedURI.split(":").length != 3) {
fixedURI = fixedURI + ":8080";
}
return fixedURI;
}
private String fixRequest(String request) {
return request.replace(" ", "");
}
@Override
public String execute(String request) {
return execute(request, this.connectTimeout, this.readTimeout);
}
private short loginCounter = 0;
@Override
public String execute(String request, int connectTimeout, int readTimeout) {
// NOTE: We will only show exceptions in the debug level, because they will be handled in the checkConnection()
// method and this changes the bridge state. If a command was send it fails than and a sensorJob will be
// execute the next time, by TimeOutExceptions. By other exceptions the checkConnection() method handles it in
// max 1 second.
String response = null;
HttpsURLConnection connection = null;
try {
String correctedRequest = checkSessionToken(request);
connection = getConnection(correctedRequest, connectTimeout, readTimeout);
if (connection != null) {
connection.connect();
final int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_FORBIDDEN) {
if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
response = IOUtils.toString(connection.getErrorStream());
} else {
response = IOUtils.toString(connection.getInputStream());
}
if (response != null) {
if (!response.contains("Authentication failed")) {
if (loginCounter > 0) {
connectionManager.checkConnection(responseCode);
}
loginCounter = 0;
} else {
connectionManager.checkConnection(ConnectionManager.AUTHENTIFICATION_PROBLEM);
loginCounter++;
}
}
}
connection.disconnect();
if (response == null && connectionManager != null
&& loginCounter <= MAY_A_NEW_SESSION_TOKEN_IS_NEEDED) {
if (responseCode == HttpURLConnection.HTTP_FORBIDDEN) {
execute(addSessionToken(correctedRequest, connectionManager.getNewSessionToken()),
connectTimeout, readTimeout);
loginCounter++;
} else {
connectionManager.checkConnection(responseCode);
loginCounter++;
return null;
}
}
return response;
}
} catch (SocketTimeoutException e) {
informConnectionManager(ConnectionManager.SOCKET_TIMEOUT_EXCEPTION);
} catch (java.net.ConnectException e) {
informConnectionManager(ConnectionManager.CONNECTION_EXCEPTION);
} catch (MalformedURLException e) {
informConnectionManager(ConnectionManager.MALFORMED_URL_EXCEPTION);
} catch (java.net.UnknownHostException e) {
informConnectionManager(ConnectionManager.UNKNOWN_HOST_EXCEPTION);
} catch (SSLHandshakeException e) {
informConnectionManager(ConnectionManager.SSL_HANDSHAKE_EXCEPTION);
} catch (IOException e) {
logger.error("An IOException occurred: ", e);
informConnectionManager(ConnectionManager.GENERAL_EXCEPTION);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
private boolean informConnectionManager(int code) {
if (connectionManager != null && loginCounter < MAY_A_NEW_SESSION_TOKEN_IS_NEEDED) {
connectionManager.checkConnection(code);
return true;
}
return false;
}
private String checkSessionToken(String request) {
if (checkNeededSessionToken(request)) {
if (connectionManager != null) {
String sessionToken = connectionManager.getSessionToken();
if (sessionToken == null) {
return addSessionToken(request, connectionManager.getNewSessionToken());
}
return addSessionToken(request, sessionToken);
}
}
return request;
}
private boolean checkNeededSessionToken(String request) {
String functionName = StringUtils.substringAfterLast(StringUtils.substringBefore(request, "?"), "/");
return !DsAPIImpl.METHODS_MUST_NOT_BE_LOGGED_IN.contains(functionName);
}
private String addSessionToken(String request, String sessionToken) {
String correctedRequest = request;
if (!correctedRequest.contains(ParameterKeys.TOKEN)) {
if (correctedRequest.contains("?")) {
correctedRequest = correctedRequest + "&" + ParameterKeys.TOKEN + "=" + sessionToken;
} else {
correctedRequest = correctedRequest + "?" + ParameterKeys.TOKEN + "=" + sessionToken;
}
} else {
correctedRequest = StringUtils.replaceOnce(correctedRequest, StringUtils.substringBefore(
StringUtils.substringAfter(correctedRequest, ParameterKeys.TOKEN + "="), "&"), sessionToken);
}
return correctedRequest;
}
private HttpsURLConnection getConnection(String request, int connectTimeout, int readTimeout) throws IOException {
String correctedRequest = request;
if (StringUtils.isNotBlank(correctedRequest)) {
correctedRequest = fixRequest(correctedRequest);
URL url = new URL(this.uri + correctedRequest);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
if (connection != null) {
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
if (sslSocketFactory != null) {
connection.setSSLSocketFactory(sslSocketFactory);
}
if (hostnameVerifier != null) {
connection.setHostnameVerifier(hostnameVerifier);
}
}
return connection;
}
return null;
}
@Override
public int checkConnection(String testRequest) {
try {
HttpsURLConnection connection = getConnection(testRequest, connectTimeout, readTimeout);
if (connection != null) {
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
if (IOUtils.toString(connection.getInputStream()).contains("Authentication failed")) {
return ConnectionManager.AUTHENTIFICATION_PROBLEM;
}
}
connection.disconnect();
return connection.getResponseCode();
} else {
return ConnectionManager.GENERAL_EXCEPTION;
}
} catch (SocketTimeoutException e) {
return ConnectionManager.SOCKET_TIMEOUT_EXCEPTION;
} catch (java.net.ConnectException e) {
return ConnectionManager.CONNECTION_EXCEPTION;
} catch (MalformedURLException e) {
return ConnectionManager.MALFORMED_URL_EXCEPTION;
} catch (java.net.UnknownHostException e) {
return ConnectionManager.UNKNOWN_HOST_EXCEPTION;
} catch (IOException e) {
return ConnectionManager.GENERAL_EXCEPTION;
}
}
@Override
public int getSensordataConnectionTimeout() {
return config != null ? config.getSensordataConnectionTimeout() : Config.DEFAULT_SENSORDATA_CONNECTION_TIMEOUT;
}
@Override
public int getSensordataReadTimeout() {
return config != null ? config.getSensordataReadTimeout() : Config.DEFAULT_SENSORDATA_READ_TIMEOUT;
}
private String readPEMCertificateStringFromFile(String path) {
if (StringUtils.isBlank(path)) {
logger.error("Path is empty.");
} else {
File dssCert = new File(path);
if (dssCert.exists()) {
if (path.endsWith(".crt")) {
try {
InputStream certInputStream = new FileInputStream(dssCert);
String cert = IOUtils.toString(certInputStream);
if (cert.startsWith(BEGIN_CERT)) {
return cert;
} else {
logger.error("File is not a PEM certificate file. PEM-Certificats starts with: {}",
BEGIN_CERT);
}
} catch (FileNotFoundException e) {
logger.error("Can't find a certificate file at the path: {}\nPlease check the path!", path);
} catch (IOException e) {
logger.error("An IOException occurred: ", e);
}
} else {
logger.error("File is not a certificate (.crt) file.");
}
} else {
logger.error("File not found");
}
}
return null;
}
@Override
public String writePEMCertFile(String path) {
String correctedPath = StringUtils.trimToEmpty(path);
File certFilePath;
if (StringUtils.isNotBlank(correctedPath)) {
certFilePath = new File(correctedPath);
boolean pathExists = certFilePath.exists();
if (!pathExists) {
pathExists = certFilePath.mkdirs();
}
if (pathExists && !correctedPath.endsWith("/")) {
correctedPath = correctedPath + "/";
}
}
InputStream certInputStream = IOUtils.toInputStream(cert);
X509Certificate trustedCert;
try {
trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(certInputStream);
certFilePath = new File(
correctedPath + trustedCert.getSubjectDN().getName().split(",")[0].substring(2) + ".crt");
if (!certFilePath.exists()) {
certFilePath.createNewFile();
FileWriter writer = new FileWriter(certFilePath, true);
writer.write(cert);
writer.flush();
writer.close();
return certFilePath.getAbsolutePath();
} else {
logger.error("File allready exists!");
}
} catch (IOException e) {
logger.error("An IOException occurred: ", e);
} catch (CertificateException e1) {
logger.error("A CertificateException occurred: ", e1);
}
return null;
}
private SSLSocketFactory generateSSLContextFromPEMCertString(String pemCert) {
if (StringUtils.isNotBlank(pemCert) && pemCert.startsWith(BEGIN_CERT)) {
try {
InputStream certInputStream = IOUtils.toInputStream(pemCert);
final X509Certificate trustedCert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(certInputStream);
final TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
throws CertificateException {
if (!certs[0].equals(trustedCert)) {
throw new CertificateException();
}
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
throws CertificateException {
if (!certs[0].equals(trustedCert)) {
throw new CertificateException();
}
}
} };
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManager, new java.security.SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
logger.error("A NoSuchAlgorithmException occurred: ", e);
} catch (KeyManagementException e) {
logger.error("A KeyManagementException occurred: ", e);
} catch (CertificateException e) {
logger.error("A CertificateException occurred: ", e);
}
} else {
logger.error("Cert is empty");
}
return null;
}
private String getPEMCertificateFromServer(String host) {
HttpsURLConnection connection = null;
try {
URL url = new URL(host);
connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier(hostnameVerifier);
connection.setSSLSocketFactory(generateSSLContextWhichAcceptAllSSLCertificats());
connection.connect();
java.security.cert.Certificate[] cert = connection.getServerCertificates();
connection.disconnect();
byte[] by = ((X509Certificate) cert[0]).getEncoded();
if (by.length != 0) {
return BEGIN_CERT + Base64.getEncoder().encodeToString(by) + END_CERT;
}
} catch (MalformedURLException e) {
if (!informConnectionManager(ConnectionManager.MALFORMED_URL_EXCEPTION)) {
logger.error("A MalformedURLException occurred: ", e);
}
} catch (IOException e) {
short code = ConnectionManager.GENERAL_EXCEPTION;
if (e instanceof java.net.ConnectException) {
code = ConnectionManager.CONNECTION_EXCEPTION;
} else if (e instanceof java.net.UnknownHostException) {
code = ConnectionManager.UNKNOWN_HOST_EXCEPTION;
}
if (!informConnectionManager(code) || code == -1) {
logger.error("An IOException occurred: ", e);
}
} catch (CertificateEncodingException e) {
logger.error("A CertificateEncodingException occurred: ", e);
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
private SSLSocketFactory generateSSLContextWhichAcceptAllSSLCertificats() {
Security.addProvider(Security.getProvider("SunJCE"));
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
} };
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext.getSocketFactory();
} catch (KeyManagementException e) {
logger.error("A KeyManagementException occurred", e);
} catch (NoSuchAlgorithmException e) {
logger.error("A NoSuchAlgorithmException occurred", e);
}
return null;
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.impl;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
/**
* The {@link JSONResponseHandler} checks an digitalSTROM-JSON response and can parse it to an {@link JsonObject}.
*
* @author Alexander Betker - Initial contribution
* @author Alex Maier - Initial contribution
* @author Michael Ochel - add Java-Doc, make methods static and change from SimpleJSON to GSON
* @author Matthias Siegele - add Java-Doc, make methods static and change from SimpleJSON to GSON
*/
public class JSONResponseHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(JSONResponseHandler.class);
/**
* Checks the digitalSTROM-JSON response and return true if it was successful, otherwise false.
*
* @param jsonResponse to check
* @return true, if successful
*/
public static boolean checkResponse(JsonObject jsonResponse) {
if (jsonResponse == null) {
return false;
} else if (jsonResponse.get(JSONApiResponseKeysEnum.OK.getKey()) != null) {
return jsonResponse.get(JSONApiResponseKeysEnum.OK.getKey()).getAsBoolean();
} else {
String message = "unknown message";
if (jsonResponse.get(JSONApiResponseKeysEnum.MESSAGE.getKey()) != null) {
message = jsonResponse.get(JSONApiResponseKeysEnum.MESSAGE.getKey()).getAsString();
}
LOGGER.error("JSONResponseHandler: error in json request. Error message : {}", message);
}
return false;
}
/**
* Returns the {@link JsonObject} from the given digitalSTROM-JSON response {@link String} or null if the json
* response was empty.
*
* @param jsonResponse to convert
* @return jsonObject
*/
public static JsonObject toJsonObject(String jsonResponse) {
if (jsonResponse != null && !jsonResponse.trim().equals("")) {
try {
return (JsonObject) new JsonParser().parse(jsonResponse);
} catch (JsonParseException e) {
LOGGER.error("An JsonParseException occurred by parsing jsonRequest: {}", jsonResponse, e);
}
}
return null;
}
/**
* Returns the result {@link JsonObject} from the given digitalSTROM-JSON response {@link JsonObject}.
*
* @param jsonObject of response
* @return json result object
*/
public static JsonObject getResultJsonObject(JsonObject jsonObject) {
if (jsonObject != null) {
return jsonObject.get(JSONApiResponseKeysEnum.RESULT.getKey()).getAsJsonObject();
}
return null;
}
}

View File

@@ -0,0 +1,306 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.NullArgumentException;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ExeptionConstants;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.InterfaceKeys;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants.ParameterKeys;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
/**
* The {@link SimpleRequestBuilder} build a request string.<br>
* <br>
* <i><b>Code example</b><br>
* String requestString = {@link SimpleRequestBuilder}.{@link #buildNewRequest(String)}.<br>
* <span style="padding-left:14em">{@link #addRequestClass(String)}.<br>
* </span>
* <span style="padding-left:14em">{@link #addFunction(String)}.<br>
* </span>
* <span style="padding-left:14em">{@link #addParameter(String, String)}. (optional)<br>
* </span>
* <span style="padding-left:14em">{@link #addParameter(String, String)}. (optional)<br>
* </span>
* <span style="padding-left:14em">{@link #buildRequestString()};<br>
* </span></i>
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class SimpleRequestBuilder {
// states
private boolean functionIsChosen = false;
private boolean parameterIsAdded = false;
private boolean classIsChosen = false;
private String request;
private static SimpleRequestBuilder builder;
private static final Lock LOCK = new ReentrantLock();
private SimpleRequestBuilder() {
}
/**
* Returns a {@link SimpleRequestBuilder} with the given intefaceKey as chosen request-interface.
*
* @param interfaceKey must not be null
* @return simpleRequestBuilder with chosen interface
* @throws NullArgumentException if the interfaceKey is null
*/
public static SimpleRequestBuilder buildNewRequest(String interfaceKey) throws NullArgumentException {
if (builder == null) {
builder = new SimpleRequestBuilder();
}
LOCK.lock();
return builder.buildNewRequestInt(interfaceKey);
}
/**
* Returns a {@link SimpleRequestBuilder} with the intefaceKey "Json" as chosen request-interface and adds the given
* requestClass to the request-string.
*
* @param requestClassKey must not be null
* @return simpleRequestBuilder with chosen requestClass
* @throws IllegalArgumentException if a requestClass is already chosen
* @throws NullArgumentException if the requestClassKey is null
*/
public static SimpleRequestBuilder buildNewJsonRequest(String requestClassKey)
throws NullArgumentException, IllegalArgumentException {
return buildNewRequest(InterfaceKeys.JSON).addRequestClass(requestClassKey);
}
private SimpleRequestBuilder buildNewRequestInt(String interfaceKey) {
if (interfaceKey == null) {
throw new NullArgumentException("interfaceKey");
}
request = "/" + interfaceKey + "/";
classIsChosen = false;
functionIsChosen = false;
parameterIsAdded = false;
return this;
}
/**
* Adds a requestClass to the request-string.
*
* @param requestClassKey must not be null
* @return simpleRequestBuilder with chosen requestClass
* @throws IllegalArgumentException if a requestClass is already chosen
* @throws NullArgumentException if the requestClassKey is null
*/
public SimpleRequestBuilder addRequestClass(String requestClassKey)
throws IllegalArgumentException, NullArgumentException {
return builder.addRequestClassInt(requestClassKey);
}
private SimpleRequestBuilder addRequestClassInt(String requestClassKey) {
if (!classIsChosen && requestClassKey != null) {
classIsChosen = true;
request = request + requestClassKey + "/";
} else {
if (!classIsChosen) {
throw new IllegalArgumentException(ExeptionConstants.CLASS_ALREADY_ADDED);
} else {
throw new NullArgumentException("requestClassKey");
}
}
return this;
}
/**
* Adds a function to the request-string.
*
* @param functionKey must not be null
* @return SimpleRequestBuilder with chosen function
* @throws IllegalArgumentException if a function is already chosen
* @throws NullArgumentException if the functionKey is null
*/
public SimpleRequestBuilder addFunction(String functionKey) throws IllegalArgumentException, NullArgumentException {
return builder.addFunctionInt(functionKey);
}
private SimpleRequestBuilder addFunctionInt(String functionKey) {
if (!classIsChosen) {
throw new IllegalArgumentException(ExeptionConstants.NO_CLASS_ADDED);
}
if (!functionIsChosen) {
if (functionKey != null) {
functionIsChosen = true;
request = request + functionKey;
} else {
throw new NullArgumentException("functionKey");
}
} else {
throw new IllegalArgumentException(ExeptionConstants.FUNCTION_ALLREADY_ADDED);
}
return this;
}
/**
* Adds a parameter to the request-string, if the parameter value is not null.
*
* @param parameterKey must not be null
* @param parameterValue can be null
* @return SimpleRequestBuilder with added parameter
* @throws IllegalArgumentException if no class and function added
* @throws NullArgumentException if the parameterKey is null
*/
public SimpleRequestBuilder addParameter(String parameterKey, String parameterValue)
throws IllegalArgumentException, NullArgumentException {
return builder.addParameterInt(parameterKey, parameterValue);
}
/**
* Adds the default parameter for zone-requests to the request-string, if the parameter value is not null.
*
* @param sessionToken
* @param zoneID
* @param zoneName
* @return SimpleRequestBuilder with added parameter
* @throws IllegalArgumentException if no class and function added
* @throws NullArgumentException if the parameterKey is null
*/
public SimpleRequestBuilder addDefaultZoneParameter(String sessionToken, Integer zoneID, String zoneName)
throws IllegalArgumentException, NullArgumentException {
return addParameter(ParameterKeys.TOKEN, sessionToken).addParameter(ParameterKeys.ID, objectToString(zoneID))
.addParameter(ParameterKeys.NAME, zoneName);
}
/**
* Adds a parameter for group-requests t the request-string, if the parameter value is not null.
*
* @param sessionToken
* @param groupID
* @param groupName
* @return SimpleRequestBuilder with added parameter
* @throws IllegalArgumentException if no class and function added
* @throws NullArgumentException if the parameterKey is null
*/
public SimpleRequestBuilder addDefaultGroupParameter(String sessionToken, Short groupID, String groupName)
throws IllegalArgumentException, NullArgumentException {
return addParameter(ParameterKeys.TOKEN, sessionToken)
.addParameter(ParameterKeys.GROUP_ID, objectToString(groupID))
.addParameter(ParameterKeys.GROUP_NAME, groupName);
}
/**
* Adds a parameter for zone-group-requests t the request-string, if the parameter value is not null.
*
* @param sessionToken
* @param zoneID
* @param zoneName
* @param groupID
* @param groupName
* @return SimpleRequestBuilder with added parameter
* @throws IllegalArgumentException if no class and function added
* @throws NullArgumentException if the parameterKey is null
*/
public SimpleRequestBuilder addDefaultZoneGroupParameter(String sessionToken, Integer zoneID, String zoneName,
Short groupID, String groupName) throws IllegalArgumentException, NullArgumentException {
return addDefaultZoneParameter(sessionToken, zoneID, zoneName)
.addParameter(ParameterKeys.GROUP_ID, objectToString(groupID))
.addParameter(ParameterKeys.GROUP_NAME, groupName);
}
/**
* Adds a parameter for device-requests the request-string, if the parameter value is not null.
*
* @param sessionToken
* @param dsid
* @param dSUID
* @param name
* @return SimpleRequestBuilder with added parameter
* @throws IllegalArgumentException if no class and function added
* @throws NullArgumentException if the parameterKey is null
*/
public SimpleRequestBuilder addDefaultDeviceParameter(String sessionToken, DSID dsid, String dSUID, String name)
throws IllegalArgumentException, NullArgumentException {
return addParameter(ParameterKeys.TOKEN, sessionToken).addParameter(ParameterKeys.DSID, objectToString(dsid))
.addParameter(ParameterKeys.DSUID, dSUID).addParameter(ParameterKeys.NAME, name);
}
private SimpleRequestBuilder addParameterInt(String parameterKey, String parameterValue) {
if (allRight()) {
if (parameterKey == null) {
throw new NullArgumentException("parameterKey");
}
if (parameterValue != null) {
if (!parameterIsAdded) {
parameterIsAdded = true;
request = request + "?" + parameterKey + "=" + parameterValue;
} else {
request = request + "&" + parameterKey + "=" + parameterValue;
}
}
}
return this;
}
/**
* Returns the request string.
*
* @return request string
* @throws IllegalArgumentException if no class or function is added.
*/
public String buildRequestString() throws IllegalArgumentException {
String request = builder.buildRequestStringInt();
LOCK.unlock();
return request;
}
private String buildRequestStringInt() {
return allRight() ? request : null;
}
private boolean allRight() {
if (!classIsChosen) {
throw new IllegalArgumentException(ExeptionConstants.NO_CLASS_ADDED);
}
if (!functionIsChosen) {
throw new IllegalArgumentException(ExeptionConstants.NO_FUNCTION);
}
return true;
}
/**
* Convert an {@link Object} to a {@link String} or null, if the obj was null or it was a negative {@link Number}.
*
* @param obj can be null
* @return the {@link String} or null
*/
public static String objectToString(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof DSID) {
return ((DSID) obj).getValue();
}
if (obj instanceof Number) {
return ((Number) obj).intValue() > -1 ? obj.toString() : null;
}
return obj.toString();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals()
*/
public boolean equals(SimpleRequestBuilder builder) {
return this.request.contains(builder.request);
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants;
/**
* The {@link ClassKeys} contains digitalSTROM-JSON class keys.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class ClassKeys {
public static final String APARTMENT = "apartment";
public static final String ZONE = "zone";
public static final String DEVICE = "device";
public static final String CIRCUIT = "circuit";
public static final String STRUCTURE = "structure";
public static final String EVENT = "event";
public static final String METERING = "metering";
public static final String SYSTEM = "system";
public static final String PROPERTY_TREE = "property";
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants;
/**
* The {@link ExeptionConstants} contains the {@link SimpleRequestBuilder} exception strings.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class ExeptionConstants {
public static final String NO_CLASS_ADDED = "No class added! Please add a class first!";
public static final String CLASS_ALREADY_ADDED = "A class is already added! You can only add one class!";
public static final String FUNCTION_ALLREADY_ADDED = "A function is already added! You can only add one function!";
public static final String NO_FUNCTION = "No function added! Please add a function!";
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants;
/**
* The {@link FunctionKeys} contains digitalSTROM-JSON function keys.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class FunctionKeys {
public static final String CALL_SCENE = "callScene";
public static final String SAVE_SCENE = "saveScene";
public static final String LOGIN = "login";
public static final String LOGOUT = "logout";
public static final String UNDO_SCENE = "undoScene";
public static final String TURN_ON = "turnOn";
public static final String TURN_OFF = "turnOff";
public static final String INCREASE_VALUE = "increaseValue";
public static final String DECREASE_VALUE = "decreaseValue";
public static final String GET_STRUCTURE = "getStructure";
public static final String GET_DEVICES = "getDevices";
public static final String GET_CIRCUITS = "getCircuits";
public static final String LOGIN_APPLICATION = "loginApplication";
public static final String GET_NAME = "getName";
public static final String SET_NAME = "setName";
public static final String SUBSCRIBE = "subscribe";
public static final String UNSUBSCRIBE = "unsubscribe";
public static final String GET = "get";
public static final String SET_VALUE = "setValue";
public static final String GET_CONSUMPTION = "getConsumption";
public static final String RESCAN = "rescan";
public static final String SCENE_SET_NAME = "sceneSetName";
public static final String SCENE_GET_NAME = "sceneGetName";
public static final String PUSH_SENSOR_VALUES = "pushSensorValues";
public static final String GET_REACHABLE_SCENES = "getReachableScenes";
public static final String GET_STATE = "getState";
public static final String GET_GROUPS = "getGroups";
public static final String GET_ENERGY_METER_VALUE = "getEnergyMeterValue";
public static final String GET_STRING = "getString";
public static final String GET_INTEGER = "getInteger";
public static final String GET_BOOLEAN = "getBoolean";
public static final String SET_STRING = "setString";
public static final String SET_INTEGER = "setInteger";
public static final String SET_BOOLEAN = "setBoolean";
public static final String GET_CHILDREN = "getChildren";
public static final String SET_FLAG = "setFlag";
public static final String GET_FLAGS = "getFlags";
public static final String QUERY = "query";
public static final String REMOVE = "remove";
public static final String GET_TYPE = "getType";
public static final String GET_SPEC = "getSpec";
public static final String VERSION = "version";
public static final String TIME = "time";
public static final String FROM_APARTMENT = "fromApartment";
public static final String BY_ZONE = "byZone";
public static final String BY_GROUP = "byGroup";
public static final String BY_DSID = "byDSID";
public static final String ADD = "add";
public static final String SUBTRACT = "subtract";
public static final String LOGGED_IN_USER = "loggedInUser";
public static final String ZONE_ADD_DEVICE = "zoneAddDevice";
public static final String ADD_ZONE = "addZone";
public static final String REMOVE_ZONE = "removeZone";
public static final String REMOVE_DEVICE = "removeDevice";
public static final String PERSIST_SET = "persistSet";
public static final String UNPERSIST_SET = "unpersistSet";
public static final String ADD_GROUP = "addGroup";
public static final String GROUP_ADD_DEVICE = "groupAddDevice";
public static final String GROUP_REMOVE_DEVICE = "groupRemoveDevice";
public static final String GET_RESOLUTIONS = "getResolutions";
public static final String GET_SERIES = "getSeries";
public static final String GET_VALUES = "getValues";
public static final String GET_LATEST = "getLatest";
public static final String ADD_TAG = "addTag";
public static final String REMOVE_TAG = "removeTag";
public static final String HAS_TAG = "hasTag";
public static final String GET_TAGS = "getTags";
public static final String LOCK = "lock";
public static final String UNLOCK = "unlock";
public static final String GET_SENSOR_EVENT_TABLE_ENTRY = "getSensorEventTableEntry";
public static final String SET_SENSOR_EVENT_TABLE_ENTRY = "setSensorEventTableEntry";
public static final String ADD_TO_AREA = "addToArea";
public static final String REMOVE_FROM_AREA = "removeFromArea";
public static final String SET_CONFIG = "setConfig";
public static final String GET_CONFIG = "getConfig";
public static final String GET_CONFIG_WORD = "getConfigWord";
public static final String SET_JOKER_GROUP = "setJokerGroup";
public static final String SET_BUTTON_ID = "setButtonID";
public static final String SET_BUTTON_INPUT_MODE = "setButtonInputMode";
public static final String SET_OUTPUT_MODE = "setOutputMode";
public static final String SET_PROG_MODE = "setProgMode";
public static final String GET_OUTPUT_VALUE = "getOutputValue";
public static final String SET_OUTPUT_VALUE = "setOutputValue";
public static final String GET_SCENE_MODE = "getSceneMode";
public static final String SET_SCENE_MODE = "setSceneMode";
public static final String GET_TRANSITION_TIME = "getTransitionTime";
public static final String SET_TRANSITION_TIME = "setTransitionTime";
public static final String GET_LED_MODE = "getLedMode";
public static final String SET_LED_MODE = "setLedMode";
public static final String GET_SENSOR_VALUE = "getSensorValue";
public static final String GET_SENSOR_TYPE = "getSensorType";
public static final String GET_DSID = "getDSID";
public static final String ENABLE_APPLICATION_TOKEN = "enableToken";
public static final String REQUEST_APPLICATION_TOKEN = "requestApplicationToken";
public static final String REVOKE_TOKEN = "revokeToken";
public static final String GET_SCENE_VALUE = "getSceneValue";
public static final String GET_TEMPERATURE_CONTROL_STATUS = "getTemperatureControlStatus";
public static final String GET_TEMPERATURE_CONTROL_CONFIG = "getTemperatureControlConfig";
public static final String GET_TEMPERATURE_CONTROL_VALUES = "getTemperatureControlValues";
public static final String GET_ASSIGNED_SENSORS = "getAssignedSensors";
public static final String SET_TEMEPERATURE_CONTROL_STATE = "setTemperatureControlState";
public static final String SET_TEMEPERATURE_CONTROL_VALUE = "setTemperatureControlValue";
public static final String GET_SENSOR_VALUES = "getSensorValues";
public static final String SET_SENSOR_SOURCE = "setSensorSource";
public static final String GET_TEMPERATURE_CONTROL_INTERNALS = "getTemperatureControlInternals";
public static final String SET_TEMPERATION_CONTROL_CONFIG = "setTemperatureControlConfig";
public static final String QUERY2 = "query2";
public static final String BLINK = "blink";
public static final String PUSH_SENSOR_VALUE = "pushSensorValue";
}

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants;
/**
* The {@link InterfaceKeys} contains digitalSTROM-JSON interface keys.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class InterfaceKeys {
public static final String JSON = "json";
}

View File

@@ -0,0 +1,115 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.serverconnection.simpledsrequestbuilder.constants;
/**
* The {@link ParameterKeys} contains digitalSTROM-JSON parameter keys.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public class ParameterKeys {
public static final String TOKEN = "token";
public static final String APPLICATION_TOKEN = "applicationToken";
public static final String APPLICATION_NAME = "applicationName";
public static final String NAME = "name";
public static final String NEW_NAME = "newName";
public static final String DSID = "dsid";
public static final String SCENENUMBER = "sceneNumber";
public static final String LOGIN_TOKEN = "loginToken";
public static final String USER = "user";
public static final String PASSWORD = "password";
public static final String SUBSCRIPTIONID = "subscriptionID";
public static final String TIMEOUT = "timeout";
public static final String GROUP_ID = "groupID";
public static final String GROUP_NAME = "groupName";
public static final String VALUE = "value";
public static final String FORCE = "force";
public static final String ID = "id";
public static final String ENABLE = "enable";
public static final String DISABLE = "disable";
public static final String UNASSIGNED = "unassigned";
public static final String SOURCE_DSID = "sourceDSID";
public static final String SENSOR_TYPE = "sensorType";
public static final String SENSOR_VALUE = "sensorValue";
public static final String FLAG = "flag";
public static final String PATH = "path";
public static final String RAISE = "raise";
public static final String CONTEXT = "context";
public static final String LOCATION = "location";
public static final String SELF = "self";
public static final String ZONE_ID = "zoneID";
public static final String ZONE_NAME = "zoneName";
public static final String OTHER = "other";
public static final String DEVICE_ID = "deviceID";
public static final String UNIT = "unit";
public static final String START_TIME = "startTime";
public static final String END_TIME = "endTime";
public static final String VALUE_COUNT = "valueCount";
public static final String RESOLUTION = "resolution";
public static final String TYPE = "type";
public static final String FROM = "from";
public static final String TAG = "tag";
public static final String CLASS = "class";
public static final String INDEX = "index";
public static final String BUTTON_ID = "buttonID";
public static final String MODE_ID = "modeID";
public static final String MODE = "mode";
public static final String OFFSET = "offset";
public static final String SCENE_ID = "sceneID";
public static final String DONT_CARE = "dontCare";
public static final String LOCAL_PRIO = "localPrio";
public static final String SPECIAL_MODE = "specialMode";
public static final String FLASH_MODE = "flashMode";
public static final String LED_CON_INDEX = "ledconIndex";
public static final String DIM_TIME_INDEX = "dimtimeIndex";
public static final String UP = "up";
public static final String DOWN = "down";
public static final String COLOR_SELECT = "colorSelect";
public static final String MODE_SELECT = "modeSelect";
public static final String DIM_MODE = "dimMode";
public static final String RGB_MODE = "rgbMode";
public static final String GROUP_COLOR_MODE = "groupColorMode";
public static final String SENSOR_INDEX = "sensorIndex";
public static final String EVENT_INDEX = "eventIndex";
public static final String AREA_SCENE = "areaScene";
public static final String EVENT_NAME = "eventName";
public static final String TEST = "test";
public static final String HYSTERSIS = "hysteresis";
public static final String VALIDITY = "validity";
public static final String ACTION = "action";
public static final String BUTTON_NUMBER = "buttonNumber";
public static final String CLICK_TYPE = "clickType";
public static final String SCENE_DEVICE_MODE = "sceneDeviceMode";
public static final String CONTROL_STATE = "ControlState";
public static final String CONTROL_VALUE = "ControlValue";
public static final String QUERY = "query";
public static final String CONTROL_MODE = "ControlMode";
public static final String CONTROL_DSUID = "ControlDSUID";
public static final String CTRL_OFFSET = "CtrlOffset";
public static final String REFERENCE_ZONE = "ReferenceZone";
public static final String EMERGENCY_VALUE = "EmergencyValue";
public static final String MANUAL_VALUE = "ManualValue";
public static final String CTRL_KP = "CtrlKp";
public static final String CTRL_TS = "CtrlTs";
public static final String CTRL_TI = "CtrlTi";
public static final String CTRL_KD = "CtrlKd";
public static final String CTRL_I_MIN = "CtrlImin";
public static final String CTRL_I_MAX = "CtrlImax";
public static final String CTRL_Y_MIN = "CtrlYmin";
public static final String CTRL_Y_MAX = "CtrlYmax";
public static final String CTRL_ANTI_WIND_UP = "CtrlAntiWindUp";
public static final String CTRL_KEEP_FLOOR_WARM = "CtrlKeepFloorWarm";
public static final String SOURCE_DSUID = "sourceDSUID";
public static final String DSUID = "dsuid";
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure;
import java.util.Map;
/**
* The {@link Apartment} represents a digitalSTROM-Apartment.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add java-doc
* @author Matthias Siegele - add java-doc
*/
public interface Apartment {
/**
* Returns the {@link Map} of all digitalSTROM-Zones with the zone id as key and the {@link Zone} as value.
*
* @return map of all zones
*/
Map<Integer, Zone> getZoneMap();
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure;
import java.util.List;
/**
* The {@link DetailedGroupInfo} represents a digitalSTROM-Group with a list of all dSUID's of the included
* digitalSTROM-Devices.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add java-doc
* @author Matthias Siegele - add java-doc
*/
public interface DetailedGroupInfo extends Group {
/**
* Returns the list of all dSUID's of the included digitalSTROM-Devices.
*
* @return list of all dSUID
*/
List<String> getDeviceList();
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure;
/**
* The {@link Group} represents a digitalSTROM-Group.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add java-doc
* @author Matthias Siegele - add java-doc
*/
public interface Group {
/**
* Returns the group id of this {@link Group}.
*
* @return group id
*/
short getGroupID();
/**
* Returns the name of this {@link Group}.
*
* @return group name
*/
String getGroupName();
}

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.Device;
/**
* The {@link Zone} represents a digitalSTROM-Zone.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add java-doc
* @author Matthias Siegele - add java-doc
*/
public interface Zone {
/**
* Returns the zone id of this {@link Zone}.
*
* @return zoneID
*/
int getZoneId();
/**
* Sets the zone id of this {@link Zone}.
*
* @param id to set
*/
void setZoneId(int id);
/**
* Returns the zone name of this {@link Zone}.
*
* @return zone name
*/
String getName();
/**
* Sets the zone name of this {@link Zone}.
*
* @param name to set
*/
void setName(String name);
/**
* Returns the {@link List} of all included groups as {@link DetailedGroupInfo}.
*
* @return list of all groups
*/
List<DetailedGroupInfo> getGroups();
/**
* Adds a group as {@link DetailedGroupInfo}.
*
* @param group to add
*/
void addGroup(DetailedGroupInfo group);
/**
* Returns a {@link List} of all included {@link Device}'s.
*
* @return device list
*/
List<Device> getDevices();
/**
* Adds a {@link Device} to this {@link Zone}.
*
* @param device to add
*/
void addDevice(Device device);
}

View File

@@ -0,0 +1,232 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.serverconnection.constants.JSONApiResponseKeysEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.ChangeableDeviceConfigEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import com.google.gson.JsonObject;
/**
* The {@link AbstractGeneralDeviceInformations} is a abstract implementation of {@link GeneralDeviceInformations} and
* can be implement by subclasses which contains the same device informations like dSID and/or mechanismen like the
* {@link DeviceStatusListener}.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public abstract class AbstractGeneralDeviceInformations implements GeneralDeviceInformation {
protected DSID dsid;
protected String dSUID;
protected Boolean isPresent;
protected Boolean isValide;
protected String name;
protected String displayID;
protected DeviceStatusListener listener;
/**
* Creates a new {@link AbstractGeneralDeviceInformations} through the digitalSTROM json response as
* {@link JsonObject}.
*
* @param jsonDeviceObject json response of the digitalSTROM-Server, must not be null
*/
public AbstractGeneralDeviceInformations(JsonObject jsonDeviceObject) {
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.NAME.getKey()) != null) {
name = jsonDeviceObject.get(JSONApiResponseKeysEnum.NAME.getKey()).getAsString();
}
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.ID.getKey()) != null) {
dsid = new DSID(jsonDeviceObject.get(JSONApiResponseKeysEnum.ID.getKey()).getAsString());
} else if (jsonDeviceObject.get(JSONApiResponseKeysEnum.DSID.getKey()) != null) {
dsid = new DSID(jsonDeviceObject.get(JSONApiResponseKeysEnum.DSID.getKey()).getAsString());
} else if (jsonDeviceObject.get(JSONApiResponseKeysEnum.DSID_LOWER_CASE.getKey()) != null) {
dsid = new DSID(jsonDeviceObject.get(JSONApiResponseKeysEnum.DSID_LOWER_CASE.getKey()).getAsString());
}
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.DSUID.getKey()) != null) {
dSUID = jsonDeviceObject.get(JSONApiResponseKeysEnum.DSUID.getKey()).getAsString();
}
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.DISPLAY_ID.getKey()) != null) {
displayID = jsonDeviceObject.get(JSONApiResponseKeysEnum.DISPLAY_ID.getKey()).getAsString();
}
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.IS_PRESENT.getKey()) != null) {
isPresent = jsonDeviceObject.get(JSONApiResponseKeysEnum.IS_PRESENT.getKey()).getAsBoolean();
} else if (jsonDeviceObject.get(JSONApiResponseKeysEnum.PRESENT.getKey()) != null) {
isPresent = jsonDeviceObject.get(JSONApiResponseKeysEnum.PRESENT.getKey()).getAsBoolean();
}
if (jsonDeviceObject.get(JSONApiResponseKeysEnum.IS_VALID.getKey()) != null) {
isValide = jsonDeviceObject.get(JSONApiResponseKeysEnum.IS_VALID.getKey()).getAsBoolean();
}
}
@Override
public synchronized String getName() {
return this.name;
}
@Override
public synchronized void setName(String name) {
this.name = name;
if (listener != null) {
listener.onDeviceConfigChanged(ChangeableDeviceConfigEnum.DEVICE_NAME);
}
}
@Override
public DSID getDSID() {
return dsid;
}
@Override
public String getDSUID() {
return this.dSUID;
}
@Override
public Boolean isPresent() {
return isPresent;
}
@Override
public void setIsPresent(boolean isPresent) {
this.isPresent = isPresent;
if (listener != null) {
if (!isPresent) {
listener.onDeviceRemoved(this);
} else {
listener.onDeviceAdded(this);
}
}
}
@Override
public Boolean isValid() {
return isValide;
}
@Override
public void setIsValid(boolean isValide) {
this.isValide = isValide;
}
@Override
public void registerDeviceStatusListener(DeviceStatusListener listener) {
if (listener != null) {
this.listener = listener;
listener.onDeviceAdded(this);
}
}
@Override
public DeviceStatusListener unregisterDeviceStatusListener() {
DeviceStatusListener listener = this.listener;
this.listener = null;
return listener;
}
@Override
public boolean isListenerRegisterd() {
return listener != null;
}
@Override
public DeviceStatusListener getDeviceStatusListener() {
return listener;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dSUID == null) ? 0 : dSUID.hashCode());
result = prime * result + ((displayID == null) ? 0 : displayID.hashCode());
result = prime * result + ((dsid == null) ? 0 : dsid.hashCode());
result = prime * result + ((isPresent == null) ? 0 : isPresent.hashCode());
result = prime * result + ((isValide == null) ? 0 : isValide.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof AbstractGeneralDeviceInformations)) {
return false;
}
AbstractGeneralDeviceInformations other = (AbstractGeneralDeviceInformations) obj;
if (dSUID == null) {
if (other.dSUID != null) {
return false;
}
} else if (!dSUID.equals(other.dSUID)) {
return false;
}
if (displayID == null) {
if (other.displayID != null) {
return false;
}
} else if (!displayID.equals(other.displayID)) {
return false;
}
if (dsid == null) {
if (other.dsid != null) {
return false;
}
} else if (!dsid.equals(other.dsid)) {
return false;
}
if (isPresent == null) {
if (other.isPresent != null) {
return false;
}
} else if (!isPresent.equals(other.isPresent)) {
return false;
}
if (isValide == null) {
if (other.isValide != null) {
return false;
}
} else if (!isValide.equals(other.isValide)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
@Override
public String getDisplayID() {
return displayID;
}
}

View File

@@ -0,0 +1,293 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices;
import java.util.List;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.CachedMeteringValue;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
/**
* The {@link Circuit} represents a circuit of the digitalStrom system. For that all information will be able to get and
* set through the same named getter- and setter-methods. To get informed about status and configuration changes a
* {@link DeviceStatusListener} can be registered. For that and to get the general device informations like the dSID the
* {@link Circuit} implements the {@link GeneralDeviceInformations} interface.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public interface Circuit extends GeneralDeviceInformation {
/**
* Returns the hardware version of this {@link Circuit} as {@link Integer}.
*
* @return hardware version
*/
Integer getHwVersion();
/**
* Sets the hardware version of this {@link Circuit} as {@link Integer}.
*
* @param hwVersion the new hardware version as {@link Integer}
*/
void setHwVersion(Integer hwVersion);
/**
* Returns the hardware version of this {@link Circuit} as {@link String}.
*
* @return hardware version
*/
String getHwVersionString();
/**
* Sets the hardware version of this {@link Circuit} as {@link String}.
*
* @param hwVersionString the new hardware version as {@link Integer}
*/
void setHwVersionString(String hwVersionString);
/**
* Returns the software version of this {@link Circuit} as {@link String}.
*
* @return the software version
*/
String getSwVersion();
/**
* Sets the software version of this {@link Circuit} as {@link String}.
*
* @param swVersion the new software version
*/
void setSwVersion(String swVersion);
/**
* Returns the arm software version of this {@link Circuit} as {@link Integer}.
*
* @return the arm software version
*/
Integer getArmSwVersion();
/**
* Sets the arm software version of this {@link Circuit} as {@link Integer}.
*
* @param armSwVersion the new arm software version
*/
void setArmSwVersion(Integer armSwVersion);
/**
* Returns the dsp software version of this {@link Circuit} as {@link Integer}.
*
* @return the dsp softwaree version
*/
Integer getDspSwVersion();
/**
* Sets the dsp software version of this {@link Circuit} as {@link Integer}.
*
* @param dspSwVersion the new dsp software version
*/
void setDspSwVersion(Integer dspSwVersion);
/**
* Returns the api version of this {@link Circuit} as {@link Integer}.
*
* @return the api version as {@link Integer}
*/
Integer getApiVersion();
/**
* Setss the api version of this {@link Circuit} as {@link Integer}.
*
* @param apiVersion the new api version
*/
void setApiVersion(Integer apiVersion);
/**
* Returns the hardware name of this {@link Circuit}.
*
* @return the hardware name
*/
String getHwName();
/**
* Sets the hardware name of this {@link Circuit}.
*
* @param hwName the new hardware name
*/
void setHwName(String hwName);
/**
* Returns the bus member type of this {@link Circuit} as {@link Integer}.
*
* @return the bus member type
*/
Integer getBusMemberType();
/**
* Sets the bus member type of this {@link Circuit} as {@link Integer}.
*
* @param busMemberType the new bus member type
*/
void setBusMemberType(Integer busMemberType);
/**
* Returns true, if this {@link Circuit} has connected {@link Device}'s, otherwise false.
*
* @return true, if {@link Device}'s are connected
*/
Boolean getHasDevices();
/**
* Sets the connected devices flag.
*
* @param hasDevices the new connected devices flag
*/
void setHasDevices(Boolean hasDevices);
/**
* Returns true, if this {@link Circuit} is valid to metering power data, otherwise false.
*
* @return true, if is valid to metering power data
*/
Boolean getHasMetering();
/**
* Sets the flag hasMetering.
*
* @param hasMetering the new hasMetering flag.
*/
void setHasMetering(Boolean hasMetering);
/**
* Returns the vdc configuration URL of this {@link Circuit} as {@link String}.
*
* @return the vdc configuration URL
*/
String getVdcConfigURL();
/**
* Sets the vdc configuration URL of this {@link Circuit} as {@link String}.
*
* @param vdcConfigURL the new vdc configuration URL
*/
void setVdcConfigURL(String vdcConfigURL);
/**
* Returns the vdc mode UID of this {@link Circuit} as {@link String}.
*
* @return the vdc mode UID
*/
String getVdcModelUID();
/**
* Sets the vdc mode UID of this {@link Circuit} as {@link String}.
*
* @param vdcModelUID the new vdc mode UID
*/
void setVdcModelUID(String vdcModelUID);
/**
* Returns the vdc hardware GUID of this {@link Circuit} as {@link String}.
*
* @return the vdc hardware GUID
*/
String getVdcHardwareGuid();
/**
* Sets the vdc hardware GUID of this {@link Circuit} as {@link String}.
*
* @param vdcHardwareGuid the new vdc hardware GUID
*/
void setVdcHardwareGuid(String vdcHardwareGuid);
/**
* Returns the vdc hardware model GUID of this {@link Circuit} as {@link String}.
*
* @return the vdc hardware mode GUID
*/
String getVdcHardwareModelGuid();
/**
* Sets the vdc hardware model GUID of this {@link Circuit} as {@link String}.
*
* @param vdcHardwareModelGuid the new vdc model GUID
*/
void setVdcHardwareModelGuid(String vdcHardwareModelGuid);
/**
* Returns the vdc vendor GUID of this {@link Circuit} as {@link String}.
*
* @return the vdc vendor GUID
*/
String getVdcVendorGuid();
/**
* Sets the vdc vendor GUID of this {@link Circuit} as {@link String}.
*
* @param vdcVendorGuid the new vdc vendor GUID
*/
void setVdcVendorGuid(String vdcVendorGuid);
/**
* Returns the vdc oem GUID of this {@link Circuit} as {@link String}.
*
* @return the vdc oem GUID
*/
String getVdcOemGuid();
/**
* Sets the vdc oem GUID of this {@link Circuit} as {@link String}.
*
* @param vdcOemGuid the new vdc oem GUID
*/
void setVdcOemGuid(String vdcOemGuid);
/**
* Returns true, if actions from new {@link Device}'s will be ignored by this {@link Circuit}, otherwise false.
*
* @return true, if actions form new device will be ignored
*/
Boolean getIgnoreActionsFromNewDevices();
/**
* Sets the flag for ignore actions from new {@link Device}'s.
*
* @param ignoreActionsFromNewDevices the new ignore actions from new devices flag
*/
void setIgnoreActionsFromNewDevices(Boolean ignoreActionsFromNewDevices);
/**
* Adds a new {@link CachedMeteringValue} or update the existing, if the new one is newer.
*
* @param cachedMeteringValue the new {@link CachedMeteringValue}
*/
void addMeteringValue(CachedMeteringValue cachedMeteringValue);
/**
* Returns the value of the given {@link CachedMeteringValue} through the {@link MeteringTypeEnum} and
* {@link MeteringUnitsEnum}.
*
* @param meteringType (must not be null)
* @param meteringUnit (can be null, default is {@link MeteringUnitsEnum#WH})
* @return the metering value or -1, if the metering value dose not exist
*/
double getMeteringValue(MeteringTypeEnum meteringType, MeteringUnitsEnum meteringUnit);
/**
* Returns the {@link List} of all {@link CachedMeteringValue}'s.
*
* @return list of all {@link CachedMeteringValue}
*/
List<CachedMeteringValue> getAllCachedMeteringValues();
}

View File

@@ -0,0 +1,870 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices;
import java.util.List;
import java.util.Map;
import org.openhab.binding.digitalstrom.internal.lib.config.Config;
import org.openhab.binding.digitalstrom.internal.lib.event.types.EventItem;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceSceneSpec;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.DeviceStateUpdate;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.FunctionalColorGroupEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.OutputModeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceBinaryInput;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DeviceSensorValue;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.InternalScene;
/**
* The {@link Device} represents a digitalSTROM internal stored device.
*
* @author Alexander Betker - Initial contribution
* @author Michael Ochel - add methods for ESH, new functionalities and JavaDoc
* @author Mathias Siegele - add methods for ESH, new functionalities and JavaDoc
*/
public interface Device extends GeneralDeviceInformation {
/**
* Returns the id of the dS-Meter in which the device is registered.
*
* @return meterDSID
*/
DSID getMeterDSID();
/**
* Sets the id of the dS-Meter in which the device is registered.
*
* @param meterDSID to set
*/
void setMeterDSID(String meterDSID);
/**
* Returns the hardware info of this device.
* You can see all available hardware info at
* http://www.digitalstrom.com/Partner/Support/Techn-Dokumentation/
*
* @return hardware info
*/
String getHWinfo();
/**
* Returns the zone id in which this device is in.
*
* @return zoneID
*/
int getZoneId();
/**
* Sets the zoneID of this device.
*
* @param zoneID to set
*/
void setZoneId(int zoneID);
/**
* Returns true, if this device is on, otherwise false.
*
* @return is on (true = on | false = off)
*/
boolean isOn();
/**
* Adds an on command as {@link DeviceStateUpdate}, if the flag is true or off command, if it is false to the list
* of
* outstanding commands.
*
* @param flag (true = on | false = off)
*/
void setIsOn(boolean flag);
/**
* Returns true, if this shade device is open, otherwise false.
*
* @return is on (true = open | false = closed)
*/
boolean isOpen();
/**
* Adds an open command as {@link DeviceStateUpdate}, if the flag is true or closed command, if it is false to the
* list of outstanding commands.
*
* @param flag (true = open | false = closed)
*/
void setIsOpen(boolean flag);
/**
* Returns true, if this device is dimmable, otherwise false.
*
* @return is dimmable (true = yes | false = no)
*/
boolean isDimmable();
/**
* Returns true, if this device is a shade device (grey), otherwise false.
*
* @return is shade (true = yes | false = no)
*/
boolean isShade();
/**
* Returns true, if the device output mode isn't disabled.
*
* @return have output mode (true = yes | false = no)
*/
boolean isDeviceWithOutput();
/**
* Returns the current functional color group of this device.
* For more informations please have a look at {@link FunctionalColorGroupEnum}.
*
* @return current functional color group
*/
FunctionalColorGroupEnum getFunctionalColorGroup();
/**
* Sets the functional color group of this device.
*
* @param fuctionalColorGroup to set
*/
void setFunctionalColorGroup(FunctionalColorGroupEnum fuctionalColorGroup);
/**
* Returns the current output mode of this device.
* Some devices are able to have different output modes e.g. the device GE-KM200 is able to
* be in dimm mode, switch mode or disabled.
* For more informations please have a look at {@link OutputModeEnum}.
*
* @return the current output mode of this device
*/
OutputModeEnum getOutputMode();
/**
* Adds an increase command as {@link DeviceStateUpdate} to the list of outstanding commands.
*/
void increase();
/**
* Adds an decrease command as {@link DeviceStateUpdate} to the list of outstanding commands.
*/
void decrease();
/**
* Returns the current slat position of this device.
*
* @return current slat position
*/
int getSlatPosition();
/**
* Adds an set slat position command as {@link DeviceStateUpdate} with the given slat position to the list of
* outstanding commands.
*
* @param slatPosition to set
*/
void setSlatPosition(int slatPosition);
/**
* Returns the maximal slat position value of this device.
*
* @return maximal slat position value
*/
int getMaxSlatPosition();
/**
* Returns the minimal slat position value of this device.
*
* @return minimal slat position value
*/
int getMinSlatPosition();
/**
* Returns the current output value of this device.
* This can be the slat position or the brightness of this device.
*
* @return current output value
*/
short getOutputValue();
/**
* Adds an set output value command as {@link DeviceStateUpdate} with the given output value to the list of
* outstanding commands.
*
* @param outputValue to set
*/
void setOutputValue(short outputValue);
/**
* Returns the maximal output value of this device.
*
* @return maximal output value
*/
short getMaxOutputValue();
/**
* Returns a list with group id's in which the device is part of.
*
* @return List of group id's
*/
List<Short> getGroups();
/**
* Adds the given groupID to the group list.
*
* @param groupID to add
*/
void addGroup(Short groupID);
/**
* Overrides the existing group list with the given new.
*
* @param newGroupList to set
*/
void setGroups(List<Short> newGroupList);
/**
* Returns the scene output value of this device of the given scene id as {@link Integer} array. The first field is
* the output value and the second is the angle value or -1 if no angle value exists.
* If the method returns null, this scene id isn't read yet.
*
* @param sceneID of the scene
* @return scene output value and scene angle value or null, if it isn't read out yet
*/
Integer[] getSceneOutputValue(short sceneID);
/**
* Sets the scene output value of this device for the given scene id and scene output value.
*
* @param sceneId to set
* @param sceneOutputValue to set
*/
void setSceneOutputValue(short sceneId, int sceneOutputValue);
/**
* This configuration is very important. The devices can
* be configured to not react to some commands (scene calls).
* So you can't imply that a device automatically turns on (by default yes,
* but if someone configured his own scenes, then maybe not) after a
* scene call. This method returns true or false, if the configuration
* for this sceneID already has been read
*
* @param sceneId the sceneID
* @return true if this device has the configuration for this specific scene
*/
boolean containsSceneConfig(short sceneId);
/**
* Add the config for this scene. The config has the configuration
* for the specific sceneID.
*
* @param sceneId scene call id
* @param sceneSpec config for this sceneID
*/
void addSceneConfig(short sceneId, DeviceSceneSpec sceneSpec);
/**
* Get the config for this scene. The config has the configuration
* for the specific sceneID.
*
* @param sceneId scene call id
* @return sceneSpec config for this sceneID
*/
DeviceSceneSpec getSceneConfig(short sceneId);
/**
* Should the device react on this scene call or not .
*
* @param sceneId scene call id
* @return true, if this device should react on this sceneID
*/
boolean doIgnoreScene(short sceneId);
// follow methods added by Michael Ochel and Matthias Siegele
/**
* Returns true, if all sensor data are up to date or false if some have to be updated.
*
* @return is up to date (true = yes | false = no)
*/
boolean isSensorDataUpToDate();
/**
* Sets the priority to refresh the data of the sensors to the given priorities.
* They can be never, low, medium or high.
*
* @param powerConsumptionRefreshPriority to set
* @param electricMeterRefreshPriority to set
* @param energyMeterRefreshPriority to set
*/
void setSensorDataRefreshPriority(String powerConsumptionRefreshPriority, String electricMeterRefreshPriority,
String energyMeterRefreshPriority);
/**
* Returns true, if the device is up to date.
*
* @return digitalSTROM-Device is up to date (true = yes | false = no)
*/
boolean isDeviceUpToDate();
/**
* Returns the next {@link DeviceStateUpdate} to update the digitalSTROM-Device on the digitalSTROM-Server.
*
* @return DeviceStateUpdate for digitalSTROM-Device
*/
DeviceStateUpdate getNextDeviceUpdateState();
/**
* Update the internal stored device object.
*
* @param deviceStateUpdate to update
*/
void updateInternalDeviceState(DeviceStateUpdate deviceStateUpdate);
/**
* Call the given {@link InternalScene} on this {@link Device} and updates it.
*
* @param scene to call
*/
void callInternalScene(InternalScene scene);
/**
* Undo the given {@link InternalScene} on this {@link Device} and updates it.
*
* @param scene to undo
*/
void undoInternalScene(InternalScene scene);
/**
* Initial a call scene for the given scene number.
*
* @param sceneNumber to call
*/
void callScene(Short sceneNumber);
/**
* Returns the current active {@link InternalScene}, otherwise null.
*
* @return active {@link InternalScene} or null
*/
InternalScene getAcitiveScene();
/**
* Undo the active scene if a scene is active.
*/
void undoScene();
/**
* Checks the scene configuration for the given scene number and initial a scene configuration reading with the
* given priority if no scene configuration exists.
*
* @param sceneNumber to check
* @param prio to update
*/
void checkSceneConfig(Short sceneNumber, short prio);
/**
* Sets the given output mode as new output mode of this {@link Device}.
*
* @param newOutputMode to set
*/
void setOutputMode(OutputModeEnum newOutputMode);
/**
* Returns a {@link List} of all saved scene-IDs configurations.
*
* @return a {@link List} of all saved scene-IDs
*/
List<Short> getSavedScenes();
/**
* Initializes a internal device update as call scene for the given scene number.
*
* @param sceneNumber to call
*/
void internalCallScene(Short sceneNumber);
/**
* Initializes a internal device update as undo scene.
*/
void internalUndoScene();
/**
* Returns true, if this {@link Device} is a device with a switch output mode.
*
* @return true, if it is a switch otherwise false
*/
boolean isSwitch();
/**
* Sets the given {@link Config} as new {@link Config}.
*
* @param config to set
*/
void setConfig(Config config);
/**
* Returns the current angle position of the {@link Device}.
*
* @return current angle position
*/
short getAnglePosition();
/**
* Adds an set angle value command as {@link DeviceStateUpdate} with the given angle value to the list of
* outstanding commands.
*
* @param angle to set
*/
void setAnglePosition(int angle);
/**
* Sets the scene output value and scene output angle of this device for the given scene id, scene output value and
* scene output angle.
*
* @param sceneId to set
* @param value to set
* @param angle to set
*/
void setSceneOutputValue(short sceneId, int value, int angle);
/**
* Returns the max angle value of the slat.
*
* @return max slat angle
*/
int getMaxSlatAngle();
/**
* Returns the min angle value of the slat.
*
* @return min slat angle
*/
int getMinSlatAngle();
/**
* Returns true, if it is a blind device.
*
* @return is blind (true = yes | false = no
*/
boolean isBlind();
/**
* Saves scene configurations from the given sceneProperties in the {@link Device}. <br>
* The {@link Map} has to be like the following format:
* <ul>
* <li><b>Key:</b> scene[sceneID]</li>
* <li><b>Value:</b> {Scene: [sceneID], dontcare: [don't care flag], localPrio: [local prio flag], specialMode:
* [special mode flag]}(0..1), {sceneValue: [scene value], sceneAngle: [scene angle]}(0..1))</li>
* </ul>
*
* @param sceneProperties to save
*/
void saveConfigSceneSpecificationIntoDevice(Map<String, String> sceneProperties);
/**
* Returns the min output value.
*
* @return min output value
*/
short getMinOutputValue();
/**
* Adds a slat increase command as {@link DeviceStateUpdate} to the list of outstanding commands.
*/
void increaseSlatAngle();
/**
* Adds a slat decrease command as {@link DeviceStateUpdate} to the list of outstanding commands.
*/
void decreaseSlatAngle();
/**
* Saves scene configurations from the given sceneProperties in the {@link Device}. <br>
* <br>
* <b>The {@link String} has to be like the following format:</b><br>
* {[sceneID] = }(1){Scene: [sceneID], dontcare: [don't care flag], localPrio: [local prio flag], specialMode:
* [special mode flag]}(0..1), {sceneValue: [sceneValue]{, sceneAngle: [scene angle]}(0..1)}{\n}(0..1)<br>
* <br>
* e.g. "10 = Scene: PRESET_4, dontcare: false, localPrio: false, specialMode: false, flashMode: false, sceneValue:
* 0\n"
*
* @param propertries to save
*/
void saveConfigSceneSpecificationIntoDevice(String propertries);
/**
* Returns true, if this {@link Device} is a sensor device. That means, that this {@link Device} has no output
* channel
* ({@link OutputModeEnum#DISABLED}), but climate sensors.
*
* @return true, if it is a sensor device
*/
boolean isSensorDevice();
/**
* Returns true, if this {@link Device} is a heating device. That means, that the output mode of this {@link Device}
* is one of the following modes {@link OutputModeEnum#PWM} or {@link OutputModeEnum#SWITCH} and the
* {@link FuncNameAndColorGroupEnum} is {@link FuncNameAndColorGroupEnum#HEATING}.
*
* @return true, if it is a heating device
*/
boolean isHeatingDevice();
/**
* Sets the refresh priority for the given power sensor as {@link SensorEnum}. <br>
* <b>Note:</b><br>
* 1. The device must have this sensor type, otherwise the set has no effect.<br>
* <br>
* 2. Valid priorities are:<br>
* - {@link Config#REFRESH_PRIORITY_NEVER}<br>
* - {@link Config#REFRESH_PRIORITY_LOW}<br>
* - {@link Config#REFRESH_PRIORITY_MEDIUM}<br>
* - {@link Config#REFRESH_PRIORITY_HIGH}<br>
* <br>
* 3. Valid sensor types are:<br>
* - {@link SensorEnum#POWER_CONSUMPTION}<br>
* - {@link SensorEnum#OUTPUT_CURRENT}<br>
* - {@link SensorEnum#ELECTRIC_METER}<br>
* - {@link SensorEnum#ACTIVE_POWER}<br>
*
* @param powerSensorType the power sensor to set
* @param refreshPriority the new refresh priority
*/
void setSensorDataRefreshPriority(SensorEnum powerSensorType, String refreshPriority);
/**
* Returns the refresh priority of the given power sensor type as {@link SensorEnum}. If the sensor type is not
* supported by
* this {@link Device} or it is not a power sensor it will be returned null.
*
* @param powerSensorType of the sensor
* @return the refresh priority
*/
String getPowerSensorRefreshPriority(SensorEnum powerSensorType);
/**
* Returns a {@link List} with all power sensors, which are supported by this {@link Device}.
*
* @return all supported power sensors
*/
List<SensorEnum> getPowerSensorTypes();
/**
* Returns a {@link List} with all climate sensors, which are supported by this {@link Device}.
*
* @return all supported climate sensors
*/
List<SensorEnum> getClimateSensorTypes();
/**
* Returns all {@link DeviceSensorValue}'s of this {@link Device}.
*
* @return list of all {@link DeviceSensorValue}'s
*/
List<DeviceSensorValue> getDeviceSensorValues();
/**
* Sets the given {@link DeviceSensorValue}. That means the given {@link DeviceSensorValue} will be added, if this
* type of {@link DeviceSensorValue} does not exist before, otherwise the existing {@link DeviceSensorValue} will be
* updated,
* if the given {@link DeviceSensorValue} is newer.
*
* @param deviceSensorValue the new device sensor value
*/
void setDeviceSensorValue(DeviceSensorValue deviceSensorValue);
/**
* Returns the {@link DeviceSensorValue} of the given sensor type as {@link SensorEnum} or null, if no
* {@link DeviceSensorValue} exists for the given sensor type.
*
* @param sensorType of the sensor
* @return the {@link DeviceSensorValue} or null
*/
DeviceSensorValue getDeviceSensorValue(SensorEnum sensorType);
/**
* Returns the {@link DeviceSensorValue} of the given sensor index as {@link Short} or null, if no
* {@link DeviceSensorValue} exists for the given sensor index.
*
* @param sensorIndex of the sensor
* @return the {@link DeviceSensorValue} or null
*/
DeviceSensorValue getDeviceSensorValue(Short sensorIndex);
/**
* Returns the sensor index for the given sensor type as {@link SensorEnum} of the {@link Device} or null, if the
* sensor type does not exist. It will be needed to readout the current sensor value of the digitalSTROM device.
*
* @param sensorType of the sensor
* @return sensor index for the sensor type
*/
Short getSensorIndex(SensorEnum sensorType);
/**
* Returns the sensor type as {@link SensorEnum} of the given sensor index or null, if the given sensor type does
* not exist.
*
* @param sensorIndex of the sensor
* @return the sensor type or null
*/
SensorEnum getSensorType(Short sensorIndex);
/**
* Returns the internal digitalSTROM sensor value for the given sensor type as {@link SensorEnum}, if the sensor
* type exists and the value is valid. The resolution can be found at {@link SensorEnum}.
*
* @param sensorType of the sensor
* @return the internal digitalSTROM sensor value or null
*/
Integer getDsSensorValue(SensorEnum sensorType);
/**
* Returns the internal digitalSTROM sensor value for the given sensor index as {@link Short}, if the sensor
* index exists and the value is valid. The resolution can be found at {@link SensorEnum}.
*
* @param sensorIndex of the sensor
* @return the internal digitalSTROM sensor value or null
*/
Integer getDsSensorValue(Short sensorIndex);
/**
* Returns the float sensor value for the given sensor type as {@link SensorEnum}, if the sensor
* type exists and the value is valid. The resolution can be found at {@link SensorEnum}.
*
* @param sensorType of the sensor
* @return the float sensor value or null
*/
Float getFloatSensorValue(SensorEnum sensorType);
/**
* Returns the float sensor value for the given sensor index as {@link Short}, if the sensor
* index exists and the value is valid. The resolution can be found at {@link SensorEnum}.
*
* @param sensorIndex of the sensor
* @return the float sensor value or null
*/
Float getFloatSensorValue(Short sensorIndex);
/**
* Sets the float sensor value for a given sensor type as {@link SensorEnum}. If the sensor type does not exist, it
* will be returned false.
*
* @param sensorType of the sensor
* @param floatSensorValue the new float sensor value
* @return true, if it was successful, otherwise false
*/
boolean setFloatSensorValue(SensorEnum sensorType, Float floatSensorValue);
/**
* Sets the float sensor value for a given sensor index as {@link Short}. If the sensor type does not exist, it
* will be returned false.
*
* @param sensorIndex of the sensor
* @param floatSensorValue the new float sensor value
* @return true, if it was successful, otherwise false
*/
boolean setFloatSensorValue(Short sensorIndex, Float floatSensorValue);
/**
* Sets the internal digitalSTROM sensor value for a given sensor index as {@link Short}. If the sensor index does
* not exist, it will be returned false.
*
* @param sensorIndex of the sensor
* @param dSSensorValue the new internal digitalSTROM sensor value
* @return true, if it was successful, otherwise false
*/
boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue);
/**
* Sets the internal digitalSTROM sensor value for a given sensor type as {@link SensorEnum}. If the sensor type
* does
* not exist, it will be returned false.
*
* @param sensorType of the sensor
* @param dSSensorValue the new internal digitalSTROM sensor value
* @return true, if it was successful, otherwise false
*/
boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue);
/**
* Sets the internal digitalSTROM and float sensor value for a given sensor index as {@link Short}. If the sensor
* index does not exist, it will be returned false.
*
* @param sensorIndex of the sensor
* @param dSSensorValue the new internal digitalSTROM sensor value
* @param floatSensorValue the new float sensor value
* @return true, if it was successful, otherwise false
*/
boolean setDsSensorValue(Short sensorIndex, Integer dSSensorValue, Float floatSensorValue);
/**
* Sets the internal digitalSTROM and float sensor value for a given sensor type as {@link SensorEnum}. If the
* sensor type does not exist, it will be returned false.
*
* @param sensorType of the sensor
* @param dSSensorValue the new internal digitalSTROM sensor value
* @param floatSensorValue the new float sensor value
* @return true, if it was successful, otherwise false
*/
boolean setDsSensorValue(SensorEnum sensorType, Integer dSSensorValue, Float floatSensorValue);
/**
* Returns true, if this {@link Device} has sensors, otherwise false.
*
* @return true, if device has sensors
*/
boolean hasSensors();
/**
* Returns true, if this {@link Device} has climate sensors, otherwise false.
*
* @return true, if device has climate sensors
*/
boolean hasClimateSensors();
/**
* Returns true, if this {@link Device} has power sensors, otherwise false.
*
* @return true, if device has power sensors
*/
boolean hasPowerSensors();
/**
* Only needed for {@link DeviceConsumptionSensorJob}'s. To set the internal digitalSTROM sensor value please use
* {@link #setDsSensorValue(SensorEnum, Integer)}.
*
* @param sensorType of the sensor
* @param value new value
*/
void setDeviceSensorDsValueBySensorJob(SensorEnum sensorType, Integer value);
/**
* Enables the internal sensor echo box for {@link EventNames#DEVICE_SENSOR_VALUE} events.
*/
void enableSensorEchoBox();
/**
* Disables the internal sensor echo box for {@link EventNames#DEVICE_SENSOR_VALUE} events.
*/
void disableSensorEchoBox();
/**
* Returns true, if the internal sensor echo box is enabled, otherwise false.
*
* @return true, if the internal sensor echo box is enabled
*/
boolean isSensorEchoBoxEnabled();
/**
* Sets the {@link DeviceSensorValue} through a {@link EventItem} of the type
* {@link EventNames#DEVICE_SENSOR_VALUE}.
*
* @param event of the sensor update
*/
void setDeviceSensorByEvent(EventItem event);
/**
* Returns true, if the refresh priority of the given power sensor type as {@link SensorEnum} is equals
* {@link Config#REFRESH_PRIORITY_NEVER}, otherwise false.
*
* @param powerSensorType of the sensor
* @return true, if refresh priority is never
*/
boolean checkPowerSensorRefreshPriorityNever(SensorEnum powerSensorType);
/**
* Returns true, if the given sensor type as {@link SensorEnum} is supported by this {@link Device}, otherwise
* false.
*
* @param sensorType of the sensor
* @return true, if the sensor type is supported
*/
boolean supportsSensorType(SensorEnum sensorType);
/**
* Returns true, if this {@link Device} is a temperature controlled device, otherwise false. That means, that the
* output mode is one of the output modes in
* {@link OutputModeEnum#outputModeIsTemperationControlled(OutputModeEnum)}.
*
* @return true, if this {@link Device} is a temperature controlled
*/
boolean isTemperatureControlledDevice();
/**
* Returns true, if this {@link Device} is a binary input device. That means it have no output mode
* ({@link OutputModeEnum#DISABLED}), but {@link DeviceBinaryInput}'s.
*
* @return true, if this {@link Device} is a binary input device
*/
boolean isBinaryInputDevice();
/**
* Returns a {@link List} which contains all currently configured {@link DeviceBinaryInput}'s.
*
* @return list with all configured {@link DeviceBinaryInput}'s
*/
List<DeviceBinaryInput> getBinaryInputs();
/**
* Returns the {@link DeviceBinaryInput} of the given binary input type as {@link DeviceBinarayInputEnum}} or null,
* if the binary input type does not exist.
*
* @param binaryInputType of the {@link DeviceBinaryInput}
* @return the {@link DeviceBinaryInput} or null
*/
DeviceBinaryInput getBinaryInput(DeviceBinarayInputEnum binaryInputType);
/**
* Returns the state of the given binary input type as {@link DeviceBinarayInputEnum}} or null, if the binary input
* type does not exist.
*
* @param binaryInputType of the {@link DeviceBinaryInput}
* @return state of the given binary input type or null
*/
Short getBinaryInputState(DeviceBinarayInputEnum binaryInputType);
/**
* Sets the state of an existing {@link DeviceBinaryInput}. If the given {@link DeviceBinarayInputEnum} does not
* exist, it will returned false, otherwise true.
*
* @param binaryInputType of the {@link DeviceBinaryInput}
* @param newState the new state
* @return true, if it was successful, otherwise false
*/
boolean setBinaryInputState(DeviceBinarayInputEnum binaryInputType, Short newState);
/**
* Sets the given {@link List} of {@link DeviceBinaryInput}'s as supported binary inputs.
*
* @param newBinaryInputs to set
*/
void setBinaryInputs(List<DeviceBinaryInput> newBinaryInputs);
/**
* Returns true, if the given power sensor type as {@link SensorEnum} is up to date and does not need a refresh,
* otherwise it will returned false.
*
* @param powerSensorType of the sensor
* @return true, if the power sensor is up to date
*/
boolean isPowerSensorUpToDate(SensorEnum powerSensorType);
/**
* Returns a {@link List} of all supported sensor types as {@link SensorEnum}.
*
* @return all supported sensor types
*/
List<SensorEnum> getSensorTypes();
}

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices;
import org.openhab.binding.digitalstrom.internal.lib.listener.DeviceStatusListener;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
/**
* The {@link GeneralDeviceInformations} interface contains all informations of digitalSTROM devices, which are
* identical for all device types. It also contains the methods to implement the mechanism of the
* {@link DeviceStatusListener}.
*
* @author Michael Ochel - initial contributer
* @author Matthias Siegele - initial contributer
*/
public interface GeneralDeviceInformation {
/**
* Returns the user defined name of this device.
*
* @return name of this device
*/
String getName();
/**
* Sets the name of this device;
*
* @param name to set
*/
void setName(String name);
/**
* Returns the dSID of this device.
*
* @return {@link DSID} dSID
*/
DSID getDSID();
/**
* Returns the dSUID of this device.
*
* @return dSID
*/
String getDSUID();
/**
* This device is available in his zone or not.
* Every 24h the dSM (meter) checks, if the devices are
* plugged in
*
* @return true, if device is available otherwise false
*/
Boolean isPresent();
/**
* Sets this device is available in his zone or not.
*
* @param isPresent (true = available | false = not available)
*/
void setIsPresent(boolean isPresent);
/**
* Register a {@link DeviceStatusListener} to this {@link Device}.
*
* @param deviceStatuslistener to register
*/
void registerDeviceStatusListener(DeviceStatusListener deviceStatuslistener);
/**
* Unregister the {@link DeviceStatusListener} to this {@link Device} if it exists.
*
* @return the unregistered {@link DeviceStatusListener} or null if no one was registered
*/
DeviceStatusListener unregisterDeviceStatusListener();
/**
* Returns true, if a {@link DeviceStatusListener} is registered to this {@link Device}, otherwise false.
*
* @return return true, if a lister is registered, otherwise false
*/
boolean isListenerRegisterd();
/**
* Returns true, if this device is valid, otherwise false.
*
* @return true, if valid
*/
Boolean isValid();
/**
* Sets the valid state.
*
* @param isValid the new valid state
*/
void setIsValid(boolean isValid);
/**
* Returns the in the digitalSTROM web interface displayed dSID.
*
* @return displayed dSID
*/
String getDisplayID();
/**
* Returns the registered {@link DeviceStatusListener} or null, if no {@link DeviceStatusListener} is registered
*
* @return the registered {@link DeviceStatusListener} or null
*/
DeviceStatusListener getDeviceStatusListener();
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters;
import java.util.Date;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringTypeEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.MeteringUnitsEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.impl.DSID;
/**
* The {@link CachedMeteringValue} saves the metering value of an digitalSTROM-Circuit.
*
* @author Alexander Betker - Initial contribution
* @author Michael Ochel - add methods getDateAsDate(), getMeteringType() and getMeteringUnit(); add missing java-doc
* @author Matthias Siegele - add methods getDateAsDate(), getMeteringType() and getMeteringUnit(); add missing java-doc
*/
public interface CachedMeteringValue {
/**
* Returns the {@link DSID} of the digitalSTROM-Circuit.
*
* @return dSID of circuit
*/
DSID getDsid();
/**
* Returns the saved sensor value.
*
* @return sensor value
*/
double getValue();
/**
* Returns the timestamp when the sensor value was read out as {@link String}.
*
* @return read out timestamp
*/
String getDate();
/**
* Returns the timestamp when the sensor value was read out as {@link Date}.
*
* @return read out timestamp
*/
Date getDateAsDate();
/**
* Returns the {@link MeteringTypeEnum} of this {@link CachedMeteringValue}.
*
* @return metering type as {@link MeteringTypeEnum}
*/
MeteringTypeEnum getMeteringType();
/**
* Returns the {@link MeteringUnitsEnum} of this {@link CachedMeteringValue}.
*
* @return metering unit as {@link MeteringUnitsEnum}
*/
MeteringUnitsEnum getMeteringUnit();
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters;
/**
* The {@link DeviceConfig} saves device configurations.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add missing java-doc
* @author Matthias Siegele - add missing java-doc
*/
public interface DeviceConfig {
/**
* Returns the digitalSTROM-Device parameter class.
*
* @return configuration class
*/
int getConfigurationClass();
/**
* Returns the digitalSTROM-Device configuration index.
*
* @return configuration index
*/
int getIndex();
/**
* Returns the digitalSTROM-Device configuration value.
*
* @return configuration value
*/
int getValue();
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters;
/**
* The {@link DeviceConstants} contains some constants for digitalSTROM devices.
*
* @author Alexander Betker - Initial contribution
* @author Michael Ochel - updated constants
* @author Matthias Siegele - updated constants
*/
public interface DeviceConstants {
/** digitalSTROM dim step for lights (this value is not in percent!) */
static final short DIM_STEP_LIGHT = 11;
/** move step for roller shutters (this value is not in percent!) */
static final short MOVE_STEP_ROLLERSHUTTER = 983;
/** move step for slats angle by blind/jalousie (this value is not in percent!) */
static final short ANGLE_STEP_SLAT = 11;
/** default move step (this value is not in percent!) */
static final short DEFAULT_MOVE_STEP = 11;
/** default max output value */
static final short DEFAULT_MAX_OUTPUTVALUE = 255;
/** max output value if device (lamp - yellow) is on */
static final short MAX_OUTPUT_VALUE_LIGHT = 255;
/** is open (special case: awning/marquee - closed) */
static final int MAX_ROLLERSHUTTER = 65535;
/** is closed (special case: awning/marquee - open) */
static final short MIN_ROLLERSHUTTER = 0;
/** max slat angle by blind/jalousie */
static final short MAX_SLAT_ANGLE = 255;
/** min slat angle by blind/jalousie */
static final short MIN_SLAT_ANGLE = 0;
/** you can't dim deeper than this value */
static final short MIN_DIM_VALUE = 16;
/** this is the index to get the output value (min-, max value) of almost all devices */
static final short DEVICE_SENSOR_OUTPUT = 0;
/** this is the index to get the output value (min-, max value) of shade devices */
static final short DEVICE_SENSOR_SLAT_POSITION_OUTPUT = 2;
/** this index is needed to get the angle of the slats (if device is a blind/jalousie) */
static final short DEVICE_SENSOR_SLAT_ANGLE_OUTPUT = 4;
}

View File

@@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters;
import org.openhab.binding.digitalstrom.internal.lib.structure.scene.constants.Scene;
/**
* The {@link DeviceSceneSpec} saves a digitalSTROM-Device scene mode.
*
* @author Alexander Betker - initial contributer
* @author Michael Ochel - add missing java-doc
* @author Matthias Siegele - add missing java-doc
*/
public interface DeviceSceneSpec {
/**
* Returns the sceneID.
*
* @return sceneID
*/
Scene getScene();
/**
* Returns true, if the don't care flag is set, otherwise false.
*
* @return true, if dont't care is set, otherwise false
*/
boolean isDontCare();
/**
* Sets the don't care flag.
*
* @param dontcare to set
*/
void setDontcare(boolean dontcare);
/**
* Returns true, if the local priority flag is set, otherwise false.
*
* @return true, if local priority is, set otherwise false
*/
boolean isLocalPrio();
/**
* Sets the local priority flag.
*
* @param localPrio to set
*/
void setLocalPrio(boolean localPrio);
/**
* Returns true, if the special mode flag is set, otherwise false.
*
* @return true, if special mode is set, otherwise false
*/
boolean isSpecialMode();
/**
* Sets the special mode flag.
*
* @param specialMode to set
*/
void setSpecialMode(boolean specialMode);
/**
* Returns true, if the flash mode flag is set, otherwise false.
*
* @return true, if flash mode is set, otherwise false
*/
boolean isFlashMode();
/**
* Sets the flash mode flag.
*
* @param flashMode to set
*/
void setFlashMode(boolean flashMode);
}

View File

@@ -0,0 +1,190 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.DeviceBinarayInputEnum;
import org.openhab.binding.digitalstrom.internal.lib.structure.devices.deviceparameters.constants.SensorEnum;
/**
* Represents a device state update for lights, joker, shades and sensor data.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*/
public interface DeviceStateUpdate {
// Update types
// in certain circumstances it is also better to rename SLAT_ANGLE to e.g. SECONDARY_OUTPUT
// light
static final String OUTPUT = "output";
static final String ON_OFF = "OnOff";
static final String OUTPUT_INCREASE = "outputIncrese";
static final String OUTPUT_DECREASE = "outputDecrese";
static final String OUTPUT_STOP = "outputStop";
static final String OUTPUT_MOVE = "outputMove";
// shades
static final String SLATPOSITION = "slatposition";
static final String SLAT_ANGLE = "slatAngle";
static final String SLAT_INCREASE = "slatIncrese";
static final String SLAT_DECREASE = "slatDecrese";
static final String SLAT_ANGLE_INCREASE = "slatAngleIncrese";
static final String SLAT_ANGLE_DECREASE = "slatAngleDecrese";
static final String OPEN_CLOSE = "openClose";
static final String OPEN_CLOSE_ANGLE = "openCloseAngle";
static final String SLAT_MOVE = "slatMove";
static final String SLAT_STOP = "slatStop";
// sensor data
static final String UPDATE_OUTPUT_VALUE = "outputValue";
static final String UPDATE_DEVICE_SENSOR = "deviceSensor-";
// metering data
static final String UPDATE_CIRCUIT_METER = "circuitMeter";
// binary inputs
static final String BINARY_INPUT = "binaryInput-";
// scene
/** A scene call can have the value between 0 and 127. */
static final String UPDATE_CALL_SCENE = "callScene";
static final String UPDATE_UNDO_SCENE = "undoScene";
static final String UPDATE_SCENE_OUTPUT = "sceneOutput";
static final String UPDATE_SCENE_CONFIG = "sceneConfig";
// general
/** command to refresh the output value of an device. */
static final String REFRESH_OUTPUT = "refreshOutput";
// standard values
static final int ON_VALUE = 1;
static final int OFF_VALUE = -1;
/**
* Returns the state update value.
* <p>
* <b>NOTE:</b>
* </p>
* <ul>
* <li>For all OnOff-types the value for off is lower than 0 and for on higher than 0.</li>
* <li>For all Increase- and Decrease-types the value is the new output value.</li>
* <li>For SceneCall-type the value is between 0 and 127.</li>
* <li>For all SceneUndo-types the value is the new output value.</li>
* <li>For all SensorUpdate-types will read the sensor data directly, if the value is 0, otherwise a
* {@link SensorJob} will be added to the {@link SensorJobExecutor}.</li>
* </ul>
*
* @return new state value
*/
Object getValue();
/**
* Returns the value as {@link Integer}.
*
* @return integer value
* @see #getValue()
*/
Integer getValueAsInteger();
/**
* Returns the value as {@link String}.
*
* @return string value
* @see #getValue()
*/
String getValueAsString();
/**
* Returns the value as {@link Short}.
*
* @return short value
* @see #getValue()
*/
Short getValueAsShort();
/**
* Returns the value as {@link Float}.
*
* @return float value
* @see #getValue()
*/
Float getValueAsFloat();
/**
* Returns the value as {@link Short}-array.
*
* @return short[] value
* @see #getValue()
*/
Short[] getValueAsShortArray();
/**
* Returns the state update type.
*
* @return state update type
*/
String getType();
/**
* Returns the update type as {@link SensorEnum} or null, if the type is not a {@link #UPDATE_DEVICE_SENSOR} type.
*
* @return type as {@link SensorEnum} or null
*/
SensorEnum getTypeAsSensorEnum();
/**
* Returns true, if this {@link DeviceStateUpdate} is a {@link #UPDATE_DEVICE_SENSOR} type, otherwise false.
*
* @return true, if it is a sensor type
*/
boolean isSensorUpdateType();
/**
* Returns the scene id of this {@link DeviceStateUpdate}, if this {@link DeviceStateUpdate} is a scene update type,
* otherwise it will be returned -1.
*
* @return the scene id or -1
*/
Short getSceneId();
/**
* Returns the scene configuration or output reading priority, if this {@link DeviceStateUpdate} is a
* {@link #UPDATE_SCENE_CONFIG} or {@link #UPDATE_SCENE_OUTPUT} type.
*
* @return scene reading priority
*/
Short getScenePriority();
/**
* Returns true, if this {@link DeviceStateUpdate} is a {@link #UPDATE_SCENE_CONFIG} or {@link #UPDATE_SCENE_OUTPUT}
* type, otherwise false.
*
* @return true, if it is a scene reading type
*/
boolean isSceneUpdateType();
/**
* Returns the update type as {@link DeviceBinarayInputEnum} or null, if the type is not a {@link #BINARY_INPUT}
* type.
*
* @return type as {@link DeviceBinarayInputEnum} or null
*/
DeviceBinarayInputEnum getTypeAsDeviceBinarayInputEnum();
/**
* Returns true, if this {@link DeviceStateUpdate} is a {@link #BINARY_INPUT} type, otherwise false.
*
* @return true, if it is a binary input type
*/
boolean isBinarayInputType();
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.digitalstrom.internal.lib.structure.devices.deviceparameters.constants;
/**
* The {@link ChangeableDeviceConfigEnum} lists all changeable device configurations.
*
* @author Michael Ochel - Initial contribution
* @author Matthias Siegele - Initial contribution
*
*/
public enum ChangeableDeviceConfigEnum {
METER_DSID,
DEVICE_NAME,
ZONE_ID,
GROUPS,
FUNCTIONAL_GROUP,
OUTPUT_MODE,
BINARY_INPUTS
}

Some files were not shown because too many files have changed in this diff Show More