added migrated 2.x add-ons

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

View File

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

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.comfoair</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

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

View File

@@ -0,0 +1,288 @@
# ComfoAir Binding
This binding allows to monitor and control Zehnder ComfoAir serial controlled ventilation systems via RS232 serial connection.
Though the binding is developed based on the protocol description for Zehnder ComfoAir devices it should also work for mostly identical systems from different manufacturers, like StorkAir WHR930, Wernig G90-380 and Paul Santos 370 DC.
It was also successfully tested on a Wernig G90-160.
### Limitations
* Either the ComfoAir binding or the CCEase Comfocontrol can be active, but not together.
* You must implement auto mode by yourself with rules, but it is more powerful.
### Prerequisites
Computer communication between ComfoAir device and openHAB via RS232 connection has to be set up.
The connection should be made with a 3-wire cable connecting pins: GND, TX, RX of RS232 sockets, but RX and TX pins should be crossed (TX of ComfoAir to RX of PC, RX of ComfoAir to TX of PC).
### Serial Port Access Rights
* Take care that the user that runs openHAB has rights to access the serial port
* On Ubuntu/Debian based systems (incl. openHABian) that usually means adding the user (e.g. openhab) to the group "dialout", i.e.
```
sudo usermod -a -G dialout openhab
```
## Supported Things
Only a single generic thing type is supported by the binding:
|Thing Type ID |Description |
|--------------|---------------------------------------------------------------------|
|comfoair |A ComfoAir ventilation system connected via RS232 serial connection. |
## Discovery
Discovery is not supported.
## Thing Configuration
For the thing creation, the appropriate serial port has to be set.
|Parameter |Values |Default |
|---------------|-----------------------------------------|---------------|
|serialPort |`/dev/ttyUSB0`, `COM1`, etc. |- |
|refreshInterval|Refresh interval in seconds (10...65535) |60 |
## Channels
The ComfoAir binding supports the following channels.
Some channels are added dynamically based on installed device options.
Available device options are shown in thing properties.
| Channel ID | Item Type | Label | Description | Read Only | Advanced |
|------------------------------------|--------------------------|---------------------------------|---------------------------------------------------------------------------------------------|-----------|----------|
| **Binding Control** | | | | | |
| bindingControl#activate | Switch | Activate Binding Control | Activate (control through openHAB) or deactivate (return control to CCEase) binding control | false | no |
| **Ventilation Values** | | | | | |
| ventilation#fanLevel | Number | Fan Level | Fan level | false | no |
| ventilation#fanOut0 | Number | Fan Out Level 0 (away) | Fan level 0 performance (%) of outgoing fan | false | yes |
| ventilation#fanOut1 | Number | Fan Out Level 1 | Fan level 1 performance (%) of outgoing fan | false | yes |
| ventilation#fanOut2 | Number | Fan Out Level 2 | Fan level 2 performance (%) of outgoing fan | false | yes |
| ventilation#fanOut3 | Number | Fan Out Level 3 | Fan level 3 performance (%) of outgoing fan | false | yes |
| ventilation#fanIn0 | Number | Fan In Level 0 (away) | Fan level 0 performance (%) of incoming fan | false | yes |
| ventilation#fanIn1 | Number | Fan In Level 1 | Fan level 1 performance (%) of incoming fan | false | yes |
| ventilation#fanIn2 | Number | Fan In Level 2 | Fan level 2 performance (%) of incoming fan | false | yes |
| ventilation#fanIn3 | Number | Fan In Level 3 | Fan level 3 performance (%) of incoming fan | false | yes |
| ventilation#fanInPercent | Number | Fan In (%) | Current relative speed (%) of incoming fan | false | yes |
| ventilation#fanOutPercent | Number | Fan Out (%) | Current relative speed (%) of outgoing fan | true | yes |
| ventilation#fanInRPM | Number | Fan In (rpm) | Current absolute speed (rpm) of incoming fan | true | yes |
| ventilation#fanOutRPM | Number | Fan Out (rpm) | Current absolute speed (rpm) of outgoing fan | true | yes |
| **Temperature Values** | | | | | |
| temperatures#targetTemperature | Number:Temperature | Target Temperature | Target (comfort) temperature | false | no |
| temperatures#outdoorTemperatureIn | Number:Temperature | Outdoor Temperature Incoming | Intake air temperature (outside) | true | yes |
| temperatures#outdoorTemperatureOut | Number:Temperature | Outdoor Temperature Outgoing | Outlet air temperature (outside) | true | yes |
| temperatures#indoorTemperatureIn | Number:Temperature | Indoor Temperature Incoming | Inlet air temperature (inside) | true | yes |
| temperatures#indoorTemperatureOut | Number:Temperature | Indoor Temperature Outgoing | Uptake air temperature (inside) | true | yes |
| temperatures#isT1Sensor | Switch | Sensor T1 Available | Availability of temperature sensor T1 (outdoor in) | true | yes |
| temperatures#isT2Sensor | Switch | Sensor T2 Available | Availability of temperature sensor T2 (indoor in) | true | yes |
| temperatures#isT3Sensor | Switch | Sensor T3 Available | Availability of temperature sensor T3 (indoor out) | true | yes |
| temperatures#isT4Sensor | Switch | Sensor T4 Available | Availability of temperature sensor T4 (outdoor out) | true | yes |
| temperatures#isEWTSensor | Switch | EWT Sensor Available | Availability of EWT temperature sensor | true | yes |
| temperatures#isHeaterSensor | Switch | Heater Sensor Available | Availability of heater temperature sensor | true | yes |
| temperatures#isCookerhoodSensor | Switch | Cookerhood Sensor Available | Availability of cookerhood temperature sensor | true | yes |
| temperatures#ewtTemperature | Number:Temperature | EWT Temperature | Temperature of geothermal heat exchanger sensor | true | yes |
| temperatures#heaterTemperature | Number:Temperature | Heater Temperature | Temperature of heater sensor | true | yes |
| temperatures#cookerhoodTemperature | Number:Temperature | Cookerhood Temperature | Temperature of cookerhood sensor | true | yes |
| **Uptimes** | | | | | |
| times#level0Time | Number:Time | Level 0 duration | Uptime at level 0 (away) | true | yes |
| times#level1Time | Number:Time | Level 1 duration | Uptime at level 1 | true | yes |
| times#level2Time | Number:Time | Level 2 duration | Uptime at level 2 | true | yes |
| times#level3Time | Number:Time | Level 3 duration | Uptime at level 3 | true | yes |
| times#freezeTime | Number:Time | Antifrost Duration | Uptime of antifrost | true | yes |
| times#preheaterTime | Number:Time | Preheater Duration | Uptime of preheater | true | yes |
| times#bypassTime | Number:Time | Bypass Duration | Hours of bypass open | true | yes |
| times#filterHours | Number:Time | Filter Duration | Uptime of the filter | true | no |
| **Menu P1: Control States** | | | | | |
| menuP1#menu20Mode | Switch | Menu 20 Mode (P10) | State of menu 20 mode (P10) | true | yes |
| menuP1#menu21Mode | Switch | Menu 21 Mode (P11) | State of menu 21 mode (P11) | true | yes |
| menuP1#menu22Mode | Switch | Menu 22 Mode (P12) | State of menu 22 mode (P12) | true | yes |
| menuP1#menu23Mode | Switch | Menu 23 Mode (P13) | State of menu 23 mode (P13) | true | yes |
| menuP1#menu24Mode | Switch | Menu 24 Mode (P14) | State of menu 24 mode (P14) | true | yes |
| menuP1#menu25Mode | Switch | Menu 25 Mode (P15) | State of menu 25 mode (P15) | true | yes |
| menuP1#menu26Mode | Switch | Menu 26 Mode (P16) | State of menu 26 mode (P16) | true | yes |
| menuP1#menu27Mode | Switch | Menu 27 Mode (P17) | State of menu 27 mode (P17) | true | yes |
| menuP1#menu28Mode | Switch | Menu 28 Mode (P18) | State of menu 28 mode (P18) | true | yes |
| menuP1#menu29Mode | Switch | Menu 29 Mode (P19) | State of menu 29 mode (P19) | true | yes |
| **Menu P2: Delay Settings** | | | | | |
| menuP2#bathroomStartDelay | Number | Menu P21 | Start delay for bathroom switch (min) | false | yes |
| menuP2#bathroomEndDelay | Number | Menu P22 | End delay for bathroom switch (min) | false | yes |
| menuP2#L1EndDelay | Number | Menu P23 | End delay for L1 switch (min) | false | yes |
| menuP2#pulseVentilation | Number | Menu P27 | Period for pulse ventilation (min) | false | yes |
| menuP2#filterWeeks | Number | Menu P24 | Usage period until filter pollution message (weeks) | false | yes |
| menuP2#RFShortDelay | Number | Menu P25 | End delay (RF short actuation) for ventilation level 3 (min) | false | yes |
| menuP2#RFLongDelay | Number | Menu P26 | End delay (RF long actuation) for ventilation level 3 (min) | false | yes |
| menuP2#cookerhoodDelay | Number | Menu P20 | End delay for cooker hood control (min) | false | yes |
| **Menu P9: Option States** | | | | | |
| menuP9#chimneyState (dynamic) | Switch | Chimney Control State | State of the chimney control | true | yes |
| menuP9#bypassState (dynamic) | Switch | Bypass State | State of the bypass (ON = open / OFF = closed) | true | yes |
| menuP9#ewtState (dynamic) | Switch | EWT State | State of the EWT valve (ON = open / OFF = closed) | true | yes |
| menuP9#heaterState (dynamic) | Switch | Heater State | State of the heater | true | yes |
| menuP9#vControlState | Switch | 0-10V Control State | State of the 0-10V control | true | yes |
| menuP9#frostState (dynamic) | Switch | Antifrost State | State of the antifrost control | true | yes |
| menuP9#cookerhoodState (dynamic) | Switch | Cookerhood State | State of the cookerhood control | true | yes |
| menuP9#enthalpyState (dynamic) | Switch | Enthalpy State | State of the enthalpy module | true | yes |
| **Error States** | | | | | |
| errors#filterError | Switch | Filter Error | Filter full | true | no |
| errors#errorsCurrent | String | Errors Current | Current errors | true | no |
| errors#errorsLast | String | Errors Last | Last errors | true | yes |
| errors#errorsPrelast | String | Errors Prelast | Prelast errors | true | yes |
| errors#errorsPrePrelast | String | Errors Pre-Prelast | Pre-Prelast errors | true | yes |
| **Bypass Values (dynamic)** | | | | | |
| bypass#bypassFactor | Number | Bypass Factor | Bypass factor value | true | yes |
| bypass#bypassLevel | Number | Bypass Level | Bypass level state | true | yes |
| bypass#bypassCorrection | Number | Bypass Correction | Bypass correction state | true | yes |
| bypass#bypassSummer | Switch | Bypass Summer Mode | Bypass summer mode | true | yes |
| **Preheater Values (dynamic)** | | | | | |
| preheater#preheaterValve | Number | Preheater Valve | State of the preheater valve | true | yes |
| preheater#preheaterFrostProtect | Switch | Frost Protection | State of the frost protection | true | yes |
| preheater#preheaterHeating | Switch | Preheater | State of the preheater | true | yes |
| preheater#preheaterFrostTime | Number | Preheater Frost Time | Frost minutes | true | yes |
| preheater#preheaterSafety | Number | Preheater Frost Safety | Frost safety setting | true | yes |
| **EWT Values (dynamic)** | | | | | |
| ewt#ewtTemperatureLow | Number:Temperature | EWT Temperature (low) | Lower temperature of the geothermal heat exchanger | true | yes |
| ewt#ewtTemperatureHigh | Number:Temperature | EWT Temperature (high) | Upper temperature of the geothermal heat exchanger | true | yes |
| ewt#ewtSpeed | Number | EWT Speed Up (%) | Speed up of the geothermal heat exchanger | true | yes |
| **Heater Values (dynamic)** | | | | | |
| heater#heaterPower | Number | Heater Power | Heater power value | true | yes |
| heater#heaterPowerI | Number | Heater Power I-parameter | Heater power I-parameter value | true | yes |
| heater#heaterTargetTemperature | Number:Temperature | Heater Target Temperature | Target temperature of the heater | true | yes |
| **Cookerhood Values (dynamic)** | | | | | |
| cookerhood#cookerhoodSpeed | Number | Cookerhood Speed Up (%) | Speed up of the cookerhood | true | yes |
| **Enthalpy Values (dynamic)** | | | | | |
| enthalpy#enthalpyTemperature | Number:Temperature | Enthalpy Sensor Temperature | Temperature of the enthalpy sensor | true | yes |
| enthalpy#enthalpyHumidity | Number | Enthalpy Sensor Humidity | Humidity of the enthalpy sensor | true | yes |
| enthalpy#enthalpyLevel | Number | Enthalpy Sensor Level | Level of the enthalpy sensor | true | yes |
| enthalpy#enthalpyTime | Number | Enthalpy Sensor Timer | Timer state of the enthalpy sensor | true | yes |
| **Inputs** | | | | | |
| inputs#isL1Switch | Switch | L1 Switch | Availability of L1 step switch | true | yes |
| inputs#isL2Switch | Switch | L2 Switch | Availability of L2 step switch | true | yes |
| inputs#isBathroomSwitch | Switch | Bathroom Switch | Availability of bathroom switch | true | yes |
| inputs#isCookerhoodSwitch | Switch | Cookerhood Switch | Availability of cookerhood switch | true | yes |
| inputs#isExternalFilter | Switch | External Filter | Availability of external filter | true | yes |
| inputs#isWTW | Switch | Heat Recovery | Availability of heat recovery (WTW) | true | yes |
| inputs#isBathroom2Switch | Switch | Bathroom Switch 2 | Availability of bathroom switch 2 (luxe) | true | yes |
| **Resets** | | | | | |
| resets#filterReset | Number | Filter Reset | Reset filter uptime | false | no |
| resets#errorReset | Number | Error Reset | Reset errors | false | no |
| **Analog Inputs [1-4]** | | | | | |
| analog[1-4]#isAnalog | Switch | Analog Input Availability | Availability of analog input [1-4] | false | yes |
| analog[1-4]#analogMode | Switch | Analog Input State | State of analog input [1-4] | false | yes |
| analog[1-4]#analogNegative | Switch | Analog Input Postive/Negative | Postive/Negative state of analog input [1-4] | false | yes |
| analog[1-4]#analogVolt | Number:ElectricPotential | Analog Input Voltage Level | Voltage level of analog input [1-4] | false | yes |
| analog[1-4]#analogMin | Number | Analog Input Min | Minimum setting for analog input [1-4] | false | yes |
| analog[1-4]#analogMax | Number | Analog Input Max | Maximum setting for analog input [1-4] | false | yes |
| analog[1-4]#analogValue | Number | Analog Input Target | Target setting for analog input [1-4] | false | yes |
| **RF Input** | | | | | |
| analogRF#isRF | Switch | RF Input Availability | Availability of RF input | false | yes |
| analogRF#RFMode | Switch | RF Input State | State of RF input | false | yes |
| analogRF#RFNegative | Switch | RF Input Postive/Negative | Postive/Negative state of RF input | false | yes |
| analogRF#RFMin | Number | RF Input Min | Minimum setting for RF input | false | yes |
| analogRF#RFMax | Number | RF Input Max | Maximum setting for RF input | false | yes |
| analogRF#RFValue | Number | RF Input Target | Target setting for RF input | false | yes |
| analogRF#analogPriority | Number | Analog Priority | Priority of control | false | yes |
## Full Example
`.things` file:
```
Thing comfoair:comfoair:myComfoAir "ComfoAir" [serialPort="/dev/ttyUSB0", refreshInterval="60"]
```
`.items` file:
```
Switch comfoairControl "Activate" <computer> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:bindingControl#activate"}
Number comfoairFanLevel "Ventilation level [%d]" <chart> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:ventilation#fanLevel"}
Number comfoairErrorReset "Error reset" <service> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:resets#errorReset"}
Number comfoairFilterReset "Filter reset" <service> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:resets#filterReset"}
Number comfoairFilterPeriod "Filter period [%d weeks]" <clock> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP2#filterWeeks"}
// Messages
String comfoairError "Error: [%s]" (ComfoAir) {channel="comfoair:comfoair:myComfoAir:errors#errorsCurrent"}
Number:Time comfoairFilterRuntime "Filter runtime [%.0f h]" (ComfoAir) {channel="comfoair:comfoair:myComfoAir:times#filterHours"}
// State
Number:Temperature comfoairTargetTemperature "Comfort temperature [%.1f °C]" <temperature> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:temperatures#targetTemperature"}
Number:Temperature comfoairOutdoorIncomingTemperature "Inlet air temperature [%.1f °C]" <temperature> (ComfoAir, comfoairTemps_Chart) {channel="comfoair:comfoair:myComfoAir:temperatures#outdoorTemperatureIn"}
Number:Temperature comfoairIndoorIncomingTemperature "Supply air temperature [%.1f °C]" <temperature> (ComfoAir, comfoairTemps_Chart) {channel="comfoair:comfoair:myComfoAir:temperatures#indoorTemperatureIn"}
Number:Temperature comfoairIndoorOutgoingTemperature "Return air temperature [%.1f °C]" <temperature> (ComfoAir, comfoairTemps_Chart) {channel="comfoair:comfoair:myComfoAir:temperatures#indoorTemperatureOut"}
Number:Temperature comfoairOutdoorOutgoingTemperature "Exhaust air temperature [%.1f °C]" <temperature> (ComfoAir, comfoairTemps_Chart) {channel="comfoair:comfoair:myComfoAir:temperatures#outdoorTemperatureOut"}
Number comfoairIncomingFan "Supply capacity [%d %%]" <fan_in> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:ventilation#fanInPercent"}
Number comfoairOutgoingFan "Exhaust capacity [%d %%]" <fan_out> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:ventilation#fanOutPercent"}
Number comfoairFanIn0 "Supply capacity - level 0 [%d %%]" <fan_in> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:ventilation#fanIn0"}
Number comfoairFanOut0 "Exhaust capacity - level 0 [%d %%]" <fan_out> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:ventilation#fanOut0"}
Switch comfoairBypassMode "Bypass [MAP(comfoair_bypass.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#bypassState"}
Switch comfoairEWTMode "EWT [MAP(comfoair_on-off.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#ewtState"}
Switch comfoairChimneyMode "Fire programme [MAP(comfoair_on-off.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#chimneyState"}
Switch comfoairHeaterMode "Heater [MAP(comfoair_on-off.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#heaterState"}
Switch comfoairCookerHoodMode "Extractor hood [MAP(comfoair_on-off.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#cookerhoodState"}
Switch comfoairEnthalpyMode "Enthalpy [MAP(comfoair_on-off.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#enthalpyState"}
Switch comfoairFreezeMode "Freeze [MAP(comfoair_freeze.map):%s]" <climate> (ComfoAir) {channel="comfoair:comfoair:myComfoAir:menuP9#frostState"}
```
`.sitemap` file:
```
sitemap comfoair label="ComfoAir" {
Frame label="Main" {
Text item=comfoairError labelcolor=[!="No Errors"="red"] valuecolor=[!="No Errors"="red"]
Switch item=comfoairControl mappings=[0="CCEase", 1="Computer"]
Switch item=comfoairErrorReset mappings=[1="Reset"]
Switch item=comfoairFilterReset mappings=[1="Reset"]
}
Frame label="Control" {
Selection item=comfoairFanLevel
Setpoint item=comfoairTargetTemperature step=0.5 minValue=15 maxValue=28 valuecolor=["black"]
}
Frame label="State" {
Text item=comfoairOutdoorIncomingTemperature valuecolor=["black"]
Text item=comfoairOutdoorOutgoingTemperature valuecolor=["black"]
Text item=comfoairIndoorIncomingTemperature valuecolor=["black"]
Text item=comfoairIndoorOutgoingTemperature valuecolor=["black"]
}
Frame {
Text item=comfoairIncomingFan valuecolor=["black"]
Text item=comfoairBypassMode valuecolor=["black"]
Text item=comfoairOutgoingFan valuecolor=["black"]
Text item=comfoairEWTMode valuecolor=[OFF="silver", ON="black"]
Text item=comfoairEfficiency valuecolor=["black"]
Text item=comfoairFreezeMode valuecolor=[OFF="black", ON="red"]
Text item=comfoairFilterRuntime_Message valuecolor=["black"]
Text item=comfoairChimneyMode valuecolor=[OFF="silver", ON="black"]
}
}
```
`comfoair_bypass.map` file:
```
ON=Opened
OFF=Closed
undefined=unknown
-=unknown
```
`comfoair_on-off.map` file:
```
ON=active
OFF=inactive
undefined=unknown
-=unknown
```
`comfoair_freeze.map` file:
```
ON=frozen
OFF=OK
undefined=unknown
-=unknown
```
## Control Protocol Reference
For reference the protocol description used can be found here (german version only):
[Protokollbeschreibung Zehnder ComfoAir](http://www.see-solutions.de/sonstiges/Protokollbeschreibung_ComfoAir.pdf)

View File

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

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.comfoair-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-comfoair" description="ComfoAir Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-serial</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.comfoair/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,202 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link ComfoAirBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Hans Böhm - Initial contribution
*/
@NonNullByDefault
public class ComfoAirBindingConstants {
private static final String BINDING_ID = "comfoair";
public static final ThingTypeUID THING_TYPE_COMFOAIR_GENERIC = new ThingTypeUID(BINDING_ID, "comfoair");
// Thing properties
// General properties
public static final String PROPERTY_SOFTWARE_MAIN_VERSION = "SOFTWARE_VERSION_MAIN";
public static final String PROPERTY_SOFTWARE_MINOR_VERSION = "SOFTWARE_VERSION_MINOR";
public static final String PROPERTY_DEVICE_NAME = "DEVICE_NAME";
// Installed options
public static final String PROPERTY_OPTION_PREHEATER = "OPTION_PREHEATER";
public static final String PROPERTY_OPTION_BYPASS = "OPTION_BYPASS";
public static final String PROPERTY_OPTION_RECU_TYPE = "DEVICE_TYPE";
public static final String PROPERTY_OPTION_RECU_SIZE = "DEVICE_SIZE";
public static final String PROPERTY_OPTION_CHIMNEY = "OPTION_CHIMNEY";
public static final String PROPERTY_OPTION_COOKERHOOD = "OPTION_COOKERHOOD";
public static final String PROPERTY_OPTION_HEATER = "OPTION_HEATER";
public static final String PROPERTY_OPTION_ENTHALPY = "OPTION_ENTHALPY";
public static final String PROPERTY_OPTION_EWT = "OPTION_EWT";
// Common option states
public static final String[] COMMON_OPTION_STATES = { "Not installed", "Installed" };
// Channel groups
public static final String CG_CONTROL_PREFIX = "bindingControl#";
public static final String CG_VENTILATION_PREFIX = "ventilation#";
public static final String CG_TEMPS_PREFIX = "temperatures#";
public static final String CG_TIMES_PREFIX = "times#";
public static final String CG_BYPASS_PREFIX = "bypass#";
public static final String CG_PREHEATER_PREFIX = "preheater#";
public static final String CG_EWT_PREFIX = "ewt#";
public static final String CG_HEATER_PREFIX = "heater#";
public static final String CG_COOKERHOOD_PREFIX = "cookerhood#";
public static final String CG_ENTHALPY_PREFIX = "enthalpy#";
public static final String CG_OPTIONS_PREFIX = "options#";
public static final String CG_MENUP1_PREFIX = "menuP1#";
public static final String CG_MENUP2_PREFIX = "menuP2#";
public static final String CG_MENUP9_PREFIX = "menuP9#";
public static final String CG_INPUTS_PREFIX = "inputs#";
public static final String CG_ANALOG1_PREFIX = "analog1#";
public static final String CG_ANALOG2_PREFIX = "analog2#";
public static final String CG_ANALOG3_PREFIX = "analog3#";
public static final String CG_ANALOG4_PREFIX = "analog4#";
public static final String CG_ANALOGRF_PREFIX = "analogRF#";
public static final String CG_ERRORS_PREFIX = "errors#";
public static final String CG_RESETS_PREFIX = "resets#";
// Channels
// Control channels
public static final String CHANNEL_ACTIVATE = "activate";
// Ventilation channels
public static final String CHANNEL_FAN_LEVEL = "fanLevel";
public static final String CHANNEL_FAN_OUT_0 = "fanOut0";
public static final String CHANNEL_FAN_OUT_1 = "fanOut1";
public static final String CHANNEL_FAN_OUT_2 = "fanOut2";
public static final String CHANNEL_FAN_OUT_3 = "fanOut3";
public static final String CHANNEL_FAN_IN_0 = "fanIn0";
public static final String CHANNEL_FAN_IN_1 = "fanIn1";
public static final String CHANNEL_FAN_IN_2 = "fanIn2";
public static final String CHANNEL_FAN_IN_3 = "fanIn3";
public static final String CHANNEL_FAN_IN_PERCENT = "fanInPercent";
public static final String CHANNEL_FAN_OUT_PERCENT = "fanOutPercent";
public static final String CHANNEL_FAN_IN_RPM = "fanInRPM";
public static final String CHANNEL_FAN_OUT_RPM = "fanOutRPM";
// Temperature channels
public static final String CHANNEL_TEMP_TARGET = "targetTemperature";
public static final String CHANNEL_TEMP_OUTDOOR_IN = "outdoorTemperatureIn";
public static final String CHANNEL_TEMP_OUTDOOR_OUT = "outdoorTemperatureOut";
public static final String CHANNEL_TEMP_INDOOR_IN = "indoorTemperatureIn";
public static final String CHANNEL_TEMP_INDOOR_OUT = "indoorTemperatureOut";
public static final String CHANNEL_IS_SENSOR_T1 = "isT1Sensor";
public static final String CHANNEL_IS_SENSOR_T2 = "isT2Sensor";
public static final String CHANNEL_IS_SENSOR_T3 = "isT3Sensor";
public static final String CHANNEL_IS_SENSOR_T4 = "isT4Sensor";
public static final String CHANNEL_IS_SENSOR_EWT = "isEWTSensor";
public static final String CHANNEL_IS_SENSOR_HEATER = "isHeaterSensor";
public static final String CHANNEL_IS_SENSOR_COOKERHOOD = "isCookerhoodSensor";
public static final String CHANNEL_TEMP_EWT = "ewtTemperature";
public static final String CHANNEL_TEMP_HEATER = "heaterTemperature";
public static final String CHANNEL_TEMP_COOKERHOOD = "cookerhoodTemperature";
// Time channels
public static final String CHANNEL_TIME_LEVEL0 = "level0Time";
public static final String CHANNEL_TIME_LEVEL1 = "level1Time";
public static final String CHANNEL_TIME_LEVEL2 = "level2Time";
public static final String CHANNEL_TIME_LEVEL3 = "level3Time";
public static final String CHANNEL_TIME_FREEZE = "freezeTime";
public static final String CHANNEL_TIME_PREHEATER = "preheaterTime";
public static final String CHANNEL_TIME_BYPASS = "bypassTime";
public static final String CHANNEL_TIME_FILTER = "filterHours";
// Bypass channels
public static final String CHANNEL_BYPASS_FACTOR = "bypassFactor";
public static final String CHANNEL_BYPASS_LEVEL = "bypassLevel";
public static final String CHANNEL_BYPASS_CORRECTION = "bypassCorrection";
public static final String CHANNEL_BYPASS_SUMMER = "bypassSummer";
// Preheater channels
public static final String CHANNEL_PREHEATER_VALVE = "preheaterValve";
public static final String CHANNEL_PREHEATER_FROST_PROTECT = "preheaterFrostProtect";
public static final String CHANNEL_PREHEATER_HEATING = "preheaterHeating";
public static final String CHANNEL_PREHEATER_FROST_TIME = "preheaterFrostTime";
public static final String CHANNEL_PREHEATER_SAFETY = "preheaterSafety";
// EWT channels
public static final String CHANNEL_EWT_TEMP_LOW = "ewtTemperatureLow";
public static final String CHANNEL_EWT_TEMP_HIGH = "ewtTemperatureHigh";
public static final String CHANNEL_EWT_SPEED = "ewtSpeed";
// Heater channels
public static final String CHANNEL_HEATER_POWER = "heaterPower";
public static final String CHANNEL_HEATER_POWER_I = "heaterPowerI";
public static final String CHANNEL_HEATER_TEMP_TARGET = "heaterTargetTemperature";
// Cookerhood channels
public static final String CHANNEL_COOKERHOOD_SPEED = "cookerhoodSpeed";
// Enthalpy channels
public static final String CHANNEL_ENTHALPY_TEMP = "enthalpyTemperature";
public static final String CHANNEL_ENTHALPY_HUMIDITY = "enthalpyHumidity";
public static final String CHANNEL_ENTHALPY_LEVEL = "enthalpyLevel";
public static final String CHANNEL_ENTHALPY_TIME = "enthalpyTime";
// Menu P1 channels
public static final String CHANNEL_MENU20_MODE = "menu20Mode";
public static final String CHANNEL_MENU21_MODE = "menu21Mode";
public static final String CHANNEL_MENU22_MODE = "menu22Mode";
public static final String CHANNEL_MENU23_MODE = "menu23Mode";
public static final String CHANNEL_MENU24_MODE = "menu24Mode";
public static final String CHANNEL_MENU25_MODE = "menu25Mode";
public static final String CHANNEL_MENU26_MODE = "menu26Mode";
public static final String CHANNEL_MENU27_MODE = "menu27Mode";
public static final String CHANNEL_MENU28_MODE = "menu28Mode";
public static final String CHANNEL_MENU29_MODE = "menu29Mode";
// Menu P2 channels
public static final String CHANNEL_BR_START_DELAY = "bathroomStartDelay";
public static final String CHANNEL_BR_END_DELAY = "bathroomEndDelay";
public static final String CHANNEL_L1_END_DELAY = "L1EndDelay";
public static final String CHANNEL_PULSE_VENTILATION = "pulseVentilation";
public static final String CHANNEL_FILTER_WEEKS = "filterWeeks";
public static final String CHANNEL_RF_SHORT_DELAY = "RFShortDelay";
public static final String CHANNEL_RF_LONG_DELAY = "RFLongDelay";
public static final String CHANNEL_COOKERHOOD_DELAY = "cookerhoodDelay";
// Menu P9 channels
public static final String CHANNEL_CHIMNEY_STATE = "chimneyState";
public static final String CHANNEL_BYPASS_STATE = "bypassState";
public static final String CHANNEL_EWT_STATE = "ewtState";
public static final String CHANNEL_HEATER_STATE = "heaterState";
public static final String CHANNEL_VCONTROL_STATE = "vControlState";
public static final String CHANNEL_FROST_STATE = "frostState";
public static final String CHANNEL_COOKERHOOD_STATE = "cookerhoodState";
public static final String CHANNEL_ENTHALPY_STATE = "enthalpyState";
// Inputs channels
public static final String CHANNEL_IS_L1_SWITCH = "isL1Switch";
public static final String CHANNEL_IS_L2_SWITCH = "isL2Switch";
public static final String CHANNEL_IS_BATHROOM_SWITCH = "isBathroomSwitch";
public static final String CHANNEL_IS_COOKERHOOD_SWITCH = "isCookerhoodSwitch";
public static final String CHANNEL_IS_EXTERNAL_FILTER = "isExternalFilter";
public static final String CHANNEL_IS_WTW = "isWTW";
public static final String CHANNEL_IS_BATHROOM2_SWITCH = "isBathroom2Switch";
// Analog channels
public static final String CHANNEL_IS_ANALOG = "isAnalog";
public static final String CHANNEL_ANALOG_MODE = "analogMode";
public static final String CHANNEL_ANALOG_NEGATIVE = "analogNegative";
public static final String CHANNEL_ANALOG_VOLT = "analogVolt";
public static final String CHANNEL_ANALOG_MIN = "analogMin";
public static final String CHANNEL_ANALOG_MAX = "analogMax";
public static final String CHANNEL_ANALOG_VALUE = "analogValue";
public static final String CHANNEL_IS_RF = "isRF";
public static final String CHANNEL_RF_MODE = "RFMode";
public static final String CHANNEL_RF_NEGATIVE = "RFNegative";
public static final String CHANNEL_RF_MIN = "RFMin";
public static final String CHANNEL_RF_MAX = "RFMax";
public static final String CHANNEL_RF_VALUE = "RFValue";
public static final String CHANNEL_ANALOG_PRIORITY = "analogPriority";
// Error channels
public static final String CHANNEL_FILTER_ERROR = "filterError";
public static final String CHANNEL_ERRORS_CURRENT = "errorsCurrent";
public static final String CHANNEL_ERRORS_LAST = "errorsLast";
public static final String CHANNEL_ERRORS_PRELAST = "errorsPrelast";
public static final String CHANNEL_ERRORS_PREPRELAST = "errorsPrePrelast";
// Reset channels
public static final String CHANNEL_FILTER_RESET = "filterReset";
public static final String CHANNEL_ERROR_RESET = "errorReset";
}

View File

@@ -0,0 +1,149 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Class to encapsulate all data which is needed to send a cmd to comfoair
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public class ComfoAirCommand {
private final List<String> keys;
private @Nullable Integer requestCmd;
private @Nullable Integer replyCmd;
private int[] requestData;
private final @Nullable Integer requestValue;
private final @Nullable Integer dataPosition;
/**
* @param key
* command key
* @param requestCmd
* command as byte value
* @param replyCmd
* reply command as byte value
* @param data
* request byte values
* @param requestValue
* request byte value
* @param dataPosition
* request byte position
*/
public ComfoAirCommand(String key, @Nullable Integer requestCmd, @Nullable Integer replyCmd, int[] data,
@Nullable Integer dataPosition, @Nullable Integer requestValue) {
this.keys = new ArrayList<String>();
this.keys.add(key);
this.requestCmd = requestCmd;
this.replyCmd = replyCmd;
this.requestData = data;
this.dataPosition = dataPosition;
this.requestValue = requestValue;
}
/*
* Constructor for basic read command
*/
public ComfoAirCommand(String key) {
this.keys = new ArrayList<String>();
this.keys.add(key);
ComfoAirCommandType commandType = ComfoAirCommandType.getCommandTypeByKey(key);
if (commandType != null) {
this.requestCmd = commandType.getReadCommand() == 0 ? null : commandType.getReadCommand();
this.replyCmd = commandType.getReadReplyCommand();
}
this.requestData = ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
this.dataPosition = null;
this.requestValue = null;
}
/**
* @param key
* additional command key
*/
public void addKey(String key) {
keys.add(key);
}
/**
* @return command keys
*/
public List<String> getKeys() {
return keys;
}
/**
* @return command byte value
*/
public @Nullable Integer getRequestCmd() {
return requestCmd;
}
/**
* @return request data as byte values
*/
public int[] getRequestData() {
return requestData;
}
/**
* @return acknowledge cmd byte value
*/
public @Nullable Integer getReplyCmd() {
return replyCmd;
}
/**
* @return request value as byte value
*/
public @Nullable Integer getRequestValue() {
return requestValue;
}
/**
* @return position of request byte
*/
public @Nullable Integer getDataPosition() {
return dataPosition;
}
/**
* set request command byte value
*/
public void setRequestCmd(@Nullable Integer newRequestCmd) {
requestCmd = newRequestCmd;
}
/**
* set reply command byte value
*/
public void setReplyCmd(@Nullable Integer newReplyCmd) {
replyCmd = newReplyCmd;
}
/**
* set request data byte values
*/
public void setRequestData(int[] newRequestData) {
requestData = newRequestData;
}
}

View File

@@ -0,0 +1,908 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.datatypes.ComfoAirDataType;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeBoolean;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeMessage;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeNumber;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeRPM;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeTemperature;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeTime;
import org.openhab.binding.comfoair.internal.datatypes.DataTypeVolt;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* Represents all valid commands which could be processed by this binding
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public enum ComfoAirCommandType {
/**
* Below all valid commands to change or read parameters from ComfoAir
*
* @param key
* command name
* @param dataType
* data type (can be: DataTypeBoolean.getInstance(), DataTypeMessage.getInstance(),
* DataTypeNumber.getInstance(), DataTypeRPM.getInstance(), DataTypeTemperature.getInstance(),
* DataTypeTime.getInstance(), DataTypeVolt.getInstance())
* @param possibleValues
* possible values for write command, if it can only take certain values
* @param changeCommand
* byte number for ComfoAir write command
* @param changeDataSize
* size of bytes list for ComfoAir write command
* @param changeDataPos
* position in bytes list to change
* @param changeAffected
* list of affected commands (can be empty)
* is mandatory for read-write command
* @param readCommand
* request byte number for ComfoAir read command
* @param readReplyCommand
* reply byte list size for ComfoAir read command (list of values only)
* @param readReplyDataPos
* list of byte positions in reply bytes list from ComfoAir
* @param readReplyDataBits
* byte value on readReplyDataPos position to be considered by command (used with
* DataTypeBoolean.class dataType)
*/
ACTIVATE(ComfoAirBindingConstants.CG_CONTROL_PREFIX + ComfoAirBindingConstants.CHANNEL_ACTIVATE,
DataTypeBoolean.getInstance(), new int[] { 0x03 }, Constants.REQUEST_SET_RS232, 1, 0,
Constants.EMPTY_TYPE_ARRAY, Constants.REPLY_SET_RS232, Constants.REPLY_SET_RS232, new int[] { 0 }, 0x03),
MENU20_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU20_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x01),
MENU21_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU21_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x02),
MENU22_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU22_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x04),
MENU23_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU23_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x08),
MENU24_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU24_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x10),
MENU25_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU25_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x20),
MENU26_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU26_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x40),
MENU27_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU27_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 6 },
0x80),
MENU28_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU28_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 7 },
0x01),
MENU29_MODE(ComfoAirBindingConstants.CG_MENUP1_PREFIX + ComfoAirBindingConstants.CHANNEL_MENU29_MODE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 7 },
0x02),
BATHROOM_START_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_BR_START_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 0, new ComfoAirCommandType[] { MENU21_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 0 }),
BATHROOM_END_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_BR_END_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 1, new ComfoAirCommandType[] { MENU22_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 1 }),
L1_END_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_L1_END_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 2, new ComfoAirCommandType[] { MENU27_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 2 }),
PULSE_VENTILATION(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_PULSE_VENTILATION,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 3, new ComfoAirCommandType[] { MENU23_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 3 }),
FILTER_WEEKS(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_FILTER_WEEKS,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 4, new ComfoAirCommandType[] { MENU24_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 4 }),
RF_SHORT_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_SHORT_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 5, new ComfoAirCommandType[] { MENU25_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 5 }),
RF_LONG_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_LONG_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 6, new ComfoAirCommandType[] { MENU26_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 6 }),
COOKERHOOD_DELAY(ComfoAirBindingConstants.CG_MENUP2_PREFIX + ComfoAirBindingConstants.CHANNEL_COOKERHOOD_DELAY,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_DELAYS, 8, 7, new ComfoAirCommandType[] { MENU20_MODE },
Constants.REQUEST_GET_DELAYS, Constants.REPLY_GET_DELAYS, new int[] { 7 }),
CHIMNEY_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_CHIMNEY_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x01),
BYPASS_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_BYPASS_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x02),
EWT_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_EWT_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x04),
HEATER_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_HEATER_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x08),
V_CONTROL_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_VCONTROL_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x10),
FROST_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_FROST_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x20),
COOKERHOOD_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_COOKERHOOD_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x40),
ENTHALPY_STATE(ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_ENTHALPY_STATE,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 8 },
0x80),
FAN_IN_PERCENT(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_PERCENT,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_FAN, Constants.REPLY_GET_FAN, new int[] { 0 }),
FAN_OUT_PERCENT(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_PERCENT,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_FAN, Constants.REPLY_GET_FAN, new int[] { 1 }),
FAN_IN_RPM(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_RPM,
DataTypeRPM.getInstance(), Constants.REQUEST_GET_FAN, Constants.REPLY_GET_FAN, new int[] { 2, 3 }),
FAN_OUT_RPM(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_RPM,
DataTypeRPM.getInstance(), Constants.REQUEST_GET_FAN, Constants.REPLY_GET_FAN, new int[] { 4, 5 }),
FAN_OUT_0(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_0,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 0,
new ComfoAirCommandType[] { FAN_OUT_PERCENT, FAN_OUT_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 0 }),
FAN_OUT_1(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_1,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 1,
new ComfoAirCommandType[] { FAN_OUT_PERCENT, FAN_OUT_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 1 }),
FAN_OUT_2(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_2,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 2,
new ComfoAirCommandType[] { FAN_OUT_PERCENT, FAN_OUT_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 2 }),
FAN_OUT_3(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_OUT_3,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 6,
new ComfoAirCommandType[] { FAN_OUT_PERCENT, FAN_OUT_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 10 }),
FAN_IN_0(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_0,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 3,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_IN_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 3 }),
FAN_IN_1(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_1,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 4,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_IN_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 4 }),
FAN_IN_2(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_2,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 5,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_IN_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 5 }),
FAN_IN_3(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_IN_3,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_FAN_LEVEL, 9, 7,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_IN_RPM }, Constants.REQUEST_GET_FAN_LEVEL,
Constants.REPLY_GET_FAN_LEVEL, new int[] { 11 }),
FAN_LEVEL(ComfoAirBindingConstants.CG_VENTILATION_PREFIX + ComfoAirBindingConstants.CHANNEL_FAN_LEVEL,
DataTypeNumber.getInstance(), new int[] { 0x01, 0x02, 0x03, 0x04 }, Constants.REQUEST_SET_LEVEL, 1, 0,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_IN_RPM, FAN_OUT_PERCENT, FAN_OUT_RPM },
Constants.REQUEST_GET_FAN_LEVEL, Constants.REPLY_GET_FAN_LEVEL, new int[] { 8 }),
LEVEL0_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_LEVEL0,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 0, 1, 2 }),
LEVEL1_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_LEVEL1,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 3, 4, 5 }),
LEVEL2_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_LEVEL2,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 6, 7, 8 }),
LEVEL3_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_LEVEL3,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS,
new int[] { 17, 18, 19 }),
FREEZE_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_FREEZE,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 9, 10 }),
PREHEATER_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_PREHEATER,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 11, 12 }),
BYPASS_TIME(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_BYPASS,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 13, 14 }),
BYPASS_FACTOR(ComfoAirBindingConstants.CG_BYPASS_PREFIX + ComfoAirBindingConstants.CHANNEL_BYPASS_FACTOR,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_BYPASS, Constants.REPLY_GET_BYPASS, new int[] { 2 }),
BYPASS_LEVEL(ComfoAirBindingConstants.CG_BYPASS_PREFIX + ComfoAirBindingConstants.CHANNEL_BYPASS_LEVEL,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_BYPASS, Constants.REPLY_GET_BYPASS, new int[] { 3 }),
BYPASS_CORRECTION(ComfoAirBindingConstants.CG_BYPASS_PREFIX + ComfoAirBindingConstants.CHANNEL_BYPASS_CORRECTION,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_BYPASS, Constants.REPLY_GET_BYPASS, new int[] { 4 }),
BYPASS_SUMMER(ComfoAirBindingConstants.CG_BYPASS_PREFIX + ComfoAirBindingConstants.CHANNEL_BYPASS_SUMMER,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_BYPASS, Constants.REPLY_GET_BYPASS, new int[] { 6 }),
ENTHALPY_TEMPERATURE(ComfoAirBindingConstants.CG_ENTHALPY_PREFIX + ComfoAirBindingConstants.CHANNEL_ENTHALPY_TEMP,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_SENSORS, Constants.REPLY_GET_SENSORS,
new int[] { 0 }),
ENTHALPY_HUMIDITY(ComfoAirBindingConstants.CG_ENTHALPY_PREFIX + ComfoAirBindingConstants.CHANNEL_ENTHALPY_HUMIDITY,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_SENSORS, Constants.REPLY_GET_SENSORS, new int[] { 1 }),
ENTHALPY_LEVEL(ComfoAirBindingConstants.CG_ENTHALPY_PREFIX + ComfoAirBindingConstants.CHANNEL_ENTHALPY_LEVEL,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_SENSORS, Constants.REPLY_GET_SENSORS, new int[] { 4 }),
ENTHALPY_TIME(ComfoAirBindingConstants.CG_ENTHALPY_PREFIX + ComfoAirBindingConstants.CHANNEL_ENTHALPY_TIME,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_SENSORS, Constants.REPLY_GET_SENSORS, new int[] { 5 }),
PREHEATER_VALVE(ComfoAirBindingConstants.CG_PREHEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_PREHEATER_VALVE,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_PREHEATER, Constants.REPLY_GET_PREHEATER,
new int[] { 0 }),
PREHEATER_FROST_PROTECT(
ComfoAirBindingConstants.CG_PREHEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_PREHEATER_FROST_PROTECT,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_PREHEATER, Constants.REPLY_GET_PREHEATER,
new int[] { 1 }),
PREHEATER_HEATING(ComfoAirBindingConstants.CG_PREHEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_PREHEATER_HEATING,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_PREHEATER, Constants.REPLY_GET_PREHEATER,
new int[] { 2 }),
PREHEATER_FROST_TIME(
ComfoAirBindingConstants.CG_PREHEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_PREHEATER_FROST_TIME,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_PREHEATER, Constants.REPLY_GET_PREHEATER,
new int[] { 3, 4 }),
PREHEATER_OPTION(ComfoAirBindingConstants.CG_PREHEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_PREHEATER_SAFETY,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_PREHEATER, Constants.REPLY_GET_PREHEATER,
new int[] { 5 }),
TARGET_TEMPERATURE(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_TARGET,
DataTypeTemperature.getInstance(), Constants.REQUEST_SET_TEMPS, 1, 0,
new ComfoAirCommandType[] { BYPASS_FACTOR, BYPASS_LEVEL, BYPASS_SUMMER }, Constants.REQUEST_GET_TEMPS,
Constants.REPLY_GET_TEMPS, new int[] { 0 }),
OUTDOOR_TEMPERATURE_IN(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_OUTDOOR_IN,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 1 }),
OUTDOOR_TEMPERATURE_OUT(
ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_OUTDOOR_OUT,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 4 }),
INDOOR_TEMPERATURE_IN(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_INDOOR_IN,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 2 }),
INDOOR_TEMPERATURE_OUT(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_INDOOR_OUT,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 3 }),
IS_T1_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_T1,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x01),
IS_T2_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_T2,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x02),
IS_T3_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_T3,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x04),
IS_T4_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_T4,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x08),
IS_EWT_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_EWT,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x10),
IS_HEATER_SENSOR(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_HEATER,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x20),
IS_COOKERHOOD_SENSOR(
ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_SENSOR_COOKERHOOD,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 5 },
0x40),
EWT_TEMPERATURE(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_EWT,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 6 }),
HEATER_TEMPERATURE(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_HEATER,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 7 }),
COOKERHOOD_TEMPERATURE(ComfoAirBindingConstants.CG_TEMPS_PREFIX + ComfoAirBindingConstants.CHANNEL_TEMP_COOKERHOOD,
DataTypeTemperature.getInstance(), Constants.REQUEST_GET_TEMPS, Constants.REPLY_GET_TEMPS, new int[] { 8 }),
EWT_SPEED(ComfoAirBindingConstants.CG_EWT_PREFIX + ComfoAirBindingConstants.CHANNEL_EWT_SPEED,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_EWT, 5, 2,
new ComfoAirCommandType[] { EWT_STATE, EWT_TEMPERATURE }, Constants.REQUEST_GET_EWT,
Constants.REPLY_GET_EWT, new int[] { 2 }),
EWT_TEMPERATURE_LOW(ComfoAirBindingConstants.CG_EWT_PREFIX + ComfoAirBindingConstants.CHANNEL_EWT_TEMP_LOW,
DataTypeTemperature.getInstance(), Constants.REQUEST_SET_EWT, 5, 0, new ComfoAirCommandType[] { EWT_STATE },
Constants.REQUEST_GET_EWT, Constants.REPLY_GET_EWT, new int[] { 0 }),
EWT_TEMPERATURE_HIGH(ComfoAirBindingConstants.CG_EWT_PREFIX + ComfoAirBindingConstants.CHANNEL_EWT_TEMP_HIGH,
DataTypeTemperature.getInstance(), Constants.REQUEST_SET_EWT, 5, 1, new ComfoAirCommandType[] { EWT_STATE },
Constants.REQUEST_GET_EWT, Constants.REPLY_GET_EWT, new int[] { 1 }),
COOKERHOOD_SPEED(ComfoAirBindingConstants.CG_COOKERHOOD_PREFIX + ComfoAirBindingConstants.CHANNEL_COOKERHOOD_SPEED,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_EWT, 5, 3,
new ComfoAirCommandType[] { COOKERHOOD_STATE, COOKERHOOD_TEMPERATURE }, Constants.REQUEST_GET_EWT,
Constants.REPLY_GET_EWT, new int[] { 3 }),
HEATER_POWER(ComfoAirBindingConstants.CG_HEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_HEATER_POWER,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_EWT, Constants.REPLY_GET_EWT, new int[] { 4 }),
HEATER_POWER_I(ComfoAirBindingConstants.CG_HEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_HEATER_POWER_I,
DataTypeNumber.getInstance(), Constants.REQUEST_GET_EWT, Constants.REPLY_GET_EWT, new int[] { 5 }),
HEATER_TARGET_TEMPERATURE(
ComfoAirBindingConstants.CG_HEATER_PREFIX + ComfoAirBindingConstants.CHANNEL_HEATER_TEMP_TARGET,
DataTypeTemperature.getInstance(), Constants.REQUEST_SET_EWT, 5, 4,
new ComfoAirCommandType[] { HEATER_STATE, HEATER_POWER, HEATER_TEMPERATURE }, Constants.REQUEST_GET_EWT,
Constants.REPLY_GET_EWT, new int[] { 6 }),
IS_PREHEATER(ComfoAirBindingConstants.PROPERTY_OPTION_PREHEATER, DataTypeBoolean.getInstance(),
Constants.REQUEST_SET_STATES, 8, 0,
new ComfoAirCommandType[] { OUTDOOR_TEMPERATURE_IN, INDOOR_TEMPERATURE_IN, PREHEATER_FROST_PROTECT,
PREHEATER_FROST_TIME, PREHEATER_HEATING, FROST_STATE, PREHEATER_OPTION, PREHEATER_TIME,
PREHEATER_VALVE },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 0 }),
IS_BYPASS(ComfoAirBindingConstants.PROPERTY_OPTION_BYPASS, DataTypeBoolean.getInstance(),
Constants.REQUEST_SET_STATES, 8, 1,
new ComfoAirCommandType[] { INDOOR_TEMPERATURE_IN, OUTDOOR_TEMPERATURE_OUT }, Constants.REQUEST_GET_STATES,
Constants.REPLY_GET_STATES, new int[] { 1 }),
RECU_TYPE(ComfoAirBindingConstants.PROPERTY_OPTION_RECU_TYPE, DataTypeNumber.getInstance(),
new int[] { 0x01, 0x02 }, Constants.REQUEST_SET_STATES, 8, 2,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_OUT_PERCENT, INDOOR_TEMPERATURE_IN, INDOOR_TEMPERATURE_OUT,
OUTDOOR_TEMPERATURE_IN, OUTDOOR_TEMPERATURE_OUT },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 2 }),
RECU_SIZE(ComfoAirBindingConstants.PROPERTY_OPTION_RECU_SIZE, DataTypeNumber.getInstance(),
new int[] { 0x01, 0x02 }, Constants.REQUEST_SET_STATES, 8, 3,
new ComfoAirCommandType[] { FAN_IN_PERCENT, FAN_OUT_PERCENT, FAN_IN_0, FAN_IN_1, FAN_IN_2, FAN_IN_3,
FAN_OUT_0, FAN_OUT_1, FAN_OUT_2, FAN_OUT_3 },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 3 }),
IS_CHIMNEY(ComfoAirBindingConstants.PROPERTY_OPTION_CHIMNEY, DataTypeBoolean.getInstance(), new int[] { 0x01 },
Constants.REQUEST_SET_STATES, 8, 4, Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_STATES,
Constants.REPLY_GET_STATES, new int[] { 4 }, 0x01),
IS_COOKERHOOD(ComfoAirBindingConstants.PROPERTY_OPTION_COOKERHOOD, DataTypeBoolean.getInstance(),
new int[] { 0x02 }, Constants.REQUEST_SET_STATES, 8, 4,
new ComfoAirCommandType[] { COOKERHOOD_DELAY, COOKERHOOD_STATE, COOKERHOOD_SPEED, COOKERHOOD_TEMPERATURE },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 4 }, 0x02),
IS_HEATER(ComfoAirBindingConstants.PROPERTY_OPTION_HEATER, DataTypeBoolean.getInstance(), new int[] { 0x04 },
Constants.REQUEST_SET_STATES, 8, 4,
new ComfoAirCommandType[] { HEATER_TARGET_TEMPERATURE, HEATER_POWER, HEATER_STATE, HEATER_POWER_I,
HEATER_TEMPERATURE },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 4 }, 0x04),
IS_ENTHALPY(ComfoAirBindingConstants.PROPERTY_OPTION_ENTHALPY, DataTypeNumber.getInstance(),
new int[] { 0x00, 0x01, 0x02 }, Constants.REQUEST_SET_STATES, 8, 6,
new ComfoAirCommandType[] { ENTHALPY_TEMPERATURE, ENTHALPY_HUMIDITY, ENTHALPY_LEVEL, ENTHALPY_STATE,
ENTHALPY_TIME },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 9 }),
IS_EWT(ComfoAirBindingConstants.PROPERTY_OPTION_EWT, DataTypeNumber.getInstance(), new int[] { 0x00, 0x01, 0x02 },
Constants.REQUEST_SET_STATES, 8, 7,
new ComfoAirCommandType[] { EWT_SPEED, EWT_TEMPERATURE_LOW, EWT_TEMPERATURE_HIGH, EWT_STATE,
EWT_TEMPERATURE },
Constants.REQUEST_GET_STATES, Constants.REPLY_GET_STATES, new int[] { 10 }),
SOFTWARE_MAIN_VERSION(ComfoAirBindingConstants.PROPERTY_SOFTWARE_MAIN_VERSION, DataTypeNumber.getInstance(),
Constants.REQUEST_GET_FIRMWARE, Constants.REPLY_GET_FIRMWARE, new int[] { 0 }),
SOFTWARE_MINOR_VERSION(ComfoAirBindingConstants.PROPERTY_SOFTWARE_MINOR_VERSION, DataTypeNumber.getInstance(),
Constants.REQUEST_GET_FIRMWARE, Constants.REPLY_GET_FIRMWARE, new int[] { 1 }),
DEVICE_NAME(ComfoAirBindingConstants.PROPERTY_DEVICE_NAME, DataTypeNumber.getInstance(),
Constants.REQUEST_GET_FIRMWARE, Constants.REPLY_GET_FIRMWARE,
new int[] { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }),
ERRORS_CURRENT(ComfoAirBindingConstants.CG_ERRORS_PREFIX + ComfoAirBindingConstants.CHANNEL_ERRORS_CURRENT,
DataTypeMessage.getInstance(), Constants.REQUEST_GET_ERRORS, Constants.REPLY_GET_ERRORS,
new int[] { 0, 1, 9, 13 }),
ERRORS_LAST(ComfoAirBindingConstants.CG_ERRORS_PREFIX + ComfoAirBindingConstants.CHANNEL_ERRORS_LAST,
DataTypeMessage.getInstance(), Constants.REQUEST_GET_ERRORS, Constants.REPLY_GET_ERRORS,
new int[] { 2, 3, 10, 14 }),
ERRORS_PRELAST(ComfoAirBindingConstants.CG_ERRORS_PREFIX + ComfoAirBindingConstants.CHANNEL_ERRORS_PRELAST,
DataTypeMessage.getInstance(), Constants.REQUEST_GET_ERRORS, Constants.REPLY_GET_ERRORS,
new int[] { 4, 5, 11, 15 }),
ERRORS_PREPRELAST(ComfoAirBindingConstants.CG_ERRORS_PREFIX + ComfoAirBindingConstants.CHANNEL_ERRORS_PREPRELAST,
DataTypeMessage.getInstance(), Constants.REQUEST_GET_ERRORS, Constants.REPLY_GET_ERRORS,
new int[] { 6, 7, 12, 16 }),
ERROR_RESET(ComfoAirBindingConstants.CG_RESETS_PREFIX + ComfoAirBindingConstants.CHANNEL_ERROR_RESET,
DataTypeNumber.getInstance(), new int[] { 0x01 }, Constants.REQUEST_SET_RESETS, 4, 0,
new ComfoAirCommandType[] { ERRORS_CURRENT, ERRORS_LAST, ERRORS_PRELAST, ERRORS_PREPRELAST }),
FILTER_ERROR(ComfoAirBindingConstants.CG_ERRORS_PREFIX + ComfoAirBindingConstants.CHANNEL_FILTER_ERROR,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_ERRORS, Constants.REPLY_GET_ERRORS, new int[] { 8 },
0x01),
FILTER_HOURS(ComfoAirBindingConstants.CG_TIMES_PREFIX + ComfoAirBindingConstants.CHANNEL_TIME_FILTER,
DataTypeTime.getInstance(), Constants.REQUEST_GET_HOURS, Constants.REPLY_GET_HOURS, new int[] { 15, 16 }),
FILTER_RESET(ComfoAirBindingConstants.CG_RESETS_PREFIX + ComfoAirBindingConstants.CHANNEL_FILTER_RESET,
DataTypeNumber.getInstance(), new int[] { 0x01 }, Constants.REQUEST_SET_RESETS, 4, 3,
new ComfoAirCommandType[] { FILTER_HOURS, FILTER_ERROR }),
ANALOG1_NEGATIVE(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_NEGATIVE,
DataTypeBoolean.getInstance(), new int[] { 0x01 }, Constants.REQUEST_SET_ANALOGS, 19, 2,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 2 },
0x01),
ANALOG2_NEGATIVE(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_NEGATIVE,
DataTypeBoolean.getInstance(), new int[] { 0x02 }, Constants.REQUEST_SET_ANALOGS, 19, 2,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 2 },
0x02),
ANALOG3_NEGATIVE(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_NEGATIVE,
DataTypeBoolean.getInstance(), new int[] { 0x04 }, Constants.REQUEST_SET_ANALOGS, 19, 2,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 2 },
0x04),
ANALOG4_NEGATIVE(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_NEGATIVE,
DataTypeBoolean.getInstance(), new int[] { 0x08 }, Constants.REQUEST_SET_ANALOGS, 19, 2,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 2 },
0x08),
RF_NEGATIVE(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_NEGATIVE,
DataTypeBoolean.getInstance(), new int[] { 0x10 }, Constants.REQUEST_SET_ANALOGS, 19, 2,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 2 },
0x10),
ANALOG1_MIN(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MIN,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 3, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 3 }),
ANALOG1_MAX(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MAX,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 4, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 4 }),
ANALOG1_VALUE(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VALUE,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 5, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 5 }),
ANALOG2_MIN(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MIN,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 6, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 6 }),
ANALOG2_MAX(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MAX,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 7, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 7 }),
ANALOG2_VALUE(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VALUE,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 8, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 8 }),
ANALOG3_MIN(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MIN,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 9, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 9 }),
ANALOG3_MAX(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MAX,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 10, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 10 }),
ANALOG3_VALUE(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VALUE,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 11, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 11 }),
ANALOG4_MIN(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MIN,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 12, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 12 }),
ANALOG4_MAX(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MAX,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 13, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 13 }),
ANALOG4_VALUE(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VALUE,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 14, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 14 }),
RF_MIN(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_MIN,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 15, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 15 }),
RF_MAX(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_MAX,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 16, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 16 }),
RF_VALUE(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_VALUE,
DataTypeNumber.getInstance(), Constants.REQUEST_SET_ANALOGS, 19, 17, Constants.EMPTY_TYPE_ARRAY,
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 17 }),
ANALOG_MODE(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_PRIORITY,
DataTypeNumber.getInstance(), new int[] { 0x00, 0x01 }, Constants.REQUEST_SET_ANALOGS, 19, 18,
Constants.EMPTY_TYPE_ARRAY, Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 18 }),
ANALOG1_VOLT(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VOLT,
DataTypeVolt.getInstance(), Constants.REQUEST_GET_ANALOG_VOLTS, Constants.REPLY_GET_ANALOG_VOLTS,
new int[] { 0 }),
ANALOG2_VOLT(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VOLT,
DataTypeVolt.getInstance(), Constants.REQUEST_GET_ANALOG_VOLTS, Constants.REPLY_GET_ANALOG_VOLTS,
new int[] { 1 }),
ANALOG3_VOLT(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VOLT,
DataTypeVolt.getInstance(), Constants.REQUEST_GET_ANALOG_VOLTS, Constants.REPLY_GET_ANALOG_VOLTS,
new int[] { 2 }),
ANALOG4_VOLT(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_VOLT,
DataTypeVolt.getInstance(), Constants.REQUEST_GET_ANALOG_VOLTS, Constants.REPLY_GET_ANALOG_VOLTS,
new int[] { 3 }),
ANALOG1_MODE(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MODE,
DataTypeBoolean.getInstance(), new int[] { 0x01 }, Constants.REQUEST_SET_ANALOGS, 19, 1,
new ComfoAirCommandType[] { ANALOG1_NEGATIVE, ANALOG1_MIN, ANALOG1_MAX, ANALOG1_VALUE, ANALOG1_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 1 }, 0x01),
ANALOG2_MODE(ComfoAirBindingConstants.CG_ANALOG2_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MODE,
DataTypeBoolean.getInstance(), new int[] { 0x02 }, Constants.REQUEST_SET_ANALOGS, 19, 1,
new ComfoAirCommandType[] { ANALOG2_NEGATIVE, ANALOG2_MIN, ANALOG2_MAX, ANALOG2_VALUE, ANALOG2_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 1 }, 0x02),
ANALOG3_MODE(ComfoAirBindingConstants.CG_ANALOG3_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MODE,
DataTypeBoolean.getInstance(), new int[] { 0x04 }, Constants.REQUEST_SET_ANALOGS, 19, 1,
new ComfoAirCommandType[] { ANALOG3_NEGATIVE, ANALOG3_MIN, ANALOG3_MAX, ANALOG3_VALUE, ANALOG3_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 1 }, 0x04),
ANALOG4_MODE(ComfoAirBindingConstants.CG_ANALOG4_PREFIX + ComfoAirBindingConstants.CHANNEL_ANALOG_MODE,
DataTypeBoolean.getInstance(), new int[] { 0x08 }, Constants.REQUEST_SET_ANALOGS, 19, 1,
new ComfoAirCommandType[] { ANALOG4_NEGATIVE, ANALOG4_MIN, ANALOG4_MAX, ANALOG4_VALUE, ANALOG4_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 1 }, 0x08),
RF_MODE(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_RF_MODE,
DataTypeBoolean.getInstance(), new int[] { 0x10 }, Constants.REQUEST_SET_ANALOGS, 19, 1,
new ComfoAirCommandType[] { RF_NEGATIVE, RF_MIN, RF_MAX, RF_VALUE }, Constants.REQUEST_GET_ANALOGS,
Constants.REPLY_GET_ANALOGS, new int[] { 1 }, 0x10),
IS_L1_SWITCH(ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_L1_SWITCH,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 0 },
0x01),
IS_L2_SWITCH(ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_L2_SWITCH,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 0 },
0x02),
IS_BATHROOM_SWITCH(ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_BATHROOM_SWITCH,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 1 },
0x01),
IS_COOKERHOOD_SWITCH(
ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_COOKERHOOD_SWITCH,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 1 },
0x02),
IS_EXTERNAL_FILTER(ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_EXTERNAL_FILTER,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 1 },
0x04),
IS_WTW(ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_WTW,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 1 },
0x08),
IS_BATHROOM2_SWITCH(
ComfoAirBindingConstants.CG_INPUTS_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_BATHROOM2_SWITCH,
DataTypeBoolean.getInstance(), Constants.REQUEST_GET_INPUTS, Constants.REPLY_GET_INPUTS, new int[] { 1 },
0x10),
IS_ANALOG1(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_ANALOG,
DataTypeBoolean.getInstance(), new int[] { 0x01 }, Constants.REQUEST_SET_ANALOGS, 19, 0,
new ComfoAirCommandType[] { ANALOG1_MODE, ANALOG1_NEGATIVE, ANALOG1_MIN, ANALOG1_MAX, ANALOG1_VALUE,
ANALOG1_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 0 }, 0x01),
IS_ANALOG2(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_ANALOG,
DataTypeBoolean.getInstance(), new int[] { 0x02 }, Constants.REQUEST_SET_ANALOGS, 19, 0,
new ComfoAirCommandType[] { ANALOG2_MODE, ANALOG2_NEGATIVE, ANALOG2_MIN, ANALOG2_MAX, ANALOG2_VALUE,
ANALOG2_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 0 }, 0x02),
IS_ANALOG3(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_ANALOG,
DataTypeBoolean.getInstance(), new int[] { 0x04 }, Constants.REQUEST_SET_ANALOGS, 19, 0,
new ComfoAirCommandType[] { ANALOG3_MODE, ANALOG3_NEGATIVE, ANALOG3_MIN, ANALOG3_MAX, ANALOG3_VALUE,
ANALOG3_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 0 }, 0x04),
IS_ANALOG4(ComfoAirBindingConstants.CG_ANALOG1_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_ANALOG,
DataTypeBoolean.getInstance(), new int[] { 0x08 }, Constants.REQUEST_SET_ANALOGS, 19, 0,
new ComfoAirCommandType[] { ANALOG4_MODE, ANALOG4_NEGATIVE, ANALOG4_MIN, ANALOG4_MAX, ANALOG4_VALUE,
ANALOG4_VOLT },
Constants.REQUEST_GET_ANALOGS, Constants.REPLY_GET_ANALOGS, new int[] { 0 }, 0x08),
IS_RF(ComfoAirBindingConstants.CG_ANALOGRF_PREFIX + ComfoAirBindingConstants.CHANNEL_IS_RF,
DataTypeBoolean.getInstance(), new int[] { 0x10 }, Constants.REQUEST_SET_ANALOGS, 19, 0,
new ComfoAirCommandType[] { RF_MODE, RF_NEGATIVE, RF_MIN, RF_MAX, RF_VALUE }, Constants.REQUEST_GET_ANALOGS,
Constants.REPLY_GET_ANALOGS, new int[] { 0 }, 0x10);
private final String key;
private final ComfoAirDataType dataType;
/*
* Possible values
*/
private final int @Nullable [] possibleValues;
/*
* Cmd code to change properties on the comfoair.
*/
private final int changeCommand;
/*
* The size of the data block.
*/
private final int changeDataSize;
/*
* The byte inside the data block which holds the crucial value.
*/
private final int changeDataPos;
/*
* Affected commands which should be refreshed after a successful change
* command call.
*/
private final ComfoAirCommandType @Nullable [] changeAffected;
/*
* Command for reading properties.
*/
private final int readCommand;
/*
* ACK Command which identifies the matching response.
*/
private final int readReplyCommand;
/*
* The byte position inside the response data.
*/
private final int @Nullable [] readReplyDataPos;
/*
* Bit mask for boolean response properties to identify a true value.
*/
private final int readReplyDataBits;
/*
* Constructor for full read/write command
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int @Nullable [] possibleValues,
int changeCommand, int changeDataSize, int changeDataPos, ComfoAirCommandType @Nullable [] changeAffected,
int readCommand, int readReplyCommand, int @Nullable [] readReplyDataPos, int readReplyDataBits) {
this.key = key;
this.dataType = dataType;
this.possibleValues = possibleValues;
this.changeCommand = changeCommand;
this.changeDataSize = changeDataSize;
this.changeDataPos = changeDataPos;
this.changeAffected = changeAffected;
this.readCommand = readCommand;
this.readReplyCommand = readReplyCommand;
this.readReplyDataPos = readReplyDataPos;
this.readReplyDataBits = readReplyDataBits;
}
/*
* Constructor for read/write command w/o predefined readReplyDataBits
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int[] possibleValues, int changeCommand,
int changeDataSize, int changeDataPos, ComfoAirCommandType[] changeAffected, int readCommand,
int readReplyCommand, int[] readReplyDataPos) {
this(key, dataType, possibleValues, changeCommand, changeDataSize, changeDataPos, changeAffected, readCommand,
readReplyCommand, readReplyDataPos, 0);
}
/*
* Constructor for read/write command w/o predefined readReplyDataBits & possibleValues
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int changeCommand, int changeDataSize,
int changeDataPos, ComfoAirCommandType[] changeAffected, int readCommand, int readReplyCommand,
int[] readReplyDataPos) {
this(key, dataType, null, changeCommand, changeDataSize, changeDataPos, changeAffected, readCommand,
readReplyCommand, readReplyDataPos, 0);
}
/*
* Constructor for write-only command (reset)
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int[] possibleValues, int changeCommand,
int changeDataSize, int changeDataPos, ComfoAirCommandType[] changeAffected) {
this(key, dataType, possibleValues, changeCommand, changeDataSize, changeDataPos, changeAffected, 0, 0, null,
0);
}
/*
* Constructor for read-only command
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int readCommand, int readReplyCommand,
int[] readReplyDataPos, int readReplyDataBits) {
this(key, dataType, null, 0, 0, 0, null, readCommand, readReplyCommand, readReplyDataPos, readReplyDataBits);
}
/*
* Constructor for read-only command w/o readReplyDataBits
*/
private ComfoAirCommandType(String key, ComfoAirDataType dataType, int readCommand, int readReplyCommand,
int[] readReplyDataPos) {
this(key, dataType, null, 0, 0, 0, null, readCommand, readReplyCommand, readReplyDataPos, 0);
}
public static class Constants {
public static final int REQUEST_GET_INPUTS = 0x03;
public static final int REPLY_GET_INPUTS = 0x04;
public static final int REQUEST_GET_FAN = 0x0b;
public static final int REPLY_GET_FAN = 0x0c;
public static final int REQUEST_GET_ANALOG_VOLTS = 0x13;
public static final int REPLY_GET_ANALOG_VOLTS = 0x14;
public static final int REQUEST_GET_FIRMWARE = 0x69;
public static final int REPLY_GET_FIRMWARE = 0x6a;
public static final int REQUEST_GET_SENSORS = 0x97;
public static final int REPLY_GET_SENSORS = 0x98;
public static final int REQUEST_SET_LEVEL = 0x99;
public static final int REQUEST_SET_RS232 = 0x9b;
public static final int REPLY_SET_RS232 = 0x9c;
public static final int REQUEST_GET_ANALOGS = 0x9d;
public static final int REPLY_GET_ANALOGS = 0x9e;
public static final int REQUEST_SET_ANALOGS = 0x9f;
public static final int REQUEST_GET_DELAYS = 0xc9;
public static final int REPLY_GET_DELAYS = 0xca;
public static final int REQUEST_SET_DELAYS = 0xcb;
public static final int REQUEST_GET_FAN_LEVEL = 0xcd;
public static final int REPLY_GET_FAN_LEVEL = 0xce;
public static final int REQUEST_SET_FAN_LEVEL = 0xcf;
public static final int REQUEST_GET_TEMPS = 0xd1;
public static final int REPLY_GET_TEMPS = 0xd2;
public static final int REQUEST_SET_TEMPS = 0xd3;
public static final int REQUEST_GET_STATES = 0xd5;
public static final int REPLY_GET_STATES = 0xd6;
public static final int REQUEST_SET_STATES = 0xd7;
public static final int REQUEST_GET_ERRORS = 0xd9;
public static final int REPLY_GET_ERRORS = 0xda;
public static final int REQUEST_SET_RESETS = 0xdb;
public static final int REQUEST_GET_HOURS = 0xdd;
public static final int REPLY_GET_HOURS = 0xde;
public static final int REQUEST_GET_BYPASS = 0xdf;
public static final int REPLY_GET_BYPASS = 0xe0;
public static final int REQUEST_GET_PREHEATER = 0xe1;
public static final int REPLY_GET_PREHEATER = 0xe2;
public static final int REQUEST_GET_RF = 0xe5;
public static final int REPLY_GET_RF = 0xe6;
public static final int REQUEST_GET_EWT = 0xeb;
public static final int REPLY_GET_EWT = 0xec;
public static final int REQUEST_SET_EWT = 0xed;
public static final String[] EMPTY_STRING_ARRAY = new String[0];
public static final int[] EMPTY_INT_ARRAY = new int[0];
public static final ComfoAirCommandType[] EMPTY_TYPE_ARRAY = new ComfoAirCommandType[0];
}
/**
* @return command key
*/
public String getKey() {
return key;
}
/**
* @return data type for this command key
*/
public ComfoAirDataType getDataType() {
return dataType;
}
/**
* @return readCommand for this command key
*/
public int getReadCommand() {
return readCommand;
}
/**
* @return readCommand for this command key
*/
public int getReadReplyCommand() {
return readReplyCommand;
}
/**
* @return possible byte values
*/
public int @Nullable [] getPossibleValues() {
return possibleValues;
}
/**
* @return relevant byte position inside the response byte value array
*/
public int getChangeDataPos() {
return changeDataPos;
}
/**
* @return generate a byte value sequence for the response stream
*/
public int[] getChangeDataTemplate() {
int[] template = new int[changeDataSize];
for (int i = 0; i < template.length; i++) {
template[i] = 0x00;
}
return template;
}
/**
* @return byte position inside the request byte value array
*/
public int @Nullable [] getReadReplyDataPos() {
return readReplyDataPos;
}
/**
* @return bit mask for the response byte value
*/
public int getReadReplyDataBits() {
return readReplyDataBits;
}
/**
* Get single read command to update item.
*
* @param key
*
* @return ComfoAirCommand identified by key
*/
public static @Nullable ComfoAirCommand getReadCommand(String key) {
ComfoAirCommandType commandType = getCommandTypeByKey(key);
if (commandType != null) {
return new ComfoAirCommand(key);
}
return null;
}
/**
* Get a command to change properties on the comfoair.
*
* @param key
* command key
* @param value
* new state
* @return initialized ComfoAirCommand
*/
public static @Nullable ComfoAirCommand getChangeCommand(String key, Command command) {
ComfoAirCommandType commandType = getCommandTypeByKey(key);
State value = UnDefType.NULL;
if (commandType != null) {
ComfoAirDataType dataType = commandType.getDataType();
if (dataType == DataTypeBoolean.getInstance() || dataType == DataTypeNumber.getInstance()
|| dataType == DataTypeRPM.getInstance() || command instanceof QuantityType<?>) {
value = (State) command;
}
if (value instanceof UnDefType) {
return null;
} else {
int[] data = dataType.convertFromState(value, commandType);
DecimalType decimalValue = value.as(DecimalType.class);
if (decimalValue != null) {
int intValue = decimalValue.intValue();
if (data != null) {
int dataPosition = commandType.getChangeDataPos();
return new ComfoAirCommand(key, commandType.changeCommand, null, data, dataPosition, intValue);
}
}
}
}
return null;
}
/**
* Get all commands which should be refreshed after a successful change
* command.
*
* @param key
* command key
* @param usedKeys
* @return ComfoAirCommand's which should be updated after a modifying
* ComfoAirCommand named by key
*/
public static Collection<ComfoAirCommand> getAffectedReadCommands(String key, Set<String> usedKeys) {
Map<Integer, ComfoAirCommand> commands = new HashMap<>();
ComfoAirCommandType commandType = getCommandTypeByKey(key);
if (commandType != null) {
uniteCommandsMap(commands, commandType);
if (commandType.changeAffected != null) {
for (ComfoAirCommandType affectedCommandType : commandType.changeAffected) {
// refresh affected event keys only when they are used
if (usedKeys.contains(affectedCommandType.getKey())) {
uniteCommandsMap(commands, affectedCommandType);
}
}
}
}
return commands.values();
}
/**
* Get all commands which receive informations to update items.
*
* @return all ComfoAirCommand's identified by keys
*/
public static Collection<ComfoAirCommand> getReadCommandsByEventTypes(List<String> keys) {
Map<Integer, ComfoAirCommand> commands = new HashMap<>();
for (ComfoAirCommandType entry : values()) {
if (keys.contains(entry.key)) {
uniteCommandsMap(commands, entry);
}
}
return commands.values();
}
/**
* Get commandtypes which matches the replyCmd.
*
* @param replyCmd
* reply command byte value
* @return ComfoAirCommandType identified by replyCmd
*/
public static List<ComfoAirCommandType> getCommandTypesByReplyCmd(int replyCmd) {
List<ComfoAirCommandType> commands = new ArrayList<>();
for (ComfoAirCommandType entry : values()) {
if (entry.readReplyCommand == replyCmd) {
commands.add(entry);
}
}
return commands;
}
/**
* Get a specific command.
*
* @param key
* command key
* @return ComfoAirCommandType identified by key
*/
public static @Nullable ComfoAirCommandType getCommandTypeByKey(String key) {
for (ComfoAirCommandType entry : values()) {
if (entry.key.equals(key)) {
return entry;
}
}
return null;
}
@SuppressWarnings("null")
private static void uniteCommandsMap(Map<Integer, ComfoAirCommand> commands, ComfoAirCommandType commandType) {
if (commandType.readReplyCommand != 0) {
int replyCmd = commandType.readReplyCommand;
ComfoAirCommand command = commands.get(replyCmd);
if (command == null) {
command = new ComfoAirCommand(commandType.key);
commands.put(replyCmd, command);
} else {
command.addKey(commandType.key);
}
}
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link ComfoAirConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Hans Böhm - Initial contribution
*/
@NonNullByDefault
public class ComfoAirConfiguration {
/**
* Serial port used for communication.
*/
public String serialPort = "";
/**
* Polling interval for state refresh.
*/
public int refreshInterval;
}

View File

@@ -0,0 +1,438 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.datatypes.ComfoAirDataType;
import org.openhab.core.io.transport.serial.SerialPortManager;
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.builder.ThingBuilder;
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;
/**
* The {@link ComfoAirHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Hans Böhm - Initial contribution
*/
@NonNullByDefault
public class ComfoAirHandler extends BaseThingHandler {
private static final int DEFAULT_REFRESH_INTERVAL_SEC = 60;
private final Logger logger = LoggerFactory.getLogger(ComfoAirHandler.class);
private final ComfoAirConfiguration config = getConfigAs(ComfoAirConfiguration.class);
private final SerialPortManager serialPortManager;
private @Nullable ScheduledFuture<?> poller;
private @Nullable ScheduledFuture<?> affectedItemsPoller;
private @Nullable ComfoAirSerialConnector comfoAirConnector;
public static final int BAUDRATE = 9600;
public ComfoAirHandler(Thing thing, final SerialPortManager serialPortManager) {
super(thing);
this.serialPortManager = serialPortManager;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
String channelId = channelUID.getId();
if (command instanceof RefreshType) {
Channel channel = this.thing.getChannel(channelUID);
if (channel != null) {
updateChannelState(channel);
}
} else {
ComfoAirCommand changeCommand = ComfoAirCommandType.getChangeCommand(channelId, command);
if (changeCommand != null) {
Set<String> keysToUpdate = getThing().getChannels().stream().map(Channel::getUID).filter(this::isLinked)
.map(ChannelUID::getId).collect(Collectors.toSet());
sendCommand(changeCommand, channelId);
Collection<ComfoAirCommand> affectedReadCommands = ComfoAirCommandType
.getAffectedReadCommands(channelId, keysToUpdate);
if (affectedReadCommands.size() > 0) {
Runnable updateThread = new AffectedItemsUpdateThread(affectedReadCommands);
affectedItemsPoller = scheduler.schedule(updateThread, 3, TimeUnit.SECONDS);
}
} else {
logger.warn("Unhandled command type: {}, channelId: {}", command.toString(), channelId);
}
}
}
@Override
public void initialize() {
String serialPort = this.config.serialPort;
if (serialPort.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial port is not configured.");
return;
} else {
ComfoAirSerialConnector comfoAirConnector = new ComfoAirSerialConnector(serialPortManager, serialPort,
BAUDRATE);
this.comfoAirConnector = comfoAirConnector;
}
updateStatus(ThingStatus.UNKNOWN);
scheduler.submit(this::connect);
}
private void connect() {
if (comfoAirConnector != null) {
try {
comfoAirConnector.open();
if (comfoAirConnector != null) {
updateStatus(ThingStatus.ONLINE);
pullDeviceProperties();
Map<String, String> properties = thing.getProperties();
List<Channel> toBeRemovedChannels = new ArrayList<>();
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_PREHEATER)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing()
.getChannelsOfGroup(ComfoAirBindingConstants.CG_PREHEATER_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_FROST_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_BYPASS)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing()
.getChannelsOfGroup(ComfoAirBindingConstants.CG_BYPASS_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_BYPASS_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_CHIMNEY)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_CHIMNEY_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_COOKERHOOD)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing().getChannelsOfGroup(
ComfoAirBindingConstants.CG_COOKERHOOD_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_COOKERHOOD_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_HEATER)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing()
.getChannelsOfGroup(ComfoAirBindingConstants.CG_HEATER_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_HEATER_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_ENTHALPY)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing()
.getChannelsOfGroup(ComfoAirBindingConstants.CG_ENTHALPY_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(ComfoAirBindingConstants.CG_MENUP9_PREFIX
+ ComfoAirBindingConstants.CHANNEL_ENTHALPY_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
if (properties.get(ComfoAirBindingConstants.PROPERTY_OPTION_EWT)
.equals(ComfoAirBindingConstants.COMMON_OPTION_STATES[0])) {
toBeRemovedChannels.addAll(getThing()
.getChannelsOfGroup(ComfoAirBindingConstants.CG_EWT_PREFIX.replaceAll("#$", "")));
Channel stateChannel = getThing().getChannel(
ComfoAirBindingConstants.CG_MENUP9_PREFIX + ComfoAirBindingConstants.CHANNEL_EWT_STATE);
if (stateChannel != null) {
toBeRemovedChannels.add(stateChannel);
}
}
ThingBuilder builder = editThing().withoutChannels(toBeRemovedChannels);
updateThing(builder.build());
List<Channel> channels = this.thing.getChannels();
poller = scheduler.scheduleWithFixedDelay(() -> {
for (Channel channel : channels) {
updateChannelState(channel);
}
}, 0, (this.config.refreshInterval > 0) ? this.config.refreshInterval
: DEFAULT_REFRESH_INTERVAL_SEC, TimeUnit.SECONDS);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
} catch (ComfoAirSerialException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
}
@Override
public void dispose() {
if (comfoAirConnector != null) {
comfoAirConnector.close();
}
final ScheduledFuture<?> localPoller = poller;
if (localPoller != null) {
localPoller.cancel(true);
poller = null;
}
final ScheduledFuture<?> localAffectedItemsPoller = affectedItemsPoller;
if (localAffectedItemsPoller != null) {
localAffectedItemsPoller.cancel(true);
affectedItemsPoller = null;
}
}
private void updateChannelState(Channel channel) {
if (!isLinked(channel.getUID())) {
return;
}
String commandKey = channel.getUID().getId();
ComfoAirCommand readCommand = ComfoAirCommandType.getReadCommand(commandKey);
if (readCommand != null) {
scheduler.submit(() -> {
State state = sendCommand(readCommand, commandKey);
updateState(channel.getUID(), state);
});
}
}
private State sendCommand(ComfoAirCommand command, String commandKey) {
ComfoAirSerialConnector comfoAirConnector = this.comfoAirConnector;
if (comfoAirConnector != null) {
Integer requestCmd = command.getRequestCmd();
Integer replyCmd = command.getReplyCmd();
int[] requestData = command.getRequestData();
Integer preRequestCmd;
Integer preReplyCmd;
int[] preResponse = ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
if (requestCmd != null) {
switch (requestCmd) {
case ComfoAirCommandType.Constants.REQUEST_SET_ANALOGS:
preRequestCmd = ComfoAirCommandType.Constants.REQUEST_GET_ANALOGS;
preReplyCmd = ComfoAirCommandType.Constants.REPLY_GET_ANALOGS;
break;
case ComfoAirCommandType.Constants.REQUEST_SET_DELAYS:
preRequestCmd = ComfoAirCommandType.Constants.REQUEST_GET_DELAYS;
preReplyCmd = ComfoAirCommandType.Constants.REPLY_GET_DELAYS;
break;
case ComfoAirCommandType.Constants.REQUEST_SET_FAN_LEVEL:
preRequestCmd = ComfoAirCommandType.Constants.REQUEST_GET_FAN_LEVEL;
preReplyCmd = ComfoAirCommandType.Constants.REPLY_GET_FAN_LEVEL;
break;
case ComfoAirCommandType.Constants.REQUEST_SET_STATES:
preRequestCmd = ComfoAirCommandType.Constants.REQUEST_GET_STATES;
preReplyCmd = ComfoAirCommandType.Constants.REPLY_GET_STATES;
break;
case ComfoAirCommandType.Constants.REQUEST_SET_EWT:
preRequestCmd = ComfoAirCommandType.Constants.REQUEST_GET_EWT;
preReplyCmd = ComfoAirCommandType.Constants.REPLY_GET_EWT;
break;
default:
preRequestCmd = requestCmd;
preReplyCmd = replyCmd;
}
if (!preRequestCmd.equals(requestCmd)) {
command.setRequestCmd(preRequestCmd);
command.setReplyCmd(preReplyCmd);
command.setRequestData(ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
preResponse = comfoAirConnector.sendCommand(command, ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
if (preResponse.length <= 0) {
return UnDefType.NULL;
} else {
command.setRequestCmd(requestCmd);
command.setReplyCmd(replyCmd);
command.setRequestData(requestData);
}
}
int[] response = comfoAirConnector.sendCommand(command, preResponse);
if (response.length > 0) {
ComfoAirCommandType comfoAirCommandType = ComfoAirCommandType.getCommandTypeByKey(commandKey);
State value = UnDefType.UNDEF;
if (comfoAirCommandType != null) {
ComfoAirDataType dataType = comfoAirCommandType.getDataType();
value = dataType.convertToState(response, comfoAirCommandType);
}
if (value instanceof UnDefType) {
if (logger.isWarnEnabled()) {
logger.warn("unexpected value for DATA: {}", ComfoAirSerialConnector.dumpData(response));
}
}
return value;
}
}
}
return UnDefType.UNDEF;
}
public void pullDeviceProperties() {
Map<String, String> properties = editProperties();
ComfoAirSerialConnector comfoAirConnector = this.comfoAirConnector;
if (comfoAirConnector != null) {
String[] namedProperties = new String[] { ComfoAirBindingConstants.PROPERTY_SOFTWARE_MAIN_VERSION,
ComfoAirBindingConstants.PROPERTY_SOFTWARE_MINOR_VERSION,
ComfoAirBindingConstants.PROPERTY_DEVICE_NAME };
String[] optionProperties = new String[] { ComfoAirBindingConstants.PROPERTY_OPTION_PREHEATER,
ComfoAirBindingConstants.PROPERTY_OPTION_BYPASS, ComfoAirBindingConstants.PROPERTY_OPTION_RECU_TYPE,
ComfoAirBindingConstants.PROPERTY_OPTION_RECU_SIZE,
ComfoAirBindingConstants.PROPERTY_OPTION_CHIMNEY,
ComfoAirBindingConstants.PROPERTY_OPTION_COOKERHOOD,
ComfoAirBindingConstants.PROPERTY_OPTION_HEATER, ComfoAirBindingConstants.PROPERTY_OPTION_ENTHALPY,
ComfoAirBindingConstants.PROPERTY_OPTION_EWT };
for (String prop : namedProperties) {
ComfoAirCommand readCommand = ComfoAirCommandType.getReadCommand(prop);
if (readCommand != null) {
int[] response = comfoAirConnector.sendCommand(readCommand,
ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
if (response.length > 0) {
ComfoAirCommandType comfoAirCommandType = ComfoAirCommandType.getCommandTypeByKey(prop);
String value = "";
if (comfoAirCommandType != null) {
ComfoAirDataType dataType = comfoAirCommandType.getDataType();
if (prop.equals(ComfoAirBindingConstants.PROPERTY_DEVICE_NAME)) {
value = dataType.calculateStringValue(response, comfoAirCommandType);
} else {
value = String.valueOf(dataType.calculateNumberValue(response, comfoAirCommandType));
}
}
properties.put(prop, value);
}
}
}
ComfoAirCommand optionsReadCommand = new ComfoAirCommand(ComfoAirBindingConstants.PROPERTY_OPTION_PREHEATER,
ComfoAirCommandType.Constants.REQUEST_GET_STATES, ComfoAirCommandType.Constants.REPLY_GET_STATES,
ComfoAirCommandType.Constants.EMPTY_INT_ARRAY, null, null);
int[] response = comfoAirConnector.sendCommand(optionsReadCommand,
ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
if (response.length > 0) {
for (String prop : optionProperties) {
ComfoAirCommandType comfoAirCommandType = ComfoAirCommandType.getCommandTypeByKey(prop);
String value = "";
if (comfoAirCommandType != null) {
ComfoAirDataType dataType = comfoAirCommandType.getDataType();
int intValue = dataType.calculateNumberValue(response, comfoAirCommandType);
switch (prop) {
case ComfoAirBindingConstants.PROPERTY_OPTION_RECU_TYPE:
value = intValue == 1 ? "LEFT" : "RIGHT";
break;
case ComfoAirBindingConstants.PROPERTY_OPTION_RECU_SIZE:
value = intValue == 1 ? "BIG" : "SMALL";
break;
case ComfoAirBindingConstants.PROPERTY_OPTION_ENTHALPY:
if (intValue == 1) {
value = ComfoAirBindingConstants.COMMON_OPTION_STATES[1];
} else if (intValue == 2) {
value = "Installed w\\o sensor";
} else {
value = ComfoAirBindingConstants.COMMON_OPTION_STATES[0];
}
break;
case ComfoAirBindingConstants.PROPERTY_OPTION_EWT:
if (intValue == 1) {
value = "Regulated";
} else if (intValue == 2) {
value = "Unregulated";
} else {
value = ComfoAirBindingConstants.COMMON_OPTION_STATES[0];
}
break;
default:
value = intValue > 0 ? ComfoAirBindingConstants.COMMON_OPTION_STATES[1]
: ComfoAirBindingConstants.COMMON_OPTION_STATES[0];
break;
}
}
properties.put(prop, value);
}
}
thing.setProperties(properties);
}
}
private class AffectedItemsUpdateThread implements Runnable {
private Collection<ComfoAirCommand> affectedReadCommands;
public AffectedItemsUpdateThread(Collection<ComfoAirCommand> affectedReadCommands) {
this.affectedReadCommands = affectedReadCommands;
}
@Override
public void run() {
for (ComfoAirCommand readCommand : this.affectedReadCommands) {
Integer replyCmd = readCommand.getReplyCmd();
if (replyCmd != null) {
List<ComfoAirCommandType> commandTypes = ComfoAirCommandType.getCommandTypesByReplyCmd(replyCmd);
for (ComfoAirCommandType commandType : commandTypes) {
String commandKey = commandType.getKey();
sendCommand(readCommand, commandKey);
}
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link ComfoAirHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Hans Böhm - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.comfoair", service = ThingHandlerFactory.class)
public class ComfoAirHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.singleton(ComfoAirBindingConstants.THING_TYPE_COMFOAIR_GENERIC);
private @NonNullByDefault({}) SerialPortManager serialPortManager;
@Reference
protected void setSerialPortManager(final SerialPortManager serialPortManager) {
this.serialPortManager = serialPortManager;
}
protected void unsetSerialPortManager(final SerialPortManager serialPortManager) {
this.serialPortManager = null;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (ComfoAirBindingConstants.THING_TYPE_COMFOAIR_GENERIC.equals(thingTypeUID)) {
return new ComfoAirHandler(thing, serialPortManager);
}
return null;
}
}

View File

@@ -0,0 +1,563 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
import org.openhab.core.library.types.OnOffType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Connector class for serial communication with ComfoAir device
*
* @author Hans Böhm - Initial contribution
*
*/
@NonNullByDefault
public class ComfoAirSerialConnector {
private final Logger logger = LoggerFactory.getLogger(ComfoAirSerialConnector.class);
private static final byte CTRL = (byte) 0x07;
private static final byte[] START = { CTRL, (byte) 0xf0 };
private static final byte[] END = { CTRL, (byte) 0x0f };
private static final byte[] ACK = { CTRL, (byte) 0xf3 };
private static final int RS232_ENABLED_VALUE = 0x03;
private static final int RS232_DISABLED_VALUE = 0x00;
private boolean isSuspended = true;
private final String serialPortName;
private final int baudRate;
private final SerialPortManager serialPortManager;
private @Nullable SerialPort serialPort;
private @Nullable InputStream inputStream;
private @Nullable OutputStream outputStream;
public ComfoAirSerialConnector(final SerialPortManager serialPortManager, final String serialPortName,
final int baudRate) {
this.serialPortManager = serialPortManager;
this.serialPortName = serialPortName;
this.baudRate = baudRate;
}
/**
* Open serial port.
*
* @throws PortInUseException, UnsupportedCommOperationException, IOException
*/
public void open() throws ComfoAirSerialException {
logger.debug("open(): Opening ComfoAir connection");
try {
SerialPortIdentifier portIdentifier = serialPortManager.getIdentifier(serialPortName);
if (portIdentifier != null) {
SerialPort serialPort = portIdentifier.open(this.getClass().getName(), 3000);
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.enableReceiveThreshold(1);
serialPort.enableReceiveTimeout(1000);
serialPort.notifyOnDataAvailable(true);
this.serialPort = serialPort;
inputStream = new DataInputStream(new BufferedInputStream(serialPort.getInputStream()));
outputStream = serialPort.getOutputStream();
ComfoAirCommand command = ComfoAirCommandType.getChangeCommand(ComfoAirCommandType.ACTIVATE.getKey(),
OnOffType.ON);
if (command != null) {
sendCommand(command, ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
} else {
logger.debug("Failure while creating COMMAND: {}", command);
}
} else {
throw new ComfoAirSerialException("No such Port: " + serialPortName);
}
} catch (PortInUseException | UnsupportedCommOperationException | IOException e) {
throw new ComfoAirSerialException(e);
}
}
/**
* Close serial port.
*/
public void close() {
logger.debug("close(): Close ComfoAir connection");
SerialPort serialPort = this.serialPort;
if (serialPort != null) {
ComfoAirCommand command = ComfoAirCommandType.getChangeCommand(ComfoAirCommandType.ACTIVATE.getKey(),
OnOffType.OFF);
if (command != null) {
sendCommand(command, ComfoAirCommandType.Constants.EMPTY_INT_ARRAY);
} else {
logger.debug("Failure while creating COMMAND: {}", command);
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
logger.debug("Error while closing input stream: {}", e.getMessage());
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
logger.debug("Error while closing output stream: {}", e.getMessage());
}
}
serialPort.close();
}
}
/**
* Prepare a command for sending using the serial port.
*
* @param command
* @param preRequestData
* @return reply byte values
*/
public synchronized int[] sendCommand(ComfoAirCommand command, int[] preRequestData) {
Integer requestCmd = command.getRequestCmd();
int retry = 0;
if (requestCmd != null) {
// Switch support for app or ccease control
if (requestCmd == ComfoAirCommandType.Constants.REQUEST_SET_RS232) {
isSuspended = !isSuspended;
} else if (requestCmd == ComfoAirCommandType.Constants.REPLY_SET_RS232) {
return new int[] { isSuspended ? RS232_DISABLED_VALUE : RS232_ENABLED_VALUE };
} else if (isSuspended) {
logger.trace("Ignore cmd. Service is currently suspended");
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
do {
// If preRequestData param was send (preRequestData is sending for write command)
int[] requestData;
if (preRequestData.length <= 0) {
requestData = command.getRequestData();
} else {
requestData = buildRequestData(command, preRequestData);
if (requestData.length <= 0) {
logger.debug("Unable to build data for write command: {}",
String.format("%02x", command.getReplyCmd()));
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
}
byte[] requestBlock = calculateRequest(requestCmd, requestData);
if (logger.isTraceEnabled()) {
logger.trace("send DATA: {}", dumpData(requestBlock));
}
if (!send(requestBlock)) {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
byte[] responseBlock = new byte[0];
try {
InputStream inputStream = this.inputStream;
// 31 is max. response length
byte[] readBuffer = new byte[31];
do {
while (inputStream != null && inputStream.available() > 0) {
int bytes = inputStream.read(readBuffer);
// merge bytes
byte[] mergedBytes = new byte[responseBlock.length + bytes];
System.arraycopy(responseBlock, 0, mergedBytes, 0, responseBlock.length);
System.arraycopy(readBuffer, 0, mergedBytes, responseBlock.length, bytes);
responseBlock = mergedBytes;
}
try {
// add wait states around reading the stream, so that
// interrupted transmissions are merged
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Transmission was interrupted: {}", e.getMessage());
throw new RuntimeException(e);
}
} while (inputStream != null && inputStream.available() > 0);
// check for ACK
if (responseBlock.length >= 2 && responseBlock[0] == ACK[0] && responseBlock[1] == ACK[1]) {
if (command.getReplyCmd() == null) {
// confirm additional data with an ACK
if (responseBlock.length > 2) {
send(ACK);
}
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
// check for start and end sequence and if the response cmd
// matches
// 11 is the minimum response length with one data byte
if (responseBlock.length >= 11 && responseBlock[2] == START[0] && responseBlock[3] == START[1]
&& responseBlock[responseBlock.length - 2] == END[0]
&& responseBlock[responseBlock.length - 1] == END[1]
&& (responseBlock[5] & 0xff) == command.getReplyCmd()) {
if (logger.isTraceEnabled()) {
logger.trace("receive RAW DATA: {}", dumpData(responseBlock));
}
byte[] cleanedBlock = cleanupBlock(responseBlock);
int dataSize = cleanedBlock[2];
// the cleanedBlock size should equal dataSize + 2 cmd
// bytes and + 1 checksum byte
if (dataSize + 3 == cleanedBlock.length - 1) {
byte checksum = cleanedBlock[dataSize + 3];
int[] replyData = new int[dataSize];
for (int i = 0; i < dataSize; i++) {
replyData[i] = cleanedBlock[i + 3] & 0xff;
}
byte[] block = Arrays.copyOf(cleanedBlock, 3 + dataSize);
// validate calculated checksum against submitted
// checksum
if (calculateChecksum(block) == checksum) {
if (logger.isTraceEnabled()) {
logger.trace("receive CMD: {} DATA: {}",
String.format("%02x", command.getReplyCmd()), dumpData(replyData));
}
send(ACK);
return replyData;
}
logger.debug("Unable to handle data. Checksum verification failed");
} else {
logger.debug("Unable to handle data. Data size not valid");
}
if (logger.isTraceEnabled()) {
logger.trace("skip CMD: {} DATA: {}", String.format("%02x", command.getReplyCmd()),
dumpData(cleanedBlock));
}
}
}
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
logger.warn("Transmission was interrupted: {}", e.getMessage());
throw new RuntimeException(e);
} catch (IOException e) {
logger.debug("IO error: {}", e.getMessage());
}
try {
Thread.sleep(1000);
if (logger.isDebugEnabled()) {
logger.debug("Retry cmd. Last call was not successful. Request: {} Response: {}",
dumpData(requestBlock), (responseBlock.length > 0 ? dumpData(responseBlock) : "null"));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Transmission was interrupted: {}", e.getMessage());
throw new RuntimeException(e);
}
} while (retry++ < 5);
if (retry == 5) {
logger.debug("Unable to send command. {} retries failed.", retry);
}
}
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
/**
* Generate the byte sequence for sending to ComfoAir (incl. START & END
* sequence and checksum).
*
* @param command
* @param requestData
* @return response byte value block with cmd, data and checksum
*/
private byte[] calculateRequest(int command, int[] requestData) {
// generate the command block (cmd and request data)
int length = requestData.length;
byte[] block = new byte[4 + length];
block[0] = 0x00;
block[1] = (byte) command;
block[2] = (byte) length;
if (requestData.length > 0) {
for (int i = 0; i < requestData.length; i++) {
block[i + 3] = (byte) requestData[i];
}
}
// calculate checksum for command block
byte checksum = calculateChecksum(block);
block[block.length - 1] = checksum;
// escape the command block with checksum included
block = escapeBlock(block);
byte[] request = new byte[4 + block.length];
request[0] = START[0];
request[1] = START[1];
System.arraycopy(block, 0, request, 2, block.length);
request[request.length - 2] = END[0];
request[request.length - 1] = END[1];
return request;
}
/**
* Calculates a checksum for a command block (cmd, data and checksum).
*
* @param block
* @return checksum byte value
*/
private byte calculateChecksum(byte[] block) {
int datasum = 0;
for (int i = 0; i < block.length; i++) {
datasum += block[i];
}
datasum += 173;
return (byte) (datasum & 0xFF);
}
/**
* Cleanup a commandblock from quoted 0x07 characters.
*
* @param processBuffer
* @return the 0x07 cleaned byte values
*/
private byte[] cleanupBlock(byte[] processBuffer) {
int pos = 0;
byte[] cleanedBuffer = new byte[processBuffer.length];
for (int i = 4; i < processBuffer.length - 2; i++) {
if (CTRL == processBuffer[i] && CTRL == processBuffer[i + 1]) {
i++;
}
cleanedBuffer[pos] = processBuffer[i];
pos++;
}
return Arrays.copyOf(cleanedBuffer, pos);
}
/**
* Escape special 0x07 character.
*
* @param cleanedBuffer
* @return escaped byte value array
*/
private byte[] escapeBlock(byte[] cleanedBuffer) {
int pos = 0;
byte[] processBuffer = new byte[50];
for (int i = 0; i < cleanedBuffer.length; i++) {
if (CTRL == cleanedBuffer[i]) {
processBuffer[pos] = CTRL;
pos++;
}
processBuffer[pos] = cleanedBuffer[i];
pos++;
}
return Arrays.copyOf(processBuffer, pos);
}
/**
* Send the byte values.
*
* @param request
* @return successful flag
*/
private boolean send(byte[] request) {
if (logger.isTraceEnabled()) {
logger.trace("send DATA: {}", dumpData(request));
}
try {
if (outputStream != null) {
outputStream.write(request);
}
return true;
} catch (IOException e) {
logger.debug("Error writing to serial port {}: {}", serialPortName, e.getLocalizedMessage());
return false;
}
}
/**
* Is used to debug byte values.
*
* @param replyData
* @return
*/
public static String dumpData(int[] replyData) {
StringBuilder sb = new StringBuilder();
for (int ch : replyData) {
sb.append(String.format(" %02x", ch));
}
return sb.toString();
}
private String dumpData(byte[] data) {
StringBuilder sb = new StringBuilder();
for (byte ch : data) {
sb.append(String.format(" %02x", ch));
}
return sb.toString();
}
/**
* Build request data based on reply data
*
* @param command
* @param preRequestData
* @return new build int values array
*/
private int[] buildRequestData(ComfoAirCommand command, int[] preRequestData) {
int[] newRequestData;
Integer requestCmd = command.getRequestCmd();
Integer dataPosition = command.getDataPosition();
Integer requestValue = command.getRequestValue();
if (requestCmd != null && dataPosition != null && requestValue != null) {
switch (requestCmd) {
case ComfoAirCommandType.Constants.REQUEST_SET_DELAYS:
newRequestData = new int[8];
if (preRequestData.length > 0 && newRequestData.length <= preRequestData.length) {
System.arraycopy(preRequestData, 0, newRequestData, 0, newRequestData.length);
newRequestData[dataPosition] = requestValue;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
break;
case ComfoAirCommandType.Constants.REQUEST_SET_FAN_LEVEL:
newRequestData = new int[9];
if (preRequestData.length > 0 && newRequestData.length <= preRequestData.length) {
System.arraycopy(preRequestData, 0, newRequestData, 0, 6);
System.arraycopy(preRequestData, 10, newRequestData, 6, newRequestData.length - 6);
newRequestData[dataPosition] = requestValue;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
break;
case ComfoAirCommandType.Constants.REQUEST_SET_STATES:
newRequestData = new int[8];
if (preRequestData.length > 0 && newRequestData.length <= preRequestData.length) {
if (dataPosition == 4) {
requestValue = preRequestData[dataPosition]
+ checkByteAndCalculateValue(command, requestValue, preRequestData[dataPosition]);
}
System.arraycopy(preRequestData, 0, newRequestData, 0, 6);
System.arraycopy(preRequestData, 9, newRequestData, 6, newRequestData.length - 6);
newRequestData[dataPosition] = requestValue;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
break;
case ComfoAirCommandType.Constants.REQUEST_SET_EWT:
newRequestData = new int[5];
if (preRequestData.length > 0 && newRequestData.length <= preRequestData.length) {
System.arraycopy(preRequestData, 0, newRequestData, 0, 4);
System.arraycopy(preRequestData, 6, newRequestData, 4, newRequestData.length - 4);
newRequestData[dataPosition] = requestValue;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
break;
case ComfoAirCommandType.Constants.REQUEST_SET_ANALOGS:
newRequestData = new int[19];
if (preRequestData.length > 0 && newRequestData.length <= preRequestData.length) {
switch (dataPosition) {
case 0:
case 1:
case 2:
requestValue = preRequestData[dataPosition] + checkByteAndCalculateValue(command,
requestValue, preRequestData[dataPosition]);
}
System.arraycopy(preRequestData, 0, newRequestData, 0, newRequestData.length);
newRequestData[dataPosition] = requestValue;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
break;
default:
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
return newRequestData;
} else {
return ComfoAirCommandType.Constants.EMPTY_INT_ARRAY;
}
}
/**
* Check if preValue contains possible byte and calculate new value
*
* @param command
* @param requestValue
* @param preValue
* @return new int value
*/
private int checkByteAndCalculateValue(ComfoAirCommand command, int requestValue, int preValue) {
String key = command.getKeys().get(0);
ComfoAirCommandType commandType = ComfoAirCommandType.getCommandTypeByKey(key);
if (commandType != null) {
int[] possibleValues = commandType.getPossibleValues();
if (possibleValues != null) {
int possibleValue = possibleValues[0];
boolean isActive = (preValue & possibleValue) == possibleValue;
int newValue;
if (isActive) {
newValue = requestValue == 1 ? 0 : -possibleValue;
} else {
newValue = requestValue == 1 ? possibleValue : 0;
}
return newValue;
}
}
return 0;
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link ConnectException} is thrown on errors relating the serial connection.
*
* @author Hans Böhm - Initial contribution
*/
@NonNullByDefault
public class ComfoAirSerialException extends Exception {
private static final long serialVersionUID = 5439939653331189828L;
public ComfoAirSerialException(Throwable cause) {
super(cause);
}
public ComfoAirSerialException(String message) {
super(message);
}
public ComfoAirSerialException(String message, Exception e) {
super(message, e);
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.types.State;
/**
* Abstract class to convert binary hex values into openHAB states and vice
* versa
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public interface ComfoAirDataType {
/**
* Generate a openHAB State object based on response data.
*
* @param response
* @param commandType
* @return converted State object
*/
State convertToState(int[] response, ComfoAirCommandType commandType);
/**
* Generate byte array based on a openHAB State.
*
* @param value
* @param commandType
* @return converted byte array
*/
int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType);
default int calculateNumberValue(int[] data, ComfoAirCommandType commandType) {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
int value = 0;
if (readReplyDataPos != null) {
int base = 0;
for (int i = readReplyDataPos.length - 1; i >= 0; i--) {
if (readReplyDataPos[i] < data.length) {
value += data[readReplyDataPos[i]] << base;
base += 8;
} else {
return -1;
}
}
} else {
value = -1;
}
return value;
}
default String calculateStringValue(int[] data, ComfoAirCommandType commandType) {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
StringBuilder value = new StringBuilder();
if (readReplyDataPos != null) {
for (int pos : readReplyDataPos) {
if (pos < data.length) {
value.append((char) data[pos]);
}
}
}
return value.toString();
}
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle boolean values which are handled as decimal 0/1 states
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public class DataTypeBoolean implements ComfoAirDataType {
private static final DataTypeBoolean SINGLETON_INSTANCE = new DataTypeBoolean();
private DataTypeBoolean() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeBoolean.class);
public static DataTypeBoolean getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeBoolean\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
int readReplyDataBits = commandType.getReadReplyDataBits();
int readCommand = commandType.getReadCommand();
boolean result;
if (readReplyDataPos != null && readReplyDataPos[0] < data.length) {
if (readReplyDataBits == 0) {
result = data[readReplyDataPos[0]] == 1;
} else {
result = (data[readReplyDataPos[0]] & readReplyDataBits) == readReplyDataBits;
}
return OnOffType.from(result);
} else if (readCommand == 0) {
return OnOffType.OFF; // handle write-only commands (resets)
} else {
return UnDefType.NULL;
}
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
if (value instanceof UnDefType) {
logger.trace("\"DataTypeBoolean\" class \"convertFromState\" undefined state");
return null;
} else {
DecimalType decimalValue = value.as(DecimalType.class);
int[] possibleValues = commandType.getPossibleValues();
int returnValue = 0x01;
if (possibleValues != null) {
returnValue = possibleValues[0];
}
if (decimalValue != null) {
int[] template = commandType.getChangeDataTemplate();
template[commandType.getChangeDataPos()] = decimalValue.intValue() == 1 ? returnValue : 0x00;
return template;
} else {
logger.trace(
"\"DataTypeBoolean\" class \"convertFromState\" method: State value conversion returned null");
return null;
}
}
}
}

View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle error messages
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public class DataTypeMessage implements ComfoAirDataType {
private static final DataTypeMessage SINGLETON_INSTANCE = new DataTypeMessage();
private DataTypeMessage() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeMessage.class);
public static DataTypeMessage getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeMessage\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
if (readReplyDataPos != null) {
int errorAlo = data[readReplyDataPos[0]];
int errorE = data[readReplyDataPos[1]];
int errorEA = data[readReplyDataPos[2]];
int errorAhi = data[readReplyDataPos[3]];
StringBuilder errorCode = new StringBuilder();
if (errorAlo > 0) {
errorCode.append("A");
errorCode.append(convertToCode(errorAlo));
} else if (errorAhi > 0) {
if (errorAhi == 0x80) {
errorCode.append("A0");
} else {
errorCode.append("A");
errorCode.append(convertToCode(errorAhi) + 8);
}
}
if (errorE > 0) {
if (errorCode.length() > 0) {
errorCode.append(" ");
}
errorCode.append("E");
errorCode.append(convertToCode(errorE));
} else if (errorEA > 0) {
if (errorCode.length() > 0) {
errorCode.append(" ");
}
errorCode.append("EA");
errorCode.append(convertToCode(errorEA));
}
return new StringType(errorCode.length() > 0 ? errorCode.toString() : "No Errors");
} else {
return UnDefType.UNDEF;
}
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
return null;
}
private int convertToCode(int code) {
switch (code) {
case 0x01:
return 1;
case 0x02:
return 2;
case 0x04:
return 3;
case 0x08:
return 4;
case 0x10:
return 5;
case 0x20:
return 6;
case 0x40:
return 7;
case 0x80:
return 8;
default:
return -1;
}
}
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle numeric values
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public class DataTypeNumber implements ComfoAirDataType {
private static final DataTypeNumber SINGLETON_INSTANCE = new DataTypeNumber();
private DataTypeNumber() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeNumber.class);
public static DataTypeNumber getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeNumber\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int value = calculateNumberValue(data, commandType);
if (value < 0) {
return UnDefType.NULL;
}
int[] possibleValues = commandType.getPossibleValues();
if (possibleValues != null) {
// fix for unexpected value for "level" value. got a 0x33. valid was
// the 0x03. 0x30 was to much.
// send DATA: 07 f0 00 cd 00 7a 07 0f
// receive CMD: ce DATA: 0f 20 32 00 0f 21 33 2d 33 03 01 5a 5b 00
for (int possibleValue : possibleValues) {
if ((value & possibleValue) == possibleValue) {
return new DecimalType(value);
}
}
return UnDefType.NULL;
}
return new DecimalType(value);
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
if (value instanceof UnDefType) {
logger.trace("\"DataTypeNumber\" class \"convertFromState\" undefined state");
return null;
} else {
int[] template = commandType.getChangeDataTemplate();
int[] possibleValues = commandType.getPossibleValues();
int position = commandType.getChangeDataPos();
int intValue = ((DecimalType) value).intValue();
if (possibleValues == null) {
template[position] = intValue;
} else {
for (int i = 0; i < possibleValues.length; i++) {
if (possibleValues[i] == intValue) {
template[position] = intValue;
break;
}
}
}
return template;
}
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle revolutions per minute values
*
* @author Grzegorz Miasko - Initial Contribution
* @author Hans Böhm - Refactoring
*/
@NonNullByDefault
public class DataTypeRPM implements ComfoAirDataType {
private static final DataTypeRPM SINGLETON_INSTANCE = new DataTypeRPM();
private DataTypeRPM() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeRPM.class);
public static DataTypeRPM getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeRPM\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int value = calculateNumberValue(data, commandType);
if (value < 0) {
return UnDefType.NULL;
}
// transferred value is (1875000 / rpm) per protocol
return new DecimalType((int) (1875000.0 / value));
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
if (value instanceof UnDefType) {
logger.trace("\"DataTypeRPM\" class \"convertFromState\" undefined state");
return null;
} else {
int[] template = commandType.getChangeDataTemplate();
// transferred value is (1875000 / rpm) per protocol
template[commandType.getChangeDataPos()] = (int) (1875000 / ((DecimalType) value).doubleValue());
return template;
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle temperature values
*
* @author Holger Hees - Initial Contribution
* @author Hans Böhm - QuantityTypes
*/
@NonNullByDefault
public class DataTypeTemperature implements ComfoAirDataType {
private static final DataTypeTemperature SINGLETON_INSTANCE = new DataTypeTemperature();
private DataTypeTemperature() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeTemperature.class);
public static DataTypeTemperature getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeTemperature\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
if (readReplyDataPos != null && readReplyDataPos[0] < data.length) {
return new QuantityType<>((((double) data[readReplyDataPos[0]]) / 2) - 20, SIUnits.CELSIUS);
} else {
return UnDefType.NULL;
}
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
int[] template = commandType.getChangeDataTemplate();
QuantityType<?> celsius = ((QuantityType<?>) value).toUnit(SIUnits.CELSIUS);
if (celsius != null) {
template[commandType.getChangeDataPos()] = (int) (celsius.doubleValue() + 20) * 2;
return template;
} else {
logger.trace("\"DataTypeTemperature\" class \"convertFromState\" undefined state");
return null;
}
}
}

View File

@@ -0,0 +1,85 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle time values
*
* @author Hans Böhm - Initial Contribution
*/
@NonNullByDefault
public class DataTypeTime implements ComfoAirDataType {
private static final DataTypeTime SINGLETON_INSTANCE = new DataTypeTime();
private DataTypeTime() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeTime.class);
public static DataTypeTime getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeTime\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int value = calculateNumberValue(data, commandType);
if (value < 0) {
return UnDefType.NULL;
}
return new QuantityType<>(value, SmartHomeUnits.HOUR);
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
int[] template = commandType.getChangeDataTemplate();
int[] possibleValues = commandType.getPossibleValues();
int position = commandType.getChangeDataPos();
QuantityType<?> hours = ((QuantityType<?>) value).toUnit(SmartHomeUnits.HOUR);
if (hours != null) {
int intValue = hours.intValue();
if (possibleValues == null) {
template[position] = intValue;
} else {
for (int i = 0; i < possibleValues.length; i++) {
if (possibleValues[i] == intValue) {
template[position] = intValue;
break;
}
}
}
return template;
} else {
logger.trace("\"DataTypeTime\" class \"convertFromState\" undefined state");
return null;
}
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.comfoair.internal.datatypes;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.comfoair.internal.ComfoAirCommandType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to handle volt values
*
* @author Grzegorz Miasko - Initial Contribution
* @author Hans Böhm - QuantityTypes
*/
@NonNullByDefault
public class DataTypeVolt implements ComfoAirDataType {
private static final DataTypeVolt SINGLETON_INSTANCE = new DataTypeVolt();
private DataTypeVolt() {
}
private final Logger logger = LoggerFactory.getLogger(DataTypeVolt.class);
public static DataTypeVolt getInstance() {
return SINGLETON_INSTANCE;
}
@Override
public State convertToState(int @Nullable [] data, ComfoAirCommandType commandType) {
if (data == null) {
logger.trace("\"DataTypeVolt\" class \"convertToState\" method parameter: null");
return UnDefType.NULL;
} else {
int[] readReplyDataPos = commandType.getReadReplyDataPos();
if (readReplyDataPos != null && readReplyDataPos[0] < data.length) {
return new QuantityType<>((double) data[readReplyDataPos[0]] * 10 / 255, SmartHomeUnits.VOLT);
} else {
return UnDefType.NULL;
}
}
}
@Override
public int @Nullable [] convertFromState(State value, ComfoAirCommandType commandType) {
int[] template = commandType.getChangeDataTemplate();
QuantityType<?> volts = ((QuantityType<?>) value).toUnit(SmartHomeUnits.VOLT);
if (volts != null) {
template[commandType.getChangeDataPos()] = (int) (volts.doubleValue() * 255 / 10);
return template;
} else {
logger.trace("\"DataTypeVolt\" class \"convertFromState\" undefined state");
return null;
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="comfoair" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>ComfoAir Binding</name>
<description>This is the binding for ComfoAir.</description>
<author>Hans Böhm</author>
</binding:binding>

View File

@@ -0,0 +1,237 @@
# binding
binding.comfoair.comfoair.name = ComfoAir Binding
binding.comfoair.comfoair.description = ComfoAir Wohnraumlüftung
# thing types
thing-type.comfoair.comfoair.label = ComfoAir Wohnraumlüftung
thing-type.comfoair.comfoair.description = Serielle Verbindung zu einer ComfoAir-kompatiblen Lüftungsanlage
# thing type configuration
thing-type.config.comfoair.comfoair.serialPort.label = Serieller Port
thing-type.config.comfoair.comfoair.serialPort.description = Serieller Port über den die Lüftungsanlage verbunden ist
thing-type.config.comfoair.comfoair.refreshInterval.label = Aktualisierungsinterval
thing-type.config.comfoair.comfoair.refreshInterval.description = Aktualisierungsinterval in Sekunden
# channel type
channel-type.comfoair.activate.label = Steuerung aktivieren
channel-type.comfoair.activate.description = Kontrolle aktivieren (über openHAB) oder deaktivieren (zurück auf CCEase)
channel-type.comfoair.fan_evel.label = Lüftungsstufe
channel-type.comfoair.fan_evel.description = Lüftungsstufe
channel-type.comfoair.target_temperature.label = Zieltemperatur
channel-type.comfoair.target_temperature.description = Ziel- bzw. Komforttemperatur
channel-type.comfoair.filter_error.label = Filterfehler
channel-type.comfoair.filter_error.description = Filter voll
channel-type.comfoair.filter_reset.label = Filterzähler Zurücksetzen
channel-type.comfoair.filter_reset.description = Zurücksetzen der Filterlaufzeit
channel-type.comfoair.error_reset.label = Fehler Zurücksetzen
channel-type.comfoair.error_reset.description = Zurücksetzen der Fehlermeldungen
channel-type.comfoair.fan_out_0.label = Abluftstufe 0 (abwesend)
channel-type.comfoair.fan_out_0.description = Lüftungsleistung (%) des Abluftventilators auf Stufe 0
channel-type.comfoair.fan_out_1.label = Abluftstufe 1
channel-type.comfoair.fan_out_1.description = Lüftungsleistung (%) des Abluftventilators auf Stufe 1
channel-type.comfoair.fan_out_2.label = Abluftstufe 2
channel-type.comfoair.fan_out_2.description = Lüftungsleistung (%) des Abluftventilators auf Stufe 2
channel-type.comfoair.fan_out_3.label = Abluftstufe 3
channel-type.comfoair.fan_out_3.description = Lüftungsleistung (%) des Abluftventilators auf Stufe 3
channel-type.comfoair.fan_in_0.label = Zuluftstufe 0 (abwesend)
channel-type.comfoair.fan_in_0.description = Lüftungsleistung (%) des Zuluftventilators auf Stufe 0
channel-type.comfoair.fan_in_1.label = Zuluftstufe 1
channel-type.comfoair.fan_in_1.description = Lüftungsleistung (%) des Zuluftventilators auf Stufe 1
channel-type.comfoair.fan_in_2.label = Zuluftstufe 2
channel-type.comfoair.fan_in_2.description = Lüftungsleistung (%) des Zuluftventilators auf Stufe 2
channel-type.comfoair.fan_in_3.label = Zuluftstufe 3
channel-type.comfoair.fan_in_3.description = Lüftungsleistung (%) des Zuluftventilators auf Stufe 3
channel-type.comfoair.fan_in_percent.label = Zuluftventilator (%)
channel-type.comfoair.fan_in_percent.description = Aktuelle Geschwindigkeit (%) des Zuluftventilators
channel-type.comfoair.fan_out_percent.label = Abluftventilator (%)
channel-type.comfoair.fan_out_percent.description = Aktuelle Geschwindigkeit (%) des Abluftventilators
channel-type.comfoair.fan_in_RPM.label = Zuluftventilator (U/min)
channel-type.comfoair.fan_in_RPM.description = Aktuelle Drehzahl des Zuluftventilators
channel-type.comfoair.fan_out_RPM.label = Abluftventilator (U/min)
channel-type.comfoair.fan_out_RPM.description = Aktuelle Drehzahl des Abluftventilators
channel-type.comfoair.outdoor_temperature_in.label = Außenlufttemperatur
channel-type.comfoair.outdoor_temperature_in.description = Temperatur der Außenzuluft
channel-type.comfoair.outdoor_temperature_out.label = Fortlufttemperatur
channel-type.comfoair.outdoor_temperature_out.description = Temperatur der Außenabluft
channel-type.comfoair.indoor_temperature_in.label = Zulufttemperatur
channel-type.comfoair.indoor_temperature_in.description = Temperatur der Raumzuluft
channel-type.comfoair.indoor_temperature_out.label = Ablufttemperatur
channel-type.comfoair.indoor_temperature_out.description = Temperatur der Raumabluft
channel-type.comfoair.is_T1_sensor.label = Sensor T1 Verfügbarkeit
channel-type.comfoair.is_T1_sensor.description = Verfügbarkeit des Temperatursensors T1 (Außenluft)
channel-type.comfoair.is_T2_sensor.label = Sensor T2 Verfügbarkeit
channel-type.comfoair.is_T2_sensor.description = Verfügbarkeit des Temperatursensors T2 (Zuluft)
channel-type.comfoair.is_T3_sensor.label = Sensor T3 Verfügbarkeit
channel-type.comfoair.is_T3_sensor.description = Verfügbarkeit des Temperatursensors T3 (Abluft)
channel-type.comfoair.is_T4_sensor.label = Sensor T4 Verfügbarkeit
channel-type.comfoair.is_T4_sensor.description = Verfügbarkeit des Temperatursensors T4 (Fortluft)
channel-type.comfoair.is_EWT_sensor.label = Sensor EWT Verfügbarkeit
channel-type.comfoair.is_EWT_sensor.description = Verfügbarkeit des EWT-Temperatursensors
channel-type.comfoair.is_heater_sensor.label = Sensor Nachheizung Verfügbarkeit
channel-type.comfoair.is_heater_sensor.description = Verfügbarkeit des Nachheizung-Temperatursensors
channel-type.comfoair.is_cookerhood_sensor.label = Sensor Dunstabzugshaube Verfügbarkeit
channel-type.comfoair.is_cookerhood_sensor.description = Verfügbarkeit des Dunstabzugshauben-Temperatursensors
channel-type.comfoair.chimney_state.label = Schornsteinfegerregelung Status
channel-type.comfoair.chimney_state.description = Status der Schornsteinfegerregelung
channel-type.comfoair.bypass_state.label = Bypass Status
channel-type.comfoair.bypass_state.description = Status der Bypassklappe (ON = offen / OFF = geschlossen)
channel-type.comfoair.ewt_state.label = EWT Status
channel-type.comfoair.ewt_state.description = Status des EWT-Ventils (ON = offen / OFF = geschlossen)
channel-type.comfoair.heater_state.label = Nacherhitzer Status
channel-type.comfoair.heater_state.description = Status des Nacherhitzers
channel-type.comfoair.v_control_state.label = 0-10V-Regelung Status
channel-type.comfoair.v_control_state.description = Status der 0-10V-Regelung
channel-type.comfoair.frost_state.label = Frostschutz Status
channel-type.comfoair.frost_state.description = Status der Frostschutzregelung
channel-type.comfoair.cookerhood_state.label = Dunstabzugshaube Status
channel-type.comfoair.cookerhood_state.description = Status der Dunstabzugshaubenregelung
channel-type.comfoair.enthalpy_state.label = Enthalpietauscher Status
channel-type.comfoair.enthalpy_state.description = Status des Enthalpietauschers
channel-type.comfoair.ewt_temperature.label = EWT Temperatur
channel-type.comfoair.ewt_temperature.description = Temperatur des Erdwärmetauschers
channel-type.comfoair.heater_temperature.label = Nacherhitzer Temperatur
channel-type.comfoair.heater_temperature.description = Temperatur des Nacherhitzers
channel-type.comfoair.cookerhood_temperature.label = Dunstabzugshaube Temperatur
channel-type.comfoair.cookerhood_temperature.description = Temperatur der Dunstabzugshaube
channel-type.comfoair.level0_time.label = Stufe 0 Laufzeit
channel-type.comfoair.level0_time.description = Betriebsstunden auf Stufe 0 (abwesend)
channel-type.comfoair.level1_time.label = Stufe 1 Laufzeit
channel-type.comfoair.level1_time.description = Betriebsstunden auf Stufe 1
channel-type.comfoair.level2_time.label = Stufe 2 Laufzeit
channel-type.comfoair.level2_time.description = Betriebsstunden auf Stufe 2
channel-type.comfoair.level3_time.label = Stufe 3 Laufzeit
channel-type.comfoair.level3_time.description = Betriebsstunden auf Stufe 3
channel-type.comfoair.freeze_time.label = Frostschutz Laufzeit
channel-type.comfoair.freeze_time.description = Betriebsstunden der Frostschutzregelung
channel-type.comfoair.preheater_time.label = Vorheizung Laufzeit
channel-type.comfoair.preheater_time.description = Betriebsstunden der Vorheizung
channel-type.comfoair.bypass_time.label = Bypass Laufzeit
channel-type.comfoair.bypass_time.description = Öffnungsstunden der Bypassklappe
channel-type.comfoair.filter_hours.label = Filter Laufzeit
channel-type.comfoair.filter_hours.description = Betriebsstunden seit Filterwechsel
channel-type.comfoair.bypass_factor.label = Bypass Faktor
channel-type.comfoair.bypass_factor.description = Aktueller Bypassfaktor
channel-type.comfoair.bypass_level.label = Bypass Stufe
channel-type.comfoair.bypass_level.description = Aktuelle Bypassstufe
channel-type.comfoair.bypass_correction.label = Bypass Korrektur
channel-type.comfoair.bypass_correction.description = Status der Bypasskorrektur
channel-type.comfoair.bypass_summer.label = Bypass Sommermodus
channel-type.comfoair.bypass_summer.description = Aktivität des Sommermodus
channel-type.comfoair.preheater_valve.label = Vorheizer Klappe
channel-type.comfoair.preheater_valve.description = Status der Vorheizerklappe
channel-type.comfoair.preheater_frost_protect.label = Frostschutz Status
channel-type.comfoair.preheater_frost_protect.description = Status der Frostschutzregelung
channel-type.comfoair.preheater_heating.label = Vorheizer Status
channel-type.comfoair.preheater_heating.description = Status der Vorheizung
channel-type.comfoair.preheater_frost_time.label = Frostminuten
channel-type.comfoair.preheater_frost_time.description = Gesamtfrostdauer in Minuten
channel-type.comfoair.preheater_safety.label = Frostsicherheit
channel-type.comfoair.preheater_safety.description = Einstellung der Frostsicherheit
channel-type.comfoair.ewt_temperature_low.label = EWT Temperatur (niedrig)
channel-type.comfoair.ewt_temperature_low.description = Untere Temperatur des Erdwärmetauschers
channel-type.comfoair.ewt_temperature_high.label = EWT Temperatur (hoch)
channel-type.comfoair.ewt_temperature_high.description = Obere Temperatur des Erdwärmetauschers
channel-type.comfoair.ewt_speed.label = EWT Hochlauf (%)
channel-type.comfoair.ewt_speed.description = Hochlauf des Erdwärmetauschers
channel-type.comfoair.heater_power.label = Nachheizung Leistung
channel-type.comfoair.heater_power.description = Leistung der Nachheizung
channel-type.comfoair.heater_power_I.label = Nachheizung Leistung I-Parameter
channel-type.comfoair.heater_power_I.description = Leistung I-Parameter der Nachheizung
channel-type.comfoair.heater_target_temperature.label = Nachheizung Zieltemperatur
channel-type.comfoair.heater_target_temperature.description = Zieltemperatur der Nachheizung
channel-type.comfoair.cookerhood_speed.label = Dunstabzugshaube Hochlauf (%)
channel-type.comfoair.cookerhood_speed.description = Hochlauf der Dunstabzugshaube
channel-type.comfoair.enthalpy_temperature.label = Enthalpiesensor Temperatur
channel-type.comfoair.enthalpy_temperature.description = Temperatur des Enthalpiesensors
channel-type.comfoair.enthalpy_humidity.label = Enthalpiesensor Feuchtigkeit
channel-type.comfoair.enthalpy_humidity.description = Feuchtigkeit des Enthalpiesensors
channel-type.comfoair.enthalpy_level.label = Enthalpietauscher Koeffizient
channel-type.comfoair.enthalpy_level.description = Koeffizient des Enthalpietauscher
channel-type.comfoair.enthalpy_time.label = Enthalpietauscher Timer
channel-type.comfoair.enthalpy_time.description = Timerstatus des Enthalpietauschers
channel-type.comfoair.is_L1_switch.label = L1-Schalter
channel-type.comfoair.is_L1_switch.description = Verfügbarkeit des L1-Schalters
channel-type.comfoair.is_L2_switch.label = L2-Schalter
channel-type.comfoair.is_L2_switch.description = Verfügbarkeit des L2-Schalters
channel-type.comfoair.is_bathroom_switch.label = Badezimmerschalter
channel-type.comfoair.is_bathroom_switch.description = Verfügbarkeit eines Badezimmerchalters
channel-type.comfoair.is_cookerhood_switch.label = Dunstabzugshaubenschalter
channel-type.comfoair.is_cookerhood_switch.description = Verfügbarkeit eines Dunstabzugshaubenschalters
channel-type.comfoair.is_external_filter.label = Externer Filter
channel-type.comfoair.is_external_filter.description = Verfügbarkeit eines externen Filters
channel-type.comfoair.is_wtw.label = Wärmerückgewinnung
channel-type.comfoair.is_wtw.description = Verfügbarkeit der Wärmerückgewinnung
channel-type.comfoair.is_bathroom2_switch.label = Badezimmerschalter 2
channel-type.comfoair.is_bathroom2_switch.description = Verfügbarkeit eines zweiten Badezimmerchalters (luxe)
channel-type.comfoair.menu20_mode.label = Menü 20 Status (P10)
channel-type.comfoair.menu20_mode.description = Menü 20 zurzeit aktiv? (P10)
channel-type.comfoair.menu21_mode.label = Menü 21 Status (P11)
channel-type.comfoair.menu21_mode.description = Menü 21 zurzeit aktiv? (P11)
channel-type.comfoair.menu22_mode.label = Menü 22 Status (P12)
channel-type.comfoair.menu22_mode.description = Menü 22 zurzeit aktiv? (P12)
channel-type.comfoair.menu23_mode.label = Menü 23 Status (P13)
channel-type.comfoair.menu23_mode.description = Menü 23 zurzeit aktiv? (P13)
channel-type.comfoair.menu24_mode.label = Menü 24 Status (P14)
channel-type.comfoair.menu24_mode.description = Menü 24 zurzeit aktiv? (P14)
channel-type.comfoair.menu25_mode.label = Menü 25 Status (P15)
channel-type.comfoair.menu25_mode.description = Menü 25 zurzeit aktiv? (P15)
channel-type.comfoair.menu26_mode.label = Menü 26 Status (P16)
channel-type.comfoair.menu26_mode.description = Menü 26 zurzeit aktiv? (P16)
channel-type.comfoair.menu27_mode.label = Menü 27 Status (P17)
channel-type.comfoair.menu27_mode.description = Menü 27 zurzeit aktiv? (P17)
channel-type.comfoair.menu28_mode.label = MMenü 28 Status (P18)
channel-type.comfoair.menu28_mode.description = Menü 28 zurzeit aktiv? (P18)
channel-type.comfoair.menu29_mode.label = Menü 29 Status (P19)
channel-type.comfoair.menu29_mode.description = Menü 29 zurzeit aktiv? (P19)
channel-type.comfoair.cookerhood_delay.label = Ausschaltverzögerung Dunstabzugshaube (P20)
channel-type.comfoair.cookerhood_delay.description = Ausschaltverzögerung für die Dunstabzugshaubenregelung (min)
channel-type.comfoair.bathroom_start_delay.label = Einschaltverzögerung Badezimmerschalter (P21)
channel-type.comfoair.bathroom_start_delay.description = Einschaltverzögerung für den Badezimmerschalter (min)
channel-type.comfoair.bathroom_end_delay.label = Ausschaltverzögerung Badezimmerschalter (P22)
channel-type.comfoair.bathroom_end_delay.description = Ausschaltverzögerung für den Badezimmerschalter (min)
channel-type.comfoair.L1_end_delay.label = Ausschaltverzögerung L1-Schalter (P23)
channel-type.comfoair.L1_end_delay.description = Ausschaltverzögerung für den L1-Schalter (min)
channel-type.comfoair.filter_weeks.label = Filterlaufzeit (P24)
channel-type.comfoair.filter_weeks.description = Laufzeit bis "Filter verschmutzt"-Meldung (weeks)
channel-type.comfoair.RF_short_delay.label = Ausschaltverzögerung RF Kurz (P25)
channel-type.comfoair.RF_short_delay.description = Ausschaltverzögerung für Lüftungsstufe 3 bei kurzer Betätigung des RF-Schalters (min)
channel-type.comfoair.RF_long_delay.label = Ausschaltverzögerung RF Lang (P26)
channel-type.comfoair.RF_long_delay.description = Ausschaltverzögerung für Lüftungsstufe 3 bei langer Betätigung des RF-Schalters (min)
channel-type.comfoair.pulse_ventilation.label = Zeit Stoßlüftung (P27)
channel-type.comfoair.pulse_ventilation.description = Dauer für die Stoßlüftung (min)
channel-type.comfoair.is_analog.label = Verfügbarkeit Analoger Eingang
channel-type.comfoair.is_analog.description = Verfügbarkeit eines analogen Eingangs
channel-type.comfoair.is_RF.label = Verfügbarkeit RF Eingang
channel-type.comfoair.is_RF.description = Verfügbarkeit eines RF-Eingangs
channel-type.comfoair.analog_mode.label = Status Analoger Eingang
channel-type.comfoair.analog_mode.description = Status des analogen Eingangs
channel-type.comfoair.RF_mode.label = Status RF Eingang
channel-type.comfoair.RF_mode.description = Status des RF-Eingangs
channel-type.comfoair.analog_negative.label = Analoger Eingang Postiv/Negativ
channel-type.comfoair.analog_negative.description = Postiv/Negativ-Einstellung des analogen Eingangs
channel-type.comfoair.RF_negative.label = RF Eingang Postiv/Negativ
channel-type.comfoair.RF_negative.description = Postiv/Negativ-Einstellung des RF-Eingangs
channel-type.comfoair.analog_volt.label = Analoger Eingang Spannungslevel
channel-type.comfoair.analog_volt.description = Spannungslevel des analogen Eingangs
channel-type.comfoair.analog_min.label = Analoger Eingang Minimum
channel-type.comfoair.analog_min.description = Minimaleinstellung des analogen Eingangs
channel-type.comfoair.analog_max.label = Analoger Eingang Maximum
channel-type.comfoair.analog_max.description = Maximaleinstellung des analogen Eingangs
channel-type.comfoair.analog_value.label = Analoger Eingang Zielwert
channel-type.comfoair.analog_value.description = Zielwert des analogen Eingangs
channel-type.comfoair.RF_min.label = RF Eingang Minimum
channel-type.comfoair.RF_min.description = Minimaleinstellung des RF-Eingangs
channel-type.comfoair.RF_max.label = RF Eingang Maximum
channel-type.comfoair.RF_max.description = Maximaleinstellung des RF-Eingangs
channel-type.comfoair.RF_value.label = RF Eingang Zielwert
channel-type.comfoair.RF_value.description = Zielwert des RF-Eingangs
channel-type.comfoair.analog_priority.label = Controller Priorität
channel-type.comfoair.analog_priority.description = Priorität der analogen Eingänge für die höchste Lufteinstellung
channel-type.comfoair.errors_current.label = Fehler Aktuell
channel-type.comfoair.errors_current.description = Aktuelle Fehler
channel-type.comfoair.errors_last.label = Fehler Letzte
channel-type.comfoair.errors_last.description = Letzte Fehler
channel-type.comfoair.errors_prelast.label = Fehler Vorletzte
channel-type.comfoair.errors_prelast.description = Vorletzte Fehler
channel-type.comfoair.errors_pre_prelast.label = Fehler Vorvorletzte
channel-type.comfoair.errors_pre_prelast.description = Vorvorletzte Fehler