[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 <software@whitebear.ch>
This commit is contained in:
Andrew Fiddian-Green 2022-03-06 20:18:02 +00:00 committed by GitHub
parent 5842ad1644
commit ba07a870fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2031 additions and 808 deletions

View File

@ -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]" <temperature> { channel="openthermgateway:otgw:1:roomtemp" }
Number:Temperature RoomSetpoint "Room setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:roomsetpoint" }
Number:Temperature TemporaryRoomSetpointOverride "Temporary room setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:temperaturetemporary" }
Number:Temperature ConstantRoomSetpointOverride "Constant room setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:temperatureconstant" }
Number:Temperature ControlSetpoint "Control setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpoint" }
Number:Temperature ControlSetpointRequested "Control setpoint requested [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpointrequested" }
Number:Temperature ControlSetpointOverride "Control setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpointoverride" }
Number:Temperature ControlSetpoint2 "Control setpoint 2 [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpoint2" }
Number:Temperature ControlSetpoint2Requested "Control setpoint 2 requested [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpoint2requested" }
Number:Temperature ControlSetpoint2Override "Control setpoint 2 override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:controlsetpoint2override" }
Number:Temperature DomesticHotWaterTemperature "Domestic hot water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:dhwtemp" }
Number:Temperature DomesticHotWaterSetpoint "Domestic hot water setpoint [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:tdhwset" }
Number:Temperature DomesticHotWaterSetpointOverride "Domestic hot water setpoint override [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:overridedhwsetpoint" }
Number:Temperature BoilerWaterTemperature "Boiler water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:flowtemp" }
Number:Temperature ReturnWaterTemperature "Return water temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:returntemp" }
Number:Temperature OutsideTemperature "Outside temperature [%.1f °C]" <temperature> { channel="openthermgateway:otgw:1:outsidetemp" }
Number:Pressure CentralHeatingWaterPressure "Central heating water pressure [%.1f bar]" { channel="openthermgateway:otgw:1:waterpressure" }
Switch CentralHeatingEnabled "Central heating enabled" <switch> { channel="openthermgateway:otgw:1:ch_enable" }
Switch CentralHeatingEnabledRequested "Central heating enabled requested" <switch> { channel="openthermgateway:otgw:1:ch_enablerequested" }
Switch CentralHeatingEnabledOverride "Central heating enabled override" <switch> { channel="openthermgateway:otgw:1:ch_enableoverride" }
Switch CentralHeating2Enabled "Central heating 2 enabled" <switch> { channel="openthermgateway:otgw:1:ch2_enable" }
Switch CentralHeating2EnabledRequested "Central 2 heating enabled requested" <switch> { channel="openthermgateway:otgw:1:ch2_enablerequested" }
Switch CentralHeating2EnabledOverride "Central 2 heating enabled override" <switch> { channel="openthermgateway:otgw:1:ch2_enableoverride" }
Switch CentralHeatingActive "Central heating active" <switch> { channel="openthermgateway:otgw:1:ch_mode" }
Switch DomesticHotWaterEnabled "Domestic hot water enabled" <switch> { channel="openthermgateway:otgw:1:dhw_enable" }
Switch DomesticHotWaterActive "Domestic hot water active" <switch> { channel="openthermgateway:otgw:1:dhw_mode" }
Switch BurnerActive "Burner active" <switch> { channel="openthermgateway:otgw:1:flame" }
Number: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" <switch> { channel="openthermgateway:otgw:1:fault" }
Switch ServiceRequest "Service required" <switch> { channel="openthermgateway:otgw:1:servicerequest" }
Switch LockoutReset "Lockout-reset" <switch> { channel="openthermgateway:otgw:1:lockout-reset" }
Switch LowWaterPress "Low water pressure fault" <switch> { channel="openthermgateway:otgw:1:lowwaterpress" }
Switch GasFlameFault "Gas or flame fault" <switch> { channel="openthermgateway:otgw:1:gasflamefault" }
Switch AirPressFault "Air pressure fault" <switch> { channel="openthermgateway:otgw:1:airpressfault" }
Switch WaterOvTemp "Water over-temperature fault" <switch> { channel="openthermgateway:otgw:1:waterovtemp" }
Number OemFaultCode "OEM fault code" { channel="openthermgateway:otgw:1:oemfaultcode" }
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%]" <temperature> { channel="openthermgateway:boiler:1:remeha:roomtemp }
Number:Temperature RoomSetpoint "Room Setpoint [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:roomsetpoint }
Number:Temperature TemporaryRoomSetpointOverride "Temporary Room Setpoint Override [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:temperaturetemporary }
Number:Temperature ConstantRoomSetpointOverride "Constant Room Setpoint Override [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:temperatureconstant }
Number:Temperature ControlSetpoint "Control Setpoint [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpoint }
Number:Temperature ControlSetpointRequested "Control Setpoint Requested [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpointrequested }
Number:Temperature ControlSetpointOverride "Control Setpoint Override [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpointoverride }
Number:Temperature ControlSetpoint2 "Control Setpoint 2 [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpoint2 }
Number:Temperature ControlSetpoint2Requested "Control Setpoint 2 Requested [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpoint2requested }
Number:Temperature ControlSetpoint2Override "Control Setpoint 2 Override [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:controlsetpoint2override }
Number:Temperature DomesticHotWaterTemperature "Domestic Hot Water Temperature [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:dhwtemp }
Number:Temperature DomesticHotWaterSetpoint "Domestic Hot Water Setpoint [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:tdhwset }
Number:Temperature DomesticHotWaterSetpointOverride "Domestic Hot Water Setpoint Override [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:overridedhwsetpoint }
Number:Temperature BoilerWaterTemperature "Boiler Water Temperature [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:flowtemp }
Number:Temperature ReturnWaterTemperature "Return Water Temperature [%.1f %unit%]" <temperature> { channel="openthermgateway:boiler:1:remeha:returntemp }
Number:Temperature OutsideTemperature "Outside Temperature [%.1f %unit%]" <temperature> { 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" <switch> { channel="openthermgateway:boiler:1:remeha:ch_enable }
Switch CentralHeatingEnabledThermostat "Central Heating Enabled Thermostat" <switch> { channel="openthermgateway:boiler:1:remeha:ch_enablerequested }
Switch CentralHeatingOverridden "Central Heating Overridden" <switch> { channel="openthermgateway:boiler:1:remeha:ch_enableoverride }
Switch CentralHeating2Enabled "Central Heating 2 Enabled" <switch> { channel="openthermgateway:boiler:1:remeha:ch2_enable }
Switch CentralHeating2EnabledThermostat "Central Heating 2 Enabled Thermostat" <switch> { channel="openthermgateway:boiler:1:remeha:ch2_enablerequested }
Switch CentralHeating2Overridden "Central Heating 2 Overridden" <switch> { channel="openthermgateway:boiler:1:remeha:ch2_enableoverride }
Switch CentralHeatingActive "Central Heating Active" <switch> { channel="openthermgateway:boiler:1:remeha:ch_mode }
Switch DomesticHotWaterEnabled "Domestic Hot Water Enabled" <switch> { channel="openthermgateway:boiler:1:remeha:dhw_enable }
Switch DomesticHotWaterActive "Domestic Hot Water Active" <switch> { channel="openthermgateway:boiler:1:remeha:dhw_mode }
Switch BurnerActive "Burner Active" <switch> { 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" <switch> { channel="openthermgateway:boiler:1:remeha:fault }
Switch ServiceRequired "Service Required" <switch> { channel="openthermgateway:boiler:1:remeha:servicerequest }
Switch LockoutResetEnabled "Lockout-Reset Enabled" <switch> { channel="openthermgateway:boiler:1:remeha:lockout-reset }
Switch LowWaterPressureFault "Low Water Pressure Fault" <switch> { channel="openthermgateway:boiler:1:remeha:lowwaterpress }
Switch GasOrFlameFault "Gas Or Flame Fault" <switch> { channel="openthermgateway:boiler:1:remeha:gasflamefault }
Switch AirPressureFault "Air Pressure Fault" <switch> { channel="openthermgateway:boiler:1:remeha:airpressfault }
Switch WaterOverTemperatureFault "Water Over-Temperature Fault" <switch> { 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" <switch> { 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" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_freeventilationmode }
Switch FaultIndication "Fault Indication" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_faultindication }
Switch VentilationMode "Ventilation Mode" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_ventilationmode }
Switch BypassStatus "Bypass Status" <switch> { 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" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_freeventilationstatus }
Switch FilterCheck "Filter Check" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_filtercheck }
Switch DiagnosticIndication "Diagnostic Indication" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_diagnosticindication }
Number:Dimensionless ControlSetpoint "Control Setpoint" { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_controlsetpoint }
Switch ServiceRequest "Service Request" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_servicerequest }
Switch ExhaustFanFault "Exhaust Fan Fault" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_exhaustfanfault }
Switch InletFanFault "Inlet Fan Fault" <switch> { channel="openthermgateway:ventilationheatrecovery:1:brink:vh_inletfanfault }
Switch FrostProtection "Frost Protection" <switch> { 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" <switch> { 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" <switch> { 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%]" <temperature> {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%]" <temperature> {channel="openthermgateway:boiler:yourGatewayId:remeha:dhwtemp"}
&c.
```

View File

@ -14,4 +14,5 @@
<name>openHAB Add-ons :: Bundles :: OpenTherm Gateway Binding</name>
<description>This binding integrates OpenTherm capable devices via an OpenThermGateway bridge.</description>
</project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,177 +27,206 @@ import org.openhab.core.library.unit.Units;
@NonNullByDefault
public class DataItemGroup {
public static final Map<Integer, DataItem[]> dataItemGroups = createDataItemGroups();
public static final Map<Integer, DataItem[]> DATAITEMGROUPS = createDataItemGroups();
private static Map<Integer, DataItem[]> createDataItemGroups() {
HashMap<Integer, DataItem[]> g = new HashMap<>();
g.put(0, new DataItem[] {
new DataItem(0, Msg.READ, ByteType.HIGHBYTE, DataType.FLAGS, 0, "ch_enable", 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;
}

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ import org.eclipse.jdt.annotation.Nullable;
*/
@NonNullByDefault
public class GatewayCommand {
private static final Map<String, @Nullable String> supportedCommands = getSupportedCommands();
private static final Map<String, @Nullable String> 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<String, @Nullable String> getSupportedCommands() {
Map<String, @Nullable String> c = new HashMap<>();
c.put(GatewayCommandCode.TemperatureTemporary, null);
c.put(GatewayCommandCode.TemperatureConstant, null);
c.put(GatewayCommandCode.TemperatureOutside, null);
c.put(GatewayCommandCode.SetClock, null);
c.put(GatewayCommandCode.HotWater, null);
c.put(GatewayCommandCode.PrintReport, "A,B,C,G,I,L,M,O,P,R,S,T,V,W");
c.put(GatewayCommandCode.PrintSummary, "0,1");
c.put(GatewayCommandCode.GateWay, "0,1,R");
c.put(GatewayCommandCode.LedA, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.LedB, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.LedC, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.LedD, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.LedE, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.LedF, "R,X,T,B,O,F,H,W,C,E,M,P");
c.put(GatewayCommandCode.GpioA, "0,1,2,3,4,5,6,7");
c.put(GatewayCommandCode.GpioB, "0,1,2,3,4,5,6,7");
c.put(GatewayCommandCode.SetBack, null);
c.put(GatewayCommandCode.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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,4 +27,8 @@ public class OpenThermGatewayConfiguration {
public int port = 0;
public int connectionRetryInterval = 60;
public int connectTimeoutSeconds = 5;
public int readTimeoutSeconds = 20;
}

View File

@ -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<Boolean> {
void sendCommand(GatewayCommand command);
boolean isConnected();
void stop();
void start();
}

View File

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

View File

@ -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<Boolean> future;
private @Nullable ExecutorService executor;
private Map<String, Entry<Long, GatewayCommand>> 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<Boolean> 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<Long, GatewayCommand> 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);
}
}

View File

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

View File

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

View File

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

View File

@ -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">
<config-description uri="thing-type:openthermgateway:otgw">
<config-description uri="thing-type:openthermgateway:openthermgateway">
<parameter-group name="connection">
<label>Connection</label>
<description>Connection settings.</description>
@ -28,6 +28,20 @@
<default>60</default>
</parameter>
<parameter name="connectTimeoutSeconds" type="integer" required="true" groupName="connection" min="5"
max="60" unit="s">
<label>Connect Timeout</label>
<description>The maximum time (seconds) to wait for establishing a connection to the gateway.</description>
<default>5</default>
</parameter>
<parameter name="readTimeoutSeconds" type="integer" required="true" groupName="connection" min="5" max="60"
unit="s">
<label>Read Timeout</label>
<description>The maximum time (seconds) to wait for reading responses from the gateway.</description>
<default>20</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -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">
<thing-type id="otgw">
<label>OpenTherm Gateway</label>
<description>OpenTherm Gateway binding</description>
<thing-type id="boiler">
<supported-bridge-type-refs>
<bridge-type-ref id="openthermgateway"/>
</supported-bridge-type-refs>
<label>Boiler</label>
<description>Boiler and thermostat</description>
<channels>
<channel id="roomtemp" typeId="roomtemp"/>
<channel id="roomsetpoint" typeId="roomsetpoint"/>
@ -44,6 +47,7 @@
<channel id="airpressfault" typeId="airpressfault"/>
<channel id="waterovtemp" typeId="waterovtemp"/>
<channel id="oemfaultcode" typeId="oemfaultcode"/>
<channel id="diag" typeId="diag"/>
<channel id="unsuccessfulburnerstarts" typeId="unsuccessfulburnerstarts"/>
<channel id="burnerstarts" typeId="burnerstarts"/>
<channel id="chpumpstarts" typeId="chpumpstarts"/>
@ -53,12 +57,11 @@
<channel id="chpumphours" typeId="chpumphours"/>
<channel id="dhwpvhours" typeId="dhwpvhours"/>
<channel id="dhwburnerhours" typeId="dhwburnerhours"/>
<channel id="sendcommand" typeId="sendcommand"/>
<channel id="tspnumber" typeId="tspnumber"/>
<channel id="tspentry" typeId="tspentry"/>
<channel id="fhbnumber" typeId="fhbnumber"/>
<channel id="fhbentry" typeId="fhbentry"/>
</channels>
<properties>
<property name="version">1.3.0</property>
</properties>
<config-description-ref uri="thing-type:openthermgateway:otgw"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -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">
<!-- generic channel type for dynamic tsp and fhb channels -->
<channel-type id="tspfhb">
<item-type>Number:Dimensionless</item-type>
<label>Set Dynamically</label>
<description>Transparent slave parameter or Fault history buffer value</description>
<state readOnly="true"/>
</channel-type>
<!-- openthermgateway -->
<channel-type id="sendcommand">
<item-type>String</item-type>
<label>Send Command</label>
<description>Channel to send commands to the OpenTherm Gateway device</description>
<state readOnly="false"/>
</channel-type>
<!-- boiler -->
<channel-type id="roomtemp">
<item-type>Number:Temperature</item-type>
<label>Room Temperature</label>
@ -349,11 +369,351 @@
<state readOnly="true"/>
</channel-type>
<channel-type id="sendcommand">
<item-type>String</item-type>
<label>Send Command</label>
<description>Channel to send commands to the OpenTherm Gateway device</description>
<state readOnly="false"/>
<channel-type id="tspnumber">
<item-type>Number:Dimensionless</item-type>
<label>Transparent Slave Parameter Number</label>
<description>Number of transparant slave parameter entries</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="tspentry">
<item-type>Number:Dimensionless</item-type>
<label>Transparent Slave Parameter Entry</label>
<description>Transparent slave parameter entry</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="fhbnumber">
<item-type>Number:Dimensionless</item-type>
<label>Fault History Buffer Number</label>
<description>Number of fault history buffer entries</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="fhbentry">
<item-type>Number:Dimensionless</item-type>
<label>Fault History Buffer Entry</label>
<description>Fault history buffer entry</description>
<state readOnly="true"/>
</channel-type>
<!-- ventilationheatrecovery -->
<channel-type id="vh_ventilationenable">
<item-type>Switch</item-type>
<label>Ventilation</label>
<description>Ventilation enabled</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_bypassposition">
<item-type>Number:Dimensionless</item-type>
<label>Bypass Position</label>
<description>Bypass position</description>
<state readOnly="true">
<options>
<option value="0">close</option>
<option value="1">open</option>
</options>
</state>
</channel-type>
<channel-type id="vh_bypassmode">
<item-type>Number:Dimensionless</item-type>
<label>Bypass Mode</label>
<description>Bypass mode</description>
<state readOnly="true">
<options>
<option value="0">manual</option>
<option value="1">automatic</option>
</options>
</state>
</channel-type>
<channel-type id="vh_freeventilationmode">
<item-type>Switch</item-type>
<label>Free Ventilation Mode</label>
<description>Free ventilation mode</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_faultindication">
<item-type>Switch</item-type>
<label>Fault Indication</label>
<description>Fault indication</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_ventilationmode">
<item-type>Switch</item-type>
<label>Ventilation Mode</label>
<description>Ventilation mode</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_bypassstatus">
<item-type>Switch</item-type>
<label>Bypass Status</label>
<description>Bypass status</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_bypassautomaticstatus">
<item-type>Number:Dimensionless</item-type>
<label>Bypass Automatic Status</label>
<description>Bypass automatic status</description>
<state readOnly="true">
<options>
<option value="0">manual</option>
<option value="1">automatic</option>
</options>
</state>
</channel-type>
<channel-type id="vh_freeventilationstatus">
<item-type>Switch</item-type>
<label>Free Ventilation Status</label>
<description>Free ventilation status</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_diagnosticindication">
<item-type>Switch</item-type>
<label>Diagnostic Indication</label>
<description>Diagnostic indication</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_controlsetpoint">
<item-type>Number:Dimensionless</item-type>
<label>Control Setpoint</label>
<description>Control setpoint</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_servicerequest">
<item-type>Switch</item-type>
<label>Service Request</label>
<description>Service request</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_exhaustfanfault">
<item-type>Switch</item-type>
<label>Exhaust Fan Fault</label>
<description>Exhaust fan fault</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_inletfanfault">
<item-type>Switch</item-type>
<label>Inlet Fan Fault</label>
<description>Inlet fan fault</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_frostprotection">
<item-type>Switch</item-type>
<label>Frost Protection</label>
<description>Frost protection</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_faultcode">
<item-type>Number:Dimensionless</item-type>
<label>Fault Code</label>
<description>Fault code</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_diagnosticcode">
<item-type>Number:Dimensionless</item-type>
<label>Diagnostic Code</label>
<description>Diagnostic code</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_systemtype">
<item-type>Number:Dimensionless</item-type>
<label>System Type</label>
<description>System type</description>
<state readOnly="true">
<options>
<option value="0">central exaust</option>
<option value="1">heat-recovery</option>
</options>
</state>
</channel-type>
<channel-type id="vh_bypass">
<item-type>Switch</item-type>
<label>Bypass</label>
<description>Bypass</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_speedcontrol">
<item-type>Number:Dimensionless</item-type>
<label>Speed Control</label>
<description>Speed control</description>
<state readOnly="true">
<options>
<option value="0">3-speed</option>
<option value="1">variable</option>
</options>
</state>
</channel-type>
<channel-type id="vh_memberid">
<item-type>Number:Dimensionless</item-type>
<label>Member ID</label>
<description>Member ID</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_openthermversion">
<item-type>Number:Dimensionless</item-type>
<label>OpenTherm Version</label>
<description>OpenTherm version</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_versiontype">
<item-type>Number:Dimensionless</item-type>
<label>Version Type</label>
<description>Version type</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_relativeventilation">
<item-type>Number:Dimensionless</item-type>
<label>Relative Ventilation</label>
<description>Relative ventilation position</description>
<state readOnly="true" pattern="%d %%"/>
</channel-type>
<channel-type id="vh_relativehumidity">
<item-type>Number:Dimensionless</item-type>
<label>Relative Humidity</label>
<description>Relative humidity exhaust air</description>
<state readOnly="true" pattern="%d %%"/>
</channel-type>
<channel-type id="vh_co2level">
<item-type>Number:Dimensionless</item-type>
<label>CO2 Level</label>
<description>CO2 level exhaust air</description>
<state readOnly="true" pattern="%d ppm"/>
</channel-type>
<channel-type id="vh_supplyinlettemp">
<item-type>Number:Temperature</item-type>
<label>Supply Inlet Temperature</label>
<description>Supply inlet temperature</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="vh_supplyoutlettemp">
<item-type>Number:Temperature</item-type>
<label>Supply Outlet Temperature</label>
<description>Supply outlet temperature</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="vh_exhaustinlettemp">
<item-type>Number:Temperature</item-type>
<label>Exhaust Inlet Temperature</label>
<description>Exhaust inlet temperature</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="vh_exhaustoutlettemp">
<item-type>Number:Temperature</item-type>
<label>Exhaust Outlet Temperature</label>
<description>Exhaust outlet temperature</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="vh_actualexhaustfanspeed">
<item-type>Number:Dimensionless</item-type>
<label>Actual Exhaust Fan Speed</label>
<description>Actual exhaust fan speed</description>
<state readOnly="true" pattern="%d rpm"/>
</channel-type>
<channel-type id="vh_actualinletfanspeed">
<item-type>Number:Dimensionless</item-type>
<label>Actual Inlet Fan Speed</label>
<description>Actual inlet fan speed</description>
<state readOnly="true" pattern="%d rpm"/>
</channel-type>
<channel-type id="vh_nominalventenable">
<item-type>Switch</item-type>
<label>Nominal Ventilation Value Transfer</label>
<description>Nominal ventilation value transfer enabled</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_nominalventrw">
<item-type>Number:Dimensionless</item-type>
<label>Nominal Ventilation Value</label>
<description>Nominal ventilation value</description>
<state readOnly="true">
<options>
<option value="0">read-only</option>
<option value="1">read/write</option>
</options>
</state>
</channel-type>
<channel-type id="vh_nominalventilationvalue">
<item-type>Number:Dimensionless</item-type>
<label>Nominal Ventilation Value</label>
<description>Nominal ventilation value</description>
<state readOnly="true" pattern="%d %%"/>
</channel-type>
<channel-type id="vh_filtercheck">
<item-type>Switch</item-type>
<label>Filter Check</label>
<description>Filter Check enabled</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_tspnumber">
<item-type>Number:Dimensionless</item-type>
<label>Transparent Slave Parameter Number</label>
<description>Number of transparent slave parameter entries</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_tspentry">
<item-type>Number:Dimensionless</item-type>
<label>Transparent Slave Parameter Entry</label>
<description>Transparent slave parameter entry</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_fhbnumber">
<item-type>Number:Dimensionless</item-type>
<label>Fault History Buffer Number</label>
<description>Number of fault history buffer entries</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_fhbentry">
<item-type>Number:Dimensionless</item-type>
<label>Fault History Buffer Entry</label>
<description>Fault history buffer entry</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="vh_ventilationsetpoint">
<item-type>Number:Dimensionless</item-type>
<label>Ventilation Setpoint</label>
<description>Ventilation setpoint override</description>
<state readOnly="false" min="0" max="100" step="1"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="openthermgateway"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<bridge-type id="openthermgateway">
<label>OpenTherm Gateway</label>
<description>OpenTherm Gateway</description>
<channels>
<channel id="sendcommand" typeId="sendcommand"/>
</channels>
<properties>
<property name="version">2.2.0</property>
</properties>
<config-description-ref uri="thing-type:openthermgateway:openthermgateway"/>
</bridge-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="openthermgateway"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="ventilationheatrecovery">
<supported-bridge-type-refs>
<bridge-type-ref id="openthermgateway"/>
</supported-bridge-type-refs>
<label>Ventilation / Heat Recovery</label>
<description>Ventilation or Heat Recovery unit</description>
<channels>
<channel id="vh_ventilationenable" typeId="vh_ventilationenable"/>
<channel id="vh_bypassposition" typeId="vh_bypassposition"/>
<channel id="vh_bypassmode" typeId="vh_bypassmode"/>
<channel id="vh_freeventilationmode" typeId="vh_freeventilationmode"/>
<channel id="vh_faultindication" typeId="vh_faultindication"/>
<channel id="vh_ventilationmode" typeId="vh_ventilationmode"/>
<channel id="vh_bypassstatus" typeId="vh_bypassstatus"/>
<channel id="vh_bypassautomaticstatus" typeId="vh_bypassautomaticstatus"/>
<channel id="vh_freeventilationstatus" typeId="vh_freeventilationstatus"/>
<channel id="vh_filtercheck" typeId="vh_filtercheck"/>
<channel id="vh_diagnosticindication" typeId="vh_diagnosticindication"/>
<channel id="vh_controlsetpoint" typeId="vh_controlsetpoint"/>
<channel id="vh_servicerequest" typeId="vh_servicerequest"/>
<channel id="vh_exhaustfanfault" typeId="vh_exhaustfanfault"/>
<channel id="vh_inletfanfault" typeId="vh_inletfanfault"/>
<channel id="vh_frostprotection" typeId="vh_frostprotection"/>
<channel id="vh_faultcode" typeId="vh_faultcode"/>
<channel id="vh_diagnosticcode" typeId="vh_diagnosticcode"/>
<channel id="vh_systemtype" typeId="vh_systemtype"/>
<channel id="vh_bypass" typeId="vh_bypass"/>
<channel id="vh_speedcontrol" typeId="vh_speedcontrol"/>
<channel id="vh_memberid" typeId="vh_memberid"/>
<channel id="vh_openthermversion" typeId="vh_openthermversion"/>
<channel id="vh_versiontype" typeId="vh_versiontype"/>
<channel id="vh_relativeventilation" typeId="vh_relativeventilation"/>
<channel id="vh_relativehumidity" typeId="vh_relativehumidity"/>
<channel id="vh_co2level" typeId="vh_co2level"/>
<channel id="vh_supplyinlettemp" typeId="vh_supplyinlettemp"/>
<channel id="vh_supplyoutlettemp" typeId="vh_supplyoutlettemp"/>
<channel id="vh_exhaustinlettemp" typeId="vh_exhaustinlettemp"/>
<channel id="vh_exhaustoutlettemp" typeId="vh_exhaustoutlettemp"/>
<channel id="vh_actualexhaustfanspeed" typeId="vh_actualexhaustfanspeed"/>
<channel id="vh_actualinletfanspeed" typeId="vh_actualinletfanspeed"/>
<channel id="vh_nominalventenable" typeId="vh_nominalventenable"/>
<channel id="vh_nominalventrw" typeId="vh_nominalventrw"/>
<channel id="vh_nominalventilationvalue" typeId="vh_nominalventilationvalue"/>
<channel id="vh_ventilationsetpoint" typeId="vh_ventilationsetpoint"/>
<channel id="vh_tspnumber" typeId="vh_tspnumber"/>
<channel id="vh_tspentry" typeId="vh_tspentry"/>
<channel id="vh_fhbnumber" typeId="vh_fhbnumber"/>
<channel id="vh_fhbentry" typeId="vh_fhbentry"/>
</channels>
</thing-type>
</thing:thing-descriptions>