mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[plugwiseha] Initial contribution (#9504)
Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
parent
7d2c8755eb
commit
8202d57965
@ -229,6 +229,7 @@
|
||||
/bundles/org.openhab.binding.playstation/ @FluBBaOfWard
|
||||
/bundles/org.openhab.binding.plclogo/ @falkena
|
||||
/bundles/org.openhab.binding.plugwise/ @wborn
|
||||
/bundles/org.openhab.binding.plugwiseha/ @lsiepel
|
||||
/bundles/org.openhab.binding.powermax/ @lolodomo
|
||||
/bundles/org.openhab.binding.pulseaudio/ @peuter
|
||||
/bundles/org.openhab.binding.pushbullet/ @hakan42
|
||||
|
@ -1126,6 +1126,11 @@
|
||||
<artifactId>org.openhab.binding.plugwise</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.plugwiseha</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.powermax</artifactId>
|
||||
|
13
bundles/org.openhab.binding.plugwiseha/NOTICE
Normal file
13
bundles/org.openhab.binding.plugwiseha/NOTICE
Normal 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/openhab2-addons
|
215
bundles/org.openhab.binding.plugwiseha/README.md
Normal file
215
bundles/org.openhab.binding.plugwiseha/README.md
Normal file
@ -0,0 +1,215 @@
|
||||
# PlugwiseHA Binding
|
||||
|
||||
The Plugwise Home Automation binding adds support to openHAB for the [Plugwise Home Automation ecosystem](https://www.plugwise.com/en_US/adam_zone_control).
|
||||
This system is built around a gateway from Plugwise called the 'Adam' which incorporates a ZigBee controller to manage thermostatic radiator valves, room thermostats, floor heating pumps, et cetera.
|
||||
|
||||
Users can manage and control this system either via a web app or a mobile phone app developed by Plugwise.
|
||||
The (web) app allows users to define heating zone's (e.g. rooms) and add radiator valves to those rooms to manage and control their heating irrespective of other rooms.
|
||||
|
||||
Using the Plugwise Home Automation binding you can incorporate the management of these devices and heating zones into openHAB.
|
||||
The binding uses the same RESTfull API that both the mobile phone app and the web app use.
|
||||
|
||||
The binding requires users to have a working Plugwise Home Automation setup consisting of at least 1 gateway device (the 'Adam') and preferably 1 radiator valve as a bare minimum.
|
||||
The 'Adam' (from hereon called the gateway) needs to be accessible from the openHAB instance via a TCP/IP connection.
|
||||
|
||||
## Supported Things
|
||||
|
||||
| Device Type | Description | Thing Type |
|
||||
|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|----------------------|
|
||||
| - | A Plugwise heating zone configured with at least 1 of the devices below | zone |
|
||||
| [Adam](https://www.plugwise.com/en_US/products/adam-ha) | The Plugwise Home Automation Bridge is needed to connect to the Adam boiler gateway | gateway |
|
||||
| [Tom](https://www.plugwise.com/en_US/products/tom) | A Plugwise Home Automation radiator valve | appliance_valve |
|
||||
| [Floor](https://www.plugwise.com/en_US/products/floor) | A Plugwise Home Automation radiator valve specifically used for floor heating | appliance_valve |
|
||||
| [Circle](https://www.plugwise.com/en_US/products/circle) | A power outlet plug that provides energy measurement and switching control of appliances (e.g. floor heating pump) | appliance_pump |
|
||||
| [Lisa](https://www.plugwise.com/en_US/products/lisa) | A room thermostat (also supports the 'Anna' room thermostat) | appliance_thermostat |
|
||||
| [Boiler] | A central boiler used for heating and/or domestic hot water | appliance_boiler |
|
||||
|
||||
|
||||
|
||||
## Discovery
|
||||
|
||||
After setting up the Plugwise Home Automation bridge you can start a manual scan to find all devices registered on the gateway.
|
||||
You can also manually add things by entering the corresponding device id as a configuration parameter.
|
||||
The device IDs can be found be enabling TRACE logging in the Karaf console.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
You must define a Plugwise Home Automation gateway (Bridge) before defining zones or appliances (Things) for this binding to work.
|
||||
|
||||
#### Plugwise Home Automation gateway (Bridge):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| --------- | ----------------------------------------------------------------------- | -------- | ------- |
|
||||
| host | The IP address or hostname of the Adam HA gateway | Required | 'adam' |
|
||||
| username | The username for the Adam HA gateway | Optional | 'smile' |
|
||||
| smileID | The 8 letter code on the sticker on the back of the Adam boiler gateway | Required | - |
|
||||
| refresh | The refresh interval in seconds | Optional | 15 |
|
||||
|
||||
#### Plugwise Home Automation zone (`zone`):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| --------- | ------------------------- | -------- | ------- |
|
||||
| id | The unique ID of the zone | Required | - |
|
||||
|
||||
#### Plugwise Home Automation appliance (`appliance_valve`):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------ | -------- | ------- |
|
||||
| id | The unique ID of the radiator valve appliance | Required | - |
|
||||
| lowBatteryPercentage | Battery charge remaining at which to trigger battery low warning. (*Only applicable for battery operated devices*) | Optional | 15 |
|
||||
|
||||
#### Plugwise Home Automation appliance (`appliance_thermostat`):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------------------------ | -------- | ------- |
|
||||
| id | The unique ID of the room thermostat appliance | Required | - |
|
||||
| lowBatteryPercentage | Battery charge remaining at which to trigger battery low warning. (*Only applicable for battery operated devices*) | Optional | 15 |
|
||||
|
||||
|
||||
#### Plugwise Home Automation appliance (`appliance_pump`):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| --------- | ----------------------------------- | -------- | ------- |
|
||||
| id | The unique ID of the pump appliance | Required | - |
|
||||
|
||||
#### Plugwise Home Automation boiler (`appliance_boiler`):
|
||||
|
||||
| Parameter | Description | Config | Default |
|
||||
| --------- | --------------------------- | -------- | ------- |
|
||||
| id | The unique ID of the boiler | Required | - |
|
||||
|
||||
## Channels
|
||||
|
||||
| channel | type | Read-only? | description |
|
||||
|----------------------|--------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| temperature | Number:Temperature | Yes | The temperature of an appliance that supports the thermostat functionality |
|
||||
| setpointTemperature | Number:Temperature | No | The setpoint temperature (read/write) of an appliance that supports the thermostat functionality |
|
||||
| power | Switch | No | Toggle an appliance ON/OFF that supports the relay functionality |
|
||||
| lock | Switch | No | Toggle an appliance lock ON/OFF that supports the relay functionality.(*When the lock is ON the gateway will not automatically control the corresponding relay switch depending on thermostat mode*) |
|
||||
| powerUsage | Number:Power | Yes | The current power usage in Watts of an appliance that supports this |
|
||||
| batteryLevel | Number | Yes | The current battery level of an appliance that is battery operated |
|
||||
| batteryLevelLow | Switch | Yes | Switches ON when the battery level of an appliance that is battery operated drops below a certain threshold |
|
||||
| chState | Switch | Yes | The current central heating state of the boiler |
|
||||
| dhwState | Switch | Yes | The current domestic hot water state of the boiler |
|
||||
| waterPressure | Number:Pressure | Yes | The current water pressure of the boiler |
|
||||
| presetScene | String | Yes | The current active scene for the zone |
|
||||
| valvePosition | Number | Yes | The current position of the valve |
|
||||
| preHeat | Switch | Yes | Toggle the pre heating of a zone ON/OFF |
|
||||
| coolingState | Switch | Yes | The current cooling state of the boiler |
|
||||
| intendedBoilerTemp | Number:Temperature | Yes | The intended boiler temperature |
|
||||
| flameState | Switch | Yes | The flame state of the boiler |
|
||||
| intendedHeatingState | Switch | Yes | The intended heating state of the boiler |
|
||||
| modulationLevel | Number | Yes | The current modulation level of the boiler |
|
||||
| otAppFaultCode | Number | Yes | The Opentherm application fault code of the boiler |
|
||||
| dhwTemperature | Number:Temperature | Yes | The current central heating state of the boiler |
|
||||
| otOEMFaultCode | Number | Yes | The Opentherm OEM fault code of the boiler |
|
||||
| boilerTemperature | Number:Temperature | Yes | The current temperature of the boiler |
|
||||
| dhwSetpoint | Number:Temperature | Yes | The domestic hot water setpoint |
|
||||
| maxBoilerTemperature | Number:Temperature | Yes | The maximum temperature of the boiler |
|
||||
| dhwComfortMode | Switch | Yes | The domestic hot water confortmode |
|
||||
|
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
**things/plugwiseha.things**
|
||||
|
||||
```
|
||||
Bridge plugwiseha:gateway:home "Plugwise Home Automation Gateway" [ smileId="abcdefgh" ] {
|
||||
Thing zone living_room_zone "Living room" [ id="$device_id" ]
|
||||
Thing appliance_valve living_room_radiator "Living room radiator valve" [ id="$device_id" ]
|
||||
Thing appliance_thermostat living_room_thermostat "Living room thermostat" [ id="$device_id" ]
|
||||
Thing appliance_pump living_room_pump "Floor heating pump" [ id="$device_id" ]
|
||||
Thing appliance_boiler main_boiler "Main boiler" [ id="$device_id" ]
|
||||
}
|
||||
```
|
||||
|
||||
Replace `$device_id` accordingly.
|
||||
|
||||
**items/plugwiseha.items**
|
||||
|
||||
```
|
||||
Number:Temperature living_room_zone_temperature "Zone temperature" {channel="plugwiseha:zone:home:living_room_zone:temperature"}
|
||||
Number:Temperature living_room_zone_temperature_setpoint "Zone temperature setpoint" {channel="plugwiseha:zone:home:living_room_zone:setpointTemperature"}
|
||||
Number:Temperature living_room_zone_preset_scene "Zone preset scene" {channel="plugwiseha:zone:home:living_room_zone:presetScene"}
|
||||
Switch living_room_zone_preheat "Zone preheat enabled" {channel="plugwiseha:zone:home:living_room_zone:preHeat"}
|
||||
|
||||
Number:Temperature living_room_radiator_temperature "Radiator valve temperature" {channel="plugwiseha:appliance_valve:home:living_room_radiator:temperature"}
|
||||
Number:Temperature living_room_radiator_temperature_setpoint "Radiator valve temperature setpoint" {channel="plugwiseha:appliance_valve:home:living_room_radiator:setpointTemperature"}
|
||||
Number living_room_radiator_valve_position "Radiator valve position" {channel="plugwiseha:appliance_valve:home:living_room_radiator:valvePosition"}
|
||||
|
||||
Number:Temperature living_room_thermostat_temperature "Room thermostat temperature" {channel="plugwiseha:appliance_valve:home:living_room_thermostat:temperature"}
|
||||
Number:Temperature living_room_thermostat_temperature_setpoint "Room thermostat temperature setpoint" {channel="plugwiseha:appliance_valve:home:living_room_thermostat:setpointTemperature"}
|
||||
Number:Temperature living_room_thermostat_temperature_offset "Room thermostat temperature offset" {channel="plugwiseha:appliance_valve:home:living_room_thermostat:offsetTemperature"}
|
||||
|
||||
Switch living_room_pump_power "Floor heating pump power" {channel="plugwiseha:appliance_pump:home:living_room_pump:power"}
|
||||
Switch living_room_pump_lock "Floor heating pump lock [MAP:(plugwiseha.map):%s]" {channel="plugwiseha:appliance_pump:home:living_room_pump:lock"}
|
||||
Number:Power living_room_pump_power_usage "Floor heating pump power [%0.2fW]" {channel="plugwiseha:appliance_pump:home:living_room_pump:powerUsage"}
|
||||
|
||||
Number:Pressure main_boiler_waterpressure "Waterpressure" { channel="plugwiseha:appliance_boiler:home:main_boiler:waterPressure"}
|
||||
Switch main_boiler_chState "Heating active" { channel="plugwiseha:appliance_boiler:home:main_boiler:chActive"}
|
||||
Switch main_boiler_dhwState "Domestic hot water active" { channel="plugwiseha:appliance_boiler:home:main_boiler:dhwActive"}
|
||||
|
||||
Switch main_boiler_coolingState "Cooling state" { channel="plugwiseha:appliance_boiler:home:main_boiler:coolingState"}
|
||||
Number:Temperature main_boiler_intendedBoilerTemp "Intended boiler temperature" {channel="plugwiseha:appliance_boiler:home:living_room_thermostat:intendedBoilerTemp"}
|
||||
Switch main_boiler_flameState "Flame state" { channel="plugwiseha:appliance_boiler:home:main_boiler:flameState"}
|
||||
Switch main_boiler_intendedHeatingState "Intended heating state" { channel="plugwiseha:appliance_boiler:home:main_boiler:intendedHeatingState"}
|
||||
Number main_boiler_modulationLevel "Modulation level" {channel="plugwiseha:appliance_boiler:home:living_room_radiator:modulationLevel"}
|
||||
Number main_boiler_otAppFaultCode "Opentherm app. faultcode" {channel="plugwiseha:appliance_boiler:home:living_room_radiator:otAppFaultCode"}
|
||||
Number:Temperature main_boiler_dhwTemperature "DHW temperature" {channel="plugwiseha:appliance_boiler:home:living_room_thermostat:dhwTemperature"}
|
||||
Number main_boiler_otOEMFaultCode "Opentherm OEM faultcode" {channel="plugwiseha:appliance_boiler:home:living_room_radiator:otOEMFaultCode"}
|
||||
Number:Temperature main_boiler_boilerTemperature "Boiler temperature" {channel="plugwiseha:appliance_boiler:home:living_room_thermostat:boilerTemperature"}
|
||||
Number:Temperature main_boiler_dhwSetpoint "DHW setpoint" {channel="plugwiseha:appliance_boiler:home:living_room_thermostat:dhwSetpoint"}
|
||||
Number:Temperature main_boiler_maxBoilerTemperature "Max. boiler temperature" {channel="plugwiseha:appliance_boiler:home:living_room_thermostat:maxBoilerTemperature"}
|
||||
Switch main_boiler_dhwComfortMode "DHW comfort mode" { channel="plugwiseha:appliance_boiler:home:main_boiler:dhwComfortMode"}
|
||||
```
|
||||
|
||||
**transform/plugwiseha.map**
|
||||
|
||||
```
|
||||
ON=Locked
|
||||
OFF=Unlocked
|
||||
```
|
||||
|
||||
**sitemaps/plugwiseha.sitemap**
|
||||
|
||||
```
|
||||
sitemap plugwiseha label="PlugwiseHA Binding"
|
||||
{
|
||||
Frame {
|
||||
Text item=living_room_zone_temperature
|
||||
Setpoint item=living_room_zone_temperature_setpoint label="Living room [%.1f °C]" minValue=5.0 maxValue=25 step=0.5
|
||||
Text item=living_room_zone_presetScene
|
||||
Switch item=living_room_zone_preheat
|
||||
|
||||
Text item=living_room_radiator_temperature
|
||||
Setpoint item=living_room_radiator_temperature_setpoint label="Living room [%.1f °C]" minValue=5.0 maxValue=25 step=0.5
|
||||
Text item=living_room_radiator_valve_position
|
||||
|
||||
Text item=living_room_thermostat_temperature
|
||||
Setpoint item=living_room_thermostat_temperature_setpoint label="Living room [%.1f °C]" minValue=5.0 maxValue=25 step=0.5
|
||||
Setpoint item=living_room_thermostat_temperature_offset label="Living room offset [%.1f °C]" minValue=-5.0 maxValue=5 step=0.5
|
||||
|
||||
Number item=living_room_pump_power_usage
|
||||
Switch item=living_room_pump_power
|
||||
Switch item=living_room_pump_lock
|
||||
|
||||
Number item=main_boiler_waterpressure
|
||||
Switch item=main_boiler_chState
|
||||
Switch item=main_boiler_dhwState
|
||||
|
||||
Switch item=main_boiler_coolingState
|
||||
Number item=main_boiler_intendedBoilerTemp
|
||||
Switch item=main_boiler_flameState
|
||||
Switch item=main_boiler_intendedHeatingState
|
||||
Number item=main_boiler_modulationLevel
|
||||
Number item=main_boiler_otAppFaultCode
|
||||
Number item=main_boiler_dhwTemperature
|
||||
Number item=main_boiler_otOEMFaultCode
|
||||
Number item=main_boiler_boilerTemperature
|
||||
Number item=main_boiler_dhwSetpoint
|
||||
Number item=main_boiler_maxBoilerTemperature
|
||||
Switch item=main_boiler_dhwComfortMode
|
||||
}
|
||||
}
|
||||
```
|
17
bundles/org.openhab.binding.plugwiseha/pom.xml
Normal file
17
bundles/org.openhab.binding.plugwiseha/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.plugwiseha</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: PlugwiseHA Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.plugwiseha-${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-plugwiseha" description="PlugwiseHA Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.plugwiseha/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHABindingConstants} class defines common constants, which
|
||||
* are used across the whole binding.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHABindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "plugwiseha";
|
||||
|
||||
// List of PlugwiseHA services, related urls, information
|
||||
|
||||
public static final String PLUGWISEHA_API_URL = "http://%s";
|
||||
public static final String PLUGWISEHA_API_APPLIANCES_URL = PLUGWISEHA_API_URL + "/core/appliances";
|
||||
public static final String PLUGWISEHA_API_APPLIANCE_URL = PLUGWISEHA_API_URL + "/core/appliances;id=%s";
|
||||
public static final String PLUGWISEHA_API_LOCATIONS_URL = PLUGWISEHA_API_URL + "/core/locations";
|
||||
public static final String PLUGWISEHA_API_LOCATION_URL = PLUGWISEHA_API_URL + "/core/locations;id=%s";
|
||||
|
||||
// Bridge
|
||||
public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway");
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone");
|
||||
public static final ThingTypeUID THING_TYPE_APPLIANCE_VALVE = new ThingTypeUID(BINDING_ID, "appliance_valve");
|
||||
public static final ThingTypeUID THING_TYPE_APPLIANCE_PUMP = new ThingTypeUID(BINDING_ID, "appliance_pump");
|
||||
public static final ThingTypeUID THING_TYPE_APPLIANCE_THERMOSTAT = new ThingTypeUID(BINDING_ID,
|
||||
"appliance_thermostat");
|
||||
public static final ThingTypeUID THING_TYPE_APPLIANCE_BOILER = new ThingTypeUID(BINDING_ID, "appliance_boiler");
|
||||
|
||||
// List of channel Type UIDs
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_BATTERYLEVEL = new ChannelTypeUID("system:battery-level");
|
||||
public static final ChannelTypeUID CHANNEL_TYPE_BATTERYLEVELLOW = new ChannelTypeUID("system:low-battery");
|
||||
|
||||
// Empty set
|
||||
public static final Set<ThingTypeUID> SUPPORTED_INTERFACE_TYPES_UIDS_EMPTY = Set.of();
|
||||
|
||||
// List of all Gateway configuration properties
|
||||
public static final String GATEWAY_CONFIG_HOST = "host";
|
||||
public static final String GATEWAY_CONFIG_USERNAME = "username";
|
||||
public static final String GATEWAY_CONFIG_SMILEID = "smileId";
|
||||
public static final String GATEWAY_CONFIG_REFRESH = "refresh";
|
||||
|
||||
// List of all Zone configuration properties
|
||||
public static final String ZONE_CONFIG_ID = "id";
|
||||
public static final String ZONE_CONFIG_NAME = "zoneName";
|
||||
|
||||
// List of all Appliance configuration properties
|
||||
public static final String APPLIANCE_CONFIG_ID = "id";
|
||||
public static final String APPLIANCE_CONFIG_NAME = "applianceName";
|
||||
public static final String APPLIANCE_CONFIG_LOWBATTERY = "lowBatteryPercentage";
|
||||
|
||||
// List of all Appliance properties
|
||||
public static final String APPLIANCE_PROPERTY_DESCRIPTION = "description";
|
||||
public static final String APPLIANCE_PROPERTY_TYPE = "type";
|
||||
public static final String APPLIANCE_PROPERTY_FUNCTIONALITIES = "functionalities";
|
||||
public static final String APPLIANCE_PROPERTY_ZB_TYPE = "zigbee type";
|
||||
public static final String APPLIANCE_PROPERTY_ZB_REACHABLE = "zigbee reachable";
|
||||
public static final String APPLIANCE_PROPERTY_ZB_POWERSOURCE = "zigboo power source";
|
||||
|
||||
// List of all Location properties
|
||||
public static final String LOCATION_PROPERTY_DESCRIPTION = "description";
|
||||
public static final String LOCATION_PROPERTY_TYPE = "type";
|
||||
public static final String LOCATION_PROPERTY_FUNCTIONALITIES = "functionalities";
|
||||
|
||||
// List of all Channel IDs
|
||||
public static final String ZONE_SETPOINT_CHANNEL = "setpointTemperature";
|
||||
public static final String ZONE_TEMPERATURE_CHANNEL = "temperature";
|
||||
public static final String ZONE_PRESETSCENE_CHANNEL = "presetScene";
|
||||
public static final String ZONE_PREHEAT_CHANNEL = "preHeat";
|
||||
|
||||
public static final String APPLIANCE_SETPOINT_CHANNEL = "setpointTemperature";
|
||||
public static final String APPLIANCE_TEMPERATURE_CHANNEL = "temperature";
|
||||
public static final String APPLIANCE_BATTERYLEVEL_CHANNEL = "batteryLevel";
|
||||
public static final String APPLIANCE_BATTERYLEVELLOW_CHANNEL = "batteryLevelLow";
|
||||
public static final String APPLIANCE_POWER_USAGE_CHANNEL = "powerUsage";
|
||||
public static final String APPLIANCE_POWER_CHANNEL = "power";
|
||||
public static final String APPLIANCE_LOCK_CHANNEL = "lock";
|
||||
public static final String APPLIANCE_WATERPRESSURE_CHANNEL = "waterPressure";
|
||||
public static final String APPLIANCE_DHWSTATE_CHANNEL = "dhwState";
|
||||
public static final String APPLIANCE_CHSTATE_CHANNEL = "chState";
|
||||
public static final String APPLIANCE_OFFSET_CHANNEL = "offsetTemperature";
|
||||
public static final String APPLIANCE_VALVEPOSITION_CHANNEL = "valvePosition";
|
||||
public static final String APPLIANCE_COOLINGSTATE_CHANNEL = "coolingState";
|
||||
public static final String APPLIANCE_INTENDEDBOILERTEMP_CHANNEL = "intendedBoilerTemp";
|
||||
public static final String APPLIANCE_FLAMESTATE_CHANNEL = "flameState";
|
||||
public static final String APPLIANCE_INTENDEDHEATINGSTATE_CHANNEL = "intendedHeatingState";
|
||||
public static final String APPLIANCE_MODULATIONLEVEL_CHANNEL = "modulationLevel";
|
||||
public static final String APPLIANCE_OTAPPLICATIONFAULTCODE_CHANNEL = "otAppFaultCode";
|
||||
public static final String APPLIANCE_DHWTEMPERATURE_CHANNEL = "dhwTemperature";
|
||||
public static final String APPLIANCE_OTOEMFAULTCODE_CHANNEL = "otOEMFaultCode";
|
||||
public static final String APPLIANCE_BOILERTEMPERATURE_CHANNEL = "boilerTemperature";
|
||||
public static final String APPLIANCE_DHWSETPOINT_CHANNEL = "dhwSetpoint";
|
||||
public static final String APPLIANCE_MAXBOILERTEMPERATURE_CHANNEL = "maxBoilerTemperature";
|
||||
public static final String APPLIANCE_DHWCOMFORTMODE_CHANNEL = "dhwComfortMode";
|
||||
|
||||
// List of all Appliance Types
|
||||
public static final String APPLIANCE_TYPE_THERMOSTAT = "thermostat";
|
||||
public static final String APPLIANCE_TYPE_GATEWAY = "gateway";
|
||||
public static final String APPLIANCE_TYPE_CENTRALHEATINGPUMP = "central_heating_pump";
|
||||
public static final String APPLIANCE_TYPE_OPENTHERMGATEWAY = "open_therm_gateway";
|
||||
public static final String APPLIANCE_TYPE_ZONETHERMOSTAT = "zone_thermostat";
|
||||
public static final String APPLIANCE_TYPE_HEATERCENTRAL = "heater_central";
|
||||
public static final String APPLIANCE_TYPE_THERMOSTATICRADIATORVALUE = "thermostatic_radiator_valve";
|
||||
|
||||
// List of Plugwise Maesure Units
|
||||
public static final String UNIT_CELSIUS = "C";
|
||||
|
||||
// Supported things
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ZONE,
|
||||
THING_TYPE_APPLIANCE_VALVE, THING_TYPE_APPLIANCE_PUMP, THING_TYPE_APPLIANCE_BOILER);
|
||||
|
||||
// Appliance types known to binding
|
||||
public static final Set<String> KNOWN_APPLIANCE_TYPES = Set.of(APPLIANCE_TYPE_THERMOSTAT, APPLIANCE_TYPE_GATEWAY,
|
||||
APPLIANCE_TYPE_CENTRALHEATINGPUMP, APPLIANCE_TYPE_OPENTHERMGATEWAY, APPLIANCE_TYPE_ZONETHERMOSTAT,
|
||||
APPLIANCE_TYPE_HEATERCENTRAL, APPLIANCE_TYPE_THERMOSTATICRADIATORVALUE);
|
||||
|
||||
public static final Set<String> SUPPORTED_APPLIANCE_TYPES = Set.of(APPLIANCE_TYPE_CENTRALHEATINGPUMP,
|
||||
APPLIANCE_TYPE_THERMOSTATICRADIATORVALUE, APPLIANCE_TYPE_ZONETHERMOSTAT, APPLIANCE_TYPE_HEATERCENTRAL);
|
||||
|
||||
// Supported bridges
|
||||
public static final Set<ThingTypeUID> SUPPORTED_BRIDGE_TYPES_UIDS = Set.of(THING_TYPE_GATEWAY);
|
||||
|
||||
// Getters & Setters
|
||||
public static String getApiUrl(String host) {
|
||||
return String.format(PLUGWISEHA_API_URL, host);
|
||||
}
|
||||
|
||||
public static String getAppliancesUrl(String host) {
|
||||
return String.format(PLUGWISEHA_API_APPLIANCES_URL, host);
|
||||
}
|
||||
|
||||
public static String getApplianceUrl(String host, String applianceId) {
|
||||
return String.format(PLUGWISEHA_API_APPLIANCE_URL, host, applianceId);
|
||||
}
|
||||
|
||||
public static String getLocationsUrl(String host) {
|
||||
return String.format(PLUGWISEHA_API_LOCATIONS_URL, host);
|
||||
}
|
||||
|
||||
public static String getLocationUrl(String host, String locationId) {
|
||||
return String.format(PLUGWISEHA_API_LOCATION_URL, host, locationId);
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.plugwiseha.internal.handler.PlugwiseHAApplianceHandler;
|
||||
import org.openhab.binding.plugwiseha.internal.handler.PlugwiseHABridgeHandler;
|
||||
import org.openhab.binding.plugwiseha.internal.handler.PlugwiseHAZoneHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.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.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAHandlerFactory} is responsible for creating things and
|
||||
* thing handlers.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.plugwiseha")
|
||||
public class PlugwiseHAHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
// Constructor
|
||||
|
||||
@Activate
|
||||
public PlugwiseHAHandlerFactory(@Reference final HttpClientFactory httpClientFactory) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* Returns whether the handler is able to create a thing or register a thing
|
||||
* handler for the given type.
|
||||
*
|
||||
* @param thingTypeUID the thing type UID
|
||||
* @return true, if the handler supports the thing type, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return (PlugwiseHABridgeHandler.supportsThingType(thingTypeUID)
|
||||
|| PlugwiseHAZoneHandler.supportsThingType(thingTypeUID))
|
||||
|| PlugwiseHAApplianceHandler.supportsThingType(thingTypeUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a thing for given arguments.
|
||||
*
|
||||
* @param thingTypeUID thing type uid (not null)
|
||||
* @param configuration configuration
|
||||
* @param thingUID thing uid, which can be null
|
||||
* @param bridgeUID bridge uid, which can be null
|
||||
* @return created thing
|
||||
*/
|
||||
@Override
|
||||
public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
|
||||
@Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
|
||||
if (PlugwiseHABridgeHandler.supportsThingType(thingTypeUID)) {
|
||||
return super.createThing(thingTypeUID, configuration, thingUID, null);
|
||||
} else if (PlugwiseHAZoneHandler.supportsThingType(thingTypeUID)) {
|
||||
return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID);
|
||||
} else if (PlugwiseHAApplianceHandler.supportsThingType(thingTypeUID)) {
|
||||
return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"The thing type " + thingTypeUID + " is not supported by the plugwiseha binding.");
|
||||
}
|
||||
|
||||
// Protected and private methods
|
||||
|
||||
/**
|
||||
* Creates a {@link ThingHandler} for the given thing.
|
||||
*
|
||||
* @param thing the thing
|
||||
* @return thing the created handler
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (PlugwiseHABridgeHandler.supportsThingType(thingTypeUID)) {
|
||||
return new PlugwiseHABridgeHandler((Bridge) thing, this.httpClient);
|
||||
} else if (PlugwiseHAZoneHandler.supportsThingType(thingTypeUID)) {
|
||||
return new PlugwiseHAZoneHandler(thing);
|
||||
} else if (PlugwiseHAApplianceHandler.supportsThingType(thingTypeUID)) {
|
||||
return new PlugwiseHAApplianceHandler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHABadRequestException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHABadRequestException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHABadRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHABadRequestException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHABadRequestException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHACommunicationException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHACommunicationException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHACommunicationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHAException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHAException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHAException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAForbiddenException} signals the controller denied a request due to invalid credentials.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAForbiddenException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHAForbiddenException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHAForbiddenException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHAForbiddenException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAInvalidHostException} signals there was a problem with the hostname of the controller.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAInvalidHostException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHAInvalidHostException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHAInvalidHostException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHAInvalidHostException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHANotAuthorizedException} signals the controller denied a request due to invalid credentials.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHANotAuthorizedException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHANotAuthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHANotAuthorizedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHANotAuthorizedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHATimeoutException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHATimeoutException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHATimeoutException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHATimeoutException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHATimeoutException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.exception;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAUnauthorizedException} represents a binding specific {@link Exception}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAUnauthorizedException extends PlugwiseHAException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PlugwiseHAUnauthorizedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PlugwiseHAUnauthorizedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public PlugwiseHAUnauthorizedException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,450 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionality;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityOffsetTemperature;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityRelay;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityThermostat;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliance;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliances;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.DomainObjects;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.GatewayInfo;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Location;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Locations;
|
||||
import org.openhab.binding.plugwiseha.internal.api.xml.PlugwiseHAXStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAController} class provides the interface to the Plugwise
|
||||
* Home Automation API and stores/caches the object model for use by the various
|
||||
* ThingHandlers of this binding.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAController {
|
||||
|
||||
// Private member variables/constants
|
||||
|
||||
private static final int MAX_AGE_MINUTES_REFRESH = 10;
|
||||
private static final int MAX_AGE_MINUTES_FULL_REFRESH = 30;
|
||||
private static final DateTimeFormatter FORMAT = DateTimeFormatter.RFC_1123_DATE_TIME; // default Date format that
|
||||
// will be used in conversion
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHAController.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final PlugwiseHAXStream xStream;
|
||||
private final Transformer domainObjectsTransformer;
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final String username;
|
||||
private final String smileId;
|
||||
|
||||
private @Nullable ZonedDateTime gatewayUpdateDateTime;
|
||||
private @Nullable ZonedDateTime gatewayFullUpdateDateTime;
|
||||
private @Nullable DomainObjects domainObjects;
|
||||
|
||||
public PlugwiseHAController(HttpClient httpClient, String host, int port, String username, String smileId)
|
||||
throws PlugwiseHAException {
|
||||
this.httpClient = httpClient;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.username = username;
|
||||
this.smileId = smileId;
|
||||
|
||||
this.xStream = new PlugwiseHAXStream();
|
||||
|
||||
ClassLoader localClassLoader = getClass().getClassLoader();
|
||||
if (localClassLoader != null) {
|
||||
this.domainObjectsTransformer = PlugwiseHAController
|
||||
.setXSLT(new StreamSource(localClassLoader.getResourceAsStream("domain_objects.xslt")));
|
||||
} else {
|
||||
throw new PlugwiseHAException("PlugwiseHAController.domainObjectsTransformer could not be initialized");
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
public void start(Runnable callback) throws PlugwiseHAException {
|
||||
refresh();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
public void refresh() throws PlugwiseHAException {
|
||||
synchronized (this) {
|
||||
this.getUpdatedDomainObjects();
|
||||
}
|
||||
}
|
||||
|
||||
// Public API methods
|
||||
|
||||
public GatewayInfo getGatewayInfo() throws PlugwiseHAException {
|
||||
return getGatewayInfo(false);
|
||||
}
|
||||
|
||||
public GatewayInfo getGatewayInfo(Boolean forceRefresh) throws PlugwiseHAException {
|
||||
GatewayInfo gatewayInfo = null;
|
||||
DomainObjects localDomainObjects = this.domainObjects;
|
||||
if (localDomainObjects != null) {
|
||||
gatewayInfo = localDomainObjects.getGatewayInfo();
|
||||
}
|
||||
|
||||
if (!forceRefresh && gatewayInfo != null) {
|
||||
this.logger.debug("Found Plugwise Home Automation gateway");
|
||||
return gatewayInfo;
|
||||
} else {
|
||||
PlugwiseHAControllerRequest<DomainObjects> request;
|
||||
|
||||
request = newRequest(DomainObjects.class, this.domainObjectsTransformer);
|
||||
|
||||
request.setPath("/core/domain_objects");
|
||||
request.addPathParameter("class", "Gateway");
|
||||
|
||||
DomainObjects domainObjects = executeRequest(request);
|
||||
this.gatewayUpdateDateTime = ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
|
||||
return mergeDomainObjects(domainObjects).getGatewayInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public Appliances getAppliances(Boolean forceRefresh) throws PlugwiseHAException {
|
||||
Appliances appliances = null;
|
||||
DomainObjects localDomainObjects = this.domainObjects;
|
||||
if (localDomainObjects != null) {
|
||||
appliances = localDomainObjects.getAppliances();
|
||||
}
|
||||
|
||||
if (!forceRefresh && appliances != null) {
|
||||
return appliances;
|
||||
} else {
|
||||
PlugwiseHAControllerRequest<DomainObjects> request;
|
||||
|
||||
request = newRequest(DomainObjects.class, this.domainObjectsTransformer);
|
||||
|
||||
request.setPath("/core/domain_objects");
|
||||
request.addPathParameter("class", "Appliance");
|
||||
|
||||
DomainObjects domainObjects = executeRequest(request);
|
||||
this.gatewayUpdateDateTime = ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
int size = 0;
|
||||
if (!(domainObjects.getAppliances() == null)) {
|
||||
size = domainObjects.getAppliances().size();
|
||||
}
|
||||
this.logger.debug("Found {} Plugwise Home Automation appliance(s)", size);
|
||||
|
||||
return mergeDomainObjects(domainObjects).getAppliances();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Appliance getAppliance(String id, Boolean forceRefresh) throws PlugwiseHAException {
|
||||
Appliances appliances = this.getAppliances(forceRefresh);
|
||||
if (!appliances.containsKey(id)) {
|
||||
this.logger.debug("Plugwise Home Automation Appliance with id {} is not known", id);
|
||||
return null;
|
||||
} else {
|
||||
return appliances.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
public Locations getLocations(Boolean forceRefresh) throws PlugwiseHAException {
|
||||
Locations locations = null;
|
||||
DomainObjects localDomainObjects = this.domainObjects;
|
||||
if (localDomainObjects != null) {
|
||||
locations = localDomainObjects.getLocations();
|
||||
}
|
||||
|
||||
if (!forceRefresh && locations != null) {
|
||||
return locations;
|
||||
} else {
|
||||
PlugwiseHAControllerRequest<DomainObjects> request;
|
||||
|
||||
request = newRequest(DomainObjects.class, this.domainObjectsTransformer);
|
||||
|
||||
request.setPath("/core/domain_objects");
|
||||
request.addPathParameter("class", "Location");
|
||||
|
||||
DomainObjects domainObjects = executeRequest(request);
|
||||
this.gatewayUpdateDateTime = ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
int size = 0;
|
||||
if (!(domainObjects.getLocations() == null)) {
|
||||
size = domainObjects.getLocations().size();
|
||||
}
|
||||
this.logger.debug("Found {} Plugwise Home Automation Zone(s)", size);
|
||||
return mergeDomainObjects(domainObjects).getLocations();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable Location getLocation(String id, Boolean forceRefresh) throws PlugwiseHAException {
|
||||
Locations locations = this.getLocations(forceRefresh);
|
||||
|
||||
if (!locations.containsKey(id)) {
|
||||
this.logger.debug("Plugwise Home Automation Zone with {} is not known", id);
|
||||
return null;
|
||||
} else {
|
||||
return locations.get(id);
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable DomainObjects getDomainObjects() throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<DomainObjects> request;
|
||||
|
||||
request = newRequest(DomainObjects.class, this.domainObjectsTransformer);
|
||||
|
||||
request.setPath("/core/domain_objects");
|
||||
request.addPathParameter("@locale", "en-US");
|
||||
|
||||
DomainObjects domainObjects = executeRequest(request);
|
||||
this.gatewayUpdateDateTime = ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
this.gatewayFullUpdateDateTime = this.gatewayUpdateDateTime;
|
||||
|
||||
return mergeDomainObjects(domainObjects);
|
||||
}
|
||||
|
||||
public @Nullable DomainObjects getUpdatedDomainObjects() throws PlugwiseHAException {
|
||||
ZonedDateTime localGatewayUpdateDateTime = this.gatewayUpdateDateTime;
|
||||
ZonedDateTime localGatewayFullUpdateDateTime = this.gatewayFullUpdateDateTime;
|
||||
if (localGatewayUpdateDateTime == null
|
||||
|| localGatewayUpdateDateTime.isBefore(ZonedDateTime.now().minusMinutes(MAX_AGE_MINUTES_REFRESH))) {
|
||||
return getDomainObjects();
|
||||
} else if (localGatewayFullUpdateDateTime == null || localGatewayFullUpdateDateTime
|
||||
.isBefore(ZonedDateTime.now().minusMinutes(MAX_AGE_MINUTES_FULL_REFRESH))) {
|
||||
return getDomainObjects();
|
||||
} else {
|
||||
return getUpdatedDomainObjects(localGatewayUpdateDateTime);
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable DomainObjects getUpdatedDomainObjects(ZonedDateTime since) throws PlugwiseHAException {
|
||||
return getUpdatedDomainObjects(since.toEpochSecond());
|
||||
}
|
||||
|
||||
public @Nullable DomainObjects getUpdatedDomainObjects(Long since) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<DomainObjects> request;
|
||||
|
||||
request = newRequest(DomainObjects.class, this.domainObjectsTransformer);
|
||||
|
||||
request.setPath("/core/domain_objects");
|
||||
request.addPathFilter("modified_date", "ge", since);
|
||||
request.addPathFilter("deleted_date", "ge", "0");
|
||||
request.addPathParameter("@memberModifiedDate", since);
|
||||
request.addPathParameter("@locale", "en-US");
|
||||
|
||||
DomainObjects domainObjects = executeRequest(request);
|
||||
this.gatewayUpdateDateTime = ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
|
||||
return mergeDomainObjects(domainObjects);
|
||||
}
|
||||
|
||||
public void setLocationThermostat(Location location, Double temperature) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
Optional<ActuatorFunctionality> thermostat = location.getActuatorFunctionalities().getFunctionalityThermostat();
|
||||
|
||||
if (thermostat.isPresent()) {
|
||||
request.setPath("/core/locations");
|
||||
|
||||
request.addPathParameter("id", String.format("%s/thermostat", location.getId()));
|
||||
request.addPathParameter("id", String.format("%s", thermostat.get().getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityThermostat(temperature));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public void setThermostat(Appliance appliance, Double temperature) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
Optional<ActuatorFunctionality> thermostat = appliance.getActuatorFunctionalities()
|
||||
.getFunctionalityThermostat();
|
||||
|
||||
if (thermostat.isPresent()) {
|
||||
request.setPath("/core/appliances");
|
||||
|
||||
request.addPathParameter("id", String.format("%s/thermostat", appliance.getId()));
|
||||
request.addPathParameter("id", String.format("%s", thermostat.get().getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityThermostat(temperature));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOffsetTemperature(Appliance appliance, Double temperature) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
Optional<ActuatorFunctionality> offsetTemperatureFunctionality = appliance.getActuatorFunctionalities()
|
||||
.getFunctionalityOffsetTemperature();
|
||||
|
||||
if (offsetTemperatureFunctionality.isPresent()) {
|
||||
request.setPath("/core/appliances");
|
||||
|
||||
request.addPathParameter("id", String.format("%s/offset", appliance.getId()));
|
||||
request.addPathParameter("id", String.format("%s", offsetTemperatureFunctionality.get().getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityOffsetTemperature(temperature));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
public void switchRelay(Appliance appliance, String state) throws PlugwiseHAException {
|
||||
List<String> allowStates = Arrays.asList("on", "off");
|
||||
if (allowStates.contains(state.toLowerCase())) {
|
||||
if (state.toLowerCase().equals("on")) {
|
||||
switchRelayOn(appliance);
|
||||
} else {
|
||||
switchRelayOff(appliance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPreHeating(Location location, Boolean state) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
Optional<ActuatorFunctionality> thermostat = location.getActuatorFunctionalities().getFunctionalityThermostat();
|
||||
|
||||
request.setPath("/core/locations");
|
||||
request.addPathParameter("id", String.format("%s/thermostat", location.getId()));
|
||||
request.addPathParameter("id", String.format("%s", thermostat.get().getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityThermostat(state));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
|
||||
public void switchRelayOn(Appliance appliance) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
|
||||
request.setPath("/core/appliances");
|
||||
request.addPathParameter("id", String.format("%s/relay", appliance.getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityRelay("on"));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
|
||||
public void switchRelayOff(Appliance appliance) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
|
||||
request.setPath("/core/appliances");
|
||||
request.addPathParameter("id", String.format("%s/relay", appliance.getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityRelay("off"));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
|
||||
public void switchRelayLock(Appliance appliance, String state) throws PlugwiseHAException {
|
||||
List<String> allowStates = Arrays.asList("on", "off");
|
||||
if (allowStates.contains(state.toLowerCase())) {
|
||||
if (state.toLowerCase().equals("on")) {
|
||||
switchRelayLockOn(appliance);
|
||||
} else {
|
||||
switchRelayLockOff(appliance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void switchRelayLockOff(Appliance appliance) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
|
||||
request.setPath("/core/appliances");
|
||||
request.addPathParameter("id", String.format("%s/relay", appliance.getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityRelay(null, false));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
|
||||
public void switchRelayLockOn(Appliance appliance) throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request = newRequest(Void.class);
|
||||
|
||||
request.setPath("/core/appliances");
|
||||
request.addPathParameter("id", String.format("%s/relay", appliance.getId()));
|
||||
request.setBodyParameter(new ActuatorFunctionalityRelay(null, true));
|
||||
|
||||
executeRequest(request);
|
||||
}
|
||||
|
||||
public ZonedDateTime ping() throws PlugwiseHAException {
|
||||
PlugwiseHAControllerRequest<Void> request;
|
||||
|
||||
request = newRequest(Void.class, null);
|
||||
|
||||
request.setPath("/cache/gateways");
|
||||
request.addPathParameter("ping");
|
||||
|
||||
executeRequest(request);
|
||||
|
||||
return ZonedDateTime.parse(request.getServerDateTime(), PlugwiseHAController.FORMAT);
|
||||
}
|
||||
|
||||
// Protected and private methods
|
||||
|
||||
private static Transformer setXSLT(StreamSource xsltSource) throws PlugwiseHAException {
|
||||
try {
|
||||
return TransformerFactory.newInstance().newTransformer(xsltSource);
|
||||
} catch (TransformerConfigurationException e) {
|
||||
throw new PlugwiseHAException("Could not create XML transformer", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> PlugwiseHAControllerRequest<T> newRequest(Class<T> responseType, @Nullable Transformer transformer) {
|
||||
return new PlugwiseHAControllerRequest<T>(responseType, this.xStream, transformer, this.httpClient, this.host,
|
||||
this.port, this.username, this.smileId);
|
||||
}
|
||||
|
||||
private <T> PlugwiseHAControllerRequest<T> newRequest(Class<T> responseType) {
|
||||
return new PlugwiseHAControllerRequest<T>(responseType, this.xStream, null, this.httpClient, this.host,
|
||||
this.port, this.username, this.smileId);
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private <T> T executeRequest(PlugwiseHAControllerRequest<T> request) throws PlugwiseHAException {
|
||||
T result;
|
||||
result = request.execute();
|
||||
return result;
|
||||
}
|
||||
|
||||
private DomainObjects mergeDomainObjects(@Nullable DomainObjects updatedDomainObjects) {
|
||||
DomainObjects localDomainObjects = this.domainObjects;
|
||||
if (localDomainObjects == null && updatedDomainObjects != null) {
|
||||
return updatedDomainObjects;
|
||||
} else if (localDomainObjects != null && updatedDomainObjects == null) {
|
||||
return localDomainObjects;
|
||||
} else if (localDomainObjects != null && updatedDomainObjects != null) {
|
||||
Appliances appliances = updatedDomainObjects.getAppliances();
|
||||
Locations locations = updatedDomainObjects.getLocations();
|
||||
|
||||
if (appliances != null) {
|
||||
localDomainObjects.mergeAppliances(appliances);
|
||||
}
|
||||
|
||||
if (locations != null) {
|
||||
localDomainObjects.mergeLocations(locations);
|
||||
}
|
||||
return localDomainObjects;
|
||||
} else {
|
||||
return new DomainObjects();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,286 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHABadRequestException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAForbiddenException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHATimeoutException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAUnauthorizedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAControllerRequest} class is a utility class to create
|
||||
* API requests to the Plugwise Home Automation controller and to deserialize
|
||||
* incoming XML into the appropriate model objects to be used by the {@link
|
||||
* PlugwiseHAController}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAControllerRequest<T> {
|
||||
|
||||
private static final String CONTENT_TYPE_TEXT_XML = MimeTypes.Type.TEXT_XML_8859_1.toString();
|
||||
private static final long TIMEOUT_SECONDS = 5;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHAControllerRequest.class);
|
||||
private final XStream xStream;
|
||||
private final HttpClient httpClient;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final Class<T> resultType;
|
||||
private final @Nullable Transformer transformer;
|
||||
|
||||
private Map<String, String> headers = new HashMap<>();
|
||||
private Map<String, String> queryParameters = new HashMap<>();
|
||||
private @Nullable Object bodyParameter;
|
||||
private String serverDateTime = "";
|
||||
private String path = "/";
|
||||
|
||||
// Constructor
|
||||
|
||||
<X extends XStream> PlugwiseHAControllerRequest(Class<T> resultType, X xStream, @Nullable Transformer transformer,
|
||||
HttpClient httpClient, String host, int port, String username, String password) {
|
||||
this.resultType = resultType;
|
||||
this.xStream = xStream;
|
||||
this.transformer = transformer;
|
||||
this.httpClient = httpClient;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
|
||||
setHeader(HttpHeader.ACCEPT.toString(), CONTENT_TYPE_TEXT_XML);
|
||||
|
||||
// Create Basic Auth header if username and password are supplied
|
||||
if (!username.isBlank() && !password.isBlank()) {
|
||||
setHeader(HttpHeader.AUTHORIZATION.toString(), "Basic " + Base64.getEncoder()
|
||||
.encodeToString(String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
public void setPath(String path) {
|
||||
this.setPath(path, (HashMap<String, String>) null);
|
||||
}
|
||||
|
||||
public void setPath(String path, @Nullable HashMap<String, String> pathParameters) {
|
||||
this.path = path;
|
||||
|
||||
if (pathParameters != null) {
|
||||
this.path += pathParameters.entrySet().stream().map(Object::toString).collect(Collectors.joining(";"));
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeader(String key, Object value) {
|
||||
this.headers.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void addPathParameter(String key) {
|
||||
this.path += String.format(";%s", key);
|
||||
}
|
||||
|
||||
public void addPathParameter(String key, Object value) {
|
||||
this.path += String.format(";%s=%s", key, value);
|
||||
}
|
||||
|
||||
public void addPathFilter(String key, String operator, Object value) {
|
||||
this.path += String.format(";%s:%s:%s", key, operator, value);
|
||||
}
|
||||
|
||||
public void setQueryParameter(String key, Object value) {
|
||||
this.queryParameters.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void setBodyParameter(Object body) {
|
||||
this.bodyParameter = body;
|
||||
}
|
||||
|
||||
public String getServerDateTime() {
|
||||
return this.serverDateTime;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @Nullable T execute() throws PlugwiseHAException {
|
||||
T result;
|
||||
String xml = getContent();
|
||||
|
||||
if (String.class.equals(resultType)) {
|
||||
if (this.transformer != null) {
|
||||
result = (T) this.transformXML(xml);
|
||||
} else {
|
||||
result = (T) xml;
|
||||
}
|
||||
} else if (!Void.class.equals(resultType)) {
|
||||
if (this.transformer != null) {
|
||||
result = (T) this.xStream.fromXML(this.transformXML(xml));
|
||||
} else {
|
||||
result = (T) this.xStream.fromXML(xml);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Protected and private methods
|
||||
|
||||
private String transformXML(String xml) throws PlugwiseHAException {
|
||||
StringReader input = new StringReader(xml);
|
||||
StringWriter output = new StringWriter();
|
||||
Transformer localTransformer = this.transformer;
|
||||
if (localTransformer != null) {
|
||||
try {
|
||||
localTransformer.transform(new StreamSource(input), new StreamResult(output));
|
||||
} catch (TransformerException e) {
|
||||
logger.debug("Could not apply XML stylesheet", e);
|
||||
throw new PlugwiseHAException("Could not apply XML stylesheet", e);
|
||||
}
|
||||
} else {
|
||||
throw new PlugwiseHAException("Could not transform XML stylesheet, the transformer is null");
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
private String getContent() throws PlugwiseHAException {
|
||||
String content;
|
||||
ContentResponse response;
|
||||
|
||||
try {
|
||||
response = getContentResponse();
|
||||
} catch (PlugwiseHATimeoutException e) {
|
||||
// Retry
|
||||
response = getContentResponse();
|
||||
}
|
||||
|
||||
int status = response.getStatus();
|
||||
switch (status) {
|
||||
case HttpStatus.OK_200:
|
||||
case HttpStatus.ACCEPTED_202:
|
||||
content = response.getContentAsString();
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("<< {} {} \n{}", status, HttpStatus.getMessage(status), content);
|
||||
}
|
||||
break;
|
||||
case HttpStatus.BAD_REQUEST_400:
|
||||
throw new PlugwiseHABadRequestException("Bad request");
|
||||
case HttpStatus.UNAUTHORIZED_401:
|
||||
throw new PlugwiseHAUnauthorizedException("Unauthorized");
|
||||
case HttpStatus.FORBIDDEN_403:
|
||||
throw new PlugwiseHAForbiddenException("Forbidden");
|
||||
default:
|
||||
throw new PlugwiseHAException("Unknown HTTP status code " + status + " returned by the controller");
|
||||
}
|
||||
|
||||
this.serverDateTime = response.getHeaders().get("Date");
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private ContentResponse getContentResponse() throws PlugwiseHAException {
|
||||
Request request = newRequest();
|
||||
ContentResponse response;
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(">> {} {}", request.getMethod(), request.getURI());
|
||||
}
|
||||
|
||||
try {
|
||||
response = request.send();
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
throw new PlugwiseHATimeoutException(e);
|
||||
} catch (ExecutionException e) {
|
||||
// Unwrap the cause and try to cleanly handle it
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof UnknownHostException) {
|
||||
// Invalid hostname
|
||||
throw new PlugwiseHAException(cause);
|
||||
} else if (cause instanceof ConnectException) {
|
||||
// Cannot connect
|
||||
throw new PlugwiseHAException(cause);
|
||||
} else if (cause instanceof SocketTimeoutException) {
|
||||
throw new PlugwiseHATimeoutException(cause);
|
||||
} else if (cause == null) {
|
||||
// Unable to unwrap
|
||||
throw new PlugwiseHAException(e);
|
||||
} else {
|
||||
// Catch all
|
||||
throw new PlugwiseHAException(cause);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private Request newRequest() {
|
||||
HttpMethod method = bodyParameter == null ? HttpMethod.GET : HttpMethod.PUT;
|
||||
HttpURI uri = new HttpURI(HttpScheme.HTTP.asString(), this.host, this.port, this.path);
|
||||
Request request = httpClient.newRequest(uri.toString()).timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.method(method);
|
||||
|
||||
for (Entry<String, String> entry : this.headers.entrySet()) {
|
||||
request.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
for (Entry<String, String> entry : this.queryParameters.entrySet()) {
|
||||
request.param(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
if (this.bodyParameter != null) {
|
||||
String xmlBody = getRequestBodyAsXml();
|
||||
ContentProvider content = new StringContentProvider(CONTENT_TYPE_TEXT_XML, xmlBody, StandardCharsets.UTF_8);
|
||||
request = request.content(content);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private String getRequestBodyAsXml() {
|
||||
return this.xStream.toXML(this.bodyParameter);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAModel} interface describes common
|
||||
* methods that need to be implemented by any object model class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public interface PlugwiseHAModel {
|
||||
|
||||
public abstract boolean isBatteryOperated();
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.converter;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
|
||||
|
||||
/**
|
||||
* The {@link DateTimeConverter} provides a SingleValueConverter for use by XStream when converting
|
||||
* XML documents with a zoned date/time field.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class DateTimeConverter extends AbstractSingleValueConverter {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DateTimeConverter.class);
|
||||
private static final DateTimeFormatter FORMAT = DateTimeFormatter.ISO_OFFSET_DATE_TIME; // default Date format that
|
||||
|
||||
@Override
|
||||
public boolean canConvert(@Nullable @SuppressWarnings("rawtypes") Class type) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
return ZonedDateTime.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ZonedDateTime fromString(@Nullable String str) {
|
||||
if (str == null || str.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ZonedDateTime dateTime = ZonedDateTime.parse(str, DateTimeConverter.FORMAT);
|
||||
return dateTime;
|
||||
} catch (DateTimeParseException e) {
|
||||
logger.debug("Invalid datetime format in {}", str);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString(ZonedDateTime dateTime) {
|
||||
return dateTime.format(DateTimeConverter.FORMAT);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The {@link ActuatorFunctionalities} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation controller
|
||||
* for the collection of actuator functionalities. (e.g. 'offset', 'relay', et
|
||||
* cetera). It extends the {@link CustomCollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
|
||||
public class ActuatorFunctionalities extends PlugwiseHACollection<ActuatorFunctionality> {
|
||||
|
||||
private static final String THERMOSTAT_FUNCTIONALITY = "thermostat";
|
||||
private static final String OFFSETTEMPERATURE_FUNCTIONALITY = "temperature_offset";
|
||||
private static final String RELAY_FUNCTIONALITY = "relay";
|
||||
|
||||
public Optional<Boolean> getRelayLockState() {
|
||||
return this.getFunctionalityRelay().flatMap(ActuatorFunctionality::getRelayLockState)
|
||||
.map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
public Optional<Boolean> getPreHeatState() {
|
||||
return this.getFunctionalityThermostat().flatMap(ActuatorFunctionality::getPreHeatState)
|
||||
.map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
public Optional<ActuatorFunctionality> getFunctionalityThermostat() {
|
||||
return Optional.ofNullable(this.get(THERMOSTAT_FUNCTIONALITY));
|
||||
}
|
||||
|
||||
public Optional<ActuatorFunctionality> getFunctionalityOffsetTemperature() {
|
||||
return Optional.ofNullable(this.get(OFFSETTEMPERATURE_FUNCTIONALITY));
|
||||
}
|
||||
|
||||
public Optional<ActuatorFunctionality> getFunctionalityRelay() {
|
||||
return Optional.ofNullable(this.get(RELAY_FUNCTIONALITY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, ActuatorFunctionality> actuatorFunctionalities) {
|
||||
if (actuatorFunctionalities != null) {
|
||||
for (ActuatorFunctionality actuatorFunctionality : actuatorFunctionalities.values()) {
|
||||
String type = actuatorFunctionality.getType();
|
||||
ActuatorFunctionality originalActuatorFunctionality = this.get(type);
|
||||
|
||||
Boolean originalIsOlder = false;
|
||||
if (originalActuatorFunctionality != null) {
|
||||
originalIsOlder = originalActuatorFunctionality.isOlderThan(actuatorFunctionality);
|
||||
}
|
||||
|
||||
if (originalActuatorFunctionality == null || originalIsOlder) {
|
||||
this.put(type, actuatorFunctionality);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* The {@link ActuatorFunctionality} class is an object model class that mirrors
|
||||
* the XML structure provided by the Plugwise Home Automation controller for the
|
||||
* any actuator functionality. It implements the {@link PlugwiseComparableDate}
|
||||
* interface and extends the abstract class {@link PlugwiseBaseModel}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("actuator_functionality")
|
||||
public class ActuatorFunctionality extends PlugwiseBaseModel implements PlugwiseComparableDate<ActuatorFunctionality> {
|
||||
|
||||
private String type;
|
||||
private String duration;
|
||||
private String setpoint;
|
||||
private String resolution;
|
||||
private String lock;
|
||||
|
||||
@XStreamAlias("preheating_allowed")
|
||||
private String preHeat;
|
||||
|
||||
@XStreamAlias("lower_bound")
|
||||
private String lowerBound;
|
||||
|
||||
@XStreamAlias("upper_bound")
|
||||
private String upperBound;
|
||||
|
||||
@XStreamAlias("updated_date")
|
||||
private ZonedDateTime updatedDate;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getSetpoint() {
|
||||
return setpoint;
|
||||
}
|
||||
|
||||
public String getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public String getLowerBound() {
|
||||
return lowerBound;
|
||||
}
|
||||
|
||||
public String getUpperBound() {
|
||||
return upperBound;
|
||||
}
|
||||
|
||||
public ZonedDateTime getUpdatedDate() {
|
||||
return updatedDate;
|
||||
}
|
||||
|
||||
public Optional<String> getPreHeatState() {
|
||||
return Optional.ofNullable(preHeat);
|
||||
}
|
||||
|
||||
public Optional<String> getRelayLockState() {
|
||||
return Optional.ofNullable(lock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareDateWith(ActuatorFunctionality compareTo) {
|
||||
if (compareTo == null) {
|
||||
return -1;
|
||||
}
|
||||
ZonedDateTime compareToDate = compareTo.getModifiedDate();
|
||||
ZonedDateTime compareFromDate = this.getModifiedDate();
|
||||
if (compareFromDate == null) {
|
||||
return -1;
|
||||
} else if (compareToDate == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareFromDate.compareTo(compareToDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewerThan(ActuatorFunctionality hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOlderThan(ActuatorFunctionality hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) < 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("offset_functionality")
|
||||
public class ActuatorFunctionalityOffsetTemperature extends ActuatorFunctionality {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Double offset;
|
||||
|
||||
public ActuatorFunctionalityOffsetTemperature(Double temperature) {
|
||||
this.offset = temperature;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("relay_functionality")
|
||||
public class ActuatorFunctionalityRelay extends ActuatorFunctionality {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String state;
|
||||
@SuppressWarnings("unused")
|
||||
private Boolean lock;
|
||||
|
||||
public ActuatorFunctionalityRelay(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ActuatorFunctionalityRelay(String state, Boolean lock) {
|
||||
this.state = state;
|
||||
this.lock = lock;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("thermostat_functionality")
|
||||
public class ActuatorFunctionalityThermostat extends ActuatorFunctionality {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Double setpoint;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@XStreamAlias("preheating_allowed")
|
||||
private Boolean preheatingAllowed;
|
||||
|
||||
public ActuatorFunctionalityThermostat(Double temperature) {
|
||||
this.setpoint = temperature;
|
||||
}
|
||||
|
||||
public ActuatorFunctionalityThermostat(Boolean preheatingAllowed) {
|
||||
this.preheatingAllowed = preheatingAllowed;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("threshold_functionality")
|
||||
public class ActuatorFunctionalityThreshold extends ActuatorFunctionality {
|
||||
|
||||
public ActuatorFunctionalityThreshold() {
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("timer_functionality")
|
||||
public class ActuatorFunctionalityTimer extends ActuatorFunctionality {
|
||||
|
||||
public ActuatorFunctionalityTimer() {
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("toggle_functionality")
|
||||
public class ActuatorFunctionalityToggle extends ActuatorFunctionality {
|
||||
|
||||
public ActuatorFunctionalityToggle() {
|
||||
}
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamImplicit;
|
||||
|
||||
/**
|
||||
* The {@link Appliance} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for a Plugwise appliance.
|
||||
* It implements the {@link PlugwiseComparableDate} interface and
|
||||
* extends the abstract class {@link PlugwiseBaseModel}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("appliance")
|
||||
public class Appliance extends PlugwiseBaseModel implements PlugwiseComparableDate<Appliance> {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private String location;
|
||||
|
||||
@XStreamAlias("module")
|
||||
private Module module;
|
||||
|
||||
@XStreamAlias("zig_bee_node")
|
||||
private ZigBeeNode zigbeeNode;
|
||||
|
||||
@XStreamImplicit(itemFieldName = "point_log", keyFieldName = "type")
|
||||
private Logs pointLogs;
|
||||
|
||||
@XStreamImplicit(itemFieldName = "actuator_functionality", keyFieldName = "type")
|
||||
private ActuatorFunctionalities actuatorFunctionalities;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public ZigBeeNode getZigbeeNode() {
|
||||
if (zigbeeNode == null) {
|
||||
zigbeeNode = new ZigBeeNode();
|
||||
}
|
||||
return zigbeeNode;
|
||||
}
|
||||
|
||||
public Module getModule() {
|
||||
if (module == null) {
|
||||
module = new Module();
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
public Logs getPointLogs() {
|
||||
if (pointLogs == null) {
|
||||
pointLogs = new Logs();
|
||||
}
|
||||
return pointLogs;
|
||||
}
|
||||
|
||||
public ActuatorFunctionalities getActuatorFunctionalities() {
|
||||
if (actuatorFunctionalities == null) {
|
||||
actuatorFunctionalities = new ActuatorFunctionalities();
|
||||
}
|
||||
return actuatorFunctionalities;
|
||||
}
|
||||
|
||||
public Optional<Double> getTemperature() {
|
||||
return this.pointLogs.getTemperature();
|
||||
}
|
||||
|
||||
public Optional<String> getTemperatureUnit() {
|
||||
return this.pointLogs.getTemperatureUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getSetpointTemperature() {
|
||||
return this.pointLogs.getThermostatTemperature();
|
||||
}
|
||||
|
||||
public Optional<String> getSetpointTemperatureUnit() {
|
||||
return this.pointLogs.getThermostatTemperatureUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getOffsetTemperature() {
|
||||
return this.pointLogs.getOffsetTemperature();
|
||||
}
|
||||
|
||||
public Optional<String> getOffsetTemperatureUnit() {
|
||||
return this.pointLogs.getOffsetTemperatureUnit();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getRelayState() {
|
||||
return this.pointLogs.getRelayState();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getRelayLockState() {
|
||||
return this.actuatorFunctionalities.getRelayLockState();
|
||||
}
|
||||
|
||||
public Optional<Double> getBatteryLevel() {
|
||||
return this.pointLogs.getBatteryLevel();
|
||||
}
|
||||
|
||||
public Optional<Double> getPowerUsage() {
|
||||
return this.pointLogs.getPowerUsage();
|
||||
}
|
||||
|
||||
public Optional<Double> getValvePosition() {
|
||||
return this.pointLogs.getValvePosition();
|
||||
}
|
||||
|
||||
public Optional<Double> getWaterPressure() {
|
||||
return this.pointLogs.getWaterPressure();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getCHState() {
|
||||
return this.pointLogs.getCHState();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getCoolingState() {
|
||||
return this.pointLogs.getCoolingState();
|
||||
}
|
||||
|
||||
public Optional<Double> getIntendedBoilerTemp() {
|
||||
return this.pointLogs.getIntendedBoilerTemp();
|
||||
}
|
||||
|
||||
public Optional<String> getIntendedBoilerTempUnit() {
|
||||
return this.pointLogs.getIntendedBoilerTempUnit();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getFlameState() {
|
||||
return this.pointLogs.getFlameState();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getIntendedHeatingState() {
|
||||
return this.pointLogs.getIntendedHeatingState();
|
||||
}
|
||||
|
||||
public Optional<Double> getModulationLevel() {
|
||||
return this.pointLogs.getModulationLevel();
|
||||
}
|
||||
|
||||
public Optional<Double> getOTAppFaultCode() {
|
||||
return this.pointLogs.getOTAppFaultCode();
|
||||
}
|
||||
|
||||
public Optional<Double> getDHWTemp() {
|
||||
return this.pointLogs.getDHWTemp();
|
||||
}
|
||||
|
||||
public Optional<String> getDHWTempUnit() {
|
||||
return this.pointLogs.getDHWTempUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getOTOEMFaultcode() {
|
||||
return this.pointLogs.getOTOEMFaultcode();
|
||||
}
|
||||
|
||||
public Optional<Double> getBoilerTemp() {
|
||||
return this.pointLogs.getBoilerTemp();
|
||||
}
|
||||
|
||||
public Optional<String> getBoilerTempUnit() {
|
||||
return this.pointLogs.getBoilerTempUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getDHTSetpoint() {
|
||||
return this.pointLogs.getDHTSetpoint();
|
||||
}
|
||||
|
||||
public Optional<String> getDHTSetpointUnit() {
|
||||
return this.pointLogs.getDHTSetpointUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getMaxBoilerTemp() {
|
||||
return this.pointLogs.getMaxBoilerTemp();
|
||||
}
|
||||
|
||||
public Optional<String> getMaxBoilerTempUnit() {
|
||||
return this.pointLogs.getMaxBoilerTempUnit();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getDHWComfortMode() {
|
||||
return this.pointLogs.getDHWComfortMode();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getDHWState() {
|
||||
return this.pointLogs.getDHWState();
|
||||
}
|
||||
|
||||
public boolean isZigbeeDevice() {
|
||||
return (this.zigbeeNode instanceof ZigBeeNode);
|
||||
}
|
||||
|
||||
public boolean isBatteryOperated() {
|
||||
if (this.zigbeeNode instanceof ZigBeeNode) {
|
||||
return this.zigbeeNode.getPowerSource().equals("battery") && this.getBatteryLevel().isPresent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareDateWith(Appliance compareTo) {
|
||||
if (compareTo == null) {
|
||||
return -1;
|
||||
}
|
||||
ZonedDateTime compareToDate = compareTo.getModifiedDate();
|
||||
ZonedDateTime compareFromDate = this.getModifiedDate();
|
||||
if (compareFromDate == null) {
|
||||
return -1;
|
||||
} else if (compareToDate == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareFromDate.compareTo(compareToDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewerThan(Appliance hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOlderThan(Appliance hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) < 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link Appliances} class is an object model class that mirrors the XML
|
||||
* structure provided by the Plugwise Home Automation controller for the
|
||||
* collection of appliances. It extends the {@link PlugwiseHACollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public class Appliances extends PlugwiseHACollection<Appliance> {
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, Appliance> appliancesToMerge) {
|
||||
if (appliancesToMerge != null) {
|
||||
for (Appliance applianceToMerge : appliancesToMerge.values()) {
|
||||
String id = applianceToMerge.getId();
|
||||
Appliance originalAppliance = this.get(id);
|
||||
|
||||
Boolean originalApplianceIsOlder = false;
|
||||
if (originalAppliance != null) {
|
||||
originalApplianceIsOlder = originalAppliance.isOlderThan(applianceToMerge);
|
||||
}
|
||||
|
||||
if (originalAppliance != null && originalApplianceIsOlder) {
|
||||
Logs updatedPointLogs = applianceToMerge.getPointLogs();
|
||||
if (updatedPointLogs != null) {
|
||||
updatedPointLogs.merge(originalAppliance.getPointLogs());
|
||||
}
|
||||
|
||||
ActuatorFunctionalities updatedActuatorFunctionalities = applianceToMerge
|
||||
.getActuatorFunctionalities();
|
||||
if (updatedActuatorFunctionalities != null) {
|
||||
updatedActuatorFunctionalities.merge(originalAppliance.getActuatorFunctionalities());
|
||||
}
|
||||
|
||||
this.put(id, applianceToMerge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamImplicit;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("domain_objects")
|
||||
public class DomainObjects {
|
||||
|
||||
@XStreamAlias("gateway")
|
||||
private GatewayInfo gatewayInfo;
|
||||
|
||||
@XStreamImplicit(itemFieldName = "appliance", keyFieldName = "id")
|
||||
private Appliances appliances = new Appliances();
|
||||
|
||||
@XStreamImplicit(itemFieldName = "location", keyFieldName = "id")
|
||||
private Locations locations = new Locations();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@XStreamImplicit(itemFieldName = "module", keyFieldName = "id")
|
||||
private Modules modules = new Modules();
|
||||
|
||||
public GatewayInfo getGatewayInfo() {
|
||||
return gatewayInfo;
|
||||
}
|
||||
|
||||
public Appliances getAppliances() {
|
||||
return appliances;
|
||||
}
|
||||
|
||||
public Locations getLocations() {
|
||||
return locations;
|
||||
}
|
||||
|
||||
public Appliances mergeAppliances(Appliances updatedAppliances) {
|
||||
if (updatedAppliances != null) {
|
||||
this.appliances.merge(updatedAppliances);
|
||||
}
|
||||
|
||||
return this.appliances;
|
||||
}
|
||||
|
||||
public Locations mergeLocations(Locations updatedLocations) {
|
||||
if (updatedLocations != null) {
|
||||
this.locations.merge(updatedLocations);
|
||||
}
|
||||
|
||||
return this.locations;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("gateway_environment")
|
||||
@SuppressWarnings("unused")
|
||||
public class GatewayEnvironment extends PlugwiseBaseModel {
|
||||
private String city;
|
||||
private String country;
|
||||
private String currency;
|
||||
private String latitude;
|
||||
private String longitude;
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("gateway")
|
||||
public class GatewayInfo extends PlugwiseBaseModel {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String hostname;
|
||||
private String timezone;
|
||||
private ZonedDateTime time;
|
||||
|
||||
@XStreamAlias("gateway_environment")
|
||||
private GatewayEnvironment gatewayEnvironment;
|
||||
|
||||
@XStreamAlias("vendor_name")
|
||||
private String vendorName;
|
||||
|
||||
@XStreamAlias("vendor_model")
|
||||
private String vendorModel;
|
||||
|
||||
@XStreamAlias("hardware_version")
|
||||
private String hardwareVersion;
|
||||
|
||||
@XStreamAlias("firmware_version")
|
||||
private String firmwareVersion;
|
||||
|
||||
@XStreamAlias("mac_address")
|
||||
private String macAddress;
|
||||
|
||||
@XStreamAlias("lan_ip")
|
||||
private String lanIp;
|
||||
|
||||
@XStreamAlias("wifi_ip")
|
||||
private String wifiIp;
|
||||
|
||||
@XStreamAlias("last_reset_date")
|
||||
private ZonedDateTime lastResetDate;
|
||||
|
||||
@XStreamAlias("last_boot_date")
|
||||
private ZonedDateTime lastBootDate;
|
||||
|
||||
public ZonedDateTime getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public String getTimezone() {
|
||||
return timezone;
|
||||
}
|
||||
|
||||
public GatewayEnvironment getGatewayEnvironment() {
|
||||
return gatewayEnvironment;
|
||||
}
|
||||
|
||||
public String getVendorName() {
|
||||
return vendorName;
|
||||
}
|
||||
|
||||
public String getVendorModel() {
|
||||
return vendorModel;
|
||||
}
|
||||
|
||||
public String getHardwareVersion() {
|
||||
return hardwareVersion;
|
||||
}
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
public String getMacAddress() {
|
||||
return macAddress;
|
||||
}
|
||||
|
||||
public String getLanIp() {
|
||||
return lanIp;
|
||||
}
|
||||
|
||||
public String getWifiIp() {
|
||||
return wifiIp;
|
||||
}
|
||||
|
||||
public ZonedDateTime getLastResetDate() {
|
||||
return lastResetDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getLastBootDate() {
|
||||
return lastBootDate;
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamImplicit;
|
||||
|
||||
/**
|
||||
* The {@link Location} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for a Plugwise zone/location.
|
||||
* It implements the {@link PlugwiseComparableDate} interface and
|
||||
* extends the abstract class {@link PlugwiseBaseModel}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("location")
|
||||
public class Location extends PlugwiseBaseModel implements PlugwiseComparableDate<Location> {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private String preset;
|
||||
|
||||
@XStreamImplicit(itemFieldName = "appliance")
|
||||
private List<String> locationAppliances = new ArrayList<String>();
|
||||
|
||||
@XStreamImplicit(itemFieldName = "point_log", keyFieldName = "type")
|
||||
private Logs pointLogs;
|
||||
|
||||
@XStreamImplicit(itemFieldName = "actuator_functionality", keyFieldName = "type")
|
||||
private ActuatorFunctionalities actuatorFunctionalities;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPreset() {
|
||||
return preset;
|
||||
}
|
||||
|
||||
public List<String> getLocationAppliances() {
|
||||
return locationAppliances;
|
||||
}
|
||||
|
||||
public Logs getPointLogs() {
|
||||
if (pointLogs == null) {
|
||||
pointLogs = new Logs();
|
||||
}
|
||||
return pointLogs;
|
||||
}
|
||||
|
||||
public ActuatorFunctionalities getActuatorFunctionalities() {
|
||||
if (actuatorFunctionalities == null) {
|
||||
actuatorFunctionalities = new ActuatorFunctionalities();
|
||||
}
|
||||
return actuatorFunctionalities;
|
||||
}
|
||||
|
||||
public Optional<Double> getTemperature() {
|
||||
return this.pointLogs.getTemperature();
|
||||
}
|
||||
|
||||
public Optional<String> getTemperatureUnit() {
|
||||
return this.pointLogs.getTemperatureUnit();
|
||||
}
|
||||
|
||||
public Optional<Double> getSetpointTemperature() {
|
||||
return this.pointLogs.getThermostatTemperature();
|
||||
}
|
||||
|
||||
public Optional<String> getSetpointTemperatureUnit() {
|
||||
return this.pointLogs.getThermostatTemperatureUnit();
|
||||
}
|
||||
|
||||
public Optional<Boolean> getPreHeatState() {
|
||||
return this.actuatorFunctionalities.getPreHeatState();
|
||||
}
|
||||
|
||||
public int applianceCount() {
|
||||
if (this.locationAppliances == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return this.locationAppliances.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareDateWith(Location compareTo) {
|
||||
if (compareTo == null) {
|
||||
return -1;
|
||||
}
|
||||
ZonedDateTime compareToDate = compareTo.getModifiedDate();
|
||||
ZonedDateTime compareFromDate = this.getModifiedDate();
|
||||
if (compareFromDate == null) {
|
||||
return -1;
|
||||
} else if (compareToDate == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareFromDate.compareTo(compareToDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewerThan(Location hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOlderThan(Location hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) < 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link Locations} class is an object model class that mirrors the XML
|
||||
* structure provided by the Plugwise Home Automation controller for the
|
||||
* collection of Plugwise locations/zones. It extends the
|
||||
* {@link PlugwiseHACollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public class Locations extends PlugwiseHACollection<Location> {
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, Location> locations) {
|
||||
if (locations != null) {
|
||||
for (Location location : locations.values()) {
|
||||
String id = location.getId();
|
||||
Location originalLocation = this.get(id);
|
||||
|
||||
Boolean originalLocationIsOlder = false;
|
||||
if (originalLocation != null) {
|
||||
originalLocationIsOlder = originalLocation.isOlderThan(location);
|
||||
}
|
||||
|
||||
if (originalLocation != null && originalLocationIsOlder) {
|
||||
Logs updatedPointLogs = location.getPointLogs();
|
||||
if (updatedPointLogs != null) {
|
||||
updatedPointLogs.merge(originalLocation.getPointLogs());
|
||||
}
|
||||
|
||||
ActuatorFunctionalities updatedActuatorFunctionalities = location.getActuatorFunctionalities();
|
||||
if (updatedActuatorFunctionalities != null) {
|
||||
updatedActuatorFunctionalities.merge(originalLocation.getActuatorFunctionalities());
|
||||
}
|
||||
|
||||
this.put(id, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("point_log")
|
||||
public class Log extends PlugwiseBaseModel implements PlugwiseComparableDate<Log> {
|
||||
|
||||
private String type;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String measurement;
|
||||
|
||||
@XStreamAlias("measurement_date")
|
||||
private ZonedDateTime measurementDate;
|
||||
|
||||
@XStreamAlias("updated_date")
|
||||
private ZonedDateTime updatedDate;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public Optional<String> getMeasurement() {
|
||||
return Optional.ofNullable(measurement);
|
||||
}
|
||||
|
||||
public Optional<Boolean> getMeasurementAsBoolean() {
|
||||
if (measurement != null) {
|
||||
switch (measurement.toLowerCase()) {
|
||||
case "on":
|
||||
return Optional.of(true);
|
||||
case "off":
|
||||
return Optional.of(false);
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Double> getMeasurementAsDouble() {
|
||||
try {
|
||||
if (measurement != null) {
|
||||
return Optional.of(Double.parseDouble(measurement));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getMeasurementUnit() {
|
||||
return Optional.ofNullable(unit);
|
||||
}
|
||||
|
||||
public ZonedDateTime getMeasurementDate() {
|
||||
return measurementDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getUpdatedDate() {
|
||||
return updatedDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareDateWith(Log compareTo) {
|
||||
if (compareTo == null) {
|
||||
return -1;
|
||||
}
|
||||
ZonedDateTime compareToDate = compareTo.getMeasurementDate();
|
||||
ZonedDateTime compareFromDate = this.getMeasurementDate();
|
||||
if (compareFromDate == null) {
|
||||
return -1;
|
||||
} else if (compareToDate == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareFromDate.compareTo(compareToDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewerThan(Log hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOlderThan(Log hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) < 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The {@link Logs} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for the collection of logs.
|
||||
* It extends the {@link PlugwiseHACollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public class Logs extends PlugwiseHACollection<Log> {
|
||||
|
||||
private static final String THERMOSTAT = "thermostat";
|
||||
private static final String TEMPERATURE = "temperature";
|
||||
private static final String TEMPERATURE_OFFSET = "temperature_offset";
|
||||
private static final String BATTERY = "battery";
|
||||
private static final String POWER_USAGE = "electricity_consumed";
|
||||
private static final String RELAY = "relay";
|
||||
private static final String DHWSTATE = "domestic_hot_water_state";
|
||||
private static final String COOLINGSTATE = "cooling_state";
|
||||
private static final String INTENDEDBOILERTEMP = "intended_boiler_temperature";
|
||||
private static final String FLAMESTATE = "flame_state";
|
||||
private static final String INTENDEDHEATINGSTATE = "intended_central_heating_state";
|
||||
private static final String MODULATIONLEVEL = "modulation_level";
|
||||
private static final String OTAPPLICATIONFAULTCODE = "open_therm_application_specific_fault_code";
|
||||
private static final String DHWTEMP = "domestic_hot_water_temperature";
|
||||
private static final String OTOEMFAULTCODE = "open_therm_oem_fault_code";
|
||||
private static final String BOILERTEMP = "boiler_temperature";
|
||||
private static final String DHWSETPOINT = "domestic_hot_water_setpoint";
|
||||
private static final String MAXBOILERTEMP = "maximum_boiler_temperature";
|
||||
private static final String DHWCOMFORTMODE = "domestic_hot_water_comfort_mode";
|
||||
private static final String CHSTATE = "central_heating_state";
|
||||
private static final String VALVE_POSITION = "valve_position";
|
||||
private static final String WATER_PRESSURE = "central_heater_water_pressure";
|
||||
|
||||
public Optional<Boolean> getCoolingState() {
|
||||
return this.getLog(COOLINGSTATE).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getIntendedBoilerTemp() {
|
||||
return this.getLog(INTENDEDBOILERTEMP).map(logEntry -> logEntry.getMeasurementAsDouble())
|
||||
.orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getIntendedBoilerTempUnit() {
|
||||
return this.getLog(INTENDEDBOILERTEMP).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getFlameState() {
|
||||
return this.getLog(FLAMESTATE).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getIntendedHeatingState() {
|
||||
return this.getLog(INTENDEDHEATINGSTATE).map(logEntry -> logEntry.getMeasurementAsBoolean())
|
||||
.orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getModulationLevel() {
|
||||
return this.getLog(MODULATIONLEVEL).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getOTAppFaultCode() {
|
||||
return this.getLog(OTAPPLICATIONFAULTCODE).map(logEntry -> logEntry.getMeasurementAsDouble())
|
||||
.orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getDHWTemp() {
|
||||
return this.getLog(DHWTEMP).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getDHWTempUnit() {
|
||||
return this.getLog(DHWTEMP).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getOTOEMFaultcode() {
|
||||
return this.getLog(OTOEMFAULTCODE).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getBoilerTemp() {
|
||||
return this.getLog(BOILERTEMP).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getBoilerTempUnit() {
|
||||
return this.getLog(BOILERTEMP).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getDHTSetpoint() {
|
||||
return this.getLog(DHWSETPOINT).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getDHTSetpointUnit() {
|
||||
return this.getLog(DHWSETPOINT).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getMaxBoilerTemp() {
|
||||
return this.getLog(MAXBOILERTEMP).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getMaxBoilerTempUnit() {
|
||||
return this.getLog(MAXBOILERTEMP).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getDHWComfortMode() {
|
||||
return this.getLog(DHWCOMFORTMODE).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getTemperature() {
|
||||
return this.getLog(TEMPERATURE).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getTemperatureUnit() {
|
||||
return this.getLog(TEMPERATURE).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getThermostatTemperature() {
|
||||
return this.getLog(THERMOSTAT).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getThermostatTemperatureUnit() {
|
||||
return this.getLog(THERMOSTAT).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getOffsetTemperature() {
|
||||
return this.getLog(TEMPERATURE_OFFSET).map(logEntry -> logEntry.getMeasurementAsDouble())
|
||||
.orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<String> getOffsetTemperatureUnit() {
|
||||
return this.getLog(TEMPERATURE_OFFSET).map(logEntry -> logEntry.getMeasurementUnit()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getRelayState() {
|
||||
return this.getLog(RELAY).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getDHWState() {
|
||||
return this.getLog(DHWSTATE).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Boolean> getCHState() {
|
||||
return this.getLog(CHSTATE).map(logEntry -> logEntry.getMeasurementAsBoolean()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getValvePosition() {
|
||||
return this.getLog(VALVE_POSITION).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getWaterPressure() {
|
||||
return this.getLog(WATER_PRESSURE).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getBatteryLevel() {
|
||||
return this.getLog(BATTERY).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Double> getPowerUsage() {
|
||||
return this.getLog(POWER_USAGE).map(logEntry -> logEntry.getMeasurementAsDouble()).orElse(Optional.empty());
|
||||
}
|
||||
|
||||
public Optional<Log> getLog(String logItem) {
|
||||
return Optional.ofNullable(this.get(logItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, Log> logsToMerge) {
|
||||
if (logsToMerge != null) {
|
||||
for (Log logToMerge : logsToMerge.values()) {
|
||||
String type = logToMerge.getType();
|
||||
Log originalLog = this.get(type);
|
||||
|
||||
if (originalLog == null || originalLog.isOlderThan(logToMerge)) {
|
||||
this.put(type, logToMerge);
|
||||
} else {
|
||||
this.put(type, originalLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamImplicit;
|
||||
|
||||
/**
|
||||
* The {@link Module} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for a Plugwise module.
|
||||
* It implements the {@link PlugwiseComparableDate} interface and
|
||||
* extends the abstract class {@link PlugwiseBaseModel}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@XStreamAlias("module")
|
||||
public class Module extends PlugwiseBaseModel implements PlugwiseComparableDate<Module> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@XStreamImplicit(itemFieldName = "service", keyFieldName = "id")
|
||||
private Services services;
|
||||
|
||||
@XStreamAlias("vendor_name")
|
||||
private String vendorName;
|
||||
|
||||
@XStreamAlias("vendor_model")
|
||||
private String vendorModel;
|
||||
|
||||
@XStreamAlias("hardware_version")
|
||||
private String hardwareVersion;
|
||||
|
||||
@XStreamAlias("firmware_version")
|
||||
private String firmwareVersion;
|
||||
|
||||
public String getVendorName() {
|
||||
return vendorName;
|
||||
}
|
||||
|
||||
public String getVendorModel() {
|
||||
return vendorModel;
|
||||
}
|
||||
|
||||
public String getHardwareVersion() {
|
||||
return hardwareVersion;
|
||||
}
|
||||
|
||||
public String getFirmwareVersion() {
|
||||
return firmwareVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareDateWith(Module compareTo) {
|
||||
if (compareTo == null) {
|
||||
return -1;
|
||||
}
|
||||
ZonedDateTime compareToDate = compareTo.getModifiedDate();
|
||||
ZonedDateTime compareFromDate = this.getModifiedDate();
|
||||
if (compareFromDate == null) {
|
||||
return -1;
|
||||
} else if (compareToDate == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return compareFromDate.compareTo(compareToDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNewerThan(Module hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOlderThan(Module hasModifiedDate) {
|
||||
return compareDateWith(hasModifiedDate) < 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link Modules} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for the collection of modules.
|
||||
* It extends the {@link PlugwiseHACollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public class Modules extends PlugwiseHACollection<Module> {
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, Module> modules) {
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseBaseModel} abstract class contains
|
||||
* methods and properties that similar for all object model classes.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public abstract class PlugwiseBaseModel {
|
||||
|
||||
private String id;
|
||||
|
||||
@XStreamAlias("created_date")
|
||||
private ZonedDateTime createdDate;
|
||||
|
||||
@XStreamAlias("modified_date")
|
||||
private ZonedDateTime modifiedDate;
|
||||
|
||||
@XStreamAlias("updated_date")
|
||||
private ZonedDateTime updateDate;
|
||||
|
||||
@XStreamAlias("deleted_date")
|
||||
private ZonedDateTime deletedDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ZonedDateTime getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getModifiedDate() {
|
||||
return modifiedDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getUpdatedDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public ZonedDateTime getDeletedDate() {
|
||||
return deletedDate;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public interface PlugwiseComparableDate<T extends PlugwiseBaseModel> {
|
||||
public int compareDateWith(T hasModifiedDate);
|
||||
|
||||
public boolean isOlderThan(T hasModifiedDate);
|
||||
|
||||
public boolean isNewerThan(T hasModifiedDate);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public abstract class PlugwiseHACollection<T> implements Map<String, T> {
|
||||
|
||||
private final Map<String, T> map = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Object key) {
|
||||
return this.map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T put(String key, T value) {
|
||||
return this.map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(Object key) {
|
||||
return this.map.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends T> m) {
|
||||
this.map.putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return this.map.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> values() {
|
||||
return this.map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, T>> entrySet() {
|
||||
return this.map.entrySet();
|
||||
}
|
||||
|
||||
public abstract void merge(Map<String, T> map);
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("service")
|
||||
public class Service extends PlugwiseBaseModel {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@XStreamAlias("log_type")
|
||||
private String logType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@XStreamAlias("point_log")
|
||||
private String pointLogId;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link Services} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for the collection of module services.
|
||||
* It extends the {@link PlugwiseHACollection} class.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
public class Services extends PlugwiseHACollection<Service> {
|
||||
|
||||
@Override
|
||||
public void merge(Map<String, Service> services) {
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.model.dto;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* The {@link ZigBeeNode} class is an object model class that
|
||||
* mirrors the XML structure provided by the Plugwise Home Automation
|
||||
* controller for a Plugwise ZigBeeNode.
|
||||
* It extends the abstract class {@link PlugwiseBaseModel}.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
@XStreamAlias("ZigBeeNode")
|
||||
public class ZigBeeNode extends PlugwiseBaseModel {
|
||||
|
||||
private String type;
|
||||
private String reachable;
|
||||
|
||||
@XStreamAlias("power_source")
|
||||
private String powerSource;
|
||||
|
||||
@XStreamAlias("mac_address")
|
||||
private String macAddress;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getReachable() {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
public String getPowerSource() {
|
||||
return powerSource;
|
||||
}
|
||||
|
||||
public String getMacAddress() {
|
||||
return macAddress;
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.api.xml;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.converter.DateTimeConverter;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalities;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionality;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityOffsetTemperature;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityRelay;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityThermostat;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityThreshold;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityTimer;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ActuatorFunctionalityToggle;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliance;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliances;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.DomainObjects;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.GatewayEnvironment;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.GatewayInfo;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Location;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Locations;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Log;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Logs;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Module;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Modules;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Service;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Services;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.ZigBeeNode;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import com.thoughtworks.xstream.io.xml.StaxDriver;
|
||||
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
|
||||
import com.thoughtworks.xstream.security.NoTypePermission;
|
||||
import com.thoughtworks.xstream.security.NullPermission;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAXStream} class is a utility class that wraps an XStream
|
||||
* object and provide additional functionality specific to the PlugwiseHA
|
||||
* binding. It automatically load the correct converter classes and processes
|
||||
* the XStream annotions used by the object classes.
|
||||
*
|
||||
* @author B. van Wetten - Initial contribution
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAXStream extends XStream {
|
||||
|
||||
private static XmlFriendlyNameCoder customCoder = new XmlFriendlyNameCoder("_-", "_");
|
||||
|
||||
public PlugwiseHAXStream() {
|
||||
super(new StaxDriver(PlugwiseHAXStream.customCoder));
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
// Protected methods
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected void allowClass(Class clz) {
|
||||
this.processAnnotations(clz);
|
||||
this.allowTypeHierarchy(clz);
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
// Configure XStream
|
||||
this.ignoreUnknownElements();
|
||||
this.setClassLoader(getClass().getClassLoader());
|
||||
|
||||
// Clear out existing
|
||||
this.addPermission(NoTypePermission.NONE);
|
||||
this.addPermission(NullPermission.NULL);
|
||||
|
||||
// Whitelist classes
|
||||
this.allowClass(GatewayInfo.class);
|
||||
this.allowClass(GatewayEnvironment.class);
|
||||
this.allowClass(Appliances.class);
|
||||
this.allowClass(Appliance.class);
|
||||
this.allowClass(Modules.class);
|
||||
this.allowClass(Module.class);
|
||||
this.allowClass(Locations.class);
|
||||
this.allowClass(Location.class);
|
||||
this.allowClass(Logs.class);
|
||||
this.allowClass(Log.class);
|
||||
this.allowClass(Services.class);
|
||||
this.allowClass(Service.class);
|
||||
this.allowClass(ZigBeeNode.class);
|
||||
this.allowClass(ActuatorFunctionalities.class);
|
||||
this.allowClass(ActuatorFunctionality.class);
|
||||
this.allowClass(ActuatorFunctionalityThermostat.class);
|
||||
this.allowClass(ActuatorFunctionalityOffsetTemperature.class);
|
||||
this.allowClass(ActuatorFunctionalityRelay.class);
|
||||
this.allowClass(ActuatorFunctionalityTimer.class);
|
||||
this.allowClass(ActuatorFunctionalityThreshold.class);
|
||||
this.allowClass(ActuatorFunctionalityToggle.class);
|
||||
this.allowClass(DomainObjects.class);
|
||||
|
||||
// Register custom converters
|
||||
this.registerConverter(new DateTimeConverter());
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHABridgeThingConfig} encapsulates all the configuration options for an instance of the
|
||||
* {@link PlugwiseHABridgeHandler}.
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHABridgeThingConfig {
|
||||
|
||||
private String host = "adam";
|
||||
|
||||
private int port = 80;
|
||||
|
||||
private String username = "smile";
|
||||
|
||||
private String smileId = "";
|
||||
|
||||
private int refresh = 15;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getsmileId() {
|
||||
return smileId;
|
||||
}
|
||||
|
||||
public int getRefresh() {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return !host.isBlank() && !username.isBlank() && !smileId.isBlank();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlugwiseHABridgeThingConfig{host = " + host + ", port = " + port + ", username = " + username
|
||||
+ ", smileId = *****, refresh = " + refresh + "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAThingConfig} encapsulates the configuration options for
|
||||
* an instance of the {@link PlugwiseHAApplianceHandler} and the
|
||||
* {@link PlugwiseHAZoneHandler}
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAThingConfig {
|
||||
|
||||
private String id = "";
|
||||
|
||||
private int lowBatteryPercentage = 15;
|
||||
|
||||
// Getters
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLowBatteryPercentage() {
|
||||
return this.lowBatteryPercentage;
|
||||
}
|
||||
|
||||
// Member methods
|
||||
|
||||
public boolean isValid() {
|
||||
return !id.isBlank() && lowBatteryPercentage > 0 && lowBatteryPercentage < 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlugwiseHAThingConfig{id = " + id + ", lowBatteryPercentage = " + lowBatteryPercentage + "}";
|
||||
}
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliance;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.DomainObjects;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Location;
|
||||
import org.openhab.binding.plugwiseha.internal.handler.PlugwiseHABridgeHandler;
|
||||
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.ThingUID;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHADiscoveryService} class is capable of discovering the
|
||||
* available data from the Plugwise Home Automation gateway
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHADiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHADiscoveryService.class);
|
||||
private static final int TIMEOUT_SECONDS = 5;
|
||||
private static final int REFRESH_SECONDS = 600;
|
||||
private @Nullable PlugwiseHABridgeHandler bridgeHandler;
|
||||
private @Nullable ScheduledFuture<?> discoveryFuture;
|
||||
|
||||
public PlugwiseHADiscoveryService() {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, TIMEOUT_SECONDS, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void startScan() {
|
||||
try {
|
||||
discoverDomainObjects();
|
||||
} catch (PlugwiseHAException e) {
|
||||
// Ignore silently
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Start Plugwise Home Automation background discovery");
|
||||
|
||||
ScheduledFuture<?> localDiscoveryFuture = discoveryFuture;
|
||||
if (localDiscoveryFuture == null || localDiscoveryFuture.isCancelled()) {
|
||||
discoveryFuture = scheduler.scheduleWithFixedDelay(this::startScan, 30, REFRESH_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stopping Plugwise Home Automation background discovery");
|
||||
|
||||
ScheduledFuture<?> localDiscoveryFuture = discoveryFuture;
|
||||
if (localDiscoveryFuture != null) {
|
||||
if (!localDiscoveryFuture.isCancelled()) {
|
||||
localDiscoveryFuture.cancel(true);
|
||||
localDiscoveryFuture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||
if (handler instanceof PlugwiseHABridgeHandler) {
|
||||
bridgeHandler = (PlugwiseHABridgeHandler) handler;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return bridgeHandler;
|
||||
}
|
||||
|
||||
private void discoverDomainObjects() throws PlugwiseHAException {
|
||||
PlugwiseHAController controller = null;
|
||||
PlugwiseHABridgeHandler localBridgeHandler = this.bridgeHandler;
|
||||
if (localBridgeHandler != null) {
|
||||
controller = localBridgeHandler.getController();
|
||||
}
|
||||
|
||||
if (controller != null) {
|
||||
DomainObjects domainObjects = controller.getDomainObjects();
|
||||
|
||||
if (domainObjects != null) {
|
||||
for (Location location : domainObjects.getLocations().values()) {
|
||||
// Only add locations with at least 1 appliance (this ignores the 'root' (home)
|
||||
// location which is the parent of all other locations.)
|
||||
if (location.applianceCount() > 0) {
|
||||
locationDiscovery(location);
|
||||
}
|
||||
}
|
||||
|
||||
for (Appliance appliance : domainObjects.getAppliances().values()) {
|
||||
// Only add appliances that are required/supported for this binding
|
||||
if (PlugwiseHABindingConstants.SUPPORTED_APPLIANCE_TYPES.contains(appliance.getType())) {
|
||||
applianceDiscovery(appliance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applianceDiscovery(Appliance appliance) {
|
||||
String applianceId = appliance.getId();
|
||||
String applianceName = appliance.getName();
|
||||
String applianceType = appliance.getType();
|
||||
|
||||
PlugwiseHABridgeHandler localBridgeHandler = this.bridgeHandler;
|
||||
if (localBridgeHandler != null) {
|
||||
ThingUID bridgeUID = localBridgeHandler.getThing().getUID();
|
||||
|
||||
ThingUID uid;
|
||||
|
||||
Map<String, Object> configProperties = new HashMap<>();
|
||||
|
||||
configProperties.put(APPLIANCE_CONFIG_ID, applianceId);
|
||||
|
||||
switch (applianceType) {
|
||||
case "thermostatic_radiator_valve":
|
||||
uid = new ThingUID(PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_VALVE, bridgeUID, applianceId);
|
||||
configProperties.put(APPLIANCE_CONFIG_LOWBATTERY, 15);
|
||||
break;
|
||||
case "central_heating_pump":
|
||||
uid = new ThingUID(PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_PUMP, bridgeUID, applianceId);
|
||||
break;
|
||||
case "heater_central":
|
||||
uid = new ThingUID(PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_BOILER, bridgeUID, applianceId);
|
||||
break;
|
||||
case "zone_thermostat":
|
||||
uid = new ThingUID(PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_THERMOSTAT, bridgeUID,
|
||||
applianceId);
|
||||
configProperties.put(APPLIANCE_CONFIG_LOWBATTERY, 15);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
|
||||
.withLabel(applianceName).withProperties(configProperties)
|
||||
.withRepresentationProperty(APPLIANCE_CONFIG_ID).build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
|
||||
logger.debug("Discovered plugwise appliance type '{}' with name '{}' with id {} ({})", applianceType,
|
||||
applianceName, applianceId, uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void locationDiscovery(Location location) {
|
||||
String locationId = location.getId();
|
||||
String locationName = location.getName();
|
||||
|
||||
PlugwiseHABridgeHandler localBridgeHandler = this.bridgeHandler;
|
||||
if (localBridgeHandler != null) {
|
||||
ThingUID bridgeUID = localBridgeHandler.getThing().getUID();
|
||||
ThingUID uid = new ThingUID(PlugwiseHABindingConstants.THING_TYPE_ZONE, bridgeUID, locationId);
|
||||
|
||||
Map<String, Object> configProperties = new HashMap<>();
|
||||
|
||||
configProperties.put(ZONE_CONFIG_ID, locationId);
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID)
|
||||
.withLabel(locationName).withRepresentationProperty(ZONE_CONFIG_ID).withProperties(configProperties)
|
||||
.build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
|
||||
logger.debug("Discovered plugwise zone '{}' with id {} ({})", locationName, locationId, uid);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,496 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.handler;
|
||||
|
||||
import static org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants.*;
|
||||
import static org.openhab.core.library.unit.MetricPrefix.*;
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.BRIDGE_OFFLINE;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.COMMUNICATION_ERROR;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Dimensionless;
|
||||
import javax.measure.quantity.Power;
|
||||
import javax.measure.quantity.Pressure;
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Appliance;
|
||||
import org.openhab.binding.plugwiseha.internal.config.PlugwiseHAThingConfig;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAApplianceHandler} class is responsible for handling
|
||||
* commands and status updates for the Plugwise Home Automation appliances.
|
||||
* Extends @{link PlugwiseHABaseHandler}
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAApplianceHandler extends PlugwiseHABaseHandler<Appliance, PlugwiseHAThingConfig> {
|
||||
|
||||
private @Nullable Appliance appliance;
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHAApplianceHandler.class);
|
||||
|
||||
// Constructor
|
||||
|
||||
public PlugwiseHAApplianceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_VALVE.equals(thingTypeUID)
|
||||
|| PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_PUMP.equals(thingTypeUID)
|
||||
|| PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_BOILER.equals(thingTypeUID)
|
||||
|| PlugwiseHABindingConstants.THING_TYPE_APPLIANCE_THERMOSTAT.equals(thingTypeUID);
|
||||
}
|
||||
|
||||
// Overrides
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize(PlugwiseHAThingConfig config, PlugwiseHABridgeHandler bridgeHandler) {
|
||||
if (thing.getStatus() == INITIALIZING) {
|
||||
logger.debug("Initializing Plugwise Home Automation appliance handler with config = {}", config);
|
||||
if (!config.isValid()) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR,
|
||||
"Invalid configuration for Plugwise Home Automation appliance handler.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PlugwiseHAController controller = bridgeHandler.getController();
|
||||
if (controller != null) {
|
||||
this.appliance = getEntity(controller, true);
|
||||
Appliance localAppliance = this.appliance;
|
||||
if (localAppliance != null) {
|
||||
if (localAppliance.isBatteryOperated()) {
|
||||
addBatteryChannels();
|
||||
}
|
||||
setApplianceProperties();
|
||||
updateStatus(ONLINE);
|
||||
} else {
|
||||
updateStatus(OFFLINE);
|
||||
}
|
||||
} else {
|
||||
updateStatus(OFFLINE, BRIDGE_OFFLINE);
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Appliance getEntity(PlugwiseHAController controller, Boolean forceRefresh)
|
||||
throws PlugwiseHAException {
|
||||
PlugwiseHAThingConfig config = getPlugwiseThingConfig();
|
||||
Appliance appliance = controller.getAppliance(config.getId(), forceRefresh);
|
||||
|
||||
return appliance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCommand(Appliance entity, ChannelUID channelUID, Command command) throws PlugwiseHAException {
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
|
||||
PlugwiseHABridgeHandler bridge = this.getPlugwiseHABridge();
|
||||
if (bridge == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlugwiseHAController controller = bridge.getController();
|
||||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelID) {
|
||||
case APPLIANCE_LOCK_CHANNEL:
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
if (command == OnOffType.ON) {
|
||||
controller.switchRelayLockOn(entity);
|
||||
} else {
|
||||
controller.switchRelayLockOff(entity);
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to switch relay lock {} for appliance '{}'", (State) command,
|
||||
entity.getName());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_OFFSET_CHANNEL:
|
||||
if (command instanceof QuantityType) {
|
||||
Unit<Temperature> unit = entity.getOffsetTemperatureUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
QuantityType<?> state = ((QuantityType<?>) command).toUnit(unit);
|
||||
|
||||
if (state != null) {
|
||||
try {
|
||||
controller.setOffsetTemperature(entity, state.doubleValue());
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to update setpoint for zone '{}': {} -> {}", entity.getName(),
|
||||
entity.getSetpointTemperature().orElse(null), state.doubleValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_POWER_CHANNEL:
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
if (command == OnOffType.ON) {
|
||||
controller.switchRelayOn(entity);
|
||||
} else {
|
||||
controller.switchRelayOff(entity);
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to switch relay {} for appliance '{}'", (State) command, entity.getName());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_SETPOINT_CHANNEL:
|
||||
if (command instanceof QuantityType) {
|
||||
Unit<Temperature> unit = entity.getSetpointTemperatureUnit().orElse(UNIT_CELSIUS)
|
||||
.equals(UNIT_CELSIUS) ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
QuantityType<?> state = ((QuantityType<?>) command).toUnit(unit);
|
||||
|
||||
if (state != null) {
|
||||
try {
|
||||
controller.setThermostat(entity, state.doubleValue());
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to update setpoint for appliance '{}': {} -> {}", entity.getName(),
|
||||
entity.getSetpointTemperature().orElse(null), state.doubleValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Ignoring unsupported command = {} for channel = {}", command, channelUID);
|
||||
}
|
||||
}
|
||||
|
||||
private State getDefaultState(String channelID) {
|
||||
State state = UnDefType.NULL;
|
||||
switch (channelID) {
|
||||
case APPLIANCE_BATTERYLEVEL_CHANNEL:
|
||||
case APPLIANCE_CHSTATE_CHANNEL:
|
||||
case APPLIANCE_DHWSTATE_CHANNEL:
|
||||
case APPLIANCE_COOLINGSTATE_CHANNEL:
|
||||
case APPLIANCE_INTENDEDBOILERTEMP_CHANNEL:
|
||||
case APPLIANCE_FLAMESTATE_CHANNEL:
|
||||
case APPLIANCE_INTENDEDHEATINGSTATE_CHANNEL:
|
||||
case APPLIANCE_MODULATIONLEVEL_CHANNEL:
|
||||
case APPLIANCE_OTAPPLICATIONFAULTCODE_CHANNEL:
|
||||
case APPLIANCE_DHWTEMPERATURE_CHANNEL:
|
||||
case APPLIANCE_OTOEMFAULTCODE_CHANNEL:
|
||||
case APPLIANCE_BOILERTEMPERATURE_CHANNEL:
|
||||
case APPLIANCE_DHWSETPOINT_CHANNEL:
|
||||
case APPLIANCE_MAXBOILERTEMPERATURE_CHANNEL:
|
||||
case APPLIANCE_DHWCOMFORTMODE_CHANNEL:
|
||||
case APPLIANCE_OFFSET_CHANNEL:
|
||||
case APPLIANCE_POWER_USAGE_CHANNEL:
|
||||
case APPLIANCE_SETPOINT_CHANNEL:
|
||||
case APPLIANCE_TEMPERATURE_CHANNEL:
|
||||
case APPLIANCE_VALVEPOSITION_CHANNEL:
|
||||
case APPLIANCE_WATERPRESSURE_CHANNEL:
|
||||
state = UnDefType.NULL;
|
||||
break;
|
||||
case APPLIANCE_BATTERYLEVELLOW_CHANNEL:
|
||||
case APPLIANCE_LOCK_CHANNEL:
|
||||
case APPLIANCE_POWER_CHANNEL:
|
||||
state = UnDefType.UNDEF;
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshChannel(Appliance entity, ChannelUID channelUID) {
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
State state = getDefaultState(channelID);
|
||||
PlugwiseHAThingConfig config = getPlugwiseThingConfig();
|
||||
|
||||
switch (channelID) {
|
||||
case APPLIANCE_BATTERYLEVEL_CHANNEL: {
|
||||
Double batteryLevel = entity.getBatteryLevel().orElse(null);
|
||||
|
||||
if (batteryLevel != null) {
|
||||
batteryLevel = batteryLevel * 100;
|
||||
state = new QuantityType<Dimensionless>(batteryLevel.intValue(), Units.PERCENT);
|
||||
if (batteryLevel <= config.getLowBatteryPercentage()) {
|
||||
updateState(APPLIANCE_BATTERYLEVELLOW_CHANNEL, OnOffType.ON);
|
||||
} else {
|
||||
updateState(APPLIANCE_BATTERYLEVELLOW_CHANNEL, OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APPLIANCE_BATTERYLEVELLOW_CHANNEL: {
|
||||
Double batteryLevel = entity.getBatteryLevel().orElse(null);
|
||||
|
||||
if (batteryLevel != null) {
|
||||
batteryLevel *= 100;
|
||||
if (batteryLevel <= config.getLowBatteryPercentage()) {
|
||||
state = OnOffType.ON;
|
||||
} else {
|
||||
state = OnOffType.OFF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APPLIANCE_CHSTATE_CHANNEL:
|
||||
if (entity.getCHState().isPresent()) {
|
||||
state = OnOffType.from(entity.getCHState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_DHWSTATE_CHANNEL:
|
||||
if (entity.getDHWState().isPresent()) {
|
||||
state = OnOffType.from(entity.getDHWState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_LOCK_CHANNEL:
|
||||
Boolean relayLockState = entity.getRelayLockState().orElse(null);
|
||||
if (relayLockState != null) {
|
||||
state = OnOffType.from(relayLockState);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_OFFSET_CHANNEL:
|
||||
if (entity.getOffsetTemperature().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getOffsetTemperatureUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getOffsetTemperature().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_POWER_CHANNEL:
|
||||
if (entity.getRelayState().isPresent()) {
|
||||
state = OnOffType.from(entity.getRelayState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_POWER_USAGE_CHANNEL:
|
||||
if (entity.getPowerUsage().isPresent()) {
|
||||
state = new QuantityType<Power>(entity.getPowerUsage().get(), Units.WATT);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_SETPOINT_CHANNEL:
|
||||
if (entity.getSetpointTemperature().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getSetpointTemperatureUnit().orElse(UNIT_CELSIUS)
|
||||
.equals(UNIT_CELSIUS) ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getSetpointTemperature().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_TEMPERATURE_CHANNEL:
|
||||
if (entity.getTemperature().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getTemperatureUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getTemperature().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_VALVEPOSITION_CHANNEL:
|
||||
if (entity.getValvePosition().isPresent()) {
|
||||
Double valvePosition = entity.getValvePosition().get() * 100;
|
||||
state = new QuantityType<Dimensionless>(valvePosition.intValue(), Units.PERCENT);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_WATERPRESSURE_CHANNEL:
|
||||
if (entity.getWaterPressure().isPresent()) {
|
||||
Unit<Pressure> unit = HECTO(SIUnits.PASCAL);
|
||||
state = new QuantityType<Pressure>(entity.getWaterPressure().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_COOLINGSTATE_CHANNEL:
|
||||
if (entity.getCoolingState().isPresent()) {
|
||||
state = OnOffType.from(entity.getCoolingState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_INTENDEDBOILERTEMP_CHANNEL:
|
||||
if (entity.getIntendedBoilerTemp().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getIntendedBoilerTempUnit().orElse(UNIT_CELSIUS)
|
||||
.equals(UNIT_CELSIUS) ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getIntendedBoilerTemp().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_FLAMESTATE_CHANNEL:
|
||||
if (entity.getFlameState().isPresent()) {
|
||||
state = OnOffType.from(entity.getFlameState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_INTENDEDHEATINGSTATE_CHANNEL:
|
||||
if (entity.getIntendedHeatingState().isPresent()) {
|
||||
state = OnOffType.from(entity.getIntendedHeatingState().get());
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_MODULATIONLEVEL_CHANNEL:
|
||||
if (entity.getModulationLevel().isPresent()) {
|
||||
Double modulationLevel = entity.getModulationLevel().get() * 100;
|
||||
state = new QuantityType<Dimensionless>(modulationLevel.intValue(), Units.PERCENT);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_OTAPPLICATIONFAULTCODE_CHANNEL:
|
||||
if (entity.getOTAppFaultCode().isPresent()) {
|
||||
state = new QuantityType<Dimensionless>(entity.getOTAppFaultCode().get().intValue(), Units.PERCENT);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_DHWTEMPERATURE_CHANNEL:
|
||||
if (entity.getDHWTemp().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getDHWTempUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getDHWTemp().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_OTOEMFAULTCODE_CHANNEL:
|
||||
if (entity.getOTOEMFaultcode().isPresent()) {
|
||||
state = new QuantityType<Dimensionless>(entity.getOTOEMFaultcode().get().intValue(), Units.PERCENT);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_BOILERTEMPERATURE_CHANNEL:
|
||||
if (entity.getBoilerTemp().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getBoilerTempUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getBoilerTemp().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_DHWSETPOINT_CHANNEL:
|
||||
if (entity.getDHTSetpoint().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getDHTSetpointUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getDHTSetpoint().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_MAXBOILERTEMPERATURE_CHANNEL:
|
||||
if (entity.getMaxBoilerTemp().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getMaxBoilerTempUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getMaxBoilerTemp().get(), unit);
|
||||
}
|
||||
break;
|
||||
case APPLIANCE_DHWCOMFORTMODE_CHANNEL:
|
||||
if (entity.getDHWComfortMode().isPresent()) {
|
||||
state = OnOffType.from(entity.getDHWComfortMode().get());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != UnDefType.NULL) {
|
||||
updateState(channelID, state);
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void addBatteryChannels() {
|
||||
logger.debug("Battery operated appliance: {} detected: adding 'Battery level' and 'Battery low level' channels",
|
||||
thing.getLabel());
|
||||
|
||||
ChannelUID channelUIDBatteryLevel = new ChannelUID(getThing().getUID(), APPLIANCE_BATTERYLEVEL_CHANNEL);
|
||||
ChannelUID channelUIDBatteryLevelLow = new ChannelUID(getThing().getUID(), APPLIANCE_BATTERYLEVELLOW_CHANNEL);
|
||||
|
||||
boolean channelBatteryLevelExists = false;
|
||||
boolean channelBatteryLowExists = false;
|
||||
|
||||
List<Channel> channels = getThing().getChannels();
|
||||
for (Channel channel : channels) {
|
||||
if (channel.getUID().equals(channelUIDBatteryLevel)) {
|
||||
channelBatteryLevelExists = true;
|
||||
} else if (channel.getUID().equals(channelUIDBatteryLevelLow)) {
|
||||
channelBatteryLowExists = true;
|
||||
}
|
||||
if (channelBatteryLevelExists && channelBatteryLowExists) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channelBatteryLevelExists) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
|
||||
Channel channelBatteryLevel = ChannelBuilder.create(channelUIDBatteryLevel, "Number")
|
||||
.withType(CHANNEL_TYPE_BATTERYLEVEL).withKind(ChannelKind.STATE).withLabel("Battery Level")
|
||||
.withDescription("Represents the battery level as a percentage (0-100%)").build();
|
||||
|
||||
thingBuilder.withChannel(channelBatteryLevel);
|
||||
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
|
||||
if (!channelBatteryLowExists) {
|
||||
ThingBuilder thingBuilder = editThing();
|
||||
|
||||
Channel channelBatteryLow = ChannelBuilder.create(channelUIDBatteryLevelLow, "Switch")
|
||||
.withType(CHANNEL_TYPE_BATTERYLEVELLOW).withKind(ChannelKind.STATE).withLabel("Battery Low Level")
|
||||
.withDescription("Switches ON when battery level gets below threshold level").build();
|
||||
|
||||
thingBuilder.withChannel(channelBatteryLow);
|
||||
|
||||
updateThing(thingBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setApplianceProperties() {
|
||||
Map<String, String> properties = editProperties();
|
||||
logger.debug("Setting thing properties to {}", thing.getLabel());
|
||||
Appliance localAppliance = this.appliance;
|
||||
if (localAppliance != null) {
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_DESCRIPTION, localAppliance.getDescription());
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_TYPE, localAppliance.getType());
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_FUNCTIONALITIES,
|
||||
String.join(", ", localAppliance.getActuatorFunctionalities().keySet()));
|
||||
|
||||
if (localAppliance.isZigbeeDevice()) {
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_ZB_TYPE,
|
||||
localAppliance.getZigbeeNode().getType());
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_ZB_REACHABLE,
|
||||
localAppliance.getZigbeeNode().getReachable());
|
||||
properties.put(PlugwiseHABindingConstants.APPLIANCE_PROPERTY_ZB_POWERSOURCE,
|
||||
localAppliance.getZigbeeNode().getPowerSource());
|
||||
properties.put(Thing.PROPERTY_MAC_ADDRESS, localAppliance.getZigbeeNode().getMacAddress());
|
||||
}
|
||||
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, localAppliance.getModule().getFirmwareVersion());
|
||||
properties.put(Thing.PROPERTY_HARDWARE_VERSION, localAppliance.getModule().getHardwareVersion());
|
||||
properties.put(Thing.PROPERTY_VENDOR, localAppliance.getModule().getVendorName());
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, localAppliance.getModule().getVendorModel());
|
||||
}
|
||||
updateProperties(properties);
|
||||
}
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.handler;
|
||||
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
|
||||
import org.openhab.binding.plugwiseha.internal.config.PlugwiseHAThingConfig;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHABaseHandler} abstract class provides common methods and
|
||||
* properties for the ThingHandlers of this binding. Extends @{link
|
||||
* BaseThingHandler}
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
* @param <E> entity - the Plugwise Home Automation entity class used by this
|
||||
* thing handler
|
||||
* @param <C> config - the Plugwise Home Automation config class used by this
|
||||
* thing handler
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public abstract class PlugwiseHABaseHandler<E, C extends PlugwiseHAThingConfig> extends BaseThingHandler {
|
||||
|
||||
protected static final String STATUS_DESCRIPTION_COMMUNICATION_ERROR = "Error communicating with the Plugwise Home Automation controller";
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(PlugwiseHABaseHandler.class);
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
// Constructor
|
||||
@SuppressWarnings("null")
|
||||
public PlugwiseHABaseHandler(Thing thing) {
|
||||
super(thing);
|
||||
clazz = (Class<?>) (((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1]);
|
||||
}
|
||||
|
||||
// Abstract methods
|
||||
|
||||
/**
|
||||
* Initializes the Plugwise Entity that this class handles.
|
||||
*
|
||||
* @param config the thing configuration
|
||||
* @param bridge the bridge that this thing is part of
|
||||
*/
|
||||
protected abstract void initialize(C config, PlugwiseHABridgeHandler bridge);
|
||||
|
||||
/**
|
||||
* Get the Plugwise Entity that belongs to this ThingHandler
|
||||
*
|
||||
* @param controller the controller for this ThingHandler
|
||||
* @param forceRefresh indicated if the entity should be refreshed from the Plugwise API
|
||||
*/
|
||||
protected abstract @Nullable E getEntity(PlugwiseHAController controller, Boolean forceRefresh)
|
||||
throws PlugwiseHAException;
|
||||
|
||||
/**
|
||||
* Handles a {@link RefreshType} command for a given channel.
|
||||
*
|
||||
* @param entity the Plugwise Entity
|
||||
* @param channelUID the channel uid the command is for
|
||||
*/
|
||||
protected abstract void refreshChannel(E entity, ChannelUID channelUID);
|
||||
|
||||
/**
|
||||
* Handles a command for a given channel.
|
||||
*
|
||||
* @param entity the Plugwise Entity
|
||||
* @param channelUID the channel uid the command is for
|
||||
* @param command the command
|
||||
*/
|
||||
protected abstract void handleCommand(E entity, ChannelUID channelUID, Command command) throws PlugwiseHAException;
|
||||
|
||||
// Overrides
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
C config = getPlugwiseThingConfig();
|
||||
|
||||
if (checkConfig(config)) {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null || bridge.getHandler() == null
|
||||
|| !(bridge.getHandler() instanceof PlugwiseHABridgeHandler)) {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"You must choose a Plugwise Home Automation bridge for this thing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bridge.getStatus() == OFFLINE) {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"The Plugwise Home Automation bridge is currently offline.");
|
||||
}
|
||||
|
||||
PlugwiseHABridgeHandler bridgeHandler = (PlugwiseHABridgeHandler) bridge.getHandler();
|
||||
if (bridgeHandler != null) {
|
||||
initialize(config, bridgeHandler);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Invalid config for Plugwise Home Automation thing handler with config = {}", config);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Handling command = {} for channel = {}", command, channelUID);
|
||||
|
||||
if (getThing().getStatus() == ONLINE) {
|
||||
PlugwiseHAController controller = getController();
|
||||
if (controller != null) {
|
||||
try {
|
||||
@Nullable
|
||||
E entity = getEntity(controller, false);
|
||||
if (entity != null) {
|
||||
if (this.isLinked(channelUID)) {
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannel(entity, channelUID);
|
||||
} else {
|
||||
handleCommand(entity, channelUID, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unexpected error handling command = {} for channel = {} : {}", command, channelUID,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Public member methods
|
||||
|
||||
public @Nullable PlugwiseHABridgeHandler getPlugwiseHABridge() {
|
||||
Bridge bridge = this.getBridge();
|
||||
if (bridge != null) {
|
||||
return (PlugwiseHABridgeHandler) bridge.getHandler();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public C getPlugwiseThingConfig() {
|
||||
return (C) getConfigAs(clazz);
|
||||
}
|
||||
|
||||
// Private & protected methods
|
||||
|
||||
private @Nullable PlugwiseHAController getController() {
|
||||
PlugwiseHABridgeHandler bridgeHandler = getPlugwiseHABridge();
|
||||
|
||||
if (bridgeHandler != null) {
|
||||
return bridgeHandler.getController();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the configuration for validity, result is reflected in the status of
|
||||
* the Thing
|
||||
*/
|
||||
private boolean checkConfig(C config) {
|
||||
if (!config.isValid()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Configuration is missing or corrupted");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
super.bridgeStatusChanged(bridgeStatusInfo);
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
|
||||
setLinkedChannelsUndef();
|
||||
}
|
||||
}
|
||||
|
||||
private void setLinkedChannelsUndef() {
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
if (this.isLinked(channelUID)) {
|
||||
updateState(channelUID, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void refresh() {
|
||||
PlugwiseHABridgeHandler bridgeHandler = getPlugwiseHABridge();
|
||||
if (bridgeHandler != null) {
|
||||
if (bridgeHandler.getThing().getStatusInfo().getStatus() == ThingStatus.ONLINE) {
|
||||
PlugwiseHAController controller = getController();
|
||||
if (controller != null) {
|
||||
@Nullable
|
||||
E entity = null;
|
||||
try {
|
||||
entity = getEntity(controller, false);
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
setLinkedChannelsUndef();
|
||||
}
|
||||
if (entity != null) {
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
if (this.isLinked(channelUID)) {
|
||||
refreshChannel(entity, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.handler;
|
||||
|
||||
import static org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants.*;
|
||||
import static org.openhab.core.thing.ThingStatus.OFFLINE;
|
||||
import static org.openhab.core.thing.ThingStatus.ONLINE;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHACommunicationException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAInvalidHostException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHANotAuthorizedException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHATimeoutException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAUnauthorizedException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAModel;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.GatewayInfo;
|
||||
import org.openhab.binding.plugwiseha.internal.config.PlugwiseHABridgeThingConfig;
|
||||
import org.openhab.binding.plugwiseha.internal.config.PlugwiseHAThingConfig;
|
||||
import org.openhab.binding.plugwiseha.internal.discovery.PlugwiseHADiscoveryService;
|
||||
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.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHABridgeHandler} class is responsible for handling
|
||||
* commands and status updates for the Plugwise Home Automation bridge.
|
||||
* Extends @{link BaseBridgeHandler}
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHABridgeHandler extends BaseBridgeHandler {
|
||||
|
||||
// Private Static error messages
|
||||
|
||||
private static final String STATUS_DESCRIPTION_COMMUNICATION_ERROR = "Error communicating with the Plugwise Home Automation controller";
|
||||
private static final String STATUS_DESCRIPTION_TIMEOUT = "Communication timeout while communicating with the Plugwise Home Automation controller";
|
||||
private static final String STATUS_DESCRIPTION_CONFIGURATION_ERROR = "Invalid or missing configuration";
|
||||
private static final String STATUS_DESCRIPTION_INVALID_CREDENTIALS = "Invalid username and/or password - please double-check your configuration";
|
||||
private static final String STATUS_DESCRIPTION_INVALID_HOSTNAME = "Invalid hostname - please double-check your configuration";
|
||||
|
||||
// Private member variables/constants
|
||||
private @Nullable ScheduledFuture<?> refreshJob;
|
||||
private @Nullable volatile PlugwiseHAController controller;
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHABridgeHandler.class);
|
||||
|
||||
// Constructor
|
||||
|
||||
public PlugwiseHABridgeHandler(Bridge bridge, HttpClient httpClient) {
|
||||
super(bridge);
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
// Public methods
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
PlugwiseHABridgeThingConfig bridgeConfig = getConfigAs(PlugwiseHABridgeThingConfig.class);
|
||||
|
||||
if (this.checkConfig(bridgeConfig)) {
|
||||
logger.debug("Initializing the Plugwise Home Automation bridge handler with config = {}", bridgeConfig);
|
||||
try {
|
||||
this.controller = new PlugwiseHAController(httpClient, bridgeConfig.getHost(), bridgeConfig.getPort(),
|
||||
bridgeConfig.getUsername(), bridgeConfig.getsmileId());
|
||||
scheduleRefreshJob(bridgeConfig);
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.warn("Invalid config for the Plugwise Home Automation bridge handler with config = {}",
|
||||
bridgeConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(PlugwiseHADiscoveryService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
this.logger.warn(
|
||||
"Ignoring command = {} for channel = {} - this channel for the Plugwise Home Automation binding is read-only!",
|
||||
command, channelUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelRefreshJob();
|
||||
if (this.controller != null) {
|
||||
this.controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_BRIDGE_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
// Getters & setters
|
||||
|
||||
public @Nullable PlugwiseHAController getController() {
|
||||
return this.controller;
|
||||
}
|
||||
|
||||
// Protected and private methods
|
||||
|
||||
/**
|
||||
* Checks the configuration for validity, result is reflected in the status of
|
||||
* the Thing
|
||||
*/
|
||||
private boolean checkConfig(PlugwiseHABridgeThingConfig bridgeConfig) {
|
||||
if (!bridgeConfig.isValid()) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_CONFIGURATION_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleRefreshJob(PlugwiseHABridgeThingConfig bridgeConfig) {
|
||||
synchronized (this) {
|
||||
if (this.refreshJob == null) {
|
||||
logger.debug("Scheduling refresh job every {}s", bridgeConfig.getRefresh());
|
||||
this.refreshJob = scheduler.scheduleWithFixedDelay(this::run, 0, bridgeConfig.getRefresh(),
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void run() {
|
||||
try {
|
||||
logger.trace("Executing refresh job");
|
||||
refresh();
|
||||
|
||||
if (super.thing.getStatus() == ThingStatus.INITIALIZING) {
|
||||
setBridgeProperties();
|
||||
}
|
||||
|
||||
} catch (PlugwiseHAInvalidHostException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_HOSTNAME);
|
||||
} catch (PlugwiseHAUnauthorizedException | PlugwiseHANotAuthorizedException e) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR, STATUS_DESCRIPTION_INVALID_CREDENTIALS);
|
||||
} catch (PlugwiseHACommunicationException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
|
||||
} catch (PlugwiseHATimeoutException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_TIMEOUT);
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void refresh() throws PlugwiseHAException {
|
||||
if (this.getController() != null) {
|
||||
logger.debug("Refreshing the Plugwise Home Automation Controller {}", getThing().getUID());
|
||||
|
||||
PlugwiseHAController controller = this.getController();
|
||||
if (controller != null) {
|
||||
controller.refresh();
|
||||
updateStatus(ONLINE);
|
||||
}
|
||||
|
||||
getThing().getThings().forEach((thing) -> {
|
||||
ThingHandler thingHandler = thing.getHandler();
|
||||
if (thingHandler instanceof PlugwiseHABaseHandler) {
|
||||
((PlugwiseHABaseHandler<PlugwiseHAModel, PlugwiseHAThingConfig>) thingHandler).refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private void cancelRefreshJob() {
|
||||
synchronized (this) {
|
||||
if (this.refreshJob != null) {
|
||||
logger.debug("Cancelling refresh job");
|
||||
this.refreshJob.cancel(true);
|
||||
this.refreshJob = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setBridgeProperties() {
|
||||
logger.debug("Setting bridge properties");
|
||||
try {
|
||||
PlugwiseHAController controller = this.getController();
|
||||
GatewayInfo localGatewayInfo = null;
|
||||
if (controller != null) {
|
||||
localGatewayInfo = controller.getGatewayInfo();
|
||||
}
|
||||
|
||||
if (localGatewayInfo != null) {
|
||||
Map<String, String> properties = editProperties();
|
||||
if (localGatewayInfo.getFirmwareVersion() != null) {
|
||||
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, localGatewayInfo.getFirmwareVersion());
|
||||
}
|
||||
if (localGatewayInfo.getHardwareVersion() != null) {
|
||||
properties.put(Thing.PROPERTY_HARDWARE_VERSION, localGatewayInfo.getHardwareVersion());
|
||||
}
|
||||
if (localGatewayInfo.getMacAddress() != null) {
|
||||
properties.put(Thing.PROPERTY_MAC_ADDRESS, localGatewayInfo.getMacAddress());
|
||||
}
|
||||
if (localGatewayInfo.getVendorName() != null) {
|
||||
properties.put(Thing.PROPERTY_VENDOR, localGatewayInfo.getVendorName());
|
||||
}
|
||||
if (localGatewayInfo.getVendorModel() != null) {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, localGatewayInfo.getVendorModel());
|
||||
}
|
||||
|
||||
updateProperties(properties);
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, STATUS_DESCRIPTION_COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.plugwiseha.internal.handler;
|
||||
|
||||
import static org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants.*;
|
||||
import static org.openhab.core.thing.ThingStatus.*;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.BRIDGE_OFFLINE;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.COMMUNICATION_ERROR;
|
||||
import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.plugwiseha.internal.PlugwiseHABindingConstants;
|
||||
import org.openhab.binding.plugwiseha.internal.api.exception.PlugwiseHAException;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.PlugwiseHAController;
|
||||
import org.openhab.binding.plugwiseha.internal.api.model.dto.Location;
|
||||
import org.openhab.binding.plugwiseha.internal.config.PlugwiseHAThingConfig;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link PlugwiseHAZoneHandler} class is responsible for handling commands
|
||||
* and status updates for the Plugwise Home Automation zones/locations.
|
||||
* Extends @{link PlugwiseHABaseHandler}
|
||||
*
|
||||
* @author Bas van Wetten - Initial contribution
|
||||
* @author Leo Siepel - finish initial contribution
|
||||
*
|
||||
*/
|
||||
|
||||
@NonNullByDefault
|
||||
public class PlugwiseHAZoneHandler extends PlugwiseHABaseHandler<Location, PlugwiseHAThingConfig> {
|
||||
|
||||
private @Nullable Location location;
|
||||
private final Logger logger = LoggerFactory.getLogger(PlugwiseHAZoneHandler.class);
|
||||
|
||||
// Constructor
|
||||
|
||||
public PlugwiseHAZoneHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return PlugwiseHABindingConstants.THING_TYPE_ZONE.equals(thingTypeUID);
|
||||
}
|
||||
|
||||
// Overrides
|
||||
|
||||
@Override
|
||||
protected synchronized void initialize(PlugwiseHAThingConfig config, PlugwiseHABridgeHandler bridgeHandler) {
|
||||
if (thing.getStatus() == INITIALIZING) {
|
||||
logger.debug("Initializing Plugwise Home Automation zone handler with config = {}", config);
|
||||
if (!config.isValid()) {
|
||||
updateStatus(OFFLINE, CONFIGURATION_ERROR,
|
||||
"Invalid configuration for Plugwise Home Automation zone handler.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PlugwiseHAController controller = bridgeHandler.getController();
|
||||
if (controller != null) {
|
||||
this.location = getEntity(controller, true);
|
||||
if (this.location != null) {
|
||||
setLocationProperties();
|
||||
updateStatus(ONLINE);
|
||||
} else {
|
||||
updateStatus(OFFLINE);
|
||||
}
|
||||
} else {
|
||||
updateStatus(OFFLINE, BRIDGE_OFFLINE);
|
||||
}
|
||||
} catch (PlugwiseHAException e) {
|
||||
updateStatus(OFFLINE, COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Location getEntity(PlugwiseHAController controller, Boolean forceRefresh)
|
||||
throws PlugwiseHAException {
|
||||
PlugwiseHAThingConfig config = getPlugwiseThingConfig();
|
||||
Location location = controller.getLocation(config.getId(), forceRefresh);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCommand(Location entity, ChannelUID channelUID, Command command) throws PlugwiseHAException {
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
PlugwiseHABridgeHandler bridge = this.getPlugwiseHABridge();
|
||||
if (bridge != null) {
|
||||
PlugwiseHAController controller = bridge.getController();
|
||||
if (controller != null) {
|
||||
switch (channelID) {
|
||||
case ZONE_SETPOINT_CHANNEL:
|
||||
if (command instanceof QuantityType) {
|
||||
Unit<Temperature> unit = entity.getSetpointTemperatureUnit().orElse(UNIT_CELSIUS)
|
||||
.equals(UNIT_CELSIUS) ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
QuantityType<?> state = ((QuantityType<?>) command).toUnit(unit);
|
||||
if (state != null) {
|
||||
try {
|
||||
controller.setLocationThermostat(entity, state.doubleValue());
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to update setpoint for zone '{}': {} -> {}", entity.getName(),
|
||||
entity.getSetpointTemperature().orElse(null), state.doubleValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZONE_PREHEAT_CHANNEL:
|
||||
if (command instanceof OnOffType) {
|
||||
try {
|
||||
controller.setPreHeating(entity, command == OnOffType.ON);
|
||||
} catch (PlugwiseHAException e) {
|
||||
logger.warn("Unable to switch zone pre heating {} for zone '{}'", (State) command,
|
||||
entity.getName());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.warn("Ignoring unsupported command = {} for channel = {}", command, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private State getDefaultState(String channelID) {
|
||||
State state = UnDefType.NULL;
|
||||
switch (channelID) {
|
||||
case ZONE_PREHEAT_CHANNEL:
|
||||
case ZONE_PRESETSCENE_CHANNEL:
|
||||
case ZONE_SETPOINT_CHANNEL:
|
||||
case ZONE_TEMPERATURE_CHANNEL:
|
||||
state = UnDefType.NULL;
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshChannel(Location entity, ChannelUID channelUID) {
|
||||
String channelID = channelUID.getIdWithoutGroup();
|
||||
State state = getDefaultState(channelID);
|
||||
|
||||
switch (channelID) {
|
||||
case ZONE_PREHEAT_CHANNEL:
|
||||
Optional<Boolean> preHeatState = entity.getPreHeatState();
|
||||
if (preHeatState.isPresent()) {
|
||||
state = OnOffType.from(preHeatState.get());
|
||||
}
|
||||
break;
|
||||
case ZONE_PRESETSCENE_CHANNEL:
|
||||
state = new StringType(entity.getPreset());
|
||||
break;
|
||||
case ZONE_SETPOINT_CHANNEL:
|
||||
if (entity.getSetpointTemperature().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getSetpointTemperatureUnit().orElse(UNIT_CELSIUS)
|
||||
.equals(UNIT_CELSIUS) ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getSetpointTemperature().get(), unit);
|
||||
}
|
||||
break;
|
||||
case ZONE_TEMPERATURE_CHANNEL:
|
||||
if (entity.getTemperature().isPresent()) {
|
||||
Unit<Temperature> unit = entity.getTemperatureUnit().orElse(UNIT_CELSIUS).equals(UNIT_CELSIUS)
|
||||
? SIUnits.CELSIUS
|
||||
: ImperialUnits.FAHRENHEIT;
|
||||
state = new QuantityType<Temperature>(entity.getTemperature().get(), unit);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != UnDefType.NULL) {
|
||||
updateState(channelID, state);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setLocationProperties() {
|
||||
if (this.location != null) {
|
||||
Map<String, String> properties = editProperties();
|
||||
|
||||
Location localLocation = this.location;
|
||||
if (localLocation != null) {
|
||||
properties.put(PlugwiseHABindingConstants.LOCATION_PROPERTY_DESCRIPTION,
|
||||
localLocation.getDescription());
|
||||
properties.put(PlugwiseHABindingConstants.LOCATION_PROPERTY_TYPE, localLocation.getType());
|
||||
properties.put(PlugwiseHABindingConstants.LOCATION_PROPERTY_FUNCTIONALITIES,
|
||||
String.join(", ", localLocation.getActuatorFunctionalities().keySet()));
|
||||
}
|
||||
|
||||
updateProperties(properties);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="plugwiseha" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Plugwise Home Automation Binding</name>
|
||||
<description>This binding supports the Plugwise Home Automation 'Adam' gateway. It allows users to access temperature
|
||||
controls of zones defined on the gateway</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<!-- Bridge -->
|
||||
<config-description uri="bridge-type:plugwiseha:gateway">
|
||||
<parameter name="host" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Host</label>
|
||||
<description>Hostname or IP address of the boiler gateway</description>
|
||||
<default>adam</default>
|
||||
</parameter>
|
||||
<parameter name="username" type="text" required="true">
|
||||
<label>Username</label>
|
||||
<description>Adam HA gateway username (default: smile)</description>
|
||||
<default>smile</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="smileId" type="text" pattern="[a-zA-Z0-9]{8}" required="true">
|
||||
<context>password</context>
|
||||
<label>Smile ID</label>
|
||||
<description>The Smile ID is the 8 letter code on the sticker on the back of the Adam boiler gateway</description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="1" max="120" required="true" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
<description>Refresh interval in seconds</description>
|
||||
<default>5</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<!-- Zone thing -->
|
||||
<config-description uri="thing-type:plugwiseha:zone">
|
||||
<parameter name="id" type="text" required="true" readOnly="false">
|
||||
<label>ID</label>
|
||||
<description>Location ID for the zone</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:plugwiseha:appliance_boiler">
|
||||
<parameter name="id" type="text" required="true" readOnly="false">
|
||||
<label>ID</label>
|
||||
<description>Appliance ID</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<!-- Appliance: Radiator valve -->
|
||||
<config-description uri="thing-type:plugwiseha:appliance_valve">
|
||||
<parameter name="id" type="text" required="true" readOnly="false">
|
||||
<label>ID</label>
|
||||
<description>Appliance ID</description>
|
||||
</parameter>
|
||||
<parameter name="lowBatteryPercentage" type="integer" min="1" max="50" required="true">
|
||||
<label>Low Battery Threshold</label>
|
||||
<unitLabel>%</unitLabel>
|
||||
<description>Battery charge remaining at which to trigger battery low warning</description>
|
||||
<default>15</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<!-- Appliance: Pump switch -->
|
||||
<config-description uri="thing-type:plugwiseha:appliance_pump">
|
||||
<parameter name="id" type="text" required="true" readOnly="false">
|
||||
<label>ID</label>
|
||||
<description>Appliance ID</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<!-- Appliance: Radiator valve -->
|
||||
<config-description uri="thing-type:plugwiseha:appliance_thermostat">
|
||||
<parameter name="id" type="text" required="true" readOnly="false">
|
||||
<label>ID</label>
|
||||
<description>Appliance ID</description>
|
||||
</parameter>
|
||||
<parameter name="lowBatteryPercentage" type="integer" min="1" max="50" required="true">
|
||||
<label>Low Battery Threshold</label>
|
||||
<unitLabel>%</unitLabel>
|
||||
<description>Battery charge remaining at which to trigger battery low warning</description>
|
||||
<default>15</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="plugwiseha"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<channel-type id="setpointTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Setpoint Temperature</label>
|
||||
<description>Gets or sets the set point of this zone</description>
|
||||
<category>heating</category>
|
||||
<state min="0.0" max="35.0" step="0.5" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Zone Temperature</label>
|
||||
<description>Gets the temperature of this zone</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="offsetTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Thermostat Temperature Offset</label>
|
||||
<description>Gets or sets the temperature offset for this thermostat</description>
|
||||
<category>heating</category>
|
||||
<state pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="preHeat">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Preheat</label>
|
||||
<description>Switch the preheating of a zone ON or OFF</description>
|
||||
<category>switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="power">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Power</label>
|
||||
<description>Switch the Plugwise Smart plug ON or OFF</description>
|
||||
<category>switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lock">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lock</label>
|
||||
<description>Locks the switch state of the Plugwise Smart plug</description>
|
||||
<category>switch</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="powerUsage">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Power Usage</label>
|
||||
<state pattern="%.2f %unit%" readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="chState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Central Heating Active</label>
|
||||
<description>Is the boiler active for central heating, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhwState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Domestic Hot Water Active</label>
|
||||
<description>Is the boiler active for domestic hot water, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="coolingState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Cooling State</label>
|
||||
<description>Is the boiler active for cooling, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="flameState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Flame State</label>
|
||||
<description>Is the boiler's flame active, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="intendedHeatingState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Intended Heating State</label>
|
||||
<description>Should the boiler be active for central heating, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhwComfortMode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Domestic Hot Water Comfort Mode</label>
|
||||
<description>Is the boiler's domestic hot water mode set to comfort, On or OFF</description>
|
||||
<category>switch</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="intendedBoilerTemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Intended Boiler Temperature</label>
|
||||
<description>Gets the intended temperature of this boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="modulationLevel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Modulelation Level</label>
|
||||
<description>Gets the modulation level of this boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.0f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="otAppFaultCode">
|
||||
<item-type>Number</item-type>
|
||||
<label>Opentherm Application Faultcode</label>
|
||||
<description>Gets the Opentherm application fault code of this boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.0f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhwTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Domestic Hot Water Temperature</label>
|
||||
<description>Gets the temperature of the domestic hot water</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="otOEMFaultCode">
|
||||
<item-type>Number</item-type>
|
||||
<label>OEM Fault Code</label>
|
||||
<description>Gets the OEM fault code of this boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.0f"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="boilerTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Boiler Temperature</label>
|
||||
<description>Gets the temperature of this boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhwSetpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Domestic Hot Water Setpoint Temperature</label>
|
||||
<description>Gets the temperature of the domestic hot water setpoint</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxBoilerTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Max Boiler Temperature</label>
|
||||
<description>Gets the maximum temperature ofthis boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="waterPressure">
|
||||
<item-type>Number:Pressure</item-type>
|
||||
<label>Water Pressure</label>
|
||||
<description>Gets the water pressure of the boiler</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="presetScene">
|
||||
<item-type>String</item-type>
|
||||
<label>Preset Scene</label>
|
||||
<description>Gets the preset scene of the zone</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="valvePosition">
|
||||
<item-type>Number</item-type>
|
||||
<label>Valve Position</label>
|
||||
<description>Gets the position of the valve (0% closed, 100% open)</description>
|
||||
<category>heating</category>
|
||||
<state readOnly="true" pattern="%.0f"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="plugwiseha"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- Bridge -->
|
||||
<bridge-type id="gateway">
|
||||
<label>Plugwise Home Automation Bridge</label>
|
||||
<description>The Plugwise Home Automation Bridge is needed to connect to the Adam boiler gateway</description>
|
||||
|
||||
<config-description-ref uri="bridge-type:plugwiseha:gateway"/>
|
||||
</bridge-type>
|
||||
|
||||
<!-- Zone thing -->
|
||||
<thing-type id="appliance_boiler" listed="true">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Boiler</label>
|
||||
<description>A Plugwise Home Automation controlled boiler</description>
|
||||
|
||||
<channels>
|
||||
<channel id="chState" typeId="chState"/>
|
||||
<channel id="dhwState" typeId="dhwState"/>
|
||||
<channel id="waterPressure" typeId="waterPressure"/>
|
||||
<channel id="coolingState" typeId="coolingState"/>
|
||||
<channel id="flameState" typeId="flameState"/>
|
||||
<channel id="intendedHeatingState" typeId="intendedHeatingState"/>
|
||||
<channel id="dhwComfortMode" typeId="dhwComfortMode"/>
|
||||
<channel id="intendedBoilerTemp" typeId="intendedBoilerTemp"/>
|
||||
<channel id="modulationLevel" typeId="modulationLevel"/>
|
||||
<channel id="otAppFaultCode" typeId="otAppFaultCode"/>
|
||||
<channel id="dhwTemperature" typeId="dhwTemperature"/>
|
||||
<channel id="otOEMFaultCode" typeId="otOEMFaultCode"/>
|
||||
<channel id="boilerTemperature" typeId="boilerTemperature"/>
|
||||
<channel id="dhwSetpoint" typeId="dhwSetpoint"/>
|
||||
<channel id="maxBoilerTemperature" typeId="maxBoilerTemperature"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:plugwiseha:appliance_boiler"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Zone thing -->
|
||||
<thing-type id="zone" listed="true">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Plugwise Zone</label>
|
||||
<description>A Plugwise Home Automation heating zone</description>
|
||||
|
||||
<channels>
|
||||
<channel id="setpointTemperature" typeId="setpointTemperature"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="presetScene" typeId="presetScene"/>
|
||||
<channel id="preHeat" typeId="preHeat"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:plugwiseha:zone"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Appliance: Radiator valve (Tom) -->
|
||||
<thing-type id="appliance_valve" listed="true">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Plugwise Radiator Valve</label>
|
||||
<description>A Plugwise Home Automation radiator valve</description>
|
||||
|
||||
<channels>
|
||||
<channel id="setpointTemperature" typeId="setpointTemperature"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="valvePosition" typeId="valvePosition"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:plugwiseha:appliance_valve"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Appliance: Pump switch (Circle) -->
|
||||
<thing-type id="appliance_pump" listed="true">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Central Heating Pump</label>
|
||||
<description>A Plugwise Home Automation smart plug switch connected to a central heating pump</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="power"/>
|
||||
<channel id="lock" typeId="lock"/>
|
||||
<channel id="powerUsage" typeId="powerUsage"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:plugwiseha:appliance_pump"/>
|
||||
</thing-type>
|
||||
|
||||
<!-- Appliance: Zone thermostat (Lisa) -->
|
||||
<thing-type id="appliance_thermostat" listed="true">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Plugwise Room Thermostat</label>
|
||||
<description>A Plugwise Home Automation room thermostat</description>
|
||||
|
||||
<channels>
|
||||
<channel id="setpointTemperature" typeId="setpointTemperature"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="offsetTemperature" typeId="offsetTemperature"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:plugwiseha:appliance_thermostat"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,127 @@
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<!-- modified identity transform -->
|
||||
<xsl:template match="/domain_objects">
|
||||
<xsl:element name="{local-name()}">
|
||||
<xsl:apply-templates select="gateway" />
|
||||
<xsl:apply-templates select="appliance" />
|
||||
<xsl:apply-templates select="location" />
|
||||
<xsl:apply-templates select="module" />
|
||||
</xsl:element>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="node()">
|
||||
<!-- prevent duplicate siblings -->
|
||||
<xsl:if test="count(preceding-sibling::node()[name()=name(current())])=0">
|
||||
<!-- copy element -->
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="appliance">
|
||||
<!-- copy element -->
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="location">
|
||||
<!-- copy element -->
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="module">
|
||||
<!-- copy element -->
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="protocols/node()[name()='zig_bee_node']"/>
|
||||
<xsl:apply-templates select="@*|node()[name()!='protocols']"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="location/appliances">
|
||||
<!-- Apply identity transform on child elements of appliances -->
|
||||
<xsl:for-each select="appliance">
|
||||
<xsl:copy>
|
||||
<xsl:value-of select="@id"/>
|
||||
</xsl:copy>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="module/services">
|
||||
<xsl:for-each select="./node()">
|
||||
<xsl:element name="service">
|
||||
<xsl:element name="point_log">
|
||||
<xsl:value-of select="functionalities/point_log/@id"/>
|
||||
</xsl:element>
|
||||
<xsl:apply-templates select="@*|node()[name()!='functionalities']"/>
|
||||
</xsl:element>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<!-- This matches 'appliance/logs' or 'location/logs' -->
|
||||
<xsl:template match="*[name() = 'location' or name()='appliance']/logs">
|
||||
<!-- Apply identity transform on child elements of logs -->
|
||||
<xsl:variable name="meter_id" select="point_log/*[substring(local-name(), string-length(local-name()) - string-length('_meter')+1) = '_meter']/@id"/>
|
||||
<xsl:apply-templates select="/domain_objects/module/services/*[@id=$meter_id]/../../protocols/zig_bee_node"/>
|
||||
|
||||
<xsl:for-each select="point_log">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="appliance/location">
|
||||
<!-- Apply identity transform on child elements of location -->
|
||||
<xsl:copy>
|
||||
<xsl:value-of select="@id"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="logs/point_log/period">
|
||||
<xsl:element name="measurement_date">
|
||||
<xsl:value-of select="measurement/@log_date"/>
|
||||
</xsl:element>
|
||||
<xsl:element name="measurement">
|
||||
<xsl:value-of select="measurement/text()"/>
|
||||
</xsl:element>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="*[name() = 'location' or name()='appliance']/actuator_functionalities">
|
||||
<xsl:for-each select="./*">
|
||||
<xsl:element name="actuator_functionality">
|
||||
<xsl:if test="not(type)">
|
||||
<xsl:choose>
|
||||
<xsl:when test="local-name()='relay_functionality'">
|
||||
<xsl:element name="type">
|
||||
<xsl:text>relay</xsl:text>
|
||||
</xsl:element>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:element name="type">
|
||||
<xsl:value-of select="local-name()"/>
|
||||
</xsl:element>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:if>
|
||||
<xsl:for-each select=".">
|
||||
<xsl:apply-templates select="@*|node()"/>
|
||||
</xsl:for-each>
|
||||
</xsl:element>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<!-- attributes to elements -->
|
||||
<xsl:template match="@*">
|
||||
<xsl:element name="{name()}">
|
||||
<xsl:value-of select="."/>
|
||||
</xsl:element>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
@ -261,6 +261,7 @@
|
||||
<module>org.openhab.binding.playstation</module>
|
||||
<module>org.openhab.binding.plclogo</module>
|
||||
<module>org.openhab.binding.plugwise</module>
|
||||
<module>org.openhab.binding.plugwiseha</module>
|
||||
<module>org.openhab.binding.powermax</module>
|
||||
<module>org.openhab.binding.pulseaudio</module>
|
||||
<module>org.openhab.binding.pushbullet</module>
|
||||
|
Loading…
Reference in New Issue
Block a user