added migrated 2.x add-ons

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

View File

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

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.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>

View File

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

View 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"
}
}
```

View File

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

View File

@@ -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>

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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()));
}

View File

@@ -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));
}
}
}

View File

@@ -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
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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
}

View File

@@ -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
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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);
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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;
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>