From ba07a870fef54e42ae9563f2a9660ead42c0c615 Mon Sep 17 00:00:00 2001 From: Andrew Fiddian-Green Date: Sun, 6 Mar 2022 20:18:02 +0000 Subject: [PATCH] [openthermgateway] Add support for Ventilation/Heat Recovery units (#12367) * [openthermgateway] clean version of prior PR * [openthermgateway] fix NoSuchMethod exception * [openthermgateway] create thread according to OH guidelines * [openthermgateway] fix annotation warning * [openthermgateway] framework handles bridge status changes * [openthermgateway] implement learnings from PR #12356 * [openthermgateway] add ReadMe chapter on migration v3.2 .. v3.3 * [openthermgateway] delete duplicate OpenThermGatewayBindingConstants class * [openthermgateway] remove redundant else clause, remove npe warning * [openthermgateway] log to debug rather than info; eliminate static import * [openthermgateway] eliminate static import * [openthermgateway] suppress unused argument warning * [openthermgateway] delete xml definitions for no longer used 'otgw' Thing type Signed-off-by: Andrew Fiddian-Green --- .../README.md | 558 +++++++++++++----- .../pom.xml | 1 + .../OpenThermGatewayBindingConstants.java | 101 ---- .../handler/BaseDeviceHandler.java | 161 +++++ .../handler/BoilerHandler.java | 29 + .../handler/OpenThermGatewayHandler.java | 252 ++++---- .../VentilationHeatRecoveryHandler.java | 29 + .../openthermgateway/internal/ByteType.java | 3 + .../openthermgateway/internal/CodeType.java | 3 + .../internal/ConnectionState.java | 28 + .../openthermgateway/internal/DataItem.java | 77 +-- .../internal/DataItemGroup.java | 361 +++++------ .../openthermgateway/internal/DataType.java | 5 +- .../internal/FlagDataItem.java | 47 ++ .../internal/FloatDataItem.java | 62 ++ .../internal/GatewayCommand.java | 88 ++- .../internal/GatewayCommandCode.java | 78 +-- .../internal/IntDataItem.java | 35 ++ .../openthermgateway/internal/Message.java | 22 +- .../internal/MessageType.java | 3 + .../openthermgateway/internal/Msg.java | 3 + .../OpenThermGatewayBindingConstants.java | 56 ++ .../internal/OpenThermGatewayCallback.java | 5 +- .../OpenThermGatewayConfiguration.java | 4 + .../internal/OpenThermGatewayConnector.java | 8 +- .../OpenThermGatewayHandlerFactory.java | 14 +- .../OpenThermGatewaySocketConnector.java | 179 +++--- .../internal/TspFhbSizeDataItem.java | 42 ++ .../internal/TspFhbValueDataItem.java | 52 ++ .../internal/UIntDataItem.java | 54 ++ .../{config.xml => openthermgateway.xml} | 16 +- .../OH-INF/thing/{otgw.xml => boiler.xml} | 19 +- .../main/resources/OH-INF/thing/channels.xml | 370 +++++++++++- .../OH-INF/thing/openthermgateway.xml | 18 + .../OH-INF/thing/ventilationheatrecovery.xml | 56 ++ 35 files changed, 2031 insertions(+), 808 deletions(-) delete mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/OpenThermGatewayBindingConstants.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BaseDeviceHandler.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BoilerHandler.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/VentilationHeatRecoveryHandler.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ConnectionState.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FlagDataItem.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FloatDataItem.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/IntDataItem.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayBindingConstants.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbSizeDataItem.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbValueDataItem.java create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/UIntDataItem.java rename bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/{config.xml => openthermgateway.xml} (66%) rename bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/{otgw.xml => boiler.xml} (87%) create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/openthermgateway.xml create mode 100644 bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/ventilationheatrecovery.xml diff --git a/bundles/org.openhab.binding.openthermgateway/README.md b/bundles/org.openhab.binding.openthermgateway/README.md index 1d88b66f8..3b736c8e2 100644 --- a/bundles/org.openhab.binding.openthermgateway/README.md +++ b/bundles/org.openhab.binding.openthermgateway/README.md @@ -1,195 +1,443 @@ # 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. +The OpenTherm Gateway is a module designed by Schelte Bron that can be connected to units that support communication using the OpenTherm protocol, such as boiler or ventilation / heat recovery unit. More information on the OpenTherm Gateway device can be found at https://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. +The binding does not require any configuration. + +## Supported Things + +The OpenTherm Gateway binding supports three Things: + +- `openthermgateway` which is the bridge that handles communication with the OpenTherm Gateway device. +- `boiler` which represents a central heating boiler unit. +- `ventilationheatrecovery` which represents a ventilation / heat recovery unit. ## 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. +### Thing Configuration for `openthermgateway` -| 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 | +The `openthermgateway` bridge 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 bridge 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 | +| `connectTimeoutSeconds` | Connect Timeout | The maximum time (seconds) to wait for establishing a connection to the gateway. | yes | 5 | +| `readTimeoutSeconds` | Read Timeout | The maximum time (seconds) to wait for reading responses from the gateway. | yes | 20 | + +### Thing Configuration for `boiler` and `ventilationheatrecovery` + +The `boiler` and `ventilationheatrecovery` things do not require any configuration settings. ## Channels -The OpenTherm Gateway binding supports the following channels: +### Channels for `openthermgateway` + +The `openthermgateway` bridge supports the following channels: | Channel Type ID | Item Type | Description | Access | |---------------------------|----------------------|----------------------------------------------------------|--------| -| roomtemp | Number:Temperature | Current sensed room temperature | R | -| roomsetpoint | Number:Temperature | Current room temperature setpoint | R | -| temperaturetemporary | Number:Temperature | Temporary override room temperature setpoint | R/W | -| temperatureconstant | Number:Temperature | Constant override room temperature setpoint | R/W | -| controlsetpoint | Number:Temperature | Central heating water setpoint set at boiler | R | -| controlsetpointrequested | Number:Temperature | Central heating water setpoint requested by thermostat | R | -| controlsetpointoverride | Number:Temperature | Central heating water setpoint configured at gateway | R/W | -| controlsetpoint2 | Number:Temperature | Central heating 2 water setpoint set at boiler | R | -| controlsetpoint2requested | Number:Temperature | Central heating 2 water setpoint requested by thermostat | R | -| controlsetpoint2override | Number:Temperature | Central heating 2 water setpoint configured at gateway | R/W | -| dhwtemp | Number:Temperature | Domestic hot water temperature | R | -| tdhwset | Number:Temperature | Domestic hot water temperature setpoint | R | -| overridedhwsetpoint | Number:Temperature | Domestic hot water temperature setpoint override | R/W | -| flowtemp | Number:Temperature | Boiler water temperature | R | -| returntemp | Number:Temperature | Return water temperature | R | -| outsidetemp | Number:Temperature | Outside temperature | R/W | -| waterpressure | Number:Pressure | Central heating water pressure | R | -| ch_enable | Switch | Central heating enabled set at boiler | R | -| ch_enablerequested | Switch | Central heating enabled requested by thermostat | R | -| ch_enableoverride | Switch | Central heating enabled overridden at gateway | R | -| ch2_enable | Switch | Central heating 2 enabled set at boiler | R | -| ch2_enablerequested | Switch | Central heating 2 enabled requested by thermostat | R | -| ch2_enableoverride | Switch | Central heating 2 enabled overridden at gateway | R | -| ch_mode | Switch | Central heating active | R | -| dhw_enable | Switch | Domestic hot water enabled | R | -| dhw_mode | Switch | Domestic hot water active | R | -| flame | Switch | Burner active | R | -| modulevel | Number:Dimensionless | Relative modulation level | R | -| maxrelmdulevel | Number:Dimensionless | Maximum relative modulation level | R | -| fault | Switch | Fault indication | R | -| servicerequest | Switch | Service required | R | -| lockout-reset | Switch | Lockout-reset enabled | R | -| lowwaterpress | Switch | Low water pressure fault | R | -| gasflamefault | Switch | Gas or flame fault | R | -| airpressfault | Switch | Air pressure fault | R | -| waterovtemp | Switch | Water over-temperature fault | R | -| oemfaultcode | Switch | OEM fault code | R | -| diag | Switch | Diagnostics indication | R | -| unsuccessfulburnerstarts | Number:Dimensionless | Unsuccessful burner starts | R | -| burnerstarts | Number:Dimensionless | Burner starts | R | -| chpumpstarts | Number:Dimensionless | Central heating pump starts | R | -| dhwpvstarts | Number:Dimensionless | Domestic hot water pump/valve starts | R | -| dhwburnerstarts | Number:Dimensionless | Domestic hot water burner starts | R | -| burnerhours | Number:Dimensionless | Burner hours | R | -| chpumphours | Number:Dimensionless | Central heating pump hours | R | -| dhwpvhours | Number:Dimensionless | Domestic hot water pump/valve hours | R | -| dhwburnerhours | Number:Dimensionless | Domestic hot water burner hours | R | | sendcommand | Text | Channel to send commands to the OpenTherm Gateway device | W | +### Channels for `boiler` + +The `boiler` thing supports the following channels: + +| Channel ID | Item Type | Description | Access | +|------------|-----------|-------------|--------| +| roomtemp | Number:Temperature | Current sensed room temperature | R | +| roomsetpoint | Number:Temperature | Current room temperature setpoint | R | +| temperaturetemporary | Number:Temperature | Temporary override room temperature setpoint | R/W | +| temperatureconstant | Number:Temperature | Constant override room temperature setpoint | R/W | +| controlsetpoint | Number:Temperature | Central heating water setpoint set at boiler | R | +| controlsetpointrequested | Number:Temperature | Central heating water setpoint requested by Thermostat | R | +| controlsetpointoverride | Number:Temperature | Central heating water setpoint configured on OTGW | R/W | +| controlsetpoint2 | Number:Temperature | Central heating 2 water setpoint set at boiler | R | +| controlsetpoint2requested | Number:Temperature | Central heating 2 water setpoint requested by Thermostat | R | +| controlsetpoint2override | Number:Temperature | Central heating 2 water setpoint configured on OTGW | R/W | +| dhwtemp | Number:Temperature | Domestic hot water temperature | R | +| tdhwset | Number:Temperature | Domestic hot water temperature setpoint | R | +| overridedhwsetpoint | Number:Temperature | Domestic hot water temperature setpoint override | R/W | +| flowtemp | Number:Temperature | Boiler water temperature | R | +| returntemp | Number:Temperature | Return water temperature | R | +| outsidetemp | Number:Temperature | Outside temperature | R/W | +| waterpressure | Number:Dimensionless | Central heating water pressure | R | +| ch_enable | Switch | Central heating enabled set at boiler | R | +| ch_enablerequested | Switch | Central heating enabled requested by thermostat | R | +| ch_enableoverride | Switch | Central heating enabled overridden at OTGW | R/W | +| ch2_enable | Switch | Central heating 2 enabled set at boiler | R | +| ch2_enablerequested | Switch | Central heating 2 enabled requested by thermostat | R | +| ch2_enableoverride | Switch | Central heating 2 enabled overridden at OTGW | R/W | +| ch_mode | Switch | Central heating active | R | +| dhw_enable | Switch | Domestic hot water enabled | R | +| dhw_mode | Switch | Domestic hot water active | R | +| flame | Switch | Burner active | R | +| modulevel | Number:Dimensionless | Relative modulation level | R | +| maxrelmdulevel | Number:Dimensionless | Maximum relative modulation level | R | +| fault | Switch | Fault indication | R | +| servicerequest | Switch | Service required | R | +| lockout-reset | Switch | Lockout-reset enabled | R | +| lowwaterpress | Switch | Low water pressure fault | R | +| gasflamefault | Switch | Gas or flame fault | R | +| airpressfault | Switch | Air pressure fault | R | +| waterovtemp | Switch | Water over-temperature fault | R | +| oemfaultcode | Number:Dimensionless | OEM fault code | R | +| diag | Switch | Diagnostics indication | R | +| unsuccessfulburnerstarts | Number:Dimensionless | Unsuccessful burner starts | R | +| burnerstarts | Number:Dimensionless | Burner starts | R | +| chpumpstarts | Number:Dimensionless | Central heating pump starts | R | +| dhwpvstarts | Number:Dimensionless | Domestic hot water pump/valve starts | R | +| dhwburnerstarts | Number:Dimensionless | Domestic hot water burner starts | R | +| burnerhours | Number:Time | Burner hours | R | +| chpumphours | Number:Time | Central heating pump hours | R | +| dhwpvhours | Number:Time | Domestic hot water pump/valve hours | R | +| dhwburnerhours | Number:Time | Domestic hot water burner hours | R | +| tspnumber | Number:Dimensionless | Number of transparant slave parameter entries | R | +| tspentry | Number:Dimensionless | Transparent slave parameter entry | R | +| fhbnumber | Number:Dimensionless | Number of fault history buffer entries | R | +| fhbentry | Number:Dimensionless | Fault history buffer entry | R | + +### Channels for `ventilationheatrecovery` + +The `ventilationheatrecovery` thing supports the following channels: + +| Channel ID | Item Type | Description | Access | +|------------|-----------|-------------|--------| +| vh_ventilationenable | Switch | Ventilation enabled | R | +| vh_bypassposition | Number:Dimensionless | Bypass position | R | +| vh_bypassmode | Number:Dimensionless | Bypass mode | R | +| vh_freeventilationmode | Switch | Free ventilation mode | R | +| vh_faultindication | Switch | Fault indication | R | +| vh_ventilationmode | Switch | Ventilation mode | R | +| vh_bypassstatus | Switch | Bypass status | R | +| vh_bypassautomaticstatus | Number:Dimensionless | Bypass automatic status | R | +| vh_freeventilationstatus | Switch | Free ventilation status | R | +| vh_filtercheck | Switch | Filter Check enabled | R | +| vh_diagnosticindication | Switch | Diagnostic indication | R | +| vh_controlsetpoint | Number:Dimensionless | Control setpoint | R | +| vh_servicerequest | Switch | Service request | R | +| vh_exhaustfanfault | Switch | Exhaust fan fault | R | +| vh_inletfanfault | Switch | Inlet fan fault | R | +| vh_frostprotection | Switch | Frost protection | R | +| vh_faultcode | Number:Dimensionless | Fault code | R | +| vh_diagnosticcode | Number:Dimensionless | Diagnostic code | R | +| vh_systemtype | Number:Dimensionless | System type | R | +| vh_bypass | Switch | Bypass | R | +| vh_speedcontrol | Number:Dimensionless | Speed control | R | +| vh_memberid | Number:Dimensionless | Member ID | R | +| vh_openthermversion | Number:Dimensionless | OpenTherm version | R | +| vh_versiontype | Number:Dimensionless | Version type | R | +| vh_relativeventilation | Number:Dimensionless | Relative ventilation position | R | +| vh_relativehumidity | Number:Dimensionless | Relative humidity exhaust air | R | +| vh_co2level | Number:Dimensionless | CO2 level exhaust air | R | +| vh_supplyinlettemp | Number:Temperature | Supply inlet temperature | R | +| vh_supplyoutlettemp | Number:Temperature | Supply outlet temperature | R | +| vh_exhaustinlettemp | Number:Temperature | Exhaust inlet temperature | R | +| vh_exhaustoutlettemp | Number:Temperature | Exhaust outlet temperature | R | +| vh_actualexhaustfanspeed | Number:Dimensionless | Actual exhaust fan speed | R | +| vh_actualinletfanspeed | Number:Dimensionless | Actual inlet fan speed | R | +| vh_nominalventenable | Switch | Nominal ventilation value transfer enabled | R | +| vh_nominalventrw | Number:Dimensionless | Nominal ventilation value | R | +| vh_nominalventilationvalue | Number:Dimensionless | Nominal ventilation value | R | +| vh_ventilationsetpoint | Number:Dimensionless | Ventilation setpoint override | R/W | +| vh_tspnumber | Number:Dimensionless | Number of transparent slave parameter entries | R | +| vh_tspentry | Number:Dimensionless | Transparent slave parameter entry | R | +| vh_fhbnumber | Number:Dimensionless | Number of fault history buffer entries | R | +| vh_fhbentry | Number:Dimensionless | Fault history buffer entry | R | + +## Transparent Slave Parameters and Fault History Buffer channels + +The transparent slave parameters (TSP) and fault history buffer (FHB) use a variable number of entries. +The number of entries is determined by a TSP or FHB size message. +Channels for TSP and FHB entries are automatically created when a TSP or FHB size message is received. +An index number is added to the base channel name to create a unique channel name for each entry. + +For example, if a TSP size message is received for a boiler unit (OpenTherm DATA-ID 10) with value 60, then channels `tspentry_0` through `tspentry_59` will be automatically created and linked to the corresponding TSP entry (OpenTherm DATA-ID 11). + +## Using OpenTherm Gateway as a master device + +When using OpenTherm with a boiler and a thermostat, the thermostat (master) periodically sends messages to the boiler to request data. +The boiler (slave) then sends a response message with the requested data which is then used by the OpenTherm Gateway binding to update the channel values in openHAB. + +If you have a setup without a master device requesting data, then the slave device may send fewer or even no OpenTherm mesages at all. + +In this case, you can make the OpenTherm Gateway act as a master device by sending Priority Message (PM) commands. +With openHAB rules, you can use the `sendcommand` channel of the `openthermgateway` bridge to periodically send PM commands to the OpenTherm Gateway. + +Example: + +``` +SendCommand.sendCommand("PM=10") +``` + +This will cause the OpenTherm Gateway to send a READ-DATA message to the slave device with DATA-ID 10. If supported, the slave device will respond with a READ-ACK message and the current value. + ## Full Example -### demo.things +### demo.things ``` -Thing openthermgateway:otgw:1 [ ipaddress="192.168.1.100", port=8000, connectionRetryInterval=60 ] +Bridge openthermgateway:openthermgateway:1 "OpenTherm Gateway" [ ipaddress="192.168.1.100", port="8000", connectionRetryInterval=60 ] { + Thing boiler remeha "Remeha Avanta 28c" + Thing ventilationheatrecovery brink "Brink Renovent Excellent 300" +} ``` -### demo.items +### demo.items for `openthermgateway` ``` -Number:Temperature RoomTemperature "Room temperature [%.1f °C]" { channel="openthermgateway:otgw:1:roomtemp" } -Number:Temperature RoomSetpoint "Room setpoint [%.1f °C]" { channel="openthermgateway:otgw:1:roomsetpoint" } -Number:Temperature TemporaryRoomSetpointOverride "Temporary room setpoint override [%.1f °C]" { channel="openthermgateway:otgw:1:temperaturetemporary" } -Number:Temperature ConstantRoomSetpointOverride "Constant room setpoint override [%.1f °C]" { channel="openthermgateway:otgw:1:temperatureconstant" } -Number:Temperature ControlSetpoint "Control setpoint [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpoint" } -Number:Temperature ControlSetpointRequested "Control setpoint requested [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpointrequested" } -Number:Temperature ControlSetpointOverride "Control setpoint override [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpointoverride" } -Number:Temperature ControlSetpoint2 "Control setpoint 2 [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpoint2" } -Number:Temperature ControlSetpoint2Requested "Control setpoint 2 requested [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpoint2requested" } -Number:Temperature ControlSetpoint2Override "Control setpoint 2 override [%.1f °C]" { channel="openthermgateway:otgw:1:controlsetpoint2override" } -Number:Temperature DomesticHotWaterTemperature "Domestic hot water temperature [%.1f °C]" { channel="openthermgateway:otgw:1:dhwtemp" } -Number:Temperature DomesticHotWaterSetpoint "Domestic hot water setpoint [%.1f °C]" { channel="openthermgateway:otgw:1:tdhwset" } -Number:Temperature DomesticHotWaterSetpointOverride "Domestic hot water setpoint override [%.1f °C]" { channel="openthermgateway:otgw:1:overridedhwsetpoint" } -Number:Temperature BoilerWaterTemperature "Boiler water temperature [%.1f °C]" { channel="openthermgateway:otgw:1:flowtemp" } -Number:Temperature ReturnWaterTemperature "Return water temperature [%.1f °C]" { channel="openthermgateway:otgw:1:returntemp" } -Number:Temperature OutsideTemperature "Outside temperature [%.1f °C]" { channel="openthermgateway:otgw:1:outsidetemp" } -Number:Pressure CentralHeatingWaterPressure "Central heating water pressure [%.1f bar]" { channel="openthermgateway:otgw:1:waterpressure" } -Switch CentralHeatingEnabled "Central heating enabled" { channel="openthermgateway:otgw:1:ch_enable" } -Switch CentralHeatingEnabledRequested "Central heating enabled requested" { channel="openthermgateway:otgw:1:ch_enablerequested" } -Switch CentralHeatingEnabledOverride "Central heating enabled override" { channel="openthermgateway:otgw:1:ch_enableoverride" } -Switch CentralHeating2Enabled "Central heating 2 enabled" { channel="openthermgateway:otgw:1:ch2_enable" } -Switch CentralHeating2EnabledRequested "Central 2 heating enabled requested" { channel="openthermgateway:otgw:1:ch2_enablerequested" } -Switch CentralHeating2EnabledOverride "Central 2 heating enabled override" { channel="openthermgateway:otgw:1:ch2_enableoverride" } -Switch CentralHeatingActive "Central heating active" { channel="openthermgateway:otgw:1:ch_mode" } -Switch DomesticHotWaterEnabled "Domestic hot water enabled" { channel="openthermgateway:otgw:1:dhw_enable" } -Switch DomesticHotWaterActive "Domestic hot water active" { channel="openthermgateway:otgw:1:dhw_mode" } -Switch BurnerActive "Burner active" { channel="openthermgateway:otgw:1:flame" } -Number:Dimensionless RelativeModulationLevel "Relative modulation level [%.1f %%]" { channel="openthermgateway:otgw:1:modulevel" } -Number:Dimensionless MaximumRelativeModulationLevel "Maximum relative modulation level [%.1f %%]" { channel="openthermgateway:otgw:1:maxrelmdulevel" } -Switch Fault "Fault indication" { channel="openthermgateway:otgw:1:fault" } -Switch ServiceRequest "Service required" { channel="openthermgateway:otgw:1:servicerequest" } -Switch LockoutReset "Lockout-reset" { channel="openthermgateway:otgw:1:lockout-reset" } -Switch LowWaterPress "Low water pressure fault" { channel="openthermgateway:otgw:1:lowwaterpress" } -Switch GasFlameFault "Gas or flame fault" { channel="openthermgateway:otgw:1:gasflamefault" } -Switch AirPressFault "Air pressure fault" { channel="openthermgateway:otgw:1:airpressfault" } -Switch WaterOvTemp "Water over-temperature fault" { channel="openthermgateway:otgw:1:waterovtemp" } -Number OemFaultCode "OEM fault code" { channel="openthermgateway:otgw:1:oemfaultcode" } -Switch Diagnostics "Diagnostics indication" { channel="openthermgateway:otgw:1:diag" } -Number:Dimensionless UnsuccessfulBurnerStarts "Unsuccessful burner starts" { channel="openthermgateway:otgw:1:unsuccessfulburnerstarts" } -Number:Dimensionless BurnerStarts "Burner starts" { channel="openthermgateway:otgw:1:burnerstarts" } -Number:Dimensionless CentralHeatingPumpStarts "Central heating pump starts" { channel="openthermgateway:otgw:1:chpumpstarts" } -Number:Dimensionless DomesticHotWaterPumpValveStarts "Domestic hot water pump/valve starts" { channel="openthermgateway:otgw:1:dhwpvstarts" } -Number:Dimensionless DomesticHotWaterBurnerStarts "Domestic hot water burner starts" { channel="openthermgateway:otgw:1:dhwburnerstarts" } -Number:Time BurnerHours "Burner hours" { channel="openthermgateway:otgw:1:burnerhours" } -Number:Time CentralHeatingPumpHours "Central heating pump hours" { channel="openthermgateway:otgw:1:chpumphours" } -Number:Time DomesticHotWaterPumpValveHours "Domestic hot water pump/valve hours" { channel="openthermgateway:otgw:1:dhwpvhours" } -Number:Time DomesticHotWaterBurnerHours "Domestic hot water burner hours" { channel="openthermgateway:otgw:1:dhwburnerhours" } -Text SendCommand "Send command channel" { channel="openthermgateway:otgw:1:sendcommand" } +Text SendCommand "Send command channel" { channel="openthermgateway:openthermgateway:1:sendcommand" } +``` + +### demo.items for `boiler` + +``` +Number:Temperature RoomTemperature "Room Temperature [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:roomtemp } +Number:Temperature RoomSetpoint "Room Setpoint [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:roomsetpoint } +Number:Temperature TemporaryRoomSetpointOverride "Temporary Room Setpoint Override [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:temperaturetemporary } +Number:Temperature ConstantRoomSetpointOverride "Constant Room Setpoint Override [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:temperatureconstant } +Number:Temperature ControlSetpoint "Control Setpoint [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpoint } +Number:Temperature ControlSetpointRequested "Control Setpoint Requested [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpointrequested } +Number:Temperature ControlSetpointOverride "Control Setpoint Override [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpointoverride } +Number:Temperature ControlSetpoint2 "Control Setpoint 2 [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpoint2 } +Number:Temperature ControlSetpoint2Requested "Control Setpoint 2 Requested [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpoint2requested } +Number:Temperature ControlSetpoint2Override "Control Setpoint 2 Override [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:controlsetpoint2override } +Number:Temperature DomesticHotWaterTemperature "Domestic Hot Water Temperature [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:dhwtemp } +Number:Temperature DomesticHotWaterSetpoint "Domestic Hot Water Setpoint [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:tdhwset } +Number:Temperature DomesticHotWaterSetpointOverride "Domestic Hot Water Setpoint Override [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:overridedhwsetpoint } +Number:Temperature BoilerWaterTemperature "Boiler Water Temperature [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:flowtemp } +Number:Temperature ReturnWaterTemperature "Return Water Temperature [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:returntemp } +Number:Temperature OutsideTemperature "Outside Temperature [%.1f %unit%]" { channel="openthermgateway:boiler:1:remeha:outsidetemp } +Number:Dimensionless CentralHeatingWaterPressure "Central Heating Water Pressure [%.1f bar]" { channel="openthermgateway:boiler:1:remeha:waterpressure } +Switch CentralHeatingEnabled "Central Heating Enabled" { channel="openthermgateway:boiler:1:remeha:ch_enable } +Switch CentralHeatingEnabledThermostat "Central Heating Enabled Thermostat" { channel="openthermgateway:boiler:1:remeha:ch_enablerequested } +Switch CentralHeatingOverridden "Central Heating Overridden" { channel="openthermgateway:boiler:1:remeha:ch_enableoverride } +Switch CentralHeating2Enabled "Central Heating 2 Enabled" { channel="openthermgateway:boiler:1:remeha:ch2_enable } +Switch CentralHeating2EnabledThermostat "Central Heating 2 Enabled Thermostat" { channel="openthermgateway:boiler:1:remeha:ch2_enablerequested } +Switch CentralHeating2Overridden "Central Heating 2 Overridden" { channel="openthermgateway:boiler:1:remeha:ch2_enableoverride } +Switch CentralHeatingActive "Central Heating Active" { channel="openthermgateway:boiler:1:remeha:ch_mode } +Switch DomesticHotWaterEnabled "Domestic Hot Water Enabled" { channel="openthermgateway:boiler:1:remeha:dhw_enable } +Switch DomesticHotWaterActive "Domestic Hot Water Active" { channel="openthermgateway:boiler:1:remeha:dhw_mode } +Switch BurnerActive "Burner Active" { channel="openthermgateway:boiler:1:remeha:flame } +Number:Dimensionless RelativeModulationLevel "Relative Modulation Level [%.1f %%]" { channel="openthermgateway:boiler:1:remeha:modulevel } +Number:Dimensionless MaximumRelativeModulationLevel "Maximum Relative Modulation Level [%.1f %%]" { channel="openthermgateway:boiler:1:remeha:maxrelmdulevel } +Switch FaultIndication "Fault Indication" { channel="openthermgateway:boiler:1:remeha:fault } +Switch ServiceRequired "Service Required" { channel="openthermgateway:boiler:1:remeha:servicerequest } +Switch LockoutResetEnabled "Lockout-Reset Enabled" { channel="openthermgateway:boiler:1:remeha:lockout-reset } +Switch LowWaterPressureFault "Low Water Pressure Fault" { channel="openthermgateway:boiler:1:remeha:lowwaterpress } +Switch GasOrFlameFault "Gas Or Flame Fault" { channel="openthermgateway:boiler:1:remeha:gasflamefault } +Switch AirPressureFault "Air Pressure Fault" { channel="openthermgateway:boiler:1:remeha:airpressfault } +Switch WaterOverTemperatureFault "Water Over-Temperature Fault" { channel="openthermgateway:boiler:1:remeha:waterovtemp } +Number:Dimensionless OEMFaultCode "OEM Fault Code" { channel="openthermgateway:boiler:1:remeha:oemfaultcode } +Number:Dimensionless UnsuccessfulBurnerStarts "Unsuccessful Burner Starts" { channel="openthermgateway:boiler:1:remeha:unsuccessfulburnerstarts } +Number:Dimensionless BurnerStarts "Burner Starts" { channel="openthermgateway:boiler:1:remeha:burnerstarts } +Number:Dimensionless CentralHeatingPumpStarts "Central Heating Pump Starts" { channel="openthermgateway:boiler:1:remeha:chpumpstarts } +Number:Dimensionless DomesticHotWaterPump/ValveStarts "Domestic Hot Water Pump/Valve Starts" { channel="openthermgateway:boiler:1:remeha:dhwpvstarts } +Number:Dimensionless DomesticHotWaterBurnerStarts "Domestic Hot Water Burner Starts" { channel="openthermgateway:boiler:1:remeha:dhwburnerstarts } +Number:Time BurnerHours "Burner Hours" { channel="openthermgateway:boiler:1:remeha:burnerhours } +Number:Time CentralHeatingPumpHours "Central Heating Pump Hours" { channel="openthermgateway:boiler:1:remeha:chpumphours } +Number:Time DomesticHotWaterPumpValveHours "Domestic Hot Water Pump/Valve Hours" { channel="openthermgateway:boiler:1:remeha:dhwpvhours } +Number:Time DomesticHotWaterBurnerHours "Domestic Hot Water Burner Hours" { channel="openthermgateway:boiler:1:remeha:dhwburnerhours } +Number:Dimensionless TransparentSlaveParameterNumber "Transparent Slave Parameter Number" { channel="openthermgateway:boiler:1:remeha:tspnumber } +Number:Dimensionless TransparentSlaveParameterEntry "Transparent Slave Parameter Entry" { channel="openthermgateway:boiler:1:remeha:tspentry } +Number:Dimensionless FaultHistoryBufferNumber "Fault History Buffer Number" { channel="openthermgateway:boiler:1:remeha:fhbnumber } +Number:Dimensionless FaultHistoryBufferEntry "Fault History Buffer Entry" { channel="openthermgateway:boiler:1:remeha:fhbentry } +``` + +### demo.items for `ventilationheatrecovery` + +``` +Switch VentilationEnabled "Ventilation Enabled" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_ventilationenable } +Number:Dimensionless BypassPosition "Bypass Position" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_bypassposition } +Number:Dimensionless BypassMode "Bypass Mode" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_bypassmode } +Switch FreeVentilationMode "Free Ventilation Mode" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_freeventilationmode } +Switch FaultIndication "Fault Indication" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_faultindication } +Switch VentilationMode "Ventilation Mode" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_ventilationmode } +Switch BypassStatus "Bypass Status" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_bypassstatus } +Number:Dimensionless BypassAutomaticStatus "Bypass Automatic Status" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_bypassautomaticstatus } +Switch FreeVentilationStatus "Free Ventilation Status" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_freeventilationstatus } +Switch FilterCheck "Filter Check" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_filtercheck } +Switch DiagnosticIndication "Diagnostic Indication" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_diagnosticindication } +Number:Dimensionless ControlSetpoint "Control Setpoint" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_controlsetpoint } +Switch ServiceRequest "Service Request" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_servicerequest } +Switch ExhaustFanFault "Exhaust Fan Fault" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_exhaustfanfault } +Switch InletFanFault "Inlet Fan Fault" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_inletfanfault } +Switch FrostProtection "Frost Protection" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_frostprotection } +Number:Dimensionless FaultCode "Fault Code" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_faultcode } +Number:Dimensionless DiagnosticCode "Diagnostic Code" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_diagnosticcode } +Number:Dimensionless SystemType "System Type" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_systemtype } +Switch Bypass "Bypass" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_bypass } +Number:Dimensionless SpeedControl "Speed Control" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_speedcontrol } +Number:Dimensionless MemberID "Member ID" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_memberid } +Number:Dimensionless OpenThermVersion "OpenTherm Version" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_openthermversion } +Number:Dimensionless VersionType "Version Type" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_versiontype } +Number:Dimensionless RelativeVentilation "Relative Ventilation [%d %%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_relativeventilation } +Number:Dimensionless RelativeHumidity "Relative Humidity [%d %%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_relativehumidity } +Number:Dimensionless CO2Level "CO2 Level [%d ppm]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_co2level } +Number:Temperature SupplyInletTemperature "Supply Inlet Temperature [%.1f %unit%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_supplyinlettemp } +Number:Temperature SupplyOutletTemperature "Supply Outlet Temperature [%.1f %unit%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_supplyoutlettemp } +Number:Temperature ExhaustInletTemperature "Exhaust Inlet Temperature [%.1f %unit%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_exhaustinlettemp } +Number:Temperature ExhaustOutletTemperature "Exhaust Outlet Temperature [%.1f %unit%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_exhaustoutlettemp } +Number:Dimensionless ActualExhaustFanSpeed "Actual Exhaust Fan Speed [%d rpm]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_actualexhaustfanspeed } +Number:Dimensionless ActualInletFanSpeed "Actual Inlet Fan Speed [%d rpm]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_actualinletfanspeed } +Switch NominalVentilationValueTransfer "Nominal Ventilation Value Transfer" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_nominalventenable } +Number:Dimensionless NominalVentilationValue "Nominal Ventilation Value" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_nominalventrw } +Number:Dimensionless NominalVentilationValue "Nominal Ventilation Value [%d %%]" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_nominalventilationvalue } +Number:Dimensionless VentilationSetpoint "Ventilation Setpoint" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_ventilationsetpoint } +Number:Dimensionless TransparentSlaveParameterNumber "Transparent Slave Parameter Number" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_tspnumber } +Number:Dimensionless TransparentSlaveParameterEntry "Transparent Slave Parameter Entry" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_tspentry } +Number:Dimensionless FaultHistoryBufferNumber "Fault History Buffer Number" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_fhbnumber } +Number:Dimensionless FaultHistoryBufferEntry "Fault History Buffer Entry" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_fhbentry } ``` ### 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="ControlSetpointRequested" icon="temperature" label="Control setpoint requested [%.1f °C]" - Setpoint item="ControlSetpointOverride" icon="temperature" label="Control setpoint override [%.1f °C]" minValue="0" maxValue="100" step="1" - 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="CentralHeatingEnabledRequested" icon="switch" label="Central heating enabled requested" - Switch item="CentralHeatingEnabledOverride" icon="switch" label="Central heating enabled override" - 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" - Switch item="Diagnostics" icon="" label="Diagnostics indication" - Text item="UnsuccessfulBurnerStarts" icon="" label="Unsuccessful burner starts" - Text item="BurnerStarts" icon="" label="Burner starts" - Text item="CentralHeatingPumpStarts" icon="" label="Central heating pump starts" - Text item="DomesticHotWaterPumpValveStarts" icon="" label="Domestic hot water pump/valve starts" - Text item="DomesticHotWaterBurnerStarts" icon="" label="Domestic hot water burner starts" - Text item="BurnerHours" icon="" label="Burner hours" - Text item="CentralHeatingPumpHours" icon="" label="Central heating pump hours" - Text item="DomesticHotWaterPumpValveHours" icon="" label="Domestic hot water pump/valve hours" - Text item="DomesticHotWaterBurnerHours" icon="" label="Domestic hot water burner hours" + Frame label="Boiler" { + Text item="RoomTemperature" icon="temperature" label="Room Temperature [%.1f %unit%]" + Text item="RoomSetpoint" icon="temperature" label="Room Setpoint [%.1f %unit%]" + Text item="TemporaryRoomSetpointOverride" icon="temperature" label="Temporary Room Setpoint Override [%.1f %unit%]" minValue="0" maxValue="30" step="0.1" + Text item="ConstantRoomSetpointOverride" icon="temperature" label="Constant Room Setpoint Override [%.1f %unit%]" minValue="0" maxValue="30" step="0.1" + Text item="ControlSetpoint" icon="temperature" label="Control Setpoint [%.1f %unit%]" + Text item="ControlSetpointRequested" icon="temperature" label="Control Setpoint Requested [%.1f %unit%]" + Text item="ControlSetpointOverride" icon="temperature" label="Control Setpoint Override [%.1f %unit%]" minValue="0" maxValue="100" step="0.1" + Text item="ControlSetpoint2" icon="temperature" label="Control Setpoint 2 [%.1f %unit%]" + Text item="ControlSetpoint2Requested" icon="temperature" label="Control Setpoint 2 Requested [%.1f %unit%]" + Text item="ControlSetpoint2Override" icon="temperature" label="Control Setpoint 2 Override [%.1f %unit%]" minValue="0" maxValue="100" step="0.1" + Text item="DomesticHotWaterTemperature" icon="temperature" label="Domestic Hot Water Temperature [%.1f %unit%]" + Text item="DomesticHotWaterSetpoint" icon="temperature" label="Domestic Hot Water Setpoint [%.1f %unit%]" + Text item="DomesticHotWaterSetpointOverride" icon="temperature" label="Domestic Hot Water Setpoint Override [%.1f %unit%]" minValue="0" maxValue="100" step="0.1" + Text item="BoilerWaterTemperature" icon="temperature" label="Boiler Water Temperature [%.1f %unit%]" + Text item="ReturnWaterTemperature" icon="temperature" label="Return Water Temperature [%.1f %unit%]" + Text item="OutsideTemperature" icon="temperature" label="Outside Temperature [%.1f %unit%]" minValue="-40" maxValue="100" step="0.1" + Text item="CentralHeatingWaterPressure" label="Central Heating Water Pressure [%.1f bar]" + Switch item="CentralHeatingEnabled" icon="switch" label="Central Heating Enabled" + Switch item="CentralHeatingEnabledThermostat" icon="switch" label="Central Heating Enabled Thermostat" + Switch item="CentralHeatingOverridden" icon="switch" label="Central Heating Overridden" + Switch item="CentralHeating2Enabled" icon="switch" label="Central Heating 2 Enabled" + Switch item="CentralHeating2EnabledThermostat" icon="switch" label="Central Heating 2 Enabled Thermostat" + Switch item="CentralHeating2Overridden" icon="switch" label="Central Heating 2 Overridden" + 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" label="Relative Modulation Level [%.1f %%]" + Text item="MaximumRelativeModulationLevel" label="Maximum Relative Modulation Level [%.1f %%]" + Switch item="FaultIndication" icon="switch" label="Fault Indication" + Switch item="ServiceRequired" icon="switch" label="Service Required" + Switch item="Lockout-ResetEnabled" icon="switch" label="Lockout-Reset Enabled" + Switch item="LowWaterPressureFault" icon="switch" label="Low Water Pressure Fault" + Switch item="GasOrFlameFault" icon="switch" label="Gas Or Flame Fault" + Switch item="AirPressureFault" icon="switch" label="Air Pressure Fault" + Switch item="WaterOver-TemperatureFault" icon="switch" label="Water Over-Temperature Fault" + Text item="OEMFaultCode" label="OEM Fault Code" + Text item="UnsuccessfulBurnerStarts" label="Unsuccessful Burner Starts" + Text item="BurnerStarts" label="Burner Starts" + Text item="CentralHeatingPumpStarts" label="Central Heating Pump Starts" + Text item="DomesticHotWaterPump/ValveStarts" label="Domestic Hot Water Pump/Valve Starts" + Text item="DomesticHotWaterBurnerStarts" label="Domestic Hot Water Burner Starts" + Text item="BurnerHours" label="Burner Hours" + Text item="CentralHeatingPumpHours" label="Central Heating Pump Hours" + Text item="DomesticHotWaterPumpValveHours" label="Domestic Hot Water Pump/Valve Hours" + Text item="DomesticHotWaterBurnerHours" label="Domestic Hot Water Burner Hours" + Text item="TransparentSlaveParameterNumber" label="Transparent Slave Parameter Number" + Text item="TransparentSlaveParameterEntry" label="Transparent Slave Parameter Entry" + Text item="FaultHistoryBufferNumber" label="Fault History Buffer Number" + Text item="FaultHistoryBufferEntry" label="Fault History Buffer Entry" } -} + + Frame label="Ventilation / Heat Recovery" { + Switch item="VentilationEnabled" icon="switch" label="Ventilation Enabled" + Text item="BypassPosition" label="Bypass Position" + Text item="BypassMode" label="Bypass Mode" + Switch item="FreeVentilationMode" icon="switch" label="Free Ventilation Mode" + Switch item="FaultIndication" icon="switch" label="Fault Indication" + Switch item="VentilationMode" icon="switch" label="Ventilation Mode" + Switch item="BypassStatus" icon="switch" label="Bypass Status" + Text item="BypassAutomaticStatus" label="Bypass Automatic Status" + Switch item="FreeVentilationStatus" icon="switch" label="Free Ventilation Status" + Switch item="FilterCheck" icon="switch" label="Filter Check" + Switch item="DiagnosticIndication" icon="switch" label="Diagnostic Indication" + Text item="ControlSetpoint" label="Control Setpoint" + Switch item="ServiceRequest" icon="switch" label="Service Request" + Switch item="ExhaustFanFault" icon="switch" label="Exhaust Fan Fault" + Switch item="InletFanFault" icon="switch" label="Inlet Fan Fault" + Switch item="FrostProtection" icon="switch" label="Frost Protection" + Text item="FaultCode" label="Fault Code" + Text item="DiagnosticCode" label="Diagnostic Code" + Text item="SystemType" label="System Type" + Switch item="Bypass" icon="switch" label="Bypass" + Text item="SpeedControl" label="Speed Control" + Text item="MemberID" label="Member ID" + Text item="OpenThermVersion" label="OpenTherm Version" + Text item="VersionType" label="Version Type" + Text item="RelativeVentilation" label="Relative Ventilation [%d %%]" + Text item="RelativeHumidity" label="Relative Humidity [%d %%]" + Text item="CO2Level" label="CO2 Level [%d ppm]" + Text item="SupplyInletTemperature" label="Supply Inlet Temperature [%.1f %unit%]" + Text item="SupplyOutletTemperature" label="Supply Outlet Temperature [%.1f %unit%]" + Text item="ExhaustInletTemperature" label="Exhaust Inlet Temperature [%.1f %unit%]" + Text item="ExhaustOutletTemperature" label="Exhaust Outlet Temperature [%.1f %unit%]" + Text item="ActualExhaustFanSpeed" label="Actual Exhaust Fan Speed [%d rpm]" + Text item="ActualInletFanSpeed" label="Actual Inlet Fan Speed [%d rpm]" + Switch item="NominalVentilationValueTransfer" icon="switch" label="Nominal Ventilation Value Transfer" + Text item="NominalVentilationValue" label="Nominal Ventilation Value" + Text item="NominalVentilationValue" label="Nominal Ventilation Value [%d %%]" + Text item="VentilationSetpoint" label="Ventilation Setpoint" minValue="0" maxValue="100" step="1" + Text item="TransparentSlaveParameterNumber" label="Transparent Slave Parameter Number" + Text item="TransparentSlaveParameterEntry" label="Transparent Slave Parameter Entry" + Text item="FaultHistoryBufferNumber" label="Fault History Buffer Number" + Text item="FaultHistoryBufferEntry" label="Fault History Buffer Entry" + } + } +``` + +## Migration from Prior Versions of the Binding + +Between openHAB v3.2 and v3.3 the structure of Things and Channels was changed. +This means that Things and their respective Channels and Items that were created on v3.2 or earlier will no longer function on version v3.3 or later. +To be specific the change is as follows.. + +- **openHAB Versions v3.2 or earlier**: There was just one single `otgw` Thing that combined the functions that communicate with the OpenTherm Gateway together with the functions of the Channels that monitor/control the connected Boiler. + +- **openHAB Versions v3.3 or later**: The communication functions have been moved to a new `openthermgateway` Bridge Thing. The connected Boiler functions have been moved to a new `boiler` Thing. And in addition (if needed) new functions for a connected Ventilation / Heat-Recovery system have been added in a new `ventilationheatrecovery` Thing. + +So if you upgrade your system from openHAB v3.2 (or lower) to v3.3 (or higher), then your Thing and Item definitions must be migrated as shown below.. + +- Divide the contents of your old `openthermgateway:otgw:yourGatewayId` Thing definition into two new parts, namely 1) a new `openthermgateway:openthermgateway:yourGatewayId` Bridge definition for the OpenTherm Gateway, and 2) a new `openthermgateway:boiler:yourGatewayId:yourBoilerId` Thing definition for the connected Boiler. + +- Change the `channel=".."` configuration entries of all your Items from referring to the ThingUID of the old `otgw` Thing to refer instead to the ThingUID of the respective newly created `boiler` Thing. + +**Old Thing Definition and respective Item Definition (example)** + +``` +Thing openthermgateway:otgw:yourGatewayId [ ipaddress="192.168.1.100", port=8000, connectionRetryInterval=60 ] + +e.g. +Number:Temperature Boiler_DHW_Temperature "Boiler DHW Temperature [%.1f %unit%]" {channel="openthermgateway:otgw:yourGatewayId:dhwtemp"} +&c. +``` + +**New Thing Definition and respective and Item Definition (example)** + +``` +Bridge openthermgateway:openthermgateway:yourGatewayId "OpenTherm Gateway" @ "Kitchen" [ipaddress="192.168.1.100", port=20108, connectionRetryInterval=60] { + Thing boiler remeha "Boiler" @ "Kitchen" +} + +e.g. +Number:Temperature Boiler_DHW_Temperature "Boiler DHW Temperature [%.1f %unit%]" {channel="openthermgateway:boiler:yourGatewayId:remeha:dhwtemp"} +&c. ``` diff --git a/bundles/org.openhab.binding.openthermgateway/pom.xml b/bundles/org.openhab.binding.openthermgateway/pom.xml index 1501e8968..b2f55a967 100644 --- a/bundles/org.openhab.binding.openthermgateway/pom.xml +++ b/bundles/org.openhab.binding.openthermgateway/pom.xml @@ -14,4 +14,5 @@ openHAB Add-ons :: Bundles :: OpenTherm Gateway Binding + This binding integrates OpenTherm capable devices via an OpenThermGateway bridge. diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/OpenThermGatewayBindingConstants.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/OpenThermGatewayBindingConstants.java deleted file mode 100644 index e8d1c34d0..000000000 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/OpenThermGatewayBindingConstants.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) 2010-2022 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.Set; - -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_REQUESTED_CENTRAL_HEATING_WATER_SETPOINT = "controlsetpointrequested"; - public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT = "controlsetpointoverride"; - public static final String CHANNEL_CENTRAL_HEATING2_WATER_SETPOINT = "controlsetpoint2"; - public static final String CHANNEL_REQUESTED_CENTRAL_HEATING2_WATER_SETPOINT = "controlsetpoint2requested"; - public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT = "controlsetpoint2override"; - 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_REQUESTED_CENTRAL_HEATING_ENABLED = "ch_enablerequested"; - public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED = "ch_enableoverride"; - public static final String CHANNEL_CENTRAL_HEATING2_ENABLED = "ch2_enable"; - public static final String CHANNEL_REQUESTED_CENTRAL_HEATING2_ENABLED = "ch2_enablerequested"; - public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED = "ch2_enableoverride"; - 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 String CHANNEL_DIAGNOSTICS_INDICATION = "diag"; - public static final String CHANNEL_UNSUCCESSFUL_BURNER_STARTS = "unsuccessfulburnerstarts"; - public static final String CHANNEL_BURNER_STARTS = "burnerstarts"; - public static final String CHANNEL_CH_PUMP_STARTS = "chpumpstarts"; - public static final String CHANNEL_DHW_PV_STARTS = "dhwpvstarts"; - public static final String CHANNEL_DHW_BURNER_STARTS = "dhwburnerstarts"; - public static final String CHANNEL_BURNER_HOURS = "burnerhours"; - public static final String CHANNEL_CH_PUMP_HOURS = "chpumphours"; - public static final String CHANNEL_DHW_PV_HOURS = "dhwpvhours"; - public static final String CHANNEL_DHW_BURNER_HOURS = "dhwburnerhours"; - - public static final Set SUPPORTED_CHANNEL_IDS = Set.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_REQUESTED_CENTRAL_HEATING_ENABLED, CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED, - CHANNEL_CENTRAL_HEATING2_ENABLED, CHANNEL_REQUESTED_CENTRAL_HEATING2_ENABLED, - CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED, CHANNEL_CENTRAL_HEATING_MODE, - CHANNEL_CENTRAL_HEATING_WATER_SETPOINT, CHANNEL_REQUESTED_CENTRAL_HEATING_WATER_SETPOINT, - CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT, CHANNEL_CENTRAL_HEATING2_WATER_SETPOINT, - CHANNEL_REQUESTED_CENTRAL_HEATING2_WATER_SETPOINT, CHANNEL_OVERRIDE_CENTRAL_HEATING2_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, CHANNEL_DIAGNOSTICS_INDICATION, CHANNEL_UNSUCCESSFUL_BURNER_STARTS, - CHANNEL_BURNER_STARTS, CHANNEL_CH_PUMP_STARTS, CHANNEL_DHW_PV_STARTS, CHANNEL_DHW_BURNER_STARTS, - CHANNEL_BURNER_HOURS, CHANNEL_CH_PUMP_HOURS, CHANNEL_DHW_PV_HOURS, CHANNEL_DHW_BURNER_HOURS); -} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BaseDeviceHandler.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BaseDeviceHandler.java new file mode 100644 index 000000000..9987ba2b1 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BaseDeviceHandler.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2010-2022 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 static org.openhab.binding.openthermgateway.internal.OpenThermGatewayBindingConstants.*; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.openthermgateway.internal.DataItem; +import org.openhab.binding.openthermgateway.internal.DataItemGroup; +import org.openhab.binding.openthermgateway.internal.Message; +import org.openhab.binding.openthermgateway.internal.TspFhbSizeDataItem; +import org.openhab.binding.openthermgateway.internal.TspFhbValueDataItem; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.ThingHandlerCallback; +import org.openhab.core.thing.type.ChannelKind; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link BaseDeviceHandler} is a base class for actual Things. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public abstract class BaseDeviceHandler extends BaseThingHandler { + + private final Logger logger = LoggerFactory.getLogger(BaseDeviceHandler.class); + + public BaseDeviceHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + if (getBridge() == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR); + } else { + // note: the framework handles bridge configuration resp. offline errors + updateStatus(ThingStatus.ONLINE); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + Bridge bridge = getBridge(); + + if (bridge != null) { + OpenThermGatewayHandler handler = (OpenThermGatewayHandler) bridge.getHandler(); + if (handler != null) { + handler.handleCommand(channelUID, command); + } + } else { + logger.debug("Bridge is missing"); + } + } + + public void receiveMessage(Message message) { + DataItem[] dataItems = DataItemGroup.DATAITEMGROUPS.get(message.getID()); + + if (dataItems == null) { + logger.debug("No DataItem found for message id {}", message.getID()); + return; + } + + for (DataItem dataItem : dataItems) { + if (dataItem instanceof TspFhbSizeDataItem) { + logger.debug("Received TSP or FHB size message {} ({})", message.getID(), dataItem.getSubject()); + + verifyTspFhbChannels(((TspFhbSizeDataItem) dataItem).getValueId(), + message.getUInt(dataItem.getByteType())); + } else { + String channelId = dataItem.getChannelId(message); + + if (thing.getChannel(channelId) == null || !dataItem.hasValidCodeType(message)) { + continue; + } + + State state = dataItem.createState(message); + + logger.debug("Received update for channel {}: {}", channelId, state); + updateState(channelId, state); + } + } + } + + private void verifyTspFhbChannels(int id, int size) { + // Dynamically create TSP or FHB value channels based on TSP or FHB size message + ThingHandlerCallback callback = getCallback(); + + if (callback == null) { + logger.debug("Unable to get thing handler callback"); + return; + } + + DataItem[] dataItems = DataItemGroup.DATAITEMGROUPS.get(id); + + if (dataItems == null) { + logger.debug("Unable to find dataItem for id {}", id); + return; + } + + if (dataItems.length != 1) { + logger.debug("Found zero or multiple dataItems for id {}", id); + return; + } + + TspFhbValueDataItem dataItem = (TspFhbValueDataItem) dataItems[0]; + + logger.debug("Checking number of TSP or FHB channels for DATA-ID {}: {}", id, size); + + // A generic Number:Dimensionless channel type for TSP and FHB values + ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TSPFHB); + + List channels = new ArrayList<>(getThing().getChannels()); + + boolean changed = false; + for (int i = 0; i < size; i++) { + String channelId = dataItem.getChannelId(i); + ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId); + + if (!channels.stream().map(Channel::getUID).anyMatch(channelUID::equals)) { + String label = dataItem.getLabel(i); + + logger.debug("Adding channel {}", channelId); + + channels.add(callback.createChannelBuilder(channelUID, channelTypeUID).withKind(ChannelKind.STATE) + .withLabel(label).build()); + changed = true; + } else { + logger.debug("Channel {} already exists", channelId); + } + } + + if (changed) { + logger.debug("Updating Thing with new channels"); + updateThing(editThing().withChannels(channels).build()); + } + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BoilerHandler.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BoilerHandler.java new file mode 100644 index 000000000..5946ba6ed --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/BoilerHandler.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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 org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link BoilerHandler} represemts a boiler with thermostat. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class BoilerHandler extends BaseDeviceHandler { + + public BoilerHandler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/OpenThermGatewayHandler.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/OpenThermGatewayHandler.java index f27f9f5bd..33b7ae7a3 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/OpenThermGatewayHandler.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/OpenThermGatewayHandler.java @@ -15,32 +15,29 @@ package org.openhab.binding.openthermgateway.handler; import java.util.concurrent.ScheduledFuture; 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.ConnectionState; 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.OpenThermGatewayBindingConstants; 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.Bridge; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; -import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.BaseBridgeHandler; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; -import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,38 +49,37 @@ import org.slf4j.LoggerFactory; * @author Arjen Korevaar - Initial contribution */ @NonNullByDefault -public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThermGatewayCallback { +public class OpenThermGatewayHandler extends BaseBridgeHandler implements OpenThermGatewayCallback { private final Logger logger = LoggerFactory.getLogger(OpenThermGatewayHandler.class); - private @Nullable OpenThermGatewayConfiguration config; + private @Nullable OpenThermGatewayConfiguration configuration; private @Nullable OpenThermGatewayConnector connector; private @Nullable ScheduledFuture reconnectTask; - private boolean connecting = false; - private boolean explicitDisconnect = false; + private @Nullable ConnectionState state; + private boolean autoReconnect = true; + private boolean disposing = false; - public OpenThermGatewayHandler(Thing thing) { - super(thing); + public OpenThermGatewayHandler(Bridge bridge) { + super(bridge); } @Override public void initialize() { - logger.debug("Initializing OpenTherm Gateway handler for uid '{}'", getThing().getUID()); + logger.debug("Initializing OpenThermGateway handler for uid {}", getThing().getUID()); - updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Initializing"); - - config = getConfigAs(OpenThermGatewayConfiguration.class); + configuration = getConfigAs(OpenThermGatewayConfiguration.class); + logger.debug("Using configuration: {}", configuration); + disposing = false; + updateStatus(ThingStatus.UNKNOWN); connect(); } @Override public void handleCommand(ChannelUID channelUID, Command command) { - @Nullable - OpenThermGatewayConnector conn = connector; - - logger.debug("Received channel: {}, command: {}", channelUID, command); + logger.debug("Received command {} for channel {}", command, channelUID); if (!(command instanceof RefreshType)) { String channel = channelUID.getId(); @@ -108,146 +104,121 @@ public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThe gatewayCommand = GatewayCommand.parse(code, command.toFullString()); } - if (conn != null && conn.isConnected()) { - conn.sendCommand(gatewayCommand); + sendCommand(gatewayCommand); - if (GatewayCommandCode.ControlSetpoint.equals(code)) { - if (gatewayCommand.getMessage().equals("0.0")) { - updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT, - UnDefType.UNDEF); - } - updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED, - OnOffType.from(!gatewayCommand.getMessage().equals("0.0"))); - } else if (GatewayCommandCode.ControlSetpoint2.equals(code)) { - if (gatewayCommand.getMessage().equals("0.0")) { - updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT, - UnDefType.UNDEF); - } - updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED, - OnOffType.from(!gatewayCommand.getMessage().equals("0.0"))); + if (GatewayCommandCode.CONTROLSETPOINT.equals(code)) { + if (gatewayCommand.getMessage().equals("0.0")) { + updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT, + UnDefType.UNDEF); } - } else { - connect(); + updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED, + OnOffType.from(!gatewayCommand.getMessage().equals("0.0"))); + } else if (GatewayCommandCode.CONTROLSETPOINT2.equals(code)) { + if (gatewayCommand.getMessage().equals("0.0")) { + updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT, + UnDefType.UNDEF); + } + updateState(OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED, + OnOffType.from(!gatewayCommand.getMessage().equals("0.0"))); } } } - @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() { + public void sendCommand(GatewayCommand gatewayCommand) { @Nullable - OpenThermGatewayConfiguration conf = config; + OpenThermGatewayConnector conn = connector; - connecting = false; - - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Disconnected"); - - // retry connection if disconnect is not explicitly requested - if (!explicitDisconnect && conf != null && conf.connectionRetryInterval > 0) { - logger.debug("Scheduling to reconnect in {} seconds.", conf.connectionRetryInterval); - reconnectTask = scheduler.schedule(this::connect, conf.connectionRetryInterval, TimeUnit.SECONDS); + if (conn != null && conn.isConnected()) { + conn.sendCommand(gatewayCommand); + } else { + logger.debug("Unable to send command {}: connector not connected", gatewayCommand.toFullString()); } } @Override public void receiveMessage(Message message) { - if (DataItemGroup.dataItemGroups.containsKey(message.getID())) { - DataItem[] dataItems = DataItemGroup.dataItemGroups.get(message.getID()); + scheduler.submit(() -> receiveMessageTask(message)); + } - for (DataItem dataItem : dataItems) { - String channelId = dataItem.getSubject(); + private void receiveMessageTask(Message message) { + int msgId = message.getID(); - if (!OpenThermGatewayBindingConstants.SUPPORTED_CHANNEL_IDS.contains(channelId) - || (dataItem.getFilteredCode() != null && dataItem.getFilteredCode() != message.getCode())) { - continue; - } + if (!DataItemGroup.DATAITEMGROUPS.containsKey(msgId)) { + logger.debug("Unsupported message id {}", msgId); + return; + } - State state = null; + for (Thing thing : getThing().getThings()) { + BaseDeviceHandler handler = (BaseDeviceHandler) thing.getHandler(); - 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 (handler != null) { + handler.receiveMessage(message); + } + } + } - if (state != null) { - logger.debug("Received update for channel '{}': {}", channelId, state); - updateState(channelId, state); - } + @Override + public void connectionStateChanged(ConnectionState state) { + scheduler.submit(() -> connectionStateChangedTask(state)); + } + + private void connectionStateChangedTask(ConnectionState state) { + if (this.state != state) { + this.state = state; + + switch (state) { + case CONNECTED: + updateStatus(ThingStatus.ONLINE); + cancelAutoReconnect(); + break; + case DISCONNECTED: + if (!disposing) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR); + autoReconnect(); + } + default: } } } @Override public void handleRemoval() { - logger.debug("Removing OpenTherm Gateway handler"); + logger.debug("Removing OpenThermGateway handler"); disconnect(); super.handleRemoval(); } @Override public void dispose() { + logger.debug("Disposing OpenThermGateway handler"); + disposing = true; disconnect(); - - ScheduledFuture localReconnectTask = reconnectTask; - if (localReconnectTask != null) { - localReconnectTask.cancel(true); - reconnectTask = null; - } - super.dispose(); } private void connect() { @Nullable - OpenThermGatewayConfiguration conf = config; + OpenThermGatewayConfiguration config = configuration; - explicitDisconnect = false; - - if (connecting) { - logger.debug("OpenTherm Gateway connector is already connecting ..."); + if (this.state == ConnectionState.CONNECTING) { + logger.debug("OpenThermGateway connector is already connecting"); return; } + // Make sure everything is cleaned up before creating a new connection disconnect(); - if (conf != null) { - logger.debug("Starting OpenTherm Gateway connector"); + if (config != null) { + connectionStateChanged(ConnectionState.INITIALIZING); - connector = new OpenThermGatewaySocketConnector(this, conf.ipaddress, conf.port); + logger.debug("Starting OpenThermGateway connector"); - Thread thread = new Thread(connector, "OpenTherm Gateway Binding - socket listener thread"); - thread.setDaemon(true); - thread.start(); + autoReconnect = true; - logger.debug("OpenTherm Gateway connector started"); + OpenThermGatewayConnector conn = connector = new OpenThermGatewaySocketConnector(this, config); + conn.start(); + + logger.debug("OpenThermGateway connector started"); } } @@ -255,36 +226,59 @@ public class OpenThermGatewayHandler extends BaseThingHandler implements OpenThe @Nullable OpenThermGatewayConnector conn = connector; - explicitDisconnect = true; + autoReconnect = false; + + cancelAutoReconnect(); if (conn != null) { - if (conn.isConnected()) { - logger.debug("Stopping OpenTherm Gateway connector"); - conn.stop(); + conn.stop(); + connector = null; + } + } + + private void autoReconnect() { + @Nullable + OpenThermGatewayConfiguration config = configuration; + + if (autoReconnect && config != null && config.connectionRetryInterval > 0) { + logger.debug("Scheduling to auto reconnect in {} seconds", config.connectionRetryInterval); + reconnectTask = scheduler.schedule(this::connect, config.connectionRetryInterval, TimeUnit.SECONDS); + } + } + + private void cancelAutoReconnect() { + ScheduledFuture localReconnectTask = reconnectTask; + + if (localReconnectTask != null) { + if (!localReconnectTask.isDone()) { + logger.debug("Cancelling auto reconnect task"); + localReconnectTask.cancel(true); } - connector = null; + reconnectTask = null; } } private @Nullable String getGatewayCodeFromChannel(String channel) throws IllegalArgumentException { switch (channel) { case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_TEMPORARY: - return GatewayCommandCode.TemperatureTemporary; + return GatewayCommandCode.TEMPERATURETEMPORARY; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_SETPOINT_CONSTANT: - return GatewayCommandCode.TemperatureConstant; + return GatewayCommandCode.TEMPERATURECONSTANT; case OpenThermGatewayBindingConstants.CHANNEL_OUTSIDE_TEMPERATURE: - return GatewayCommandCode.TemperatureOutside; + return GatewayCommandCode.TEMPERATUREOUTSIDE; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_DHW_SETPOINT: - return GatewayCommandCode.SetpointWater; + return GatewayCommandCode.SETPOINTWATER; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT: - return GatewayCommandCode.ControlSetpoint; + return GatewayCommandCode.CONTROLSETPOINT; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED: - return GatewayCommandCode.CentralHeating; + return GatewayCommandCode.CENTRALHEATING; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT: - return GatewayCommandCode.ControlSetpoint2; + return GatewayCommandCode.CONTROLSETPOINT2; case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED: - return GatewayCommandCode.CentralHeating2; + return GatewayCommandCode.CENTRALHEATING2; + case OpenThermGatewayBindingConstants.CHANNEL_OVERRIDE_VENTILATION_SETPOINT: + return GatewayCommandCode.VENTILATIONSETPOINT; case OpenThermGatewayBindingConstants.CHANNEL_SEND_COMMAND: return null; default: diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/VentilationHeatRecoveryHandler.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/VentilationHeatRecoveryHandler.java new file mode 100644 index 000000000..3719f0821 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/handler/VentilationHeatRecoveryHandler.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2010-2022 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 org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Thing; + +/** + * The {@link VentilationHeatRecoveryHandler} represents a Ventilation/Heat Recovery unit. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class VentilationHeatRecoveryHandler extends BaseDeviceHandler { + + public VentilationHeatRecoveryHandler(Thing thing) { + super(thing); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ByteType.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ByteType.java index 4e47356c0..69437c0e4 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ByteType.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ByteType.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.openthermgateway.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link ByteType} enum specifies whether the upper, lower or both bytes are used * * @author Arjen Korevaar - Initial contribution */ +@NonNullByDefault public enum ByteType { HIGHBYTE, LOWBYTE, diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/CodeType.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/CodeType.java index 70f64c2ae..8bb1d6819 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/CodeType.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/CodeType.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.openthermgateway.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link CodeType} field is not part of OpenTherm specification, but added by OpenTherm Gateway. * It can be any of the following: @@ -25,6 +27,7 @@ package org.openhab.binding.openthermgateway.internal; * @author Arjen Korevaar - Initial contribution * @author James Melville - Introduced code filtering functionality */ +@NonNullByDefault public enum CodeType { /** * Message received from the thermostat diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ConnectionState.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ConnectionState.java new file mode 100644 index 000000000..58dbc41af --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/ConnectionState.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2010-2022 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 ConnectionState} is used to indicate changes in connection state. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public enum ConnectionState { + INITIALIZING, + DISCONNECTED, + CONNECTING, + CONNECTED +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItem.java index 4eb89453a..d16eae713 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItem.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItem.java @@ -12,81 +12,62 @@ */ package org.openhab.binding.openthermgateway.internal; -import javax.measure.Unit; - import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.types.State; /** - * The {@link DataItem} holds the internal OpenTherm message and meta data. + * The {@link DataItem} represents the base dataitem. * * @author Arjen Korevaar - Initial contribution */ + @NonNullByDefault -public class DataItem { - private int id; +public abstract class DataItem { private Msg msg; private ByteType byteType; - private DataType dataType; - private int bitpos; private String subject; - private @Nullable Unit unit; - private @Nullable CodeType filteredCode; - - public int getID() { - return id; - } + private @Nullable CodeType codeType; public Msg getMsg() { return msg; } public ByteType getByteType() { - return this.byteType; - } - - public DataType getDataType() { - return dataType; - } - - public int getBitPos() { - return bitpos; + return byteType; } public String getSubject() { return subject; } - public @Nullable Unit getUnit() { - return unit; + public @Nullable CodeType getCodeType() { + return codeType; } - public @Nullable CodeType getFilteredCode() { - return filteredCode; - } - - public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject) { - this(id, msg, byteType, dataType, bit, subject, null, null); - } - - public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject, Unit unit) { - this(id, msg, byteType, dataType, bit, subject, unit, null); - } - - public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject, - CodeType filteredCode) { - this(id, msg, byteType, dataType, bit, subject, null, filteredCode); - } - - public DataItem(int id, Msg msg, ByteType byteType, DataType dataType, int bit, String subject, - @Nullable Unit unit, @Nullable CodeType filteredCode) { - this.id = id; + public DataItem(Msg msg, ByteType byteType, String subject, @Nullable CodeType codeType) { this.msg = msg; this.byteType = byteType; - this.dataType = dataType; - this.bitpos = bit; this.subject = subject; - this.unit = unit; - this.filteredCode = filteredCode; + this.codeType = codeType; } + + public boolean hasValidCodeType(Message message) { + // Used to bind a dataitem to a specific TBRA code + @Nullable + CodeType code = this.getCodeType(); + + return (code == null || code == message.getCodeType()); + } + + /** + * @param message unused in this default implementation + * @return the channel id + */ + public String getChannelId(Message message) { + // Default implementation + return subject; + } + + public abstract State createState(Message message); } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItemGroup.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItemGroup.java index a700bddf5..62a5b7dbe 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItemGroup.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataItemGroup.java @@ -27,177 +27,206 @@ import org.openhab.core.library.unit.Units; @NonNullByDefault public class DataItemGroup { - public static final Map dataItemGroups = createDataItemGroups(); + public static final Map DATAITEMGROUPS = createDataItemGroups(); private static Map createDataItemGroups() { HashMap g = new HashMap<>(); - g.put(0, new DataItem[] { - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "ch_enable", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "ch_enablerequested", CodeType.T), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "ch_enableoverride", CodeType.R), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 1, "dhw_enable", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 2, "cooling_enabled", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 3, "otc_active", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "ch2_enable", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "ch2_enablerequested", CodeType.T), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 4, "ch2_enableoverride", CodeType.R), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 5, "0x00:5", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 6, "0x00:6", CodeType.B), - new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 7, "0x00:7", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 0, "fault", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 1, "ch_mode", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 2, "dhw_mode", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 3, "flame", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 4, "cooling", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 5, "ch2E", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 6, "diag", CodeType.B), - new DataItem(0, Msg.READ, ByteType.LOWBYTE, DataType.FLAGS, 7, "0x00:7", CodeType.B) }); - g.put(1, new DataItem[] { - new DataItem(1, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpoint", SIUnits.CELSIUS, - CodeType.B), - new DataItem(1, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpointrequested", - SIUnits.CELSIUS, CodeType.T), - new DataItem(1, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpointoverride", SIUnits.CELSIUS, - CodeType.R) }); - 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", SIUnits.CELSIUS, - CodeType.B), - new DataItem(8, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpoint2requested", - SIUnits.CELSIUS, CodeType.T), - new DataItem(8, Msg.WRITE, ByteType.BOTH, DataType.FLOAT, 0, "controlsetpoint2override", - SIUnits.CELSIUS, CodeType.R) }); - 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", Units.PERCENT) }); - 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", Units.PERCENT) }); - g.put(18, new DataItem[] { - new DataItem(18, Msg.READ, ByteType.BOTH, DataType.FLOAT, 0, "waterpressure", Units.BAR) }); - 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(0, new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "ch_enable", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "ch_enablerequested", CodeType.T), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "ch_enableoverride", CodeType.R), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "dhw_enable", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "cooling_enabled", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "otc_active", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "ch2_enable", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "ch2_enablerequested", CodeType.T), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "ch2_enableoverride", CodeType.R), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "0x00:5", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "0x00:6", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "0x00:7", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 0, "fault", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 1, "ch_mode", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 2, "dhw_mode", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 3, "flame", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 4, "cooling", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 5, "ch2E", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 6, "diag", CodeType.B), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 7, "0x00:7", CodeType.B) }); + g.put(1, new DataItem[] { new FloatDataItem(Msg.WRITE, "controlsetpoint", SIUnits.CELSIUS, CodeType.B), + new FloatDataItem(Msg.WRITE, "controlsetpointrequested", SIUnits.CELSIUS, CodeType.T), + new FloatDataItem(Msg.WRITE, "controlsetpointoverride", SIUnits.CELSIUS, CodeType.R) }); + g.put(2, new DataItem[] { new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 0, "0x02:0"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 1, "0x02:1"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 2, "0x02:2"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 3, "0x02:3"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 4, "0x02:4"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 5, "0x02:5"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 6, "0x02:6"), + new FlagDataItem(Msg.WRITE, ByteType.HIGHBYTE, 7, "0x02:7"), + new UIntDataItem(Msg.WRITE, ByteType.LOWBYTE, "mastermemberid") }); + g.put(3, new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "dhwpresent"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "controltype"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "coolingsupport"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "dhwconfig"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "masterlowoff"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "ch2present"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "0x03:6"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "0x03:7"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "slavememberid") }); + g.put(4, new DataItem[] { new UIntDataItem(Msg.WRITE, ByteType.HIGHBYTE, "commandcode"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "commandresponse") }); + g.put(5, new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "servicerequest"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "lockout-reset"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "lowwaterpress"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "gasflamefault"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "airpressfault"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "waterovtemp"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "0x05:6"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "0x05:7"), + new IntDataItem(Msg.READ, ByteType.LOWBYTE, "oemfaultcode") }); + g.put(6, new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "0x06:h0"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "0x06:h1"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "0x06:h2"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "0x06:h3"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "0x06:h4"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "0x06:h5"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "0x06:h6"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "0x06:h7"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 0, "0x06:l0"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 1, "0x06:l1"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 2, "0x06:l2"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 3, "0x06:l3"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 4, "0x06:l4"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 5, "0x06:l5"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 6, "0x06:l6"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 7, "0x06:l7") }); + g.put(7, new DataItem[] { new FloatDataItem(Msg.WRITE, "coolingcontrolsignal") }); + g.put(8, new DataItem[] { new FloatDataItem(Msg.WRITE, "controlsetpoint2", SIUnits.CELSIUS, CodeType.B), + new FloatDataItem(Msg.WRITE, "controlsetpoint2requested", SIUnits.CELSIUS, CodeType.T), + new FloatDataItem(Msg.WRITE, "controlsetpoint2override", SIUnits.CELSIUS, CodeType.R) }); + g.put(9, new DataItem[] { new FloatDataItem(Msg.READ, "overridesetpoint") }); + g.put(10, new DataItem[] { new TspFhbSizeDataItem(Msg.WRITE, ByteType.HIGHBYTE, 11, "tspnumber") }); + g.put(11, new DataItem[] { new TspFhbValueDataItem(Msg.READWRITE, "tspentry") }); + g.put(12, new DataItem[] { new TspFhbSizeDataItem(Msg.READ, ByteType.HIGHBYTE, 13, "fhbnumber") }); + g.put(13, new DataItem[] { new TspFhbValueDataItem(Msg.READ, "fhbentry") }); + g.put(14, new DataItem[] { new FloatDataItem(Msg.READ, "maxrelmdulevel", Units.PERCENT) }); + g.put(15, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.HIGHBYTE, "maxcapkw"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "maxcapprc") }); + g.put(16, new DataItem[] { new FloatDataItem(Msg.WRITE, "roomsetpoint", SIUnits.CELSIUS) }); + g.put(17, new DataItem[] { new FloatDataItem(Msg.READ, "modulevel", Units.PERCENT) }); + g.put(18, new DataItem[] { new FloatDataItem(Msg.READ, "waterpressure", Units.BAR) }); + g.put(19, new DataItem[] { new FloatDataItem(Msg.READ, "dhwflow") }); + g.put(20, new DataItem[] { new UIntDataItem(Msg.READWRITE, ByteType.BOTH, "dowtod") }); + g.put(21, new DataItem[] { new UIntDataItem(Msg.READWRITE, ByteType.HIGHBYTE, "month"), + new UIntDataItem(Msg.READWRITE, ByteType.LOWBYTE, "day") }); + g.put(22, new DataItem[] { new UIntDataItem(Msg.READWRITE, ByteType.BOTH, "year") }); + g.put(23, new DataItem[] { new FloatDataItem(Msg.WRITE, "setpointch2") }); + g.put(24, new DataItem[] { new FloatDataItem(Msg.WRITE, "roomtemp", SIUnits.CELSIUS) }); + g.put(25, new DataItem[] { new FloatDataItem(Msg.READ, "flowtemp", SIUnits.CELSIUS) }); + g.put(26, new DataItem[] { new FloatDataItem(Msg.READ, "dhwtemp", SIUnits.CELSIUS) }); + g.put(27, new DataItem[] { new FloatDataItem(Msg.READ, "outsidetemp", SIUnits.CELSIUS) }); + g.put(28, new DataItem[] { new FloatDataItem(Msg.READ, "returntemp", SIUnits.CELSIUS) }); + + g.put(29, new DataItem[] { new FloatDataItem(Msg.READ, "ss_temperature") }); + g.put(30, new DataItem[] { new FloatDataItem(Msg.READ, "ss_collectortemperature") }); + + g.put(31, new DataItem[] { new FloatDataItem(Msg.READ, "flowtemp2") }); + g.put(32, new DataItem[] { new FloatDataItem(Msg.READ, "dhw2temp") }); + g.put(33, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "exhausttemp") }); + g.put(48, new DataItem[] { new IntDataItem(Msg.READ, ByteType.HIGHBYTE, "tdhwsetu"), + new IntDataItem(Msg.READ, ByteType.LOWBYTE, "tdhwsetl") }); + g.put(49, new DataItem[] { new IntDataItem(Msg.READ, ByteType.HIGHBYTE, "maxchu"), + new IntDataItem(Msg.READ, ByteType.LOWBYTE, "maxchl") }); + g.put(50, new DataItem[] { new IntDataItem(Msg.READ, ByteType.HIGHBYTE, "otcu"), + new IntDataItem(Msg.READ, ByteType.LOWBYTE, "otcl") }); + g.put(56, new DataItem[] { new FloatDataItem(Msg.READWRITE, "tdhwset", SIUnits.CELSIUS) }); + g.put(57, new DataItem[] { new FloatDataItem(Msg.READWRITE, "tchmax") }); + g.put(58, new DataItem[] { new FloatDataItem(Msg.READWRITE, "otchcratio") }); + g.put(70, + new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "vh_ventilationenable"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "vh_bypassposition"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "vh_bypassmode"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "vh_freeventilationmode"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 0, "vh_faultindication"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 1, "vh_ventilationmode"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 2, "vh_bypassstatus"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 3, "vh_bypassautomaticstatus"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 4, "vh_freeventilationstatus"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 5, "vh_filtercheck"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 6, "vh_diagnosticindication") }); + g.put(71, new DataItem[] { new UIntDataItem(Msg.WRITE, ByteType.LOWBYTE, "vh_controlsetpoint") }); + g.put(72, + new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "vh_servicerequest"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "vh_exhaustfanfault"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "vh_inletfanfault"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "vh_frostprotection"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "vh_faultcode") }); + g.put(73, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_diagnosticcode") }); + g.put(74, + new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "vh_systemtype"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "vh_bypass"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "vh_speedcontrol"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "vh_memberid") }); + g.put(75, new DataItem[] { new FloatDataItem(Msg.READ, "vh_openthermversion") }); + g.put(76, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_versiontype") }); + g.put(77, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_relativeventilation") }); + g.put(78, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_relativehumidity") }); + g.put(79, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_co2level") }); + g.put(80, new DataItem[] { new FloatDataItem(Msg.READ, "vh_supplyinlettemp") }); + g.put(81, new DataItem[] { new FloatDataItem(Msg.READ, "vh_supplyoutlettemp") }); + g.put(82, new DataItem[] { new FloatDataItem(Msg.READ, "vh_exhaustinlettemp") }); + g.put(83, new DataItem[] { new FloatDataItem(Msg.READ, "vh_exhaustoutlettemp") }); + g.put(84, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_actualexhaustfanspeed") }); + g.put(85, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "vh_actualinletfanspeed") }); + g.put(86, new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "vh_nominalventenable"), + new FlagDataItem(Msg.READ, ByteType.LOWBYTE, 0, "vh_nominalventrw") }); + g.put(87, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.HIGHBYTE, "vh_nominalventilationvalue") }); + g.put(88, new DataItem[] { new TspFhbSizeDataItem(Msg.READ, ByteType.HIGHBYTE, 89, "vh_tspnumber") }); + g.put(89, new DataItem[] { new TspFhbValueDataItem(Msg.READ, "vh_tspentry") }); + g.put(90, new DataItem[] { new TspFhbSizeDataItem(Msg.READ, ByteType.HIGHBYTE, 91, "vh_fhbnumber") }); + g.put(91, new DataItem[] { new TspFhbValueDataItem(Msg.READ, "vh_fhbentry") }); 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(113, new DataItem[] { - new DataItem(113, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "unsuccessfulburnerstarts") }); - 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", Units.HOUR) }); - g.put(121, new DataItem[] { - new DataItem(121, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "chpumphours", Units.HOUR) }); - g.put(122, new DataItem[] { - new DataItem(122, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwpvhours", Units.HOUR) }); - g.put(123, new DataItem[] { - new DataItem(123, Msg.READ, ByteType.BOTH, DataType.UINT16, 0, "dhwburnerhours", Units.HOUR) }); - 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") }); + new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "rof0"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "rof1"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "rof2"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "rof3"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "rof4"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "rof5"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "rof6"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "rof7") }); + + g.put(101, + new DataItem[] { new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 0, "rof0"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 1, "rof1"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 2, "rof2"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 3, "rof3"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 4, "rof4"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 5, "rof5"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 6, "rof6"), + new FlagDataItem(Msg.READ, ByteType.HIGHBYTE, 7, "rof7") }); + g.put(102, new DataItem[] {}); + + g.put(105, new DataItem[] { new TspFhbSizeDataItem(Msg.READ, ByteType.HIGHBYTE, 106, "ss_tspnumber") }); + g.put(106, new DataItem[] { new TspFhbValueDataItem(Msg.READ, "ss_tspentry") }); + + g.put(107, new DataItem[] { new TspFhbSizeDataItem(Msg.READ, ByteType.HIGHBYTE, 108, "ss_fhbnumber") }); + g.put(108, new DataItem[] { new TspFhbValueDataItem(Msg.READ, "ss_fhbentry") }); + g.put(113, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "unsuccessfulburnerstarts") }); + g.put(115, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "oemdiagcode") }); + g.put(116, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "burnerstarts") }); + g.put(117, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "chpumpstarts") }); + g.put(118, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "dhwpvstarts") }); + g.put(119, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "dhwburnerstarts") }); + g.put(120, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "burnerhours", Units.HOUR) }); + g.put(121, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "chpumphours", Units.HOUR) }); + g.put(122, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "dhwpvhours", Units.HOUR) }); + g.put(123, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.BOTH, "dhwburnerhours", Units.HOUR) }); + g.put(124, new DataItem[] { new FloatDataItem(Msg.WRITE, "masterotversion") }); + g.put(125, new DataItem[] { new FloatDataItem(Msg.READ, "slaveotversion") }); + g.put(126, new DataItem[] { new UIntDataItem(Msg.WRITE, ByteType.HIGHBYTE, "masterproducttype"), + new UIntDataItem(Msg.WRITE, ByteType.LOWBYTE, "masterproductversion") }); + g.put(127, new DataItem[] { new UIntDataItem(Msg.READ, ByteType.HIGHBYTE, "slaveproducttype"), + new UIntDataItem(Msg.READ, ByteType.LOWBYTE, "slaveproductversion") }); return g; } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataType.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataType.java index 442355598..40b7b65f8 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataType.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/DataType.java @@ -12,11 +12,14 @@ */ package org.openhab.binding.openthermgateway.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link DataType} enum indicates the type of data from a DataItem. - * + * * @author Arjen Korevaar - Initial contribution */ +@NonNullByDefault public enum DataType { FLAGS, UINT8, diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FlagDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FlagDataItem.java new file mode 100644 index 000000000..dddb2b42f --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FlagDataItem.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2010-2022 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.core.library.types.OnOffType; +import org.openhab.core.types.State; + +/** + * The {@link DataItem} holds the internal OpenTherm message and meta data. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class FlagDataItem extends DataItem { + private int bitpos; + + public int getBitPos() { + return bitpos; + } + + public FlagDataItem(Msg msg, ByteType byteType, int bitpos, String subject) { + this(msg, byteType, bitpos, subject, null); + } + + public FlagDataItem(Msg msg, ByteType byteType, int bitpos, String subject, @Nullable CodeType codeType) { + super(msg, byteType, subject, codeType); + + this.bitpos = bitpos; + } + + @Override + public State createState(Message message) { + return OnOffType.from(message.getBit(super.getByteType(), this.getBitPos())); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FloatDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FloatDataItem.java new file mode 100644 index 000000000..f090621e9 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/FloatDataItem.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2010-2022 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; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.State; + +/** + * The {@link FloatDataItem} represents a 2-byte float value in a 2's complement format. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class FloatDataItem extends DataItem { + + private @Nullable Unit unit; + + public @Nullable Unit getUnit() { + return unit; + } + + public FloatDataItem(Msg msg, String subject) { + this(msg, subject, null, null); + } + + public FloatDataItem(Msg msg, String subject, Unit unit) { + this(msg, subject, unit, null); + } + + public FloatDataItem(Msg msg, String subject, CodeType codetype) { + this(msg, subject, null, codetype); + } + + public FloatDataItem(Msg msg, String subject, @Nullable Unit unit, @Nullable CodeType codetype) { + super(msg, ByteType.BOTH, subject, codetype); + + this.unit = unit; + } + + @Override + public State createState(Message message) { + @Nullable + Unit unit = this.getUnit(); + float value = message.getFloat(); + return (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommand.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommand.java index 2df7da896..141962019 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommand.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommand.java @@ -26,7 +26,7 @@ import org.eclipse.jdt.annotation.Nullable; */ @NonNullByDefault public class GatewayCommand { - private static final Map supportedCommands = getSupportedCommands(); + private static final Map SUPPORTEDCOMMANDS = getSupportedCommands(); private String code; private String validationSet; @@ -83,19 +83,17 @@ public class GatewayCommand { if (code != null && code.length() == 2) { String codeUpperCase = code.toUpperCase(); - if (supportedCommands.containsKey(codeUpperCase)) { - String validateSet = supportedCommands.get(codeUpperCase); + 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("Unsupported gateway code '%s'", code.toUpperCase())); } - throw new IllegalArgumentException( String.format("Unable to parse gateway command with code '%s' and message '%s'", code, message)); } @@ -103,45 +101,45 @@ public class GatewayCommand { private static Map getSupportedCommands() { Map 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.TemperatureSensor, "O,R"); - 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.ControlSetpoint2, null); - c.put(GatewayCommandCode.CentralHeating, "0,1"); - c.put(GatewayCommandCode.CentralHeating2, "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); + 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.TEMPERATURESENSOR, "O,R"); + 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.CONTROLSETPOINT2, null); + c.put(GatewayCommandCode.CENTRALHEATING, "0,1"); + c.put(GatewayCommandCode.CENTRALHEATING2, "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; } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommandCode.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommandCode.java index 7e6e87f22..bd870d8f2 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommandCode.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/GatewayCommandCode.java @@ -21,43 +21,43 @@ import org.eclipse.jdt.annotation.NonNullByDefault; */ @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 TemperatureSensor = "TS"; - 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 ControlSetpoint2 = "C2"; - public static final String CentralHeating = "CH"; - public static final String CentralHeating2 = "H2"; - 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"; + 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 TEMPERATURESENSOR = "TS"; + 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 CONTROLSETPOINT2 = "C2"; + public static final String CENTRALHEATING = "CH"; + public static final String CENTRALHEATING2 = "H2"; + 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"; } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/IntDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/IntDataItem.java new file mode 100644 index 000000000..51cad883b --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/IntDataItem.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2022 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.openhab.core.library.types.DecimalType; +import org.openhab.core.types.State; + +/** + * The {@link IntDataItem} represents an 8 or 16 bit signed integer. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class IntDataItem extends DataItem { + + public IntDataItem(Msg msg, ByteType byteType, String subject) { + super(msg, byteType, subject, null); + } + + @Override + public State createState(Message message) { + return new DecimalType(message.getInt(super.getByteType())); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Message.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Message.java index b7edd35ea..8a48ae11c 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Message.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Message.java @@ -25,15 +25,15 @@ import org.eclipse.jdt.annotation.Nullable; @NonNullByDefault public class Message { - private static final Pattern messagePattern = Pattern.compile("[TBRA]{1}[A-F0-9]{8}"); + private static final Pattern MESSAGEPATTERN = Pattern.compile("[TBRA]{1}[A-F0-9]{8}"); - private CodeType code; + private CodeType codeType; private MessageType messageType; private int id; private String data; - public CodeType getCode() { - return this.code; + public CodeType getCodeType() { + return codeType; } public MessageType getMessageType() { @@ -129,30 +129,30 @@ public class Message { // 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() && (this.code == CodeType.R || this.code == CodeType.A); + return other != null && this.getID() == other.getID() && (codeType == CodeType.R || codeType == CodeType.A); } @Override public String toString() { - return String.format("%s - %s - %s", this.code, this.id, this.data); + return String.format("%s - %s - %s", this.codeType, this.id, this.data); } - public Message(CodeType code, MessageType messageType, int id, String data) { - this.code = code; + public Message(CodeType codeType, MessageType messageType, int id, String data) { + this.codeType = codeType; this.messageType = messageType; this.id = id; this.data = data; } public static @Nullable Message parse(String message) { - if (messagePattern.matcher(message).matches()) { + if (MESSAGEPATTERN.matcher(message).matches()) { // For now, only parse TBRA codes - CodeType code = CodeType.valueOf(message.substring(0, 1)); + CodeType codeType = CodeType.valueOf(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 new Message(codeType, messageType, id, data); } return null; diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/MessageType.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/MessageType.java index 3f7dacc10..d4dbc954c 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/MessageType.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/MessageType.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.openthermgateway.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * The {@link MessageType} indicates the type of message received by the OpenTherm Gateway, based * on the OpenTherm specification. * * @author Arjen Korevaar - Initial contribution */ +@NonNullByDefault public enum MessageType { READDATA, // 000 READACK, // 100 diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Msg.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Msg.java index 9c1884b4f..a68c9172f 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Msg.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/Msg.java @@ -12,12 +12,15 @@ */ package org.openhab.binding.openthermgateway.internal; +import org.eclipse.jdt.annotation.NonNullByDefault; + /** * 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 */ +@NonNullByDefault public enum Msg { READ, WRITE, diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayBindingConstants.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayBindingConstants.java new file mode 100644 index 000000000..d33667c79 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayBindingConstants.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2022 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.Set; + +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 { + + // Binding Id + public static final String BINDING_ID = "openthermgateway"; + + // List of all the ThingType UID's + public static final ThingTypeUID OPENTHERM_GATEWAY_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, + "openthermgateway"); + public static final ThingTypeUID BOILER_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "boiler"); + public static final ThingTypeUID VENTILATION_HEATRECOVERY_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, + "ventilationheatrecovery"); + + public static final Set SUPPORTED_THING_TYPE_UIDS = Set.of(OPENTHERM_GATEWAY_THING_TYPE_UID, + BOILER_THING_TYPE_UID, VENTILATION_HEATRECOVERY_THING_TYPE_UID); + + // List of id's for writeable channels + 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_OVERRIDE_CENTRAL_HEATING_WATER_SETPOINT = "controlsetpointoverride"; + public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING2_WATER_SETPOINT = "controlsetpoint2override"; + public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING_ENABLED = "ch_enableoverride"; + public static final String CHANNEL_OVERRIDE_CENTRAL_HEATING2_ENABLED = "ch2_enableoverride"; + public static final String CHANNEL_OUTSIDE_TEMPERATURE = "outsidetemp"; + public static final String CHANNEL_OVERRIDE_VENTILATION_SETPOINT = "vh_ventilationsetpoint"; + + // Generic channel type for Transparent Slave Parameter and Fault History Buffer values + public static final String CHANNEL_TSPFHB = "tspfhb"; +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayCallback.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayCallback.java index ad6969d40..d78a9a35a 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayCallback.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayCallback.java @@ -22,11 +22,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; */ @NonNullByDefault public interface OpenThermGatewayCallback { - void connecting(); - void connected(); - - void disconnected(); + void connectionStateChanged(ConnectionState state); void receiveMessage(Message message); } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConfiguration.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConfiguration.java index 01af7b4da..9fc7aaf8a 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConfiguration.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConfiguration.java @@ -27,4 +27,8 @@ public class OpenThermGatewayConfiguration { public int port = 0; public int connectionRetryInterval = 60; + + public int connectTimeoutSeconds = 5; + + public int readTimeoutSeconds = 20; } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConnector.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConnector.java index f98eafac9..980a9ef9d 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConnector.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayConnector.java @@ -12,19 +12,23 @@ */ package org.openhab.binding.openthermgateway.internal; +import java.util.concurrent.Callable; + 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 { +public interface OpenThermGatewayConnector extends Callable { void sendCommand(GatewayCommand command); boolean isConnected(); void stop(); + + void start(); } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayHandlerFactory.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayHandlerFactory.java index 7aeb4402e..9374572dc 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayHandlerFactory.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewayHandlerFactory.java @@ -14,8 +14,10 @@ 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.BoilerHandler; import org.openhab.binding.openthermgateway.handler.OpenThermGatewayHandler; +import org.openhab.binding.openthermgateway.handler.VentilationHeatRecoveryHandler; +import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandlerFactory; @@ -35,15 +37,19 @@ public class OpenThermGatewayHandlerFactory extends BaseThingHandlerFactory { @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { - return thingTypeUID.equals(OpenThermGatewayBindingConstants.MAIN_THING_TYPE); + return OpenThermGatewayBindingConstants.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID); } @Override protected @Nullable ThingHandler createHandler(Thing thing) { ThingTypeUID thingTypeUID = thing.getThingTypeUID(); - if (thingTypeUID.equals(OpenThermGatewayBindingConstants.MAIN_THING_TYPE)) { - return new OpenThermGatewayHandler(thing); + if (thingTypeUID.equals(OpenThermGatewayBindingConstants.OPENTHERM_GATEWAY_THING_TYPE_UID)) { + return new OpenThermGatewayHandler((Bridge) thing); + } else if (thingTypeUID.equals(OpenThermGatewayBindingConstants.BOILER_THING_TYPE_UID)) { + return new BoilerHandler(thing); + } else if (thingTypeUID.equals(OpenThermGatewayBindingConstants.VENTILATION_HEATRECOVERY_THING_TYPE_UID)) { + return new VentilationHeatRecoveryHandler(thing); } return null; diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewaySocketConnector.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewaySocketConnector.java index 6968a102d..7e8f0942d 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewaySocketConnector.java +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/OpenThermGatewaySocketConnector.java @@ -12,6 +12,8 @@ */ package org.openhab.binding.openthermgateway.internal; +import static org.openhab.binding.openthermgateway.internal.OpenThermGatewayBindingConstants.BINDING_ID; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -22,12 +24,16 @@ import java.util.AbstractMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; 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.openhab.core.common.NamedThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,107 +45,138 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class OpenThermGatewaySocketConnector implements OpenThermGatewayConnector { - private static final int COMMAND_RESPONSE_TIME_MILLISECONDS = 100; - private static final int COMMAND_TIMEOUT_MILLISECONDS = 5000; + private static final int COMMAND_RESPONSE_MIN_WAIT_TIME_MILLISECONDS = 100; + private static final int COMMAND_RESPONSE_MAX_WAIT_TIME_MILLISECONDS = 5000; private final Logger logger = LoggerFactory.getLogger(OpenThermGatewaySocketConnector.class); private final OpenThermGatewayCallback callback; private final String ipaddress; private final int port; + private final int connectTimeoutMilliseconds; + private final int readTimeoutMilliSeconds; - private @Nullable PrintWriter writer; - - private volatile boolean stopping; - private boolean connected; + private @Nullable volatile PrintWriter writer; + private @Nullable volatile Thread thread; + private @Nullable Future future; + private @Nullable ExecutorService executor; private Map> pendingCommands = new ConcurrentHashMap<>(); - public OpenThermGatewaySocketConnector(OpenThermGatewayCallback callback, String ipaddress, int port) { + public OpenThermGatewaySocketConnector(OpenThermGatewayCallback callback, OpenThermGatewayConfiguration config) { this.callback = callback; - this.ipaddress = ipaddress; - this.port = port; + ipaddress = config.ipaddress; + port = config.port; + connectTimeoutMilliseconds = config.connectTimeoutSeconds * 1000; + readTimeoutMilliSeconds = config.readTimeoutSeconds * 1000; } @Override - public void run() { - stopping = false; - connected = false; - - logger.debug("Connecting OpenThermGatewaySocketConnector to {}:{}", this.ipaddress, this.port); - - callback.connecting(); - + public Boolean call() throws Exception { + thread = Thread.currentThread(); try (Socket socket = new Socket()) { - socket.connect(new InetSocketAddress(this.ipaddress, this.port), COMMAND_TIMEOUT_MILLISECONDS); - socket.setSoTimeout(COMMAND_TIMEOUT_MILLISECONDS); + logger.debug("Connecting OpenThermGatewaySocketConnector to {}:{}", this.ipaddress, this.port); + callback.connectionStateChanged(ConnectionState.CONNECTING); - connected = true; - - callback.connected(); + socket.connect(new InetSocketAddress(ipaddress, port), connectTimeoutMilliseconds); + socket.setSoTimeout(readTimeoutMilliSeconds); logger.debug("OpenThermGatewaySocketConnector connected"); + callback.connectionStateChanged(ConnectionState.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")); + sendCommand(GatewayCommand.parse(GatewayCommandCode.PRINTREPORT, "A")); // Set the OTGW to report every message it receives and transmits - sendCommand(GatewayCommand.parse(GatewayCommandCode.PrintSummary, "0")); + sendCommand(GatewayCommand.parse(GatewayCommandCode.PRINTSUMMARY, "0")); - while (!stopping && !Thread.currentThread().isInterrupted()) { + while (!Thread.currentThread().isInterrupted()) { @Nullable String message = reader.readLine(); if (message != null) { handleMessage(message); } else { - logger.debug("Connection closed by OpenTherm Gateway"); + logger.debug("Received NULL message from OpenTherm Gateway (EOF)"); break; } } - - logger.debug("Stopping OpenThermGatewaySocketConnector"); - } finally { - connected = false; - - logger.debug("OpenThermGatewaySocketConnector disconnected"); - callback.disconnected(); + } catch (IOException ex) { + logger.warn("Error communicating with OpenTherm Gateway: '{}'", ex.getMessage()); } } catch (IOException ex) { - logger.warn("Unable to connect to the OpenTherm Gateway.", ex); - callback.disconnected(); + logger.warn("Unable to connect to the OpenTherm Gateway: '{}'", ex.getMessage()); } + thread = null; + writer = null; + logger.debug("OpenThermGatewaySocketConnector disconnected"); + callback.connectionStateChanged(ConnectionState.DISCONNECTED); + return true; } @Override public void stop() { logger.debug("Stopping OpenThermGatewaySocketConnector"); - stopping = true; + + Thread thread = this.thread; + Future future = this.future; + ExecutorService executor = this.executor; + + if (executor != null) { + executor.shutdown(); + } + if ((thread != null) && thread.isAlive()) { + thread.interrupt(); + } + if (future != null) { + try { + future.get(readTimeoutMilliSeconds, TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + // expected exception due to e.g. IOException on socket close + } catch (TimeoutException | InterruptedException e) { + // unexpected exception + logger.warn("stop() exception '{}' => PLEASE REPORT !!", e.getMessage()); + } + } + + this.thread = null; + this.future = null; + this.executor = null; } @Override - public boolean isConnected() { - return connected; + public void start() { + logger.debug("Starting OpenThermGatewaySocketConnector"); + ExecutorService executor = this.executor = Executors + .newSingleThreadExecutor(new NamedThreadFactory("binding-" + BINDING_ID)); + future = executor.submit(this); } @Override - public void sendCommand(GatewayCommand command) { - @Nullable - PrintWriter wrtr = writer; + public synchronized boolean isConnected() { + Thread thread = this.thread; + return (thread != null) && thread.isAlive(); + } - String msg = command.toFullString(); + @Override + public synchronized void sendCommand(GatewayCommand command) { + PrintWriter wrt = writer; pendingCommands.put(command.getCode(), new AbstractMap.SimpleImmutableEntry<>(System.currentTimeMillis(), command)); - if (connected) { + String msg = command.toFullString(); + + if (isConnected() && (wrt != null)) { logger.debug("Sending message: {}", msg); - if (wrtr != null) { - wrtr.print(msg + "\r\n"); - wrtr.flush(); + wrt.print(msg + "\r\n"); + wrt.flush(); + if (wrt.checkError()) { + logger.warn("sendCommand() error sending message to OpenTherm Gateway => PLEASE REPORT !!"); + stop(); } } else { logger.debug("Unable to send message: {}. OpenThermGatewaySocketConnector is not connected.", msg); @@ -159,8 +196,8 @@ public class OpenThermGatewaySocketConnector implements OpenThermGatewayConnecto long currentTime = System.currentTimeMillis(); for (Entry timeAndCommand : pendingCommands.values()) { - long responseTime = timeAndCommand.getKey() + COMMAND_RESPONSE_TIME_MILLISECONDS; - long timeoutTime = timeAndCommand.getKey() + COMMAND_TIMEOUT_MILLISECONDS; + long responseTime = timeAndCommand.getKey() + COMMAND_RESPONSE_MIN_WAIT_TIME_MILLISECONDS; + long timeoutTime = timeAndCommand.getKey() + COMMAND_RESPONSE_MAX_WAIT_TIME_MILLISECONDS; if (currentTime > responseTime && currentTime <= timeoutTime) { logger.debug("Resending command: {}", timeAndCommand.getValue()); @@ -175,47 +212,11 @@ public class OpenThermGatewaySocketConnector implements OpenThermGatewayConnecto 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); - } - } - + logger.trace("Received message: {}, {} {} {}", message, msg.getID(), msg.getCodeType(), msg.getMessageType()); if (msg.getMessageType() == MessageType.READACK || msg.getMessageType() == MessageType.WRITEDATA || msg.getID() == 0 || msg.getID() == 1) { - receiveMessage(msg); + callback.receiveMessage(msg); } } - - private void receiveMessage(Message message) { - callback.receiveMessage(message); - } } diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbSizeDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbSizeDataItem.java new file mode 100644 index 000000000..17c5932d9 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbSizeDataItem.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2010-2022 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.openhab.core.library.types.DecimalType; +import org.openhab.core.types.State; + +/** + * The {@link DataItem} represents a transparent slave parameter or fault history buffer size. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class TspFhbSizeDataItem extends DataItem { + private int valueId; + + public int getValueId() { + return valueId; + } + + public TspFhbSizeDataItem(Msg msg, ByteType byteType, int valueId, String subject) { + super(msg, byteType, subject, null); + + this.valueId = valueId; + } + + @Override + public State createState(Message message) { + return new DecimalType(message.getUInt(super.getByteType())); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbValueDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbValueDataItem.java new file mode 100644 index 000000000..21ccdf410 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/TspFhbValueDataItem.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2010-2022 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.openhab.core.library.types.DecimalType; +import org.openhab.core.types.State; + +/** + * The {@link TspFhbValueDataItem} represents a transparent slave parameter or fault history buffer value. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class TspFhbValueDataItem extends DataItem { + + public TspFhbValueDataItem(Msg msg, String subject) { + super(msg, ByteType.BOTH, subject, null); + } + + @Override + public String getChannelId(Message message) { + // With TSP or FHB values, the index is HIGHBYTE, the value is LOWBYTE + int index = message.getUInt(ByteType.HIGHBYTE); + return getChannelId(index); + } + + public String getChannelId(int index) { + return super.getSubject() + "_" + index; + } + + public String getLabel(int index) { + return super.getSubject() + " " + index; + } + + @Override + public State createState(Message message) { + // With TSP or FHB values, the index is HIGHBYTE, the value is LOWBYTE + // TSP values are treated as Number:Dimensionless + return new DecimalType(message.getUInt(ByteType.LOWBYTE)); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/UIntDataItem.java b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/UIntDataItem.java new file mode 100644 index 000000000..9acfce1f0 --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/java/org/openhab/binding/openthermgateway/internal/UIntDataItem.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2010-2022 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; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.State; + +/** + * The {@link UIntDataItem} represents an 8 or 16 bit unsigned integer. + * + * @author Arjen Korevaar - Initial contribution + */ +@NonNullByDefault +public class UIntDataItem extends DataItem { + + private @Nullable Unit unit; + + public @Nullable Unit getUnit() { + return unit; + } + + public UIntDataItem(Msg msg, ByteType byteType, String subject) { + this(msg, byteType, subject, null); + } + + public UIntDataItem(Msg msg, ByteType byteType, String subject, @Nullable Unit unit) { + super(msg, byteType, subject, null); + + this.unit = unit; + } + + @Override + public State createState(Message message) { + @Nullable + Unit unit = getUnit(); + int value = message.getUInt(super.getByteType()); + return (unit == null) ? new DecimalType(value) : new QuantityType<>(value, unit); + } +} diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/openthermgateway.xml similarity index 66% rename from bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/config.xml rename to bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/openthermgateway.xml index db806e807..6713e1fa4 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/config/openthermgateway.xml @@ -4,7 +4,7 @@ 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"> - + Connection settings. @@ -28,6 +28,20 @@ 60 + + + The maximum time (seconds) to wait for establishing a connection to the gateway. + 5 + + + + + The maximum time (seconds) to wait for reading responses from the gateway. + 20 + + diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/otgw.xml b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/boiler.xml similarity index 87% rename from bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/otgw.xml rename to bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/boiler.xml index 373527a1c..26471ea40 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/otgw.xml +++ b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/boiler.xml @@ -3,9 +3,12 @@ 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"> - - - OpenTherm Gateway binding + + + + + + Boiler and thermostat @@ -44,6 +47,7 @@ + @@ -53,12 +57,11 @@ - + + + + - - 1.3.0 - - diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/channels.xml index 5d9700ca3..4314198f6 100644 --- a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/channels.xml +++ b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/channels.xml @@ -4,6 +4,26 @@ 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"> + + + + Number:Dimensionless + + Transparent slave parameter or Fault history buffer value + + + + + + + String + + Channel to send commands to the OpenTherm Gateway device + + + + + Number:Temperature @@ -349,11 +369,351 @@ - - String - - Channel to send commands to the OpenTherm Gateway device - + + Number:Dimensionless + + Number of transparant slave parameter entries + + + + + Number:Dimensionless + + Transparent slave parameter entry + + + + + Number:Dimensionless + + Number of fault history buffer entries + + + + + Number:Dimensionless + + Fault history buffer entry + + + + + + + Switch + + Ventilation enabled + + + + + Number:Dimensionless + + Bypass position + + + + + + + + + + Number:Dimensionless + + Bypass mode + + + + + + + + + + Switch + + Free ventilation mode + + + + + Switch + + Fault indication + + + + + Switch + + Ventilation mode + + + + + Switch + + Bypass status + + + + + Number:Dimensionless + + Bypass automatic status + + + + + + + + + + Switch + + Free ventilation status + + + + + Switch + + Diagnostic indication + + + + + Number:Dimensionless + + Control setpoint + + + + + Switch + + Service request + + + + + Switch + + Exhaust fan fault + + + + + Switch + + Inlet fan fault + + + + + Switch + + Frost protection + + + + + Number:Dimensionless + + Fault code + + + + + Number:Dimensionless + + Diagnostic code + + + + + Number:Dimensionless + + System type + + + + + + + + + + Switch + + Bypass + + + + + Number:Dimensionless + + Speed control + + + + + + + + + + Number:Dimensionless + + Member ID + + + + + Number:Dimensionless + + OpenTherm version + + + + + Number:Dimensionless + + Version type + + + + + Number:Dimensionless + + Relative ventilation position + + + + + Number:Dimensionless + + Relative humidity exhaust air + + + + + Number:Dimensionless + + CO2 level exhaust air + + + + + Number:Temperature + + Supply inlet temperature + + + + + Number:Temperature + + Supply outlet temperature + + + + + Number:Temperature + + Exhaust inlet temperature + + + + + Number:Temperature + + Exhaust outlet temperature + + + + + Number:Dimensionless + + Actual exhaust fan speed + + + + + Number:Dimensionless + + Actual inlet fan speed + + + + + Switch + + Nominal ventilation value transfer enabled + + + + + Number:Dimensionless + + Nominal ventilation value + + + + + + + + + + Number:Dimensionless + + Nominal ventilation value + + + + + Switch + + Filter Check enabled + + + + + Number:Dimensionless + + Number of transparent slave parameter entries + + + + + Number:Dimensionless + + Transparent slave parameter entry + + + + + Number:Dimensionless + + Number of fault history buffer entries + + + + + Number:Dimensionless + + Fault history buffer entry + + + + + Number:Dimensionless + + Ventilation setpoint override + diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/openthermgateway.xml b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/openthermgateway.xml new file mode 100644 index 000000000..ac5a39d6a --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/openthermgateway.xml @@ -0,0 +1,18 @@ + + + + + OpenTherm Gateway + + + + + 2.2.0 + + + + + diff --git a/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/ventilationheatrecovery.xml b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/ventilationheatrecovery.xml new file mode 100644 index 000000000..84ed5814c --- /dev/null +++ b/bundles/org.openhab.binding.openthermgateway/src/main/resources/OH-INF/thing/ventilationheatrecovery.xml @@ -0,0 +1,56 @@ + + + + + + + + Ventilation or Heat Recovery unit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +