added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.openthermgateway/.classpath
Normal file
32
bundles/org.openhab.binding.openthermgateway/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.openthermgateway/.project
Normal file
23
bundles/org.openhab.binding.openthermgateway/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.openthermgateway</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.openthermgateway/NOTICE
Normal file
13
bundles/org.openhab.binding.openthermgateway/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
|
||||
142
bundles/org.openhab.binding.openthermgateway/README.md
Normal file
142
bundles/org.openhab.binding.openthermgateway/README.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# OpenTherm Gateway Binding
|
||||
|
||||
This binding is used to integrate the OpenTherm Gateway into openHAB.
|
||||
The OpenTherm Gateway is a module designed by Schelte Bron that is connected in between a boiler and a thermostat and communicates using the OpenTherm protocol.
|
||||
|
||||
More information on the OpenTherm Gateway device can be found at http://otgw.tclcode.com/
|
||||
|
||||
## Supported Things
|
||||
|
||||
The OpenTherm Gateway binding currently only supports one thing, and that's the gateway itself.
|
||||
|
||||
## Discovery
|
||||
|
||||
The binding does not support auto discovery.
|
||||
|
||||
## Binding Configuration
|
||||
|
||||
The binding itself does not require any configuration.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The binding is designed to support various ways of connecting to the OpenTherm Gateway device, but currently only supports a TCP socket connection.
|
||||
The configuration settings for the thing are Hostname/IP address and Port, which are used to connect to the gateway, and an automatic connection retry interval in case the connection to the OpenTherm Gateway device is lost.
|
||||
|
||||
| Parameter | Name | Description | Required | Default |
|
||||
|---------------------------|---------------------------|-----------------------------------------------------------------|----------|---------|
|
||||
| `ipaddress` | Hostname or IP address | The hostname or IP address to connect to the OpenTherm Gateway. | yes | |
|
||||
| `port` | Port | The port used to connect to the OpenTherm Gateway. | yes | |
|
||||
| `connectionRetryInterval` | Connection Retry Interval | The interval in seconds to retry connecting (0 = disabled). | yes | 60 |
|
||||
|
||||
## Channels
|
||||
|
||||
The OpenTherm Gateway binding supports the following channels:
|
||||
|
||||
| Channel Type ID | Item Type | Description | Read Only |
|
||||
|----------------------|-----------|----------------------------------------------------------|-----------|
|
||||
| roomtemp | Number | Current sensed room temperature | yes |
|
||||
| roomsetpoint | Number | Current room temperature setpoint | yes |
|
||||
| temperaturetemporary | Number | Temporary override room temperature setpoint | no |
|
||||
| temperatureconstant | Number | Constant override room temperature setpoint | no |
|
||||
| controlsetpoint | Number | Central heating water setpoint | yes |
|
||||
| dhwtemp | Number | Domestic hot water temperature | yes |
|
||||
| tdhwset | Number | Domestic hot water temperature setpoint | yes |
|
||||
| overridedhwsetpoint | Number | Domestic hot water temperature setpoint override | no |
|
||||
| flowtemp | Number | Boiler water temperature | yes |
|
||||
| returntemp | Number | Return water temperature | yes |
|
||||
| outsidetemp | Number | Outside temperature | no |
|
||||
| waterpressure | Number | Central heating water pressure | yes |
|
||||
| ch_enable | Switch | Central heating enabled | yes |
|
||||
| ch_mode | Switch | Central heating active | yes |
|
||||
| dhw_enable | Switch | Domestic hot water enabled | yes |
|
||||
| dhw_mode | Switch | Domestic hot water active | yes |
|
||||
| flame | Switch | Burner active | yes |
|
||||
| modulevel | Number | Relative modulation level | yes |
|
||||
| maxrelmdulevel | Number | Maximum relative modulation level | yes |
|
||||
| fault | Switch | Fault indication | yes |
|
||||
| servicerequest | Switch | Service required | yes |
|
||||
| lockout-reset | Switch | Lockout-reset enabled | yes |
|
||||
| lowwaterpress | Switch | Low water pressure fault | yes |
|
||||
| gasflamefault | Switch | Gas or flame fault | yes |
|
||||
| airpressfault | Switch | Air pressure fault | yes |
|
||||
| waterovtemp | Switch | Water over-temperature fault | yes |
|
||||
| oemfaultcode | Switch | OEM fault code | yes |
|
||||
| sendcommand | Text | Channel to send commands to the OpenTherm Gateway device | no |
|
||||
|
||||
## Full Example
|
||||
|
||||
### demo.things
|
||||
|
||||
```
|
||||
Thing openthermgateway:otgw:1 [ ipaddress="192.168.1.100", port=8000, connectionRetryInterval=60 ]
|
||||
```
|
||||
|
||||
### demo.items
|
||||
|
||||
```
|
||||
Number RoomTemperature "Room temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:roomtemp" }
|
||||
Number RoomSetpoint "Room setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:roomsetpoint" }
|
||||
Number TemporaryRoomSetpointOverride "Temporary room setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:temperaturetemporary" }
|
||||
Number ConstantRoomSetpointOverride "Constant room setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:temperatureconstant" }
|
||||
Number ControlSetpoint "Control setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpoint" }
|
||||
Number DomesticHotWaterTemperature "Domestic hot water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:dhwtemp" }
|
||||
Number DomesticHotWaterSetpoint "Domestic hot water setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:tdhwset" }
|
||||
Number DomesticHotWaterSetpointOverride "Domestic hot water setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:overridedhwsetpoint" }
|
||||
Number BoilerWaterTemperature "Boiler water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:flowtemp" }
|
||||
Number ReturnWaterTemperature "Return water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:returntemp" }
|
||||
Number OutsideTemperature "Outside temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:outsidetemp" }
|
||||
Number CentralHeatingWaterPressure "Central heating water pressure [%.1f bar]" { channel="openthermgateway:otgw:1:waterpressure" }
|
||||
Switch CentralHeatingEnabled "Central heating enabled" <switch> { channel="openthermgateway:otgw:1:ch_enable" }
|
||||
Switch CentralHeatingActive "Central heating active" <switch> { channel="openthermgateway:otgw:1:ch_mode" }
|
||||
Switch DomesticHotWaterEnabled "Domestic hot water enabled" <switch> { channel="openthermgateway:otgw:1:dhw_enable" }
|
||||
Switch DomesticHotWaterActive "Domestic hot water active" <switch> { channel="openthermgateway:otgw:1:dhw_mode" }
|
||||
Switch BurnerActive "Burner active" <switch> { channel="openthermgateway:otgw:1:flame" }
|
||||
Number RelativeModulationLevel "Relative modulation level [%.1f %%]" { channel="openthermgateway:otgw:1:modulevel" }
|
||||
Number MaximumRelativeModulationLevel "Maximum relative modulation level [%.1f %%]" { channel="openthermgateway:otgw:1:maxrelmdulevel" }
|
||||
Switch Fault "Fault indication" <switch> { channel="openthermgateway:otgw:1:fault" }
|
||||
Switch ServiceRequest "Service required" <switch> { channel="openthermgateway:otgw:1:servicerequest" }
|
||||
Switch LockoutReset "Lockout-reset" <switch> { channel="openthermgateway:otgw:1:lockout-reset" }
|
||||
Switch LowWaterPress "Low water pressure fault" <switch> { channel="openthermgateway:otgw:1:lowwaterpress" }
|
||||
Switch GasFlameFault "Gas or flame fault" <switch> { channel="openthermgateway:otgw:1:gasflamefault" }
|
||||
Switch AirPressFault "Air pressure fault" <switch> { channel="openthermgateway:otgw:1:airpressfault" }
|
||||
Switch WaterOvTemp "Water over-temperature fault" <switch> { channel="openthermgateway:otgw:1:waterovtemp" }
|
||||
Number OemFaultCode "OEM fault code" { channel="openthermgateway:otgw:1:oemfaultcode" }
|
||||
Text SendCommand "Send command channel" { channel="openthermgateway:otgw:1:sendcommand" }
|
||||
```
|
||||
|
||||
### demo.sitemap
|
||||
|
||||
```
|
||||
sitemap demo label="Main Menu" {
|
||||
Frame label="OpenTherm Gateway" {
|
||||
Text item="RoomTemperature" icon="temperature" label="Room temperature [%.1f °C]"
|
||||
Text item="RoomSetpoint" icon="temperature" label="Room setpoint [%.1f °C]"
|
||||
Setpoint item="TemporaryRoomSetpointOverride" icon="temperature" label="Temporary room setpoint override [%.1f °C]" minValue="0" maxValue="30" step="0.1"
|
||||
Setpoint item="ConstantRoomSetpointOverride" icon="temperature" label="Constant room setpoint override [%.1f °C]" minValue="0" maxValue="30" step="0.1"
|
||||
Text item="ControlSetpoint" icon="temperature" label="Control setpoint [%.1f °C]"
|
||||
Text item="DomesticHotWaterTemperature" icon="temperature" label="Domestic hot water temperature [%.1f °C]"
|
||||
Text item="DomesticHotWaterSetpoint" icon="temperature" label="Domestic hot water setpoint [%.1f °C]"
|
||||
Setpoint item="DomesticHotWaterSetpointOverride" icon="temperature" label="Domestic hot water setpoint override [%.1f °C]" minValue="0" maxValue="100" step="0.1"
|
||||
Text item="BoilerWaterTemperature" icon="temperature" label="Boiler water temperature [%.1f °C]"
|
||||
Text item="ReturnWaterTemperature" icon="temperature" label="Return water temperature [%.1f °C]"
|
||||
Setpoint item="OutsideTemperature" icon="temperature" label="Outside temperature [%.1f °C]" minValue="-40" maxValue="100" step="0.1"
|
||||
Text item="CentralHeatingWaterPressure" icon="" label="Central heating water pressure [%.1f bar]"
|
||||
Switch item="CentralHeatingEnabled" icon="switch" label="Central heating enabled"
|
||||
Switch item="CentralHeatingActive" icon="switch" label="Central heating active"
|
||||
Switch item="DomesticHotWaterEnabled" icon="switch" label="Domestic hot water enabled"
|
||||
Switch item="DomesticHotWaterActive" icon="switch" label="Domestic hot water active"
|
||||
Switch item="BurnerActive" icon="switch" label="Burner active"
|
||||
Text item="RelativeModulationLevel" icon="" label="Relative modulation level [%.1f %%]"
|
||||
Text item="MaximumRelativeModulationLevel" icon="" label="Maximum relative modulation level [%.1f %%]"
|
||||
Switch item="Fault" icon="" label="Fault indication"
|
||||
Switch item="ServiceRequest" icon="" label="Service required"
|
||||
Switch item="LockoutReset" icon="" label="Lockout-reset"
|
||||
Switch item="LowWaterPress" icon="" label="Low water pressure fault"
|
||||
Switch item="GasFlameFault" icon="" label="Gas or flame fault"
|
||||
Switch item="AirPressFault" icon="" label="Air pressure fault"
|
||||
Switch item="waterOvTemp" icon="" label="Water over-temperature fault"
|
||||
Text item="OemFaultCode" icon="" label="OEM fault code"
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
17
bundles/org.openhab.binding.openthermgateway/pom.xml
Normal file
17
bundles/org.openhab.binding.openthermgateway/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.openthermgateway</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: OpenTherm Gateway Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.openthermgateway-${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-openthermgateway" description="OpenThermGateway Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.openthermgateway/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenThermGatewayBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "openthermgateway";
|
||||
|
||||
// List of all Thing Type UID's
|
||||
public static final ThingTypeUID MAIN_THING_TYPE = new ThingTypeUID(BINDING_ID, "otgw");
|
||||
|
||||
// List of all Channel id's
|
||||
public static final String CHANNEL_SEND_COMMAND = "sendcommand";
|
||||
|
||||
public static final String CHANNEL_OVERRIDE_SETPOINT_TEMPORARY = "temperaturetemporary";
|
||||
public static final String CHANNEL_OVERRIDE_SETPOINT_CONSTANT = "temperatureconstant";
|
||||
public static final String CHANNEL_OVERRIDE_DHW_SETPOINT = "overridedhwsetpoint";
|
||||
public static final String CHANNEL_ROOM_TEMPERATURE = "roomtemp";
|
||||
public static final String CHANNEL_ROOM_SETPOINT = "roomsetpoint";
|
||||
public static final String CHANNEL_FLOW_TEMPERATURE = "flowtemp";
|
||||
public static final String CHANNEL_RETURN_TEMPERATURE = "returntemp";
|
||||
public static final String CHANNEL_OUTSIDE_TEMPERATURE = "outsidetemp";
|
||||
public static final String CHANNEL_CENTRAL_HEATING_WATER_SETPOINT = "controlsetpoint";
|
||||
public static final String CHANNEL_CENTRAL_HEATING_WATER_PRESSURE = "waterpressure";
|
||||
public static final String CHANNEL_CENTRAL_HEATING_ENABLED = "ch_enable";
|
||||
public static final String CHANNEL_CENTRAL_HEATING_MODE = "ch_mode";
|
||||
public static final String CHANNEL_DOMESTIC_HOT_WATER_TEMPERATURE = "dhwtemp";
|
||||
public static final String CHANNEL_DOMESTIC_HOT_WATER_ENABLED = "dhw_enable";
|
||||
public static final String CHANNEL_DOMESTIC_HOT_WATER_MODE = "dhw_mode";
|
||||
public static final String CHANNEL_DOMESTIC_HOT_WATER_SETPOINT = "tdhwset";
|
||||
public static final String CHANNEL_FLAME = "flame";
|
||||
public static final String CHANNEL_RELATIVE_MODULATION_LEVEL = "modulevel";
|
||||
public static final String CHANNEL_MAXIMUM_MODULATION_LEVEL = "maxrelmdulevel";
|
||||
public static final String CHANNEL_FAULT = "fault";
|
||||
public static final String CHANNEL_SERVICEREQUEST = "servicerequest";
|
||||
public static final String CHANNEL_REMOTE_RESET = "lockout-reset";
|
||||
public static final String CHANNEL_LOW_WATER_PRESSURE = "lowwaterpress";
|
||||
public static final String CHANNEL_GAS_FLAME_FAULT = "gasflamefault";
|
||||
public static final String CHANNEL_AIR_PRESSURE_FAULT = "airpressfault";
|
||||
public static final String CHANNEL_WATER_OVER_TEMP = "waterovtemp";
|
||||
public static final String CHANNEL_OEM_FAULTCODE = "oemfaultcode";
|
||||
|
||||
public static final Set<String> SUPPORTED_CHANNEL_IDS = Collections
|
||||
.unmodifiableSet(Stream.of(CHANNEL_ROOM_TEMPERATURE, CHANNEL_ROOM_SETPOINT, CHANNEL_FLOW_TEMPERATURE,
|
||||
CHANNEL_RETURN_TEMPERATURE, CHANNEL_OUTSIDE_TEMPERATURE, CHANNEL_CENTRAL_HEATING_WATER_PRESSURE,
|
||||
CHANNEL_CENTRAL_HEATING_ENABLED, CHANNEL_CENTRAL_HEATING_MODE,
|
||||
CHANNEL_CENTRAL_HEATING_WATER_SETPOINT, CHANNEL_DOMESTIC_HOT_WATER_TEMPERATURE,
|
||||
CHANNEL_DOMESTIC_HOT_WATER_ENABLED, CHANNEL_DOMESTIC_HOT_WATER_MODE,
|
||||
CHANNEL_DOMESTIC_HOT_WATER_SETPOINT, CHANNEL_FLAME, CHANNEL_RELATIVE_MODULATION_LEVEL,
|
||||
CHANNEL_MAXIMUM_MODULATION_LEVEL, CHANNEL_FAULT, CHANNEL_SERVICEREQUEST, CHANNEL_REMOTE_RESET,
|
||||
CHANNEL_LOW_WATER_PRESSURE, CHANNEL_GAS_FLAME_FAULT, CHANNEL_AIR_PRESSURE_FAULT,
|
||||
CHANNEL_WATER_OVER_TEMP, CHANNEL_OEM_FAULTCODE).collect(Collectors.toSet()));
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.handler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openthermgateway.OpenThermGatewayBindingConstants;
|
||||
import org.openhab.binding.openthermgateway.internal.DataItem;
|
||||
import org.openhab.binding.openthermgateway.internal.DataItemGroup;
|
||||
import org.openhab.binding.openthermgateway.internal.GatewayCommand;
|
||||
import org.openhab.binding.openthermgateway.internal.GatewayCommandCode;
|
||||
import org.openhab.binding.openthermgateway.internal.Message;
|
||||
import org.openhab.binding.openthermgateway.internal.OpenThermGatewayCallback;
|
||||
import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConfiguration;
|
||||
import org.openhab.binding.openthermgateway.internal.OpenThermGatewayConnector;
|
||||
import org.openhab.binding.openthermgateway.internal.OpenThermGatewaySocketConnector;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThermGatewayCallback {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class);
|
||||
|
||||
private @Nullable OpenThermGatewayConfiguration config;
|
||||
|
||||
private @Nullable OpenThermGatewayConnector connector;
|
||||
|
||||
private boolean connecting = false;
|
||||
|
||||
private boolean explicitDisconnect = false;
|
||||
|
||||
public OpenThermGatewayHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing OpenTherm Gateway handler for uid '{}'", getThing().getUID());
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Initializing");
|
||||
|
||||
config = getConfigAs(OpenThermGatewayConfiguration.class);
|
||||
|
||||
connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Received channel: {}, command: {}", channelUID, command);
|
||||
|
||||
if (!(command instanceof RefreshType)) {
|
||||
String channel = channelUID.getId();
|
||||
String code = getGatewayCodeFromChannel(channel);
|
||||
|
||||
GatewayCommand gatewayCommand = null;
|
||||
|
||||
if (command instanceof QuantityType<?>) {
|
||||
QuantityType<?> quantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
|
||||
|
||||
if (quantityType != null) {
|
||||
double value = quantityType.doubleValue();
|
||||
gatewayCommand = GatewayCommand.parse(code, Double.toString(value));
|
||||
}
|
||||
}
|
||||
|
||||
if (gatewayCommand == null) {
|
||||
gatewayCommand = GatewayCommand.parse(code, command.toFullString());
|
||||
}
|
||||
|
||||
if (checkConnection()) {
|
||||
@Nullable
|
||||
OpenThermGatewayConnector conn = connector;
|
||||
|
||||
if (conn != null) {
|
||||
conn.sendCommand(gatewayCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connecting() {
|
||||
connecting = true;
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Connecting");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected() {
|
||||
connecting = false;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
@Nullable
|
||||
OpenThermGatewayConnector conn = connector;
|
||||
|
||||
@Nullable
|
||||
OpenThermGatewayConfiguration conf = config;
|
||||
|
||||
connecting = false;
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected");
|
||||
|
||||
// retry connection if disconnect is not explicitly requested
|
||||
if (conf != null && !explicitDisconnect && conf.connectionRetryInterval > 0) {
|
||||
scheduler.schedule(() -> {
|
||||
if (conn != null && !connecting && !conn.isConnected()) {
|
||||
connect();
|
||||
}
|
||||
}, conf.connectionRetryInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveMessage(Message message) {
|
||||
if (DataItemGroup.dataItemGroups.containsKey(message.getID())) {
|
||||
DataItem[] dataItems = DataItemGroup.dataItemGroups.get(message.getID());
|
||||
|
||||
for (DataItem dataItem : dataItems) {
|
||||
String channelId = dataItem.getSubject();
|
||||
|
||||
if (!OpenThermGatewayBindingConstants.SUPPORTED_CHANNEL_IDS.contains(channelId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
State state = null;
|
||||
|
||||
switch (dataItem.getDataType()) {
|
||||
case FLAGS:
|
||||
state = OnOffType.from(message.getBit(dataItem.getByteType(), dataItem.getBitPos()));
|
||||
break;
|
||||
case UINT8:
|
||||
case UINT16:
|
||||
state = new DecimalType(message.getUInt(dataItem.getByteType()));
|
||||
break;
|
||||
case INT8:
|
||||
case INT16:
|
||||
state = new DecimalType(message.getInt(dataItem.getByteType()));
|
||||
break;
|
||||
case FLOAT:
|
||||
float value = message.getFloat();
|
||||
@Nullable
|
||||
Unit<?> unit = dataItem.getUnit();
|
||||
state = (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit);
|
||||
break;
|
||||
case DOWTOD:
|
||||
break;
|
||||
}
|
||||
|
||||
if (state != null) {
|
||||
logger.debug("Received update for channel '{}': {}", channelId, state);
|
||||
updateState(channelId, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
logger.debug("Removing OpenTherm Gateway handler");
|
||||
disconnect();
|
||||
super.handleRemoval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disconnect();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private boolean checkConnection() {
|
||||
@Nullable
|
||||
OpenThermGatewayConnector conn = connector;
|
||||
|
||||
if (conn != null && conn.isConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return connect();
|
||||
}
|
||||
|
||||
private boolean connect() {
|
||||
@Nullable
|
||||
OpenThermGatewayConfiguration conf = config;
|
||||
|
||||
disconnect();
|
||||
|
||||
if (conf != null) {
|
||||
logger.debug("Starting OpenTherm Gateway connector");
|
||||
|
||||
explicitDisconnect = false;
|
||||
|
||||
connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port);
|
||||
|
||||
Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
logger.debug("OpenTherm Gateway connector started");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
@Nullable
|
||||
OpenThermGatewayConnector conn = connector;
|
||||
|
||||
if (conn != null) {
|
||||
if (conn.isConnected()) {
|
||||
logger.debug("Stopping OpenTherm Gateway connector");
|
||||
|
||||
explicitDisconnect = true;
|
||||
conn.stop();
|
||||
}
|
||||
|
||||
connector = null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException {
|
||||
switch (channel) {
|
||||
case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY:
|
||||
return GatewayCommandCode.TemperatureTemporary;
|
||||
case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT:
|
||||
return GatewayCommandCode.TemperatureConstant;
|
||||
case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE:
|
||||
return GatewayCommandCode.TemperatureOutside;
|
||||
case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT:
|
||||
return GatewayCommandCode.SetpointWater;
|
||||
case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND:
|
||||
return null;
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Unknown channel %s", channel));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
/**
|
||||
* The {@link ByteType} enum specifies whether the upper, lower or both bytes are used
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
public enum ByteType {
|
||||
HIGHBYTE,
|
||||
LOWBYTE,
|
||||
BOTH
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link DataItem} holds the internal OpenTherm message and meta data.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataItem {
|
||||
private int id;
|
||||
private Msg msg;
|
||||
private ByteType byteType;
|
||||
private DataType dataType;
|
||||
private int bitpos;
|
||||
private String subject;
|
||||
private @Nullable Unit<?> unit;
|
||||
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Msg getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public ByteType getByteType() {
|
||||
return this.byteType;
|
||||
}
|
||||
|
||||
public DataType getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public int getBitPos() {
|
||||
return bitpos;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public @Nullable Unit<?> getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject) {
|
||||
this.id = id;
|
||||
this.msg = msg;
|
||||
this.byteType = byteType;
|
||||
this.dataType = dataType;
|
||||
this.bitpos = bit;
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject, Unit<?> unit) {
|
||||
this(id, msg, byteType, dataType, bit, subject);
|
||||
this.unit = unit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
|
||||
/**
|
||||
* The {@link DataItemGroup} represents a list of all possible DataItem messages within the OpenTherm specification.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataItemGroup {
|
||||
|
||||
public static final Map<Integer, DataItem[]> dataItemGroups = createDataItemGroups();
|
||||
|
||||
private static Map<Integer, DataItem[]> createDataItemGroups() {
|
||||
HashMap<Integer, DataItem[]> g = new HashMap<>();
|
||||
|
||||
g.put(0, new DataItem[] { new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "ch_enable"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "dhw_enable"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "cooling_enabled"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "otc_active"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "ch2_enable"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "0x00:5"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x00:6"),
|
||||
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x00:7"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 0, "fault"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 1, "ch_mode"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 2, "dhw_mode"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 3, "flame"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 4, "cooling"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 5, "ch2E"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 6, "diag"),
|
||||
new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 7, "0x00:7") });
|
||||
g.put(1, new DataItem[] {
|
||||
new DataItem(1, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpoint", SIUnits.CELSIUS) });
|
||||
g.put(2, new DataItem[] { new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 0, "0x02:0"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 1, "0x02:1"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 2, "0x02:2"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 3, "0x02:3"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 4, "0x02:4"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 5, "0x02:5"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x02:6"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x02:7"),
|
||||
new DataItem(2, Msg.WRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "mastermemberid") });
|
||||
g.put(3, new DataItem[] { new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "dhwpresent"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "controltype"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "coolingsupport"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "dhwconfig"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "masterlowoff"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "ch2present"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x03:6"),
|
||||
new DataItem(3, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x03:7"),
|
||||
new DataItem(3, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "slavememberid") });
|
||||
g.put(4, new DataItem[] { new DataItem(4, Msg.WRITE, ByteType.HIGHBYTE, DataType.UINT8, 0, "commandcode"),
|
||||
new DataItem(4, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "commandresponse") });
|
||||
g.put(5, new DataItem[] { new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "servicerequest"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "lockout-reset"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "lowwaterpress"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "gasflamefault"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "airpressfault"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "waterovtemp"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x05:6"),
|
||||
new DataItem(5, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x05:7"),
|
||||
new DataItem(5, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "oemfaultcode") });
|
||||
g.put(6, new DataItem[] { new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 0, "0x06:l0"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 1, "0x06:l1"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 2, "0x06:l2"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 3, "0x06:l3"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 4, "0x06:l4"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 5, "0x06:l5"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 6, "0x06:l6"),
|
||||
new DataItem(6, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 7, "0x06:l7"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "0x06:h0"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "0x06:h1"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "0x06:h2"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "0x06:h3"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "0x06:h4"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "0x06:h5"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x06:h6"),
|
||||
new DataItem(6, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x06:h7") });
|
||||
g.put(7, new DataItem[] { new DataItem(7, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "0x07") });
|
||||
g.put(8, new DataItem[] { new DataItem(8, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpoint2") });
|
||||
g.put(9, new DataItem[] { new DataItem(9, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "overridesetpoint") });
|
||||
g.put(10, new DataItem[] { new DataItem(10, Msg.WRITE, ByteType.HIGHBYTE, DataType.UINT8, 0, "0x0a:h"),
|
||||
new DataItem(10, Msg.WRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "0x0a:l") });
|
||||
g.put(11, new DataItem[] { new DataItem(11, Msg.READWRITE, ByteType.HIGHBYTE, DataType.UINT8, 0, "tspindex"),
|
||||
new DataItem(11, Msg.READWRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "tspvalue") });
|
||||
g.put(12, new DataItem[] { new DataItem(12, Msg.READ, ByteType.HIGHBYTE, DataType.UINT8, 0, "0x0c:h"),
|
||||
new DataItem(12, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "0x0c:l") });
|
||||
g.put(13, new DataItem[] { new DataItem(13, Msg.READ, ByteType.HIGHBYTE, DataType.UINT8, 0, "0x0d:h"),
|
||||
new DataItem(13, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "0x0d:l") });
|
||||
g.put(14, new DataItem[] { new DataItem(14, Msg.READ, ByteType.LOWBYTE, DataType.FLOAT, 0, "maxrelmdulevel") });
|
||||
g.put(15, new DataItem[] { new DataItem(15, Msg.READ, ByteType.HIGHBYTE, DataType.UINT8, 0, "maxcapkw"),
|
||||
new DataItem(15, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "maxcapprc") });
|
||||
g.put(16, new DataItem[] {
|
||||
new DataItem(16, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "roomsetpoint", SIUnits.CELSIUS) });
|
||||
g.put(17, new DataItem[] { new DataItem(17, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "modulevel") });
|
||||
g.put(18, new DataItem[] { new DataItem(18, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "waterpressure") });
|
||||
g.put(19, new DataItem[] { new DataItem(19, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "dhwflow") });
|
||||
g.put(20, new DataItem[] { new DataItem(20, Msg.READWRITE, ByteType.BOTH, DataType.DOWTOD, 0, "dowtod") });
|
||||
g.put(21, new DataItem[] { new DataItem(21, Msg.READWRITE, ByteType.HIGHBYTE, DataType.UINT8, 0, "month"),
|
||||
new DataItem(21, Msg.READWRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "dom") });
|
||||
g.put(22, new DataItem[] { new DataItem(22, Msg.READWRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "year") });
|
||||
g.put(23, new DataItem[] { new DataItem(23, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "setpointch2") });
|
||||
g.put(24, new DataItem[] {
|
||||
new DataItem(24, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "roomtemp", SIUnits.CELSIUS) });
|
||||
g.put(25, new DataItem[] {
|
||||
new DataItem(25, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "flowtemp", SIUnits.CELSIUS) });
|
||||
g.put(26, new DataItem[] {
|
||||
new DataItem(26, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "dhwtemp", SIUnits.CELSIUS) });
|
||||
g.put(27, new DataItem[] {
|
||||
new DataItem(27, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "outsidetemp", SIUnits.CELSIUS) });
|
||||
g.put(28, new DataItem[] {
|
||||
new DataItem(28, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "returntemp", SIUnits.CELSIUS) });
|
||||
g.put(29, new DataItem[] { new DataItem(29, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "solstortemp") });
|
||||
g.put(30, new DataItem[] { new DataItem(30, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "solcolltemp") });
|
||||
g.put(31, new DataItem[] { new DataItem(31, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "flowtemp2") });
|
||||
g.put(32, new DataItem[] { new DataItem(32, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "dhw2temp") });
|
||||
g.put(33, new DataItem[] { new DataItem(33, Msg.READ, ByteType.BOTH, DataType.INT16, 0, "exhausttemp") });
|
||||
g.put(48, new DataItem[] { new DataItem(48, Msg.READ, ByteType.HIGHBYTE, DataType.INT8, 0, "tdhwsetu"),
|
||||
new DataItem(48, Msg.READ, ByteType.LOWBYTE, DataType.INT8, 0, "tdhwsetl") });
|
||||
g.put(49, new DataItem[] { new DataItem(49, Msg.READ, ByteType.HIGHBYTE, DataType.INT8, 0, "maxchu"),
|
||||
new DataItem(49, Msg.READ, ByteType.LOWBYTE, DataType.INT8, 0, "maxchl") });
|
||||
g.put(50, new DataItem[] { new DataItem(50, Msg.READ, ByteType.HIGHBYTE, DataType.INT8, 0, "otcu"),
|
||||
new DataItem(50, Msg.READ, ByteType.LOWBYTE, DataType.INT8, 0, "otcl") });
|
||||
g.put(56, new DataItem[] {
|
||||
new DataItem(56, Msg.READWRITE, ByteType.BOTH, DataType.FLOAT, 0, "tdhwset", SIUnits.CELSIUS) });
|
||||
g.put(57, new DataItem[] { new DataItem(57, Msg.READWRITE, ByteType.BOTH, DataType.FLOAT, 0, "tchmax") });
|
||||
g.put(58, new DataItem[] { new DataItem(58, Msg.READWRITE, ByteType.BOTH, DataType.FLOAT, 0, "otchcratio") });
|
||||
g.put(100,
|
||||
new DataItem[] { new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "rof0"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "rof1"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "rof2"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "rof3"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "rof4"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "rof5"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "rof6"),
|
||||
new DataItem(100, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "rof7") });
|
||||
g.put(115, new DataItem[] { new DataItem(115, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "oemdiagcode") });
|
||||
g.put(116, new DataItem[] { new DataItem(116, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "burnerstarts") });
|
||||
g.put(117, new DataItem[] { new DataItem(117, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "chpumpstarts") });
|
||||
g.put(118, new DataItem[] { new DataItem(118, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwpvstarts") });
|
||||
g.put(119,
|
||||
new DataItem[] { new DataItem(119, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwburnerstarts") });
|
||||
g.put(120, new DataItem[] { new DataItem(120, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "burnerhours") });
|
||||
g.put(121, new DataItem[] { new DataItem(121, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "chpumphours") });
|
||||
g.put(122, new DataItem[] { new DataItem(122, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwpvhours") });
|
||||
g.put(123, new DataItem[] { new DataItem(123, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwburnerhours") });
|
||||
g.put(124,
|
||||
new DataItem[] { new DataItem(124, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "masterotversion") });
|
||||
g.put(125, new DataItem[] { new DataItem(125, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "slaveotversion") });
|
||||
g.put(126,
|
||||
new DataItem[] {
|
||||
new DataItem(126, Msg.WRITE, ByteType.HIGHBYTE, DataType.UINT8, 0, "masterproducttype"),
|
||||
new DataItem(126, Msg.WRITE, ByteType.LOWBYTE, DataType.UINT8, 0, "masterproductversion") });
|
||||
g.put(127,
|
||||
new DataItem[] { new DataItem(127, Msg.READ, ByteType.HIGHBYTE, DataType.UINT8, 0, "slaveproducttype"),
|
||||
new DataItem(127, Msg.READ, ByteType.LOWBYTE, DataType.UINT8, 0, "slaveproductversion") });
|
||||
|
||||
return g;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
/**
|
||||
* The {@link DataType} enum indicates the type of data from a DataItem.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
public enum DataType {
|
||||
FLAGS,
|
||||
UINT8,
|
||||
INT8,
|
||||
FLOAT,
|
||||
UINT16,
|
||||
INT16,
|
||||
DOWTOD
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link GatewayCommand} is used to validate and match commands send through the binding
|
||||
* to the OpenTherm gateway device.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GatewayCommand {
|
||||
private static final Map<String, @Nullable String> supportedCommands = getSupportedCommands();
|
||||
|
||||
private String code;
|
||||
private String validationSet;
|
||||
private String message;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public String getValidationSet() {
|
||||
return validationSet;
|
||||
}
|
||||
|
||||
public String toFullString() {
|
||||
return this.code + "=" + this.message;
|
||||
}
|
||||
|
||||
private GatewayCommand(String code, String message, String validationSet) throws IllegalArgumentException {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.validationSet = validationSet;
|
||||
|
||||
if (!validate()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid value '%s' for code '%s'", this.message, this.code));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validate() {
|
||||
if (this.validationSet.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] validations = this.validationSet.split(",");
|
||||
|
||||
for (String validation : validations) {
|
||||
if (this.message.equals(validation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static GatewayCommand parse(@Nullable String code, String message) throws IllegalArgumentException {
|
||||
if ((code == null || code.isEmpty()) && message.length() > 2 && message.charAt(2) == '=') {
|
||||
return parse(message.substring(0, 2), message.substring(3));
|
||||
}
|
||||
|
||||
if (code != null && code.length() == 2) {
|
||||
String codeUpperCase = code.toUpperCase();
|
||||
|
||||
if (supportedCommands.containsKey(codeUpperCase)) {
|
||||
String validateSet = supportedCommands.get(codeUpperCase);
|
||||
|
||||
if (validateSet == null) {
|
||||
validateSet = "";
|
||||
}
|
||||
|
||||
return new GatewayCommand(codeUpperCase, message, validateSet);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unsupported gateway code '%s'", code.toUpperCase()));
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Unable to parse gateway command with code '%s' and message '%s'", code, message));
|
||||
}
|
||||
|
||||
private static Map<String, @Nullable String> getSupportedCommands() {
|
||||
Map<String, @Nullable String> c = new HashMap<>();
|
||||
|
||||
c.put(GatewayCommandCode.TemperatureTemporary, null);
|
||||
c.put(GatewayCommandCode.TemperatureConstant, null);
|
||||
c.put(GatewayCommandCode.TemperatureOutside, null);
|
||||
c.put(GatewayCommandCode.SetClock, null);
|
||||
c.put(GatewayCommandCode.HotWater, null);
|
||||
c.put(GatewayCommandCode.PrintReport, "A,B,C,G,I,L,M,O,P,R,S,T,V,W");
|
||||
c.put(GatewayCommandCode.PrintSummary, "0,1");
|
||||
c.put(GatewayCommandCode.GateWay, "0,1,R");
|
||||
c.put(GatewayCommandCode.LedA, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.LedB, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.LedC, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.LedD, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.LedE, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.LedF, "R,X,T,B,O,F,H,W,C,E,M,P");
|
||||
c.put(GatewayCommandCode.GpioA, "0,1,2,3,4,5,6,7");
|
||||
c.put(GatewayCommandCode.GpioB, "0,1,2,3,4,5,6,7");
|
||||
c.put(GatewayCommandCode.SetBack, null);
|
||||
c.put(GatewayCommandCode.AddAlternative, null);
|
||||
c.put(GatewayCommandCode.DeleteAlternative, null);
|
||||
c.put(GatewayCommandCode.UnknownID, null);
|
||||
c.put(GatewayCommandCode.KnownID, null);
|
||||
c.put(GatewayCommandCode.PriorityMessage, null);
|
||||
c.put(GatewayCommandCode.SetResponse, null);
|
||||
c.put(GatewayCommandCode.ClearResponse, null);
|
||||
c.put(GatewayCommandCode.SetpointHeating, null);
|
||||
c.put(GatewayCommandCode.SetpointWater, null);
|
||||
c.put(GatewayCommandCode.MaximumModulation, null);
|
||||
c.put(GatewayCommandCode.ControlSetpoint, null);
|
||||
c.put(GatewayCommandCode.CentralHeating, "0,1");
|
||||
c.put(GatewayCommandCode.VentilationSetpoint, null);
|
||||
c.put(GatewayCommandCode.Reset, null);
|
||||
c.put(GatewayCommandCode.IgnoreTransition, "0,1");
|
||||
c.put(GatewayCommandCode.OverrideHighbyte, "0,1");
|
||||
c.put(GatewayCommandCode.ForceThermostat, "0,1");
|
||||
c.put(GatewayCommandCode.VoltageReference, "0,1,2,3,4,5,6,7,8,9");
|
||||
c.put(GatewayCommandCode.DebugPointer, null);
|
||||
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link GatewayCommandCode} provides a set of supported OpenTherm Gateway commands.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GatewayCommandCode {
|
||||
public static final String TemperatureTemporary = "TT";
|
||||
public static final String TemperatureConstant = "TC";
|
||||
public static final String TemperatureOutside = "OT";
|
||||
public static final String SetClock = "ST";
|
||||
public static final String HotWater = "HW";
|
||||
public static final String PrintReport = "PR";
|
||||
public static final String PrintSummary = "PS";
|
||||
public static final String GateWay = "GW";
|
||||
public static final String LedA = "LA";
|
||||
public static final String LedB = "LB";
|
||||
public static final String LedC = "LC";
|
||||
public static final String LedD = "LD";
|
||||
public static final String LedE = "LE";
|
||||
public static final String LedF = "LF";
|
||||
public static final String GpioA = "GA";
|
||||
public static final String GpioB = "GB";
|
||||
public static final String SetBack = "SB";
|
||||
public static final String AddAlternative = "AA";
|
||||
public static final String DeleteAlternative = "DA";
|
||||
public static final String UnknownID = "UI";
|
||||
public static final String KnownID = "KI";
|
||||
public static final String PriorityMessage = "PM";
|
||||
public static final String SetResponse = "SR";
|
||||
public static final String ClearResponse = "CR";
|
||||
public static final String SetpointHeating = "SH";
|
||||
public static final String SetpointWater = "SW";
|
||||
public static final String MaximumModulation = "MM";
|
||||
public static final String ControlSetpoint = "CS";
|
||||
public static final String CentralHeating = "CH";
|
||||
public static final String VentilationSetpoint = "VS";
|
||||
public static final String Reset = "RS";
|
||||
public static final String IgnoreTransition = "IT";
|
||||
public static final String OverrideHighbyte = "OH";
|
||||
public static final String ForceThermostat = "FT";
|
||||
public static final String VoltageReference = "VR";
|
||||
public static final String DebugPointer = "DP";
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link Message} represent a single message received from the OpenTherm Gateway.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Message {
|
||||
|
||||
private static final Pattern messagePattern = Pattern.compile("[TBRA]{1}[A-F0-9]{8}");
|
||||
|
||||
/*
|
||||
* The code field is not part of OpenTherm specification, but added by OpenTherm Gateway.
|
||||
* It can be any of the following:
|
||||
*
|
||||
* T: Message received from the thermostat
|
||||
* B: Message received from the boiler
|
||||
* R: Request sent to the boiler
|
||||
* A: Response returned to the thermostat
|
||||
* E: Parity or stop bit error
|
||||
*/
|
||||
private String code;
|
||||
private MessageType messageType;
|
||||
private int id;
|
||||
private String data;
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public MessageType getMessageType() {
|
||||
return messageType;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public @Nullable String getData(ByteType byteType) {
|
||||
if (this.data.length() == 4) {
|
||||
switch (byteType) {
|
||||
case HIGHBYTE:
|
||||
return this.data.substring(0, 2);
|
||||
case LOWBYTE:
|
||||
return this.data.substring(2, 4);
|
||||
case BOTH:
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getBit(ByteType byteType, int pos) {
|
||||
@Nullable
|
||||
String data = getData(byteType);
|
||||
|
||||
if (data != null) {
|
||||
// First parse the hex value to an integer
|
||||
int parsed = Integer.parseInt(data, 16);
|
||||
|
||||
// Then right shift it pos positions so that the required bit is at the front
|
||||
// and then apply a bitmask of 00000001 (1)
|
||||
return ((parsed >> pos) & 1) == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getUInt(ByteType byteType) {
|
||||
@Nullable
|
||||
String data = getData(byteType);
|
||||
|
||||
if (data != null) {
|
||||
return Integer.parseInt(data, 16);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getInt(ByteType byteType) {
|
||||
@Nullable
|
||||
String data = getData(byteType);
|
||||
|
||||
if (data != null) {
|
||||
return parseSignedInteger(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat() {
|
||||
// f8.8, two's complement
|
||||
@Nullable
|
||||
String data = getData(ByteType.BOTH);
|
||||
|
||||
if (data != null) {
|
||||
long value = Long.parseLong(data, 16);
|
||||
|
||||
// left padded with zeros
|
||||
String binary = String.format("%16s", Long.toBinaryString(value)).replace(' ', '0');
|
||||
|
||||
if (binary.charAt(0) == '1') {
|
||||
// negative value
|
||||
|
||||
String inverted = invertBinary(binary);
|
||||
|
||||
value = Long.parseLong(inverted, 2);
|
||||
value = value + 1;
|
||||
value = value * -1;
|
||||
}
|
||||
|
||||
// divide by 2^8 = 256
|
||||
return (float) value / 256;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean overrides(@Nullable Message other) {
|
||||
// If the message is a Request sent to the boiler or an Answer returned to the
|
||||
// thermostat, and it's ID is equal to the previous message, then this is an
|
||||
// override sent by the OpenTherm Gateway
|
||||
return other != null && this.getID() == other.getID()
|
||||
&& ("R".equals(this.getCode()) || "A".equals(this.getCode()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s - %s - %s", this.code, this.id, this.data);
|
||||
}
|
||||
|
||||
public Message(String code, MessageType messageType, int id, String data) {
|
||||
this.code = code;
|
||||
this.messageType = messageType;
|
||||
this.id = id;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static @Nullable Message parse(String message) {
|
||||
if (messagePattern.matcher(message).matches()) {
|
||||
// For now, only parse TBRA codes
|
||||
String code = message.substring(0, 1);
|
||||
MessageType messageType = getMessageType(message.substring(1, 3));
|
||||
int id = Integer.valueOf(message.substring(3, 5), 16);
|
||||
String data = message.substring(5);
|
||||
|
||||
return new Message(code, messageType, id, data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MessageType getMessageType(String value) {
|
||||
// First parse the hex value to an integer
|
||||
int integer = Integer.parseInt(value, 16);
|
||||
|
||||
// Then right shift it 4 bits so that the message type bits are at the front
|
||||
int shifted = integer >> 4;
|
||||
|
||||
// Then mask it with 00000111 (7), so that we only get the first 3 bits,
|
||||
// effectively cutting off the parity bit.
|
||||
int cutoff = shifted & 7;
|
||||
|
||||
switch (cutoff) {
|
||||
case 0: // 000
|
||||
return MessageType.READDATA;
|
||||
case 1: // 001
|
||||
return MessageType.WRITEDATA;
|
||||
case 2: // 010
|
||||
return MessageType.INVALIDDATA;
|
||||
case 3: // 011
|
||||
return MessageType.RESERVED;
|
||||
case 4: // 100
|
||||
return MessageType.READACK;
|
||||
case 5: // 101
|
||||
return MessageType.WRITEACK;
|
||||
case 6: // 110
|
||||
return MessageType.DATAINVALID;
|
||||
case 7: // 111
|
||||
default:
|
||||
return MessageType.UNKNOWNDATAID;
|
||||
}
|
||||
}
|
||||
|
||||
private int parseSignedInteger(String data) {
|
||||
// First parse the hex value to an unsigned integer value
|
||||
int result = Integer.parseInt(data, 16);
|
||||
|
||||
if (data.length() == 4) {
|
||||
// This is a two byte value, apply a bitmask of 01111111 11111111 (32767) to cut
|
||||
// off the sign bit
|
||||
result = result & 32767;
|
||||
|
||||
// Then apply a bitmask of 10000000 00000000 (32768) to check the sign bit
|
||||
if ((result & 32768) == 32768) {
|
||||
// If the sign is 1000000 00000000 (32768) then it's a negative
|
||||
result = -32768 + result;
|
||||
}
|
||||
} else {
|
||||
// This is a one byte value, apply a bitmask of 01111111 (127), to cut off the
|
||||
// sign bit
|
||||
result = result & 127;
|
||||
|
||||
// Then apply a bitmask of 10000000 (128) to check the sign bit
|
||||
if ((result & 128) == 128) {
|
||||
// If the sign is 1000000 (128) then it's a negative
|
||||
result = -128 + result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String invertBinary(String value) {
|
||||
// There is probably a better solution, but for now this works
|
||||
String result = value;
|
||||
|
||||
result = result.replace('1', 'X');
|
||||
result = result.replace('0', '1');
|
||||
result = result.replace('X', '0');
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
/**
|
||||
* The {@link MessageType} indicates the type of message received by the OpenTherm Gateway, based
|
||||
* on the OpenTherm specification.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
public enum MessageType {
|
||||
READDATA, // 000
|
||||
READACK, // 100
|
||||
WRITEDATA, // 001
|
||||
WRITEACK, // 101
|
||||
INVALIDDATA, // 010
|
||||
DATAINVALID, // 110
|
||||
RESERVED, // 011
|
||||
UNKNOWNDATAID // 111
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
/**
|
||||
* The {@link Msg} flag is used to indicate whether the message is sent for Reading, Writing
|
||||
* or both, based on the OpenTherm specification.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
public enum Msg {
|
||||
READ,
|
||||
WRITE,
|
||||
READWRITE
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayCallback} is used as a callback interface by a connector to signal status
|
||||
* and relay incoming messages to be processed by the binding.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface OpenThermGatewayCallback {
|
||||
void connecting();
|
||||
|
||||
void connected();
|
||||
|
||||
void disconnected();
|
||||
|
||||
void receiveMessage(Message message);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenThermGatewayConfiguration {
|
||||
|
||||
public String ipaddress = "";
|
||||
|
||||
public int port = 0;
|
||||
|
||||
public int connectionRetryInterval = 60;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayConnector} interface is used to allow multiple types of connectors
|
||||
* to be implemented and used to connect to the OpenTherm Gateway.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface OpenThermGatewayConnector extends Runnable {
|
||||
void sendCommand(GatewayCommand command);
|
||||
|
||||
boolean isConnected();
|
||||
|
||||
void stop();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openthermgateway.OpenThermGatewayBindingConstants;
|
||||
import org.openhab.binding.openthermgateway.handler.OpenThermGatewayHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewayHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.openthermgateway")
|
||||
@NonNullByDefault
|
||||
public class OpenThermGatewayHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return thingTypeUID.equals(OpenThermGatewayBindingConstants.MAIN_THING_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(OpenThermGatewayBindingConstants.MAIN_THING_TYPE)) {
|
||||
return new OpenThermGatewayHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openthermgateway.internal;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link OpenThermGatewaySocketConnector} is responsible for handling the socket connection
|
||||
*
|
||||
* @author Arjen Korevaar - Initial contribution
|
||||
* @author Arjan Mels - Improved robustness by re-sending commands, handling all message types (not only Boiler)
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class OpenThermGatewaySocketConnector implements OpenThermGatewayConnector {
|
||||
private static final int COMMAND_RESPONSE_TIME_MILLISECONDS = 100;
|
||||
private static final int COMMAND_TIMEOUT_MILLISECONDS = 5000;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OpenThermGatewaySocketConnector.class);
|
||||
|
||||
private final OpenThermGatewayCallback callback;
|
||||
private final String ipaddress;
|
||||
private final int port;
|
||||
|
||||
private @Nullable PrintWriter writer;
|
||||
|
||||
private volatile boolean stopping;
|
||||
private boolean connected;
|
||||
|
||||
private Map<String, Entry<Long, GatewayCommand>> pendingCommands = new ConcurrentHashMap<>();
|
||||
|
||||
public OpenThermGatewaySocketConnector(OpenThermGatewayCallback callback, String ipaddress, int port) {
|
||||
this.callback = callback;
|
||||
this.ipaddress = ipaddress;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
stopping = false;
|
||||
connected = false;
|
||||
|
||||
logger.debug("Connecting OpenThermGatewaySocketConnector to {}:{}", this.ipaddress, this.port);
|
||||
|
||||
callback.connecting();
|
||||
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(this.ipaddress, this.port), COMMAND_TIMEOUT_MILLISECONDS);
|
||||
socket.setSoTimeout(COMMAND_TIMEOUT_MILLISECONDS);
|
||||
|
||||
connected = true;
|
||||
|
||||
callback.connected();
|
||||
|
||||
logger.debug("OpenThermGatewaySocketConnector connected");
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
PrintWriter wrt = new PrintWriter(socket.getOutputStream(), true)) {
|
||||
// Make writer accessible on class level
|
||||
writer = wrt;
|
||||
|
||||
sendCommand(GatewayCommand.parse(GatewayCommandCode.PrintReport, "A"));
|
||||
// Set the OTGW to report every message it receives and transmits
|
||||
sendCommand(GatewayCommand.parse(GatewayCommandCode.PrintSummary, "0"));
|
||||
|
||||
while (!stopping && !Thread.currentThread().isInterrupted()) {
|
||||
@Nullable
|
||||
String message = reader.readLine();
|
||||
|
||||
if (message != null) {
|
||||
handleMessage(message);
|
||||
} else {
|
||||
logger.debug("Connection closed by OpenTherm Gateway");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Stopping OpenThermGatewaySocketConnector");
|
||||
} finally {
|
||||
connected = false;
|
||||
|
||||
logger.debug("OpenThermGatewaySocketConnector disconnected");
|
||||
callback.disconnected();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.warn("Unable to connect to the OpenTherm Gateway.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
logger.debug("Stopping OpenThermGatewaySocketConnector");
|
||||
stopping = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCommand(GatewayCommand command) {
|
||||
@Nullable
|
||||
PrintWriter wrtr = writer;
|
||||
|
||||
String msg = command.toFullString();
|
||||
|
||||
pendingCommands.put(command.getCode(),
|
||||
new AbstractMap.SimpleImmutableEntry<>(System.currentTimeMillis(), command));
|
||||
|
||||
if (connected) {
|
||||
logger.debug("Sending message: {}", msg);
|
||||
if (wrtr != null) {
|
||||
wrtr.print(msg + "\r\n");
|
||||
wrtr.flush();
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unable to send message: {}. OpenThermGatewaySocketConnector is not connected.", msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMessage(String message) {
|
||||
if (message.length() > 2 && message.charAt(2) == ':') {
|
||||
String code = message.substring(0, 2);
|
||||
String value = message.substring(3);
|
||||
|
||||
logger.debug("Received command confirmation: {}: {}", code, value);
|
||||
pendingCommands.remove(code);
|
||||
return;
|
||||
}
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
for (Entry<Long, GatewayCommand> timeAndCommand : pendingCommands.values()) {
|
||||
long responseTime = timeAndCommand.getKey() + COMMAND_RESPONSE_TIME_MILLISECONDS;
|
||||
long timeoutTime = timeAndCommand.getKey() + COMMAND_TIMEOUT_MILLISECONDS;
|
||||
|
||||
if (currentTime > responseTime && currentTime <= timeoutTime) {
|
||||
logger.debug("Resending command: {}", timeAndCommand.getValue());
|
||||
sendCommand(timeAndCommand.getValue());
|
||||
} else if (currentTime > timeoutTime) {
|
||||
pendingCommands.remove(timeAndCommand.getValue().getCode());
|
||||
}
|
||||
}
|
||||
|
||||
Message msg = Message.parse(message);
|
||||
|
||||
if (msg == null) {
|
||||
logger.trace("Received message: {}, (unknown)", message);
|
||||
return;
|
||||
} else {
|
||||
logger.trace("Received message: {}, {} {} {}", message, msg.getID(), msg.getCode(), msg.getMessageType());
|
||||
}
|
||||
|
||||
if (DataItemGroup.dataItemGroups.containsKey(msg.getID())) {
|
||||
DataItem[] dataItems = DataItemGroup.dataItemGroups.get(msg.getID());
|
||||
|
||||
for (DataItem dataItem : dataItems) {
|
||||
State state = null;
|
||||
|
||||
switch (dataItem.getDataType()) {
|
||||
case FLAGS:
|
||||
state = OnOffType.from(msg.getBit(dataItem.getByteType(), dataItem.getBitPos()));
|
||||
break;
|
||||
case UINT8:
|
||||
case UINT16:
|
||||
state = new DecimalType(msg.getUInt(dataItem.getByteType()));
|
||||
break;
|
||||
case INT8:
|
||||
case INT16:
|
||||
state = new DecimalType(msg.getInt(dataItem.getByteType()));
|
||||
break;
|
||||
case FLOAT:
|
||||
state = new DecimalType(msg.getFloat());
|
||||
break;
|
||||
case DOWTOD:
|
||||
break;
|
||||
}
|
||||
|
||||
logger.trace(" Data: {} {} {} {}", dataItem.getID(), dataItem.getSubject(), dataItem.getDataType(),
|
||||
state == null ? "" : state);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.getMessageType() == MessageType.READACK || msg.getMessageType() == MessageType.WRITEDATA) {
|
||||
receiveMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveMessage(Message message) {
|
||||
callback.receiveMessage(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="openthermgateway" 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>OpenTherm Gateway Binding</name>
|
||||
<description>
|
||||
This is the binding for the OpenTherm Gateway, designed by Schelte Bron.
|
||||
For more information about the
|
||||
OpenTherm Gateway please see http://otgw.tclcode.com/
|
||||
</description>
|
||||
<author>Arjen Korevaar</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:openthermgateway:otgw">
|
||||
<parameter-group name="connection">
|
||||
<label>Connection</label>
|
||||
<description>Connection settings.</description>
|
||||
</parameter-group>
|
||||
|
||||
<parameter name="ipaddress" type="text" required="true" groupName="connection">
|
||||
<label>Hostname or IP address</label>
|
||||
<description>The hostname or IP address to connect to the OpenTherm Gateway.</description>
|
||||
<context>network-address</context>
|
||||
</parameter>
|
||||
|
||||
<parameter name="port" type="integer" required="true" groupName="connection" min="0" max="65535">
|
||||
<label>Port</label>
|
||||
<description>The port used to connect to the OpenTherm Gateway.</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="connectionRetryInterval" type="integer" required="true" groupName="connection" min="0"
|
||||
max="3600" unit="s">
|
||||
<label>Connection Retry Interval</label>
|
||||
<description>The interval in seconds to retry connecting (0 = disabled).</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openthermgateway"
|
||||
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="roomtemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Room Temperature</label>
|
||||
<description>Current sensed room temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="roomsetpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Room Setpoint</label>
|
||||
<description>Current room temperature setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperaturetemporary">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temporary Room Setpoint Override</label>
|
||||
<description>Temporary override room temperature setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false" min="0" max="30" step="0.1" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperatureconstant">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Constant Room Setpoint Override</label>
|
||||
<description>Constant override room temperature setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false" min="0" max="30" step="0.1" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="controlsetpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Control Setpoint</label>
|
||||
<description>Central heating water setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhwtemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Domestic Hot Water Temperature</label>
|
||||
<description>Domestic hot water temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tdhwset">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Domestic Hot Water Setpoint</label>
|
||||
<description>Domestic hot water temperature setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="overridedhwsetpoint">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Domestic Hot Water Setpoint Override</label>
|
||||
<description>Domestic hot water temperature setpoint override</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false" min="0" max="100" step="0.1" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="flowtemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Boiler Water Temperature</label>
|
||||
<description>Boiler water temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="returntemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Return Water Temperature</label>
|
||||
<description>Return water temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="outsidetemp">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Outside Temperature</label>
|
||||
<description>Outside temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="false" min="-40" max="100" step="0.1" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="waterpressure">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Central Heating Water Pressure</label>
|
||||
<description>Central heating water pressure</description>
|
||||
<state readOnly="true" pattern="%.1f bar"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ch_enable">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Central Heating Enabled</label>
|
||||
<description>Central heating enabled</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="ch_mode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Central Heating Active</label>
|
||||
<description>Central heating active</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhw_enable">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Domestic Hot Water Enabled</label>
|
||||
<description>Domestic hot water enabled</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dhw_mode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Domestic Hot Water Active</label>
|
||||
<description>Domestic hot water active</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="flame">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Burner Active</label>
|
||||
<description>Burner active</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="modulevel">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Relative Modulation Level</label>
|
||||
<description>Relative modulation level</description>
|
||||
<state readOnly="true" pattern="%.1f %%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="maxrelmdulevel">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Maximum Relative Modulation Level</label>
|
||||
<description>Maximum relative modulation level</description>
|
||||
<state readOnly="true" pattern="%.1f %%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fault">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Fault Indication</label>
|
||||
<description>Fault indication</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="servicerequest">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Service Required</label>
|
||||
<description>Service required</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lockout-reset">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Lockout-Reset Enabled</label>
|
||||
<description>Lockout-reset enabled</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="lowwaterpress">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Low Water Pressure Fault</label>
|
||||
<description>Low water pressure fault</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="gasflamefault">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Gas Or Flame Fault</label>
|
||||
<description>Gas or flame fault</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="airpressfault">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Air Pressure Fault</label>
|
||||
<description>Air pressure fault</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="waterovtemp">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Water Over-Temperature Fault</label>
|
||||
<description>Water over-temperature fault</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="oemfaultcode">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>OEM Fault Code</label>
|
||||
<description>OEM fault code</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sendcommand">
|
||||
<item-type>String</item-type>
|
||||
<label>Send Command</label>
|
||||
<description>Channel to send commands to the OpenTherm Gateway device</description>
|
||||
<state readOnly="false"></state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openthermgateway"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
<thing-type id="otgw">
|
||||
<label>OpenTherm Gateway</label>
|
||||
<description>OpenTherm Gateway binding</description>
|
||||
<channels>
|
||||
<channel id="roomtemp" typeId="roomtemp"/>
|
||||
<channel id="roomsetpoint" typeId="roomsetpoint"/>
|
||||
<channel id="temperaturetemporary" typeId="temperaturetemporary"/>
|
||||
<channel id="temperatureconstant" typeId="temperatureconstant"/>
|
||||
<channel id="controlsetpoint" typeId="controlsetpoint"/>
|
||||
<channel id="dhwtemp" typeId="dhwtemp"/>
|
||||
<channel id="tdhwset" typeId="tdhwset"/>
|
||||
<channel id="overridedhwsetpoint" typeId="overridedhwsetpoint"/>
|
||||
<channel id="flowtemp" typeId="flowtemp"/>
|
||||
<channel id="returntemp" typeId="returntemp"/>
|
||||
<channel id="outsidetemp" typeId="outsidetemp"/>
|
||||
<channel id="waterpressure" typeId="waterpressure"/>
|
||||
<channel id="ch_enable" typeId="ch_enable"/>
|
||||
<channel id="ch_mode" typeId="ch_mode"/>
|
||||
<channel id="dhw_enable" typeId="dhw_enable"/>
|
||||
<channel id="dhw_mode" typeId="dhw_mode"/>
|
||||
<channel id="flame" typeId="flame"/>
|
||||
<channel id="modulevel" typeId="modulevel"/>
|
||||
<channel id="maxrelmdulevel" typeId="maxrelmdulevel"/>
|
||||
<channel id="fault" typeId="fault"/>
|
||||
<channel id="servicerequest" typeId="servicerequest"/>
|
||||
<channel id="lockout-reset" typeId="lockout-reset"/>
|
||||
<channel id="lowwaterpress" typeId="lowwaterpress"/>
|
||||
<channel id="gasflamefault" typeId="gasflamefault"/>
|
||||
<channel id="airpressfault" typeId="airpressfault"/>
|
||||
<channel id="waterovtemp" typeId="waterovtemp"/>
|
||||
<channel id="oemfaultcode" typeId="oemfaultcode"/>
|
||||
<channel id="sendcommand" typeId="sendcommand"/>
|
||||
</channels>
|
||||
<properties>
|
||||
<property name="version">1.0.2</property>
|
||||
</properties>
|
||||
<config-description-ref uri="thing-type:openthermgateway:otgw"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user