]> git.basschouten.com Git - openhab-addons.git/commitdiff
[touchwand] - Add support for Risco alarm system sensors and AcWand thermostat (...
authorRoie Geron <roie.geron@gmail.com>
Sun, 30 May 2021 09:46:41 +0000 (12:46 +0300)
committerGitHub <noreply@github.com>
Sun, 30 May 2021 09:46:41 +0000 (11:46 +0200)
* notify listeners on status change using discovery

Signed-off-by: Roie Geron <roie.geron@gmail.com>
20 files changed:
bundles/org.openhab.binding.touchwand/README.md
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandAlarmSensorHandler.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBSensorHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBaseUnitHandler.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBindingConstants.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBridgeHandler.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandHandlerFactory.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandRestClient.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandThermostatHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/config/TouchwandBridgeConfiguration.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/discovery/TouchWandUnitDiscoveryService.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/IdData.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandBSensorUnitData.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatCurrentStatus.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatUnitData.java [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandUnitData.java
bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandUnitFromJson.java
bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/bridge.xml
bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/bsensor.xml [new file with mode: 0644]
bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/thermostat.xml [new file with mode: 0644]

index a9a86338163ec9060c9fe5000f4e4074359d7edd..73f870c666c3924c7980c904402b81a4aed714bb 100644 (file)
@@ -9,6 +9,11 @@ TouchWand products are compatible with most major Z-Wave products, IP controlled
 ## Supported Things
 
 This binding supports switches, shutters dimmers alarm sensors and wall controllers configured in Touchwand Wanderfull™ Hub Controller.
+The binding also supports [AcWand™](http://www.touchwand.com/products/touchwand-acwand/) - smart control for your air conditioner controller.
+
+
+![AcWand](http://www.touchwand.com/wp-content/uploads/2019/04/AcWand-300x350.png)
+
 
 ## Control and Status
 
@@ -17,10 +22,13 @@ This binding supports switches, shutters dimmers alarm sensors and wall controll
 3. **dimmer**  - control - ON/OFF/BRIGHTNESS
 4. **wallcontroller** - control - LONG/SHORT
 5. **alarmsensor** - status channels depend on alarm sensor type
+5. **bsensor** - binary status channels depend on alarm sensor type (motion, door , smoke)
+6. **thermostat** - AcWand™ smart control for your air conditioner 
 
 ## Discovery
 
-After adding TouchWand Hub the auto discovery will add all switches dimmers alarm sensors and shutters to the inbox.
+After adding TouchWand Hub the auto discovery will add all suppored devuces to inbox.
+Auto discovery scans priodically and add to the Inbox new devices added to the Touchwand Wanderfull™ Hub
 
 ## Bridge Configuration
 
@@ -32,7 +40,6 @@ After adding TouchWand Hub the auto discovery will add all switches dimmers alar
 | password          | Touchwand hub password                                                | string  | yes      |
 | ipAddress         | Touchwand hub hostname or IP address                                  | string  | yes      |
 | port              | Management port (default 80)                                          | integer | no       |
-| statusrefresh     | Unit status refresh interval in seconds                               | integer | no       |
 | addSecondaryUnits | If the controller is primary, add secondary controllers units as well | bool    | no       |
 
 
@@ -46,23 +53,37 @@ No thing configuration is needed
 note **Touchwand Wanderfull™** supports various types of alarm sensors such as water leak, door/window sensor and motion sensor.
 Alarm Sensor thing represents a generic sensor, relevant sensor channels will be displayed once a sensor is added as a Thing.
 
+## Switch Shutters Channels 
 
 | Channel Type ID   | Item Type          | Description
 |-------------------|--------------------|-----------------------------------------------------------------------|
 | switch            | Switch             | This channel supports switching the device on and off.                |
 | shutter           | Rollershutter      | This channel controls the shutter position                            |
 | brightness        | Dimmer             | This channel supports adjusting the brightness value.                 |
+| wallaction        | String             | This channel indicate SHORT or LONG wallcontroller button pressed     |
+
+## Alarm Sensors Channels 
+
+| Channel Type ID   | Item Type          | Description                                                                                                                             
+|-------------------|--------------------|-----------------------------------------------------------------------|
 | illumination      | Number:Illuminance | This channel shows the current illuminance measured by the sensor.    |
 | temperature       | Number:Temperature | This channel shows the current temperature measured by the sensor.    |
 | leak              | Switch             | This channel alert when water leak is detected by the sensor          |
 | motion            | Switch             | This channel alert when motion detected by the sensor.                |
+| smoke             | Switch             | This channel alert when smoke detected by the sensor.                |
 | isOpen            | Contact            | This channel shows the status of Door/Window sensor.                  |
 | battery_level     | Number             | This channel shows the battery level.                                 |
 | battery_low       | Switch             | This channel indicates whether the battery is low or not.             |
-| wallaction        | String             | This channel indicate SHORT or LONG wallcontroller button pressed     |
-
 
+## Thermostat Channels 
 
+| Channel Type ID   | Item Type          | Description                                                                                                                             
+|-------------------|--------------------|-----------------------------------------------------------------------|
+| State             | Switch             | Set and read the device state ON or OFF.                              |
+| targetTemperature | Number:Temperature | Shows the current set point of the thrermostat.                       |
+| roomTemperature   | Number:Temperature | Shows the current termprature measured by the thermostat.             |
+| mode              | String             | Set/Read Thermostat mode - Cool, Heat, Fan, Dry, Auto                 |
+| fanLevel          | String             | Set/Read fan leval - Low, Medium, High, Auto                          |
 
 ## Full Example
 
@@ -98,7 +119,7 @@ Rollershutter   Rollershutter_346      "Living Room South shutter"    {channel="
 
 ```
 /* Switches and Dimmers */
-Switch  Switch_408      "Strairs light"                 {channel="touchwand:switch:1921681116:408:switch"}
+Switch  Switch_408      "Stairs light"                  {channel="touchwand:switch:1921681116:408:switch"}
 Switch  Switch_411      "South Garden light"            {channel="touchwand:switch:1921681116:411:switch"}
 Dimmer  Switch_415      "Living Room Ceiling dimmer"    {channel="touchwand:switch:1921681116:415:switch"}
 Switch  Switch_418      "South Garden light"            {channel="touchwand:switch:1921681116:418:switch"}
index 098c24c0180a22029f24d4a91361654c0bd0712e..4a62ef9050102a653d3ab1cb8d4f5c84ee7614a0 100644 (file)
@@ -144,8 +144,7 @@ public class TouchWandAlarmSensorHandler extends TouchWandBaseUnitHandler {
                     toBeRemovedChannels.remove(thing.getChannel(CHANNEL_DOORWINDOW));
                     break;
                 case SENSOR_TYPE_LEAK:
-                    Channel channel = thing.getChannel(CHANNEL_LEAK);
-                    toBeRemovedChannels.remove(channel);
+                    toBeRemovedChannels.remove(thing.getChannel(CHANNEL_LEAK));
                     break;
             }
         }
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBSensorHandler.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandBSensorHandler.java
new file mode 100644 (file)
index 0000000..ca42f9c
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal;
+
+import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
+
+import java.util.ArrayList;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.touchwand.internal.dto.TouchWandBSensorUnitData;
+import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.OpenClosedType;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.types.Command;
+
+/**
+ * The {@link TouchWandBSensorHandler} is responsible for handling command for Binary Sensor unit
+ *
+ * @author Roie Geron - Initial contribution
+ */
+@NonNullByDefault
+public class TouchWandBSensorHandler extends TouchWandBaseUnitHandler {
+
+    private boolean isFirstUpdateTouchWandUnitState = true;
+
+    public TouchWandBSensorHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    void updateTouchWandUnitState(TouchWandUnitData unitData) {
+        if (unitData instanceof TouchWandBSensorUnitData) {
+            if (isFirstUpdateTouchWandUnitState) {
+                removeUnsupportedChannels((TouchWandBSensorUnitData) unitData);
+                isFirstUpdateTouchWandUnitState = false;
+            }
+            String sensorSubType = ((TouchWandBSensorUnitData) unitData).getIdData().getSubType();
+            switch (sensorSubType) {
+                case BSENSOR_SUBTYPE_DOORWINDOW:
+                    updateChannelDoorWindow((TouchWandBSensorUnitData) unitData);
+                    break;
+                case BSENSOR_SUBTYPE_MOTION:
+                    updateChannelMotion((TouchWandBSensorUnitData) unitData);
+                    break;
+                case BSENSOR_SUBTYPE_SMOKE:
+                    updateChannelSmoke((TouchWandBSensorUnitData) unitData);
+                    break;
+                default:
+            }
+        } else {
+            logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
+        }
+    }
+
+    @Override
+    void touchWandUnitHandleCommand(Command command) {
+    }
+
+    void updateChannelDoorWindow(TouchWandBSensorUnitData unitData) {
+        OpenClosedType myOpenClose;
+        String isOpen = unitData.getCurrStatus();
+        logger.debug("recieved status {} from door unit {} ", isOpen, unitData.getName());
+        if (isOpen.equals(BSENSOR_STATUS_OPEN)) {
+            myOpenClose = OpenClosedType.OPEN;
+        } else if (isOpen.equals(BSENSOR_STATUS_CLOSE)) {
+            myOpenClose = OpenClosedType.CLOSED;
+        } else {
+            logger.debug("TouchWandBSensorUnitData illegal update value {}", isOpen);
+            return;
+        }
+        updateState(CHANNEL_DOORWINDOW, myOpenClose);
+    }
+
+    void updateChannelMotion(TouchWandBSensorUnitData unitData) {
+        String motion = unitData.getCurrStatus();
+        logger.debug("recieved status {} from motion unit {} ", motion, unitData.getName());
+        OnOffType status;
+        if (motion.equals(BSENSOR_STATUS_OPEN)) {
+            status = OnOffType.ON;
+        } else if (motion.equals(BSENSOR_STATUS_CLOSE)) {
+            status = OnOffType.OFF;
+        } else {
+            logger.debug("TouchWandBSensorUnitData illegal update value {}", motion);
+            return;
+        }
+        updateState(CHANNEL_MOTION, status);
+    }
+
+    void updateChannelSmoke(TouchWandBSensorUnitData unitData) {
+        String hasSmoke = unitData.getCurrStatus();
+        OnOffType status;
+        if (hasSmoke.equals(BSENSOR_STATUS_OPEN)) {
+            status = OnOffType.ON;
+        } else if (hasSmoke.equals(BSENSOR_STATUS_CLOSE)) {
+            status = OnOffType.OFF;
+        } else {
+            logger.debug("TouchWandBSensorUnitData illegal update value {}", hasSmoke);
+            return;
+        }
+        updateState(CHANNEL_SMOKE, status);
+    }
+
+    void removeUnsupportedChannels(TouchWandBSensorUnitData unitData) {
+        ArrayList<Channel> toBeRemovedChannels = new ArrayList<>(thing.getChannels());
+        String sensorSubType = unitData.getIdData().getSubType();
+        switch (sensorSubType) {
+            case BSENSOR_SUBTYPE_DOORWINDOW:
+                toBeRemovedChannels.remove(thing.getChannel(CHANNEL_DOORWINDOW));
+                break;
+            case BSENSOR_SUBTYPE_MOTION:
+                toBeRemovedChannels.remove(thing.getChannel(CHANNEL_MOTION));
+                break;
+            case BSENSOR_SUBTYPE_SMOKE:
+                Channel channel = thing.getChannel(CHANNEL_SMOKE);
+                toBeRemovedChannels.remove(channel);
+                break;
+        }
+
+        ThingBuilder thingBuilder = editThing();
+        thingBuilder.withoutChannels(toBeRemovedChannels);
+        updateThing(thingBuilder.build());
+    }
+}
index ad871b396d404cbf3ef446f8a45621ee6db0d69b..3f6e2c6d21f8728473cd6d5e4fba5df60e922022 100644 (file)
@@ -47,7 +47,8 @@ public abstract class TouchWandBaseUnitHandler extends BaseThingHandler implemen
     private static final int UNITS_STATUS_UPDATE_DELAY_SEC = 1;
     protected final Logger logger = LoggerFactory.getLogger(TouchWandBaseUnitHandler.class);
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_SHUTTER, THING_TYPE_SWITCH,
-            THING_TYPE_WALLCONTROLLER, THING_TYPE_DIMMER, THING_TYPE_ALARMSENSOR);
+            THING_TYPE_WALLCONTROLLER, THING_TYPE_DIMMER, THING_TYPE_ALARMSENSOR, THING_TYPE_BSENSOR,
+            THING_TYPE_THERMOSTAT);
     protected String unitId = "";
 
     protected @Nullable TouchWandBridgeHandler bridgeHandler;
index cdce6602583b6ae6fbfdc60f45654bec8bb917c6..995992777f4289e92e21745c7eb5b0726cc66db6 100644 (file)
@@ -37,6 +37,8 @@ public class TouchWandBindingConstants {
     public static final ThingTypeUID THING_TYPE_WALLCONTROLLER = new ThingTypeUID(BINDING_ID, "wallcontroller");
     public static final ThingTypeUID THING_TYPE_DIMMER = new ThingTypeUID(BINDING_ID, "dimmer");
     public static final ThingTypeUID THING_TYPE_ALARMSENSOR = new ThingTypeUID(BINDING_ID, "alarmsensor");
+    public static final ThingTypeUID THING_TYPE_BSENSOR = new ThingTypeUID(BINDING_ID, "bsensor");
+    public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");
 
     // List of all Channel ids
     public static final String CHANNEL_SWITCH = "switch";
@@ -51,6 +53,12 @@ public class TouchWandBindingConstants {
     public static final String CHANNEL_ILLUMINATION = "illumination";
     public static final String CHANNEL_DOORWINDOW = "isOpen";
     public static final String CHANNEL_TEMPERATURE = "temperature";
+    public static final String CHANNEL_THERMOSTAT_STATE = "state";
+    public static final String CHANNEL_THERMOSTAT_TARGET_TEMPERATURE = "targetTemperature";
+    public static final String CHANNEL_THERMOSTAT_ROOM_TEMPERATURE = "roomTemperature";
+    public static final String CHANNEL_THERMOSTAT_MODE = "mode";
+    public static final String CHANNEL_THERMOSTAT_FAN_LEVEL = "fanLevel";
+    public static final String CHANNEL_SMOKE = "smoke";
 
     // List of configuration parameters
 
@@ -70,11 +78,18 @@ public class TouchWandBindingConstants {
 
     public static final String CONNECTIVITY_KNX = "knx";
     public static final String CONNECTIVITY_ZWAVE = "zwave";
+    public static final String CONNECTIVITY_RISCO = "risco";
+    public static final String CONNECTIVITY_PIMA = "pima";
+    public static final String CONNECTIVITY_ACWAND = "acwand";
 
     // commands
+
     public static final String SWITCH_STATUS_ON = "255";
     public static final String SWITCH_STATUS_OFF = "0";
 
+    // thermostat commands
+    public static final String THERMOSTAT_STATE_ON = "1";
+    public static final String THERMOSTAT_STATE_OFF = "0";
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
 
     static {
@@ -83,6 +98,8 @@ public class TouchWandBindingConstants {
         SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_WALLCONTROLLER);
         SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_DIMMER);
         SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_ALARMSENSOR);
+        SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_BSENSOR);
+        SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_THERMOSTAT);
     }
 
     public static final String TYPE_WALLCONTROLLER = "WallController";
@@ -90,6 +107,8 @@ public class TouchWandBindingConstants {
     public static final String TYPE_SHUTTER = "shutter";
     public static final String TYPE_DIMMER = "dimmer";
     public static final String TYPE_ALARMSENSOR = "AlarmSensor";
+    public static final String TYPE_BSENSOR = "bsensor";
+    public static final String TYPE_THERMOSTAT = "thermostat";
     public static final String TYPE_UNKNOWN = "unknown";
 
     public static final int SENSOR_TYPE_TEMPERATURE = 1;
@@ -98,6 +117,15 @@ public class TouchWandBindingConstants {
     public static final int SENSOR_TYPE_DOOR_WINDOW = 10;
     public static final int SENSOR_TYPE_MOTION = 12;
 
+    // bsensor currStatus options
+
+    public static final String BSENSOR_STATUS_OPEN = "open";
+    public static final String BSENSOR_STATUS_CLOSE = "close";
+
+    public static final String BSENSOR_SUBTYPE_DOORWINDOW = "doors &windows";
+    public static final String BSENSOR_SUBTYPE_MOTION = "motion";
+    public static final String BSENSOR_SUBTYPE_SMOKE = "smoke";
+
     public static final String[] SUPPORTED_TOUCHWAND_TYPES = { TYPE_WALLCONTROLLER, TYPE_SWITCH, TYPE_SHUTTER,
-            TYPE_DIMMER, TYPE_ALARMSENSOR };
+            TYPE_DIMMER, TYPE_ALARMSENSOR, TYPE_BSENSOR, TYPE_THERMOSTAT };
 }
index 9cd41581d43f4b33e9e008f3fe8e9a2e0db47298..71af0e7547810db27c42857f8633e4471f58b5d7 100644 (file)
@@ -47,7 +47,6 @@ import org.slf4j.LoggerFactory;
 public class TouchWandBridgeHandler extends BaseBridgeHandler implements TouchWandUnitStatusUpdateListener {
     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE);
     private final Logger logger = LoggerFactory.getLogger(TouchWandBridgeHandler.class);
-    private int statusRefreshRateSec;
     private boolean addSecondaryUnits;
     private @Nullable TouchWandWebSockets touchWandWebSockets;
     private Map<String, TouchWandUnitUpdateListener> unitUpdateListeners = new ConcurrentHashMap<>();
@@ -73,7 +72,6 @@ public class TouchWandBridgeHandler extends BaseBridgeHandler implements TouchWa
 
         host = config.ipAddress;
         port = config.port;
-        statusRefreshRateSec = config.statusrefresh;
         addSecondaryUnits = config.addSecondaryUnits;
 
         isRunning = true;
@@ -108,10 +106,6 @@ public class TouchWandBridgeHandler extends BaseBridgeHandler implements TouchWa
         return addSecondaryUnits;
     }
 
-    public int getStatusRefreshTime() {
-        return statusRefreshRateSec;
-    }
-
     @Override
     public synchronized void dispose() {
         isRunning = false;
index e644dc2c2c26a09ac6eb7ec106f3dc3769f9a617..dd0385682928beb0da61b05b13b995b2edf60991 100644 (file)
@@ -70,6 +70,10 @@ public class TouchWandHandlerFactory extends BaseThingHandlerFactory {
             return new TouchWandDimmerHandler(thing);
         } else if (THING_TYPE_ALARMSENSOR.equals(thingTypeUID)) {
             return new TouchWandAlarmSensorHandler(thing);
+        } else if (THING_TYPE_BSENSOR.equals(thingTypeUID)) {
+            return new TouchWandBSensorHandler(thing);
+        } else if (THING_TYPE_THERMOSTAT.equals(thingTypeUID)) {
+            return new TouchWandThermostatHandler(thing);
         }
 
         return null;
index b1405923491f981dfbda85a067411c1e02c4f6e7..d865f95ce69642b35c66474094184cf757077496 100644 (file)
@@ -67,6 +67,11 @@ public class TouchWandRestClient {
     private static final String ACTION_SHUTTER_STOP = "{\"id\":%s,\"value\":0,\"type\":\"stop\"}";
     private static final String ACTION_SHUTTER_POSITION = "{\"id\":%s,\"value\":%s}";
     private static final String ACTION_DIMMER_POSITION = "{\"id\":%s,\"value\":%s}";
+    private static final String ACTION_THERMOSTAT_ON = "{\"id\":%s,\"value\":" + THERMOSTAT_STATE_ON + "}";
+    private static final String ACTION_THERMOSTAT_OFF = "{\"id\":%s,\"value\":" + THERMOSTAT_STATE_OFF + "}";
+    private static final String ACTION_THERMOSTAT_MODE = "{\"id\":%s,\"ac-all\":\"mode\",\"fan\":\"%s\"}";
+    private static final String ACTION_THERMOSTAT_FAN_LEVEL = "{\"id\":%s,\"ac-all\":\"fan\",\"fan\":\"%s\"}";
+    private static final String ACTION_THERMOSTAT_TARGET_TEMPERATURE = "{\"id\":%s,\"ac-all\":\"temp\",\"temp_val\":%s}";
 
     private static final String CONTENT_TYPE_APPLICATION_JSON = MimeTypes.Type.APPLICATION_JSON.asString();
 
@@ -170,6 +175,32 @@ public class TouchWandRestClient {
         cmdUnitAction(action);
     }
 
+    public void cmdThermostatOnOff(String id, OnOffType onoff) {
+        String action;
+
+        if (OnOffType.OFF.equals(onoff)) {
+            action = String.format(ACTION_THERMOSTAT_OFF, id);
+        } else {
+            action = String.format(ACTION_THERMOSTAT_ON, id);
+        }
+        cmdUnitAction(action);
+    }
+
+    public void cmdThermostatMode(String id, String mode) {
+        String action = String.format(ACTION_THERMOSTAT_MODE, id, mode);
+        cmdUnitAction(action);
+    }
+
+    public void cmdThermostatFanLevel(String id, String fanLevel) {
+        String action = String.format(ACTION_THERMOSTAT_FAN_LEVEL, id, fanLevel);
+        cmdUnitAction(action);
+    }
+
+    public void cmdThermostatTargetTemperature(String id, String targetTemperature) {
+        String action = String.format(ACTION_THERMOSTAT_TARGET_TEMPERATURE, id, targetTemperature);
+        cmdUnitAction(action);
+    }
+
     private String cmdUnitAction(String action) {
         String response = "";
         if (isConnected) {
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandThermostatHandler.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/TouchWandThermostatHandler.java
new file mode 100644 (file)
index 0000000..dd755dc
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal;
+
+import static org.openhab.binding.touchwand.internal.TouchWandBindingConstants.*;
+
+import javax.measure.quantity.Temperature;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.touchwand.internal.dto.TouchWandThermostatUnitData;
+import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.library.unit.SIUnits;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.types.Command;
+
+/**
+ * The {@link TouchWandAlarmSensorHandler} is responsible for handling command for Alarm Sensor unit
+ *
+ * @author Roie Geron - Initial contribution
+ */
+@NonNullByDefault
+public class TouchWandThermostatHandler extends TouchWandBaseUnitHandler {
+
+    public TouchWandThermostatHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    void updateTouchWandUnitState(TouchWandUnitData unitData) {
+        if (unitData instanceof TouchWandThermostatUnitData) {
+            TouchWandThermostatUnitData thermostat = (TouchWandThermostatUnitData) unitData;
+            updateThermostatState(thermostat);
+            updateTargetTemperature(thermostat);
+            updateRoomTemperature(thermostat);
+            updateMode(thermostat);
+            updateFanLevel(thermostat);
+        } else {
+            logger.warn("updateTouchWandUnitState incompatible TouchWandUnitData instance");
+        }
+    }
+
+    @Override
+    void touchWandUnitHandleCommand(Command command) {
+        TouchWandBridgeHandler touchWandBridgeHandler = bridgeHandler;
+        if (touchWandBridgeHandler != null) {
+            if (command instanceof OnOffType) {
+                touchWandBridgeHandler.touchWandClient.cmdThermostatOnOff(unitId, (OnOffType) command);
+            } else {
+                String sCommand = command.toString();
+                switch (sCommand) {
+                    case "cool":
+                    case "heat":
+                    case "fan":
+                    case "auto":
+                    case "dry":
+                        touchWandBridgeHandler.touchWandClient.cmdThermostatMode(unitId, sCommand);
+                        break;
+                    case "low":
+                    case "medium":
+                    case "high":
+                        touchWandBridgeHandler.touchWandClient.cmdThermostatFanLevel(unitId, sCommand);
+                        break;
+                    case "fanAuto":
+                        touchWandBridgeHandler.touchWandClient.cmdThermostatFanLevel(unitId, "auto");
+                        break;
+                    default:
+                        touchWandBridgeHandler.touchWandClient.cmdThermostatTargetTemperature(unitId, sCommand);
+                        break;
+                }
+            }
+        }
+    }
+
+    void updateThermostatState(TouchWandThermostatUnitData unitData) {
+        String state = unitData.getCurrStatus().getState();
+        updateState(CHANNEL_THERMOSTAT_STATE, OnOffType.from(state));
+    }
+
+    void updateTargetTemperature(TouchWandThermostatUnitData unitData) {
+        int targetTemperature = unitData.getCurrStatus().getTargetTemperature();
+        QuantityType<Temperature> temperatureValue = new QuantityType<Temperature>(targetTemperature, SIUnits.CELSIUS);
+        updateState(CHANNEL_THERMOSTAT_TARGET_TEMPERATURE, temperatureValue);
+    }
+
+    void updateRoomTemperature(TouchWandThermostatUnitData unitData) {
+        int roomTemperature = unitData.getCurrStatus().getRoomTemperature();
+        QuantityType<Temperature> temperatureValue = new QuantityType<Temperature>(roomTemperature, SIUnits.CELSIUS);
+        updateState(CHANNEL_THERMOSTAT_ROOM_TEMPERATURE, temperatureValue);
+    }
+
+    void updateMode(TouchWandThermostatUnitData unitData) {
+        String mode = unitData.getCurrStatus().getMode();
+        StringType newVal = StringType.valueOf(mode);
+        updateState(CHANNEL_THERMOSTAT_MODE, newVal);
+    }
+
+    void updateFanLevel(TouchWandThermostatUnitData unitData) {
+        String fanLevel = unitData.getCurrStatus().getFanLevel();
+        StringType newVal = StringType.valueOf(fanLevel);
+        updateState(CHANNEL_THERMOSTAT_FAN_LEVEL, newVal);
+    }
+}
index e6f7c2a8316074dfce13ee4d832c45176bc537b9..4439f11df44183b092e61c0e355025ad494a58c0 100644 (file)
@@ -26,6 +26,5 @@ public class TouchwandBridgeConfiguration {
     public String password = "";
     public String ipAddress = "";
     public int port;
-    public int statusrefresh;
     public boolean addSecondaryUnits;
 }
index e2eff422da6b56332eb3009d4e5241fce18267bc..eb86b82f5fada4b0878b8d8393959fb456000a06 100644 (file)
@@ -31,7 +31,6 @@ import org.openhab.binding.touchwand.internal.dto.TouchWandUnitData;
 import org.openhab.binding.touchwand.internal.dto.TouchWandUnitFromJson;
 import org.openhab.core.config.discovery.AbstractDiscoveryService;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
-import org.openhab.core.config.discovery.DiscoveryService;
 import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.thing.ThingUID;
@@ -51,13 +50,13 @@ import com.google.gson.JsonSyntaxException;
  * @author Roie Geron - Initial contribution
  */
 @NonNullByDefault
-public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
-        implements DiscoveryService, ThingHandlerService {
+public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
 
     private static final int SEARCH_TIME_SEC = 10;
     private static final int SCAN_INTERVAL_SEC = 60;
     private static final int LINK_DISCOVERY_SERVICE_INITIAL_DELAY_SEC = 5;
-    private static final String[] CONNECTIVITY_OPTIONS = { CONNECTIVITY_KNX, CONNECTIVITY_ZWAVE };
+    private static final String[] CONNECTIVITY_OPTIONS = { CONNECTIVITY_KNX, CONNECTIVITY_ZWAVE, CONNECTIVITY_RISCO,
+            CONNECTIVITY_PIMA, CONNECTIVITY_ACWAND };
     private @NonNullByDefault({}) TouchWandBridgeHandler touchWandBridgeHandler;
     private final Logger logger = LoggerFactory.getLogger(TouchWandUnitDiscoveryService.class);
 
@@ -115,6 +114,12 @@ public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
                             case TYPE_ALARMSENSOR:
                                 addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_ALARMSENSOR);
                                 break;
+                            case TYPE_BSENSOR:
+                                addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_BSENSOR);
+                                break;
+                            case TYPE_THERMOSTAT:
+                                addDeviceDiscoveryResult(touchWandUnit, THING_TYPE_THERMOSTAT);
+                                break;
                             default:
                                 continue;
                         }
@@ -125,7 +130,7 @@ public class TouchWandUnitDiscoveryService extends AbstractDiscoveryService
                 }
             }
         } catch (JsonSyntaxException msg) {
-            logger.warn("Could not parse list units response {}", msg.getMessage());
+            logger.warn("Could not parse list units response error:{} ", msg.getMessage());
         }
     }
 
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/IdData.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/IdData.java
new file mode 100644 (file)
index 0000000..095290c
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link IdData} implements IdData data class.
+ *
+ * @author Roie Geron - Initial contribution
+ */
+@NonNullByDefault
+public class IdData {
+
+    private String status = "";
+    private String subType = "";
+    private String zone = "";
+    private String id = "";
+    private boolean hasOff;
+    private boolean hasMode;
+    private String type = "";
+    private boolean isTempSensor;
+
+    public String getId() {
+        return this.id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public boolean isHasOff() {
+        return this.hasOff;
+    }
+
+    public boolean getHasOff() {
+        return this.hasOff;
+    }
+
+    public void setHasOff(boolean hasOff) {
+        this.hasOff = hasOff;
+    }
+
+    public boolean isHasMode() {
+        return this.hasMode;
+    }
+
+    public boolean getHasMode() {
+        return this.hasMode;
+    }
+
+    public void setHasMode(boolean hasMode) {
+        this.hasMode = hasMode;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public boolean isIsTempSensor() {
+        return this.isTempSensor;
+    }
+
+    public boolean getIsTempSensor() {
+        return this.isTempSensor;
+    }
+
+    public void setIsTempSensor(boolean isTempSensor) {
+        this.isTempSensor = isTempSensor;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getSubType() {
+        return subType;
+    }
+
+    public void setSubType(String subType) {
+        this.subType = subType;
+    }
+
+    public String getZone() {
+        return zone;
+    }
+
+    public void setZone(String zone) {
+        this.zone = zone;
+    }
+}
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandBSensorUnitData.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandBSensorUnitData.java
new file mode 100644 (file)
index 0000000..5a27487
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal.dto;
+
+/**
+ * The {@link TouchWandBSensorUnitData} implements BSensor units property.
+ *
+ * @author Roie Geron - Initial contribution
+ */
+public class TouchWandBSensorUnitData extends TouchWandUnitData {
+
+    private String currStatus = "";
+
+    private IdData idData;
+
+    @Override
+    public String getCurrStatus() {
+        if (currStatus == null) {
+            currStatus = new String("");
+        }
+        return currStatus;
+    }
+
+    public void setCurrStatus(String currStatus) {
+        this.currStatus = currStatus;
+    }
+
+    public IdData getIdData() {
+        return idData;
+    }
+
+    public void setIdData(IdData idData) {
+        this.idData = idData;
+    }
+}
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatCurrentStatus.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatCurrentStatus.java
new file mode 100644 (file)
index 0000000..bc84d0a
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link TouchWandThermostatCurrentStatus} implements Thermostat unit
+ * CurrentStatus data property.
+ *
+ * @author Roie Geron - Initial contribution
+ */
+
+@NonNullByDefault
+public class TouchWandThermostatCurrentStatus {
+
+    private String ac_type = "";
+    private int value;
+    private int thermo_mode;
+    private String mode = "";
+    private int temp;
+    private int thermo_temp;
+    private String state = "";
+    private int roomTemp;
+    private String fan = "";
+    private String communication_status = "";
+
+    public String getAcType() {
+        return this.ac_type;
+    }
+
+    public void setAcType(String ac_type) {
+        this.ac_type = ac_type;
+    }
+
+    public int getValue() {
+        return this.value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+
+    public int getThermoMode() {
+        return this.thermo_mode;
+    }
+
+    public void setThermoMode(int thermo_mode) {
+        this.thermo_mode = thermo_mode;
+    }
+
+    public String getMode() {
+        return this.mode;
+    }
+
+    public void setMode(String mode) {
+        this.mode = mode;
+    }
+
+    public int getTemp() {
+        return this.temp;
+    }
+
+    public void setTemp(int temp) {
+        this.temp = temp;
+    }
+
+    public int getTargetTemperature() {
+        return this.thermo_temp;
+    }
+
+    public void setTargetTemperature(int thermo_temp) {
+        this.thermo_temp = thermo_temp;
+    }
+
+    public String getState() {
+        return this.state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    public int getRoomTemperature() {
+        return this.roomTemp;
+    }
+
+    public void setRoomTemperature(int roomTemp) {
+        this.roomTemp = roomTemp;
+    }
+
+    public String getFanLevel() {
+        return this.fan;
+    }
+
+    public void setFanLevel(String fan) {
+        this.fan = fan;
+    }
+
+    public String getCommunicationStatus() {
+        return this.communication_status;
+    }
+
+    public void setCommunicationStatus(String communication_status) {
+        this.communication_status = communication_status;
+    }
+}
diff --git a/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatUnitData.java b/bundles/org.openhab.binding.touchwand/src/main/java/org/openhab/binding/touchwand/internal/dto/TouchWandThermostatUnitData.java
new file mode 100644 (file)
index 0000000..090c21d
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2010-2021 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.touchwand.internal.dto;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link TouchWandUnitDataAlarmSensor} implements Alarm Sensor unit
+ * data property.
+ *
+ * @author Roie Geron - Initial contribution
+ */
+@NonNullByDefault
+public class TouchWandThermostatUnitData extends TouchWandUnitData {
+
+    private TouchWandThermostatCurrentStatus currStatus = new TouchWandThermostatCurrentStatus();
+
+    @Override
+    public TouchWandThermostatCurrentStatus getCurrStatus() {
+        return this.currStatus;
+    }
+}
index 4816a29aea525d41cd2dd4784b66362662fc2e51..9550ec4f505e55d3e67bd38abe4080a9278491d7 100644 (file)
  */
 package org.openhab.binding.touchwand.internal.dto;
 
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
 /**
  * The {@link TouchWandUnitData} implements unit property.
  *
  * @author Roie Geron - Initial contribution
  */
-@NonNullByDefault
 public abstract class TouchWandUnitData {
 
     private Integer id = 0;
@@ -99,6 +96,9 @@ public abstract class TouchWandUnitData {
     }
 
     public String getStatus() {
+        if (status == null) {
+            status = new String("");
+        }
         return status;
     }
 
index a904d513827bd20b8688a16ac24ba972dbd6cb8c..f550d04b97f2d692d54f1418847463a2cbe48907 100644 (file)
@@ -66,6 +66,12 @@ public class TouchWandUnitFromJson {
                         .create();
                 touchWandUnit = builder.fromJson(jsonUnit, TouchWandUnitDataAlarmSensor.class);
                 break;
+            case TYPE_BSENSOR:
+                touchWandUnit = gson.fromJson(jsonUnit, TouchWandBSensorUnitData.class);
+                break;
+            case TYPE_THERMOSTAT:
+                touchWandUnit = gson.fromJson(jsonUnit, TouchWandThermostatUnitData.class);
+                break;
             case TYPE_UNKNOWN:
                 touchWandUnit = new TouchWandUnknownTypeUnitData();
                 break;
index a586c03d73709303e123c007606a29c818c14f0a..9c8bdeb8f948097d4af5d740f4499d3742db6bc2 100644 (file)
                                <default>80</default>
                                <advanced>true</advanced>
                        </parameter>
-                       <parameter name="statusrefresh" type="integer" unit="s">
-                               <default>120</default>
-                               <description>Unit status refresh interval (seconds)</description>
-                               <label>Refresh</label>
-                               <advanced>true</advanced>
-                       </parameter>
                        <parameter name="addSecondaryUnits" type="boolean">
                                <default>false</default>
                                <description>If the controller is primary, add secondary controllers units as well</description>
diff --git a/bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/bsensor.xml b/bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/bsensor.xml
new file mode 100644 (file)
index 0000000..123547a
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="touchwand"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <thing-type id="bsensor">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"></bridge-type-ref>
+               </supported-bridge-type-refs>
+               <label>TouchWand Binary Sensor Unit</label>
+               <channels>
+                       <channel id="isOpen" typeId="isOpen"/>
+                       <channel id="motion" typeId="motion"/>
+                       <channel id="smoke" typeId="smoke"/>
+               </channels>
+       </thing-type>
+       <channel-type id="isOpen">
+               <item-type>Contact</item-type>
+               <label>Open Status</label>
+               <category>Contact</category>
+               <state readOnly="true"></state>
+       </channel-type>
+       <channel-type id="motion">
+               <item-type>Switch</item-type>
+               <label>Motion Detected</label>
+               <category>Motion</category>
+               <state readOnly="true"></state>
+       </channel-type>
+       <channel-type id="smoke">
+               <item-type>Switch</item-type>
+               <label>Smoke Detected</label>
+               <category>Smoke</category>
+               <state readOnly="true"></state>
+       </channel-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/thermostat.xml b/bundles/org.openhab.binding.touchwand/src/main/resources/OH-INF/thing/thermostat.xml
new file mode 100644 (file)
index 0000000..a287a43
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="touchwand"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <thing-type id="thermostat">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bridge"></bridge-type-ref>
+               </supported-bridge-type-refs>
+               <label>TouchWand Thermostat Unit</label>
+               <channels>
+                       <channel id="state" typeId="state"/>
+                       <channel id="targetTemperature" typeId="targetTemperature"/>
+                       <channel id="roomTemperature" typeId="roomTemperature"/>
+                       <channel id="mode" typeId="mode"/>
+                       <channel id="fanLevel" typeId="fanLevel"/>
+               </channels>
+       </thing-type>
+       <channel-type id="state">
+               <item-type>Switch</item-type>
+               <label>AC On Off State</label>
+       </channel-type>
+       <channel-type id="targetTemperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Target Temperature</label>
+               <description>The set point temperature</description>
+               <category>Temperature</category>
+               <state min="16" max="30" step="1" pattern="%.1f %unit%">
+               </state>
+       </channel-type>
+       <channel-type id="roomTemperature">
+               <item-type>Number:Temperature</item-type>
+               <label>Room Temperature</label>
+               <description>Current room temperature</description>
+               <category>Temperature</category>
+               <state pattern="%.1f %unit%" readOnly="true">
+               </state>
+       </channel-type>
+       <channel-type id="mode">
+               <item-type>String</item-type>
+               <label>Mode</label>
+               <description>Thermostat mode (Cool, Heat, Fan, Dry, Auto)</description>
+               <state readOnly="false">
+                       <options>
+                               <option value="cool">Cool</option>
+                               <option value="heat">Heat</option>
+                               <option value="fan">Fan</option>
+                               <option value="auto">Auto</option>
+                               <option value="dry">Dry</option>
+                       </options>
+               </state>
+       </channel-type>
+       <channel-type id="fanLevel">
+               <item-type>String</item-type>
+               <label>Fan Level</label>
+               <description>Fan level (Low, Medium, High, Auto)</description>
+               <state readOnly="false">
+                       <options>
+                               <option value="low">Low</option>
+                               <option value="medium">Medium</option>
+                               <option value="high">High</option>
+                               <option value="fanAuto">Auto</option>
+                       </options>
+               </state>
+       </channel-type>
+</thing:thing-descriptions>