### Channels
Name | Type | Description | Read/Write
--|-|-|-|-
+-|-|-|-
`homePresence` | String | Current presence value of the tado home. `HOME` and `AWAY` can be set | RW
## `zone` Thing
`acPower` | Switch | Indicates if the Air-Conditioning is Off or On | R | `AC`
`hvacMode` | String | Active mode, one of `OFF`, `HEAT`, `COOL`, `DRY`, `FAN`, `AUTO` | RW | `HEATING` and `DHW` support `OFF` and `HEAT`, `AC` can support more
`targetTemperature` | Number:Temperature | Set point | RW | `HEATING`, `AC`, `DHW`
-`fanspeed` | String | Fan speed, one of `AUTO`, `LOW`, `MIDDLE`, `HIGH` | RW | `AC`
-`swing` | Switch | Swing on/off | RW | `AC`
+`fanspeed`<sup>1)</sup> | String | Fan speed, one of `AUTO`, `LOW`, `MIDDLE`, `HIGH` | RW | `AC`
+`fanLevel`<sup>1)</sup> | String | Fan speed, one of <sup>3)</sup> `AUTO`, `SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`, `LEVEL5` | RW | `AC`
+`swing`<sup>2)</sup> | Switch | Swing on/off | RW | `AC`
+`verticalSwing`<sup>2)</sup> | String | Vertical swing state, one of <sup>3)</sup> `OFF`, `ON`, `UP`, `MID_UP`, `MID`, `MID_DOWN`, `DOWN`, `AUTO` | RW | `AC`
+`horizontalSwing`<sup>2)</sup> | String | Horizontal swing state, one of <sup>3)</sup> `OFF`, `ON`, `LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`, `AUTO` | RW | `AC`
`overlayExpiry` | DateTime | End date and time of a timer | R | `HEATING`, `AC`, `DHW`
`timerDuration` | Number | Timer duration in minutes | RW | `HEATING`, `AC`, `DHW`
`operationMode` | String | Operation mode the zone is currently in. One of `SCHEDULE` (follow smart schedule), `MANUAL` (override until ended manually), `TIMER` (override for a given time), `UNTIL_CHANGE` (active until next smart schedule block or until AWAY mode becomes active) | RW | `HEATING`, `AC`, `DHW`
`batteryLowAlarm` | Switch | A control device in the Zone has a low battery (if applicable) | R | Any Zone
`openWindowDetected` | Switch | An open window has been detected in the Zone | R | Any Zone
+`light` | Switch | State (`ON`, `OFF`) of the control panel light (if applicable) | RW | `AC`
The `RW` items are used to either override the schedule or to return to it (if `hvacMode` is set to `SCHEDULE`).
+<sup>1)</sup> Simpler A/C units have fan speed settings in the range [`LOW`, `MIDDLE`, `HIGH`].
+However, more sophisticated devices have settings in the range [`SILENT`, `LEVEL1`, `LEVEL2`, `LEVEL3`, `LEVEL4`].
+So you need to choose the respective channel type name that matches the features of your device.
+
+<sup>2)</sup> Simpler A/C units have a single switch type swing function that is either `ON` or `OFF`.
+However, more sophisticated devices may have either a vertical swing, a horizontal swing, or both, which could also have more complex settings.
+For example the horizontal swing function could simply be `ON` or it could have more complex settings in the range [`LEFT`, `MID_LEFT`, `MID`, `MID_RIGHT`, `RIGHT`].
+So you need to choose the respective channel type name that matches the features of your device.
+
+<sup>3)</sup> The _'one of'_ list contains all possible state values supported within the tado° binding.
+However, in reality different A/C units might only support a **_subset_** of those values.
+And indeed the subset of supported values might depend on the current state of the `acPower` and `hvacMode` channels.
+In that case, if you send a channel command value to an A/C unit which does not (currently) support that particular state value, then openHAB will report a '422' run-time error in the log.
+
### Item Command Collection
Item changes are not immediately applied, but instead collected and only when no change is done for 5 seconds (by default - see `hvacChangeDebounce` above), the combined HVAC change is sent to the server.
- HIGH
- AUTO
+ ACFanLevel:
+ type: string
+ description: Cooling system fan speed.
+ enum:
+ - SILENT
+ - LEVEL1
+ - LEVEL2
+ - LEVEL3
+ - LEVEL4
+ - LEVEL5
+ - AUTO
+
+ ACHorizontalSwing:
+ type: string
+ description: Horizontal swing.
+ enum:
+ - 'OFF'
+ - 'ON'
+ - LEFT
+ - MID_LEFT
+ - MID
+ - MID_RIGHT
+ - RIGHT
+ - AUTO
+
+ ACVerticalSwing:
+ type: string
+ description: Vertical swing.
+ enum:
+ - 'OFF'
+ - 'ON'
+ - UP
+ - MID_UP
+ - MID
+ - MID_DOWN
+ - DOWN
+ - AUTO
+
AcMode:
type: string
description: Cooling system mode.
swing:
description: Whether the angle of the air stream should be fixed or not, if power is `ON` and configuring this is supported in this AC mode.
$ref: "#/definitions/Power"
+ light:
+ description: State of the control panel light.
+ $ref: "#/definitions/Power"
+ fanLevel:
+ description: The desired fan speed level, if power is `ON` and fan speeds are supported in this AC mode.
+ $ref: "#/definitions/ACFanLevel"
+ verticalSwing:
+ description: Whether the angle of the vertical air stream should be fixed or not, if power is `ON` and configuring this is supported in this AC mode. And if it is fixed, determines the respective position.
+ $ref: "#/definitions/ACVerticalSwing"
+ horizontalSwing:
+ description: Whether the angle of the horizontal air stream should be fixed or not, if power is `ON` and configuring this is supported in this AC mode. And if it is fixed, determines the respective position.
+ $ref: "#/definitions/ACHorizontalSwing"
required:
- power
temperatures:
$ref: "#/definitions/TemperatureRange"
fanSpeeds:
- description: Cooling system fan speed.
+ description: Cooling system fan speeds.
type: array
items:
$ref: "#/definitions/AcFanSpeed"
swings:
- description: Cooling system swing mode.
+ description: Cooling system swing modes.
+ type: array
+ items:
+ $ref: "#/definitions/Power"
+ light:
+ description: Control panel light state. (Tado confusingly names this array without an 's')
type: array
items:
$ref: "#/definitions/Power"
+ fanLevel:
+ description: Cooling system fan speeds. (Tado confusingly names this array without an 's')
+ type: array
+ items:
+ $ref: "#/definitions/ACFanLevel"
+ horizontalSwing:
+ description: Cooling system horizontal swing modes. (Tado confusingly names this array without an 's')
+ type: array
+ items:
+ $ref: "#/definitions/ACHorizontalSwing"
+ verticalSwing:
+ description: Cooling system vertical swing modes. (Tado confusingly names this array without an 's')
+ type: array
+ items:
+ $ref: "#/definitions/ACVerticalSwing"
HeatingCapabilities:
x-discriminator-value: HEATING
public static final String CHANNEL_ZONE_SWING = "swing";
+ public static final String CHANNEL_ZONE_LIGHT = "light";
+
public static final String CHANNEL_ZONE_FAN_SPEED = "fanspeed";
public static enum FanSpeed {
AUTO
}
+ public static final String CHANNEL_ZONE_FAN_LEVEL = "fanLevel";
+
+ public static enum FanLevel {
+ SILENT,
+ LEVEL1,
+ LEVEL2,
+ LEVEL3,
+ LEVEL4,
+ LEVEL5,
+ AUTO
+ }
+
+ public static final String CHANNEL_ZONE_HORIZONTAL_SWING = "horizontalSwing";
+
+ public static enum HorizontalSwing {
+ OFF,
+ ON,
+ LEFT,
+ MID_LEFT,
+ MID,
+ MID_RIGHT,
+ RIGHT,
+ AUTO
+ }
+
+ public static final String CHANNEL_ZONE_VERTICAL_SWING = "verticalSwing";
+
+ public static enum VerticalSwing {
+ OFF,
+ ON,
+ UP,
+ MID_UP,
+ MID,
+ MID_DOWN,
+ DOWN,
+ AUTO
+ }
+
public static final String CHANNEL_ZONE_OPERATION_MODE = "operationMode";
public static enum OperationMode {
import java.io.IOException;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
import org.openhab.binding.tado.internal.api.model.Overlay;
return this;
}
+ public TadoHvacChange withFanLevel(FanLevel fanLevel) {
+ settingsBuilder.withFanLevel(fanLevel);
+ return this;
+ }
+
+ public TadoHvacChange withHorizontalSwing(HorizontalSwing horizontalSwing) {
+ settingsBuilder.withHorizontalSwing(horizontalSwing);
+ return this;
+ }
+
+ public TadoHvacChange withVerticalSwing(VerticalSwing verticalSwing) {
+ settingsBuilder.withVerticalSwing(verticalSwing);
+ return this;
+ }
+
public void apply() throws IOException, ApiException {
if (followSchedule) {
zoneHandler.removeOverlay();
return overlay;
}
+
+ public TadoHvacChange withLight(boolean lightOn) {
+ settingsBuilder.withLight(lightOn);
+ return this;
+ }
}
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
+import org.openhab.binding.tado.internal.api.model.ACFanLevel;
+import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
+import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
+import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
import org.openhab.binding.tado.internal.api.model.AcPowerDataPoint;
import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
public State getFanSpeed() {
if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
- CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
- return setting.getFanSpeed() != null ? StringType.valueOf(setting.getFanSpeed().getValue())
- : UnDefType.NULL;
- } else {
- return UnDefType.UNDEF;
+ AcFanSpeed result = ((CoolingZoneSetting) zoneState.getSetting()).getFanSpeed();
+ return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
}
+ return UnDefType.UNDEF;
}
public State getSwing() {
if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
- CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
- if (setting.getSwing() == null) {
- return UnDefType.NULL;
- } else if (setting.getSwing() == Power.ON) {
- return OnOffType.ON;
- } else {
- return OnOffType.OFF;
- }
- } else {
- return UnDefType.UNDEF;
+ Power result = ((CoolingZoneSetting) zoneState.getSetting()).getSwing();
+ return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
}
+ return UnDefType.UNDEF;
+ }
+
+ public State getFanLevel() {
+ if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
+ ACFanLevel result = ((CoolingZoneSetting) zoneState.getSetting()).getFanLevel();
+ return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
+ }
+ return UnDefType.UNDEF;
+ }
+
+ public State getHorizontalSwing() {
+ if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
+ ACHorizontalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getHorizontalSwing();
+ return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
+ }
+ return UnDefType.UNDEF;
+ }
+
+ public State getVerticalSwing() {
+ if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
+ ACVerticalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getVerticalSwing();
+ return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
+ }
+ return UnDefType.UNDEF;
}
public StringType getOperationMode() {
}
return OnOffType.OFF;
}
+
+ public State getLight() {
+ if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
+ Power result = ((CoolingZoneSetting) zoneState.getSetting()).getLight();
+ return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
+ }
+ return UnDefType.UNDEF;
+ }
}
*/
package org.openhab.binding.tado.internal.api;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
+import org.openhab.binding.tado.internal.api.model.ACFanLevel;
+import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
+import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
import org.openhab.binding.tado.internal.api.model.AcMode;
import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
return null;
}
+ public static ACFanLevel getFanLevel(FanLevel fanLevel) {
+ if (fanLevel == null) {
+ return null;
+ }
+
+ switch (fanLevel) {
+ case AUTO:
+ return ACFanLevel.AUTO;
+ case LEVEL1:
+ return ACFanLevel.LEVEL1;
+ case LEVEL2:
+ return ACFanLevel.LEVEL2;
+ case LEVEL3:
+ return ACFanLevel.LEVEL3;
+ case LEVEL4:
+ return ACFanLevel.LEVEL4;
+ case LEVEL5:
+ return ACFanLevel.LEVEL5;
+ case SILENT:
+ return ACFanLevel.SILENT;
+ }
+
+ return null;
+ }
+
+ public static ACHorizontalSwing getHorizontalSwing(HorizontalSwing horizontalSwing) {
+ if (horizontalSwing == null) {
+ return null;
+ }
+
+ switch (horizontalSwing) {
+ case LEFT:
+ return ACHorizontalSwing.LEFT;
+ case MID_LEFT:
+ return ACHorizontalSwing.MID_LEFT;
+ case MID:
+ return ACHorizontalSwing.MID;
+ case MID_RIGHT:
+ return ACHorizontalSwing.MID_RIGHT;
+ case RIGHT:
+ return ACHorizontalSwing.RIGHT;
+ case ON:
+ return ACHorizontalSwing.ON;
+ case OFF:
+ return ACHorizontalSwing.OFF;
+ case AUTO:
+ return ACHorizontalSwing.AUTO;
+ }
+
+ return null;
+ }
+
+ public static ACVerticalSwing getVerticalSwing(VerticalSwing verticalSwing) {
+ if (verticalSwing == null) {
+ return null;
+ }
+
+ switch (verticalSwing) {
+ case AUTO:
+ return ACVerticalSwing.AUTO;
+ case UP:
+ return ACVerticalSwing.UP;
+ case MID_UP:
+ return ACVerticalSwing.MID_UP;
+ case MID:
+ return ACVerticalSwing.MID;
+ case MID_DOWN:
+ return ACVerticalSwing.MID_DOWN;
+ case DOWN:
+ return ACVerticalSwing.DOWN;
+ case ON:
+ return ACVerticalSwing.ON;
+ case OFF:
+ return ACVerticalSwing.OFF;
+ }
+
+ return null;
+ }
+
public static AcModeCapabilities getModeCapabilities(AirConditioningCapabilities capabilities, AcMode mode) {
AcModeCapabilities modeCapabilities = null;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
import org.openhab.binding.tado.internal.api.ApiException;
+import org.openhab.binding.tado.internal.api.model.ACFanLevel;
+import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
+import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
import org.openhab.binding.tado.internal.api.model.AcMode;
import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
setting.setSwing(swing ? Power.ON : Power.OFF);
}
+ if (light != null) {
+ setting.setLight(light ? Power.ON : Power.OFF);
+ }
+
if (fanSpeed != null) {
setting.setFanSpeed(getAcFanSpeed(fanSpeed));
}
+ if (fanLevel != null) {
+ setting.setFanLevel(getFanLevel(fanLevel));
+ }
+
+ if (horizontalSwing != null) {
+ setting.setHorizontalSwing(getHorizontalSwing(horizontalSwing));
+ }
+
+ if (verticalSwing != null) {
+ setting.setVerticalSwing(getVerticalSwing(verticalSwing));
+ }
+
addMissingSettingParts(zoneStateProvider, genericCapabilities, setting);
return setting;
AcModeCapabilities capabilities = getModeCapabilities((AirConditioningCapabilities) genericCapabilities,
setting.getMode());
- if (capabilities.getTemperatures() != null && setting.getTemperature() == null) {
- TemperatureObject targetTemperature = getCurrentOrDefaultTemperature(zoneStateProvider,
- capabilities.getTemperatures());
- setting.setTemperature(targetTemperature);
+ TemperatureRange temperatures = capabilities.getTemperatures();
+ if (temperatures != null && setting.getTemperature() == null) {
+ setting.setTemperature(getCurrentOrDefaultTemperature(zoneStateProvider, temperatures));
}
- if (capabilities.getFanSpeeds() != null && !capabilities.getFanSpeeds().isEmpty()
- && setting.getFanSpeed() == null) {
- AcFanSpeed fanSpeed = getCurrentOrDefaultFanSpeed(zoneStateProvider, capabilities.getFanSpeeds());
- setting.setFanSpeed(fanSpeed);
+ List<AcFanSpeed> fanSpeeds = capabilities.getFanSpeeds();
+ if (fanSpeeds != null && !fanSpeeds.isEmpty() && setting.getFanSpeed() == null) {
+ setting.setFanSpeed(getCurrentOrDefaultFanSpeed(zoneStateProvider, fanSpeeds));
}
- if (capabilities.getSwings() != null && !capabilities.getSwings().isEmpty() && setting.getSwing() == null) {
- Power swing = getCurrentOrDefaultSwing(zoneStateProvider, capabilities.getSwings());
- setting.setSwing(swing);
+ List<Power> swings = capabilities.getSwings();
+ if (swings != null && !swings.isEmpty() && setting.getSwing() == null) {
+ setting.setSwing(getCurrentOrDefaultSwing(zoneStateProvider, swings));
+ }
+
+ // Tado confusingly calls the List / getter method 'fanLevel' / 'getFanLevel()' without 's'
+ List<ACFanLevel> fanLevels = capabilities.getFanLevel();
+ if (fanLevels != null && !fanLevels.isEmpty() && setting.getFanLevel() == null) {
+ setting.setFanLevel(getCurrentOrDefaultFanLevel(zoneStateProvider, fanLevels));
+ }
+
+ // Tado confusingly calls the List / getter method 'horizontalSwing' / 'getHorizontalSwing()' without 's'
+ List<ACHorizontalSwing> horizontalSwings = capabilities.getHorizontalSwing();
+ if (horizontalSwings != null && !horizontalSwings.isEmpty() && setting.getHorizontalSwing() == null) {
+ setting.setHorizontalSwing(getCurrentOrDefaultHorizontalSwing(zoneStateProvider, horizontalSwings));
+ }
+
+ // Tado confusingly calls the List / getter method 'verticalSwing' / 'getVerticalSwing()' without 's'
+ List<ACVerticalSwing> verticalSwings = capabilities.getVerticalSwing();
+ if (verticalSwings != null && !verticalSwings.isEmpty() && setting.getVerticalSwing() == null) {
+ setting.setVerticalSwing(getCurrentOrDefaultVerticalSwing(zoneStateProvider, verticalSwings));
+ }
+
+ // Tado confusingly calls the List / getter method 'light' / 'getLight()' without 's'
+ List<Power> lights = capabilities.getLight();
+ if (lights != null && !lights.isEmpty() && setting.getLight() == null) {
+ setting.setLight(getCurrentOrDefaultLight(zoneStateProvider, lights));
}
}
return swings.get(0);
}
+ private Power getCurrentOrDefaultLight(ZoneStateProvider zoneStateProvider, List<Power> lights)
+ throws IOException, ApiException {
+ CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
+
+ if (zoneSetting.getLight() != null && lights.contains(zoneSetting.getLight())) {
+ return zoneSetting.getLight();
+ }
+
+ return lights.get(0);
+ }
+
+ private ACFanLevel getCurrentOrDefaultFanLevel(ZoneStateProvider zoneStateProvider, List<ACFanLevel> fanLevels)
+ throws IOException, ApiException {
+ CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
+
+ if (zoneSetting.getFanLevel() != null && fanLevels.contains(zoneSetting.getFanLevel())) {
+ return zoneSetting.getFanLevel();
+ }
+
+ return fanLevels.get(0);
+ }
+
+ private ACHorizontalSwing getCurrentOrDefaultHorizontalSwing(ZoneStateProvider zoneStateProvider,
+ List<ACHorizontalSwing> horizontalSwings) throws IOException, ApiException {
+ CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
+
+ if (zoneSetting.getHorizontalSwing() != null && horizontalSwings.contains(zoneSetting.getHorizontalSwing())) {
+ return zoneSetting.getHorizontalSwing();
+ }
+
+ return horizontalSwings.get(0);
+ }
+
+ private ACVerticalSwing getCurrentOrDefaultVerticalSwing(ZoneStateProvider zoneStateProvider,
+ List<ACVerticalSwing> verticalSwings) throws IOException, ApiException {
+ CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
+
+ if (zoneSetting.getVerticalSwing() != null && verticalSwings.contains(zoneSetting.getVerticalSwing())) {
+ return zoneSetting.getVerticalSwing();
+ }
+
+ return verticalSwings.get(0);
+ }
+
private CoolingZoneSetting coolingSetting(boolean powerOn) {
CoolingZoneSetting setting = new CoolingZoneSetting();
setting.setType(TadoSystemType.AIR_CONDITIONING);
import java.io.IOException;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
throw new IllegalArgumentException("Heating zones don't support SWING");
}
+ @Override
+ public ZoneSettingsBuilder withLight(boolean lightOn) {
+ throw new IllegalArgumentException("Heating zones don't support LIGHT");
+ }
+
@Override
public ZoneSettingsBuilder withFanSpeed(FanSpeed fanSpeed) {
throw new IllegalArgumentException("Heating zones don't support FAN SPEED");
}
+ @Override
+ public ZoneSettingsBuilder withFanLevel(FanLevel fanLevel) {
+ throw new IllegalArgumentException("Heating zones don't support FAN LEVEL");
+ }
+
+ @Override
+ public ZoneSettingsBuilder withHorizontalSwing(HorizontalSwing horizontalSwing) {
+ throw new IllegalArgumentException("Heating zones don't support HORIZONTAL SWING");
+ }
+
+ @Override
+ public ZoneSettingsBuilder withVerticalSwing(VerticalSwing verticalSwing) {
+ throw new IllegalArgumentException("Heating zones don't support VERTICAL SWING");
+ }
+
@Override
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
throws IOException, ApiException {
import java.io.IOException;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
throw new IllegalArgumentException("Hot Water zones don't support SWING");
}
+ @Override
+ public ZoneSettingsBuilder withLight(boolean lightOn) {
+ throw new IllegalArgumentException("Hot Water zones don't support LIGHT");
+ }
+
@Override
public ZoneSettingsBuilder withFanSpeed(FanSpeed fanSpeed) {
throw new IllegalArgumentException("Hot Water zones don't support FAN SPEED");
}
+ @Override
+ public ZoneSettingsBuilder withFanLevel(FanLevel fanLevel) {
+ throw new IllegalArgumentException("Hot Water zones don't support FAN LEVEL");
+ }
+
+ @Override
+ public ZoneSettingsBuilder withHorizontalSwing(HorizontalSwing horizontalSwing) {
+ throw new IllegalArgumentException("Hot Water zones don't support HORIZONTAL SWING");
+ }
+
+ @Override
+ public ZoneSettingsBuilder withVerticalSwing(VerticalSwing verticalSwing) {
+ throw new IllegalArgumentException("Hot Water zones don't support VERTICAL SWING");
+ }
+
@Override
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
throws IOException, ApiException {
import java.io.IOException;
import org.openhab.binding.tado.internal.TadoBindingConstants;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
protected Float temperature = null;
protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS;
protected Boolean swing = null;
+ protected Boolean light = null;
protected FanSpeed fanSpeed = null;
+ protected FanLevel fanLevel = null;
+ protected HorizontalSwing horizontalSwing = null;
+ protected VerticalSwing verticalSwing = null;
public ZoneSettingsBuilder withMode(HvacMode mode) {
this.mode = mode;
return this;
}
+ public ZoneSettingsBuilder withFanLevel(FanLevel fanLevel) {
+ this.fanLevel = fanLevel;
+ return this;
+ }
+
+ public ZoneSettingsBuilder withHorizontalSwing(HorizontalSwing horizontalSwing) {
+ this.horizontalSwing = horizontalSwing;
+ return this;
+ }
+
+ public ZoneSettingsBuilder withVerticalSwing(VerticalSwing verticalSwing) {
+ this.verticalSwing = verticalSwing;
+ return this;
+ }
+
public abstract GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
throws IOException, ApiException;
return temperatureObject;
}
+
+ public ZoneSettingsBuilder withLight(boolean lightOn) {
+ this.light = lightOn;
+ return this;
+ }
}
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
/**
* The {@link TadoHandlerFactory} is responsible for creating things and thing
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
+ private final TadoStateDescriptionProvider stateDescriptionProvider;
+
+ @Activate
+ public TadoHandlerFactory(final @Reference TadoStateDescriptionProvider stateDescriptionProvider) {
+ this.stateDescriptionProvider = stateDescriptionProvider;
+ }
+
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
registerTadoDiscoveryService(tadoHomeHandler);
return tadoHomeHandler;
} else if (thingTypeUID.equals(THING_TYPE_ZONE)) {
- return new TadoZoneHandler(thing);
+ return new TadoZoneHandler(thing, stateDescriptionProvider);
} else if (thingTypeUID.equals(THING_TYPE_MOBILE_DEVICE)) {
return new TadoMobileDeviceHandler(thing);
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.tado.internal.handler;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.events.EventPublisher;
+import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
+import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
+import org.openhab.core.thing.link.ItemChannelLinkRegistry;
+import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * The {@link TadoStateDescriptionProvider} is responsible for providing a dynamic state description of channels whose
+ * capabilities are not hard coded in the API.
+ *
+ * @author Andrew Fiddian-Green - Initial contribution
+ *
+ */
+@NonNullByDefault
+@Component(service = { DynamicStateDescriptionProvider.class, TadoStateDescriptionProvider.class })
+public class TadoStateDescriptionProvider extends BaseDynamicStateDescriptionProvider {
+
+ @Activate
+ public TadoStateDescriptionProvider(final @Reference EventPublisher eventPublisher,
+ final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry,
+ final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
+ this.eventPublisher = eventPublisher;
+ this.itemChannelLinkRegistry = itemChannelLinkRegistry;
+ this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
+ }
+}
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.terminationConditionTemplateToTerminationCondition;
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.ZoneType;
import org.openhab.binding.tado.internal.TadoHvacChange;
import org.openhab.binding.tado.internal.adapter.TadoZoneStateAdapter;
import org.openhab.binding.tado.internal.api.ApiException;
+import org.openhab.binding.tado.internal.api.TadoApiTypeUtils;
import org.openhab.binding.tado.internal.api.client.HomeApi;
+import org.openhab.binding.tado.internal.api.model.ACFanLevel;
+import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
+import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
+import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
+import org.openhab.binding.tado.internal.api.model.AirConditioningCapabilities;
+import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
+import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
import org.openhab.binding.tado.internal.api.model.Overlay;
import org.openhab.binding.tado.internal.api.model.OverlayTemplate;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
+import org.openhab.binding.tado.internal.api.model.TadoSystemType;
import org.openhab.binding.tado.internal.api.model.Zone;
import org.openhab.binding.tado.internal.api.model.ZoneState;
import org.openhab.binding.tado.internal.config.TadoZoneConfig;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
+import org.openhab.core.types.StateOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TadoZoneHandler extends BaseHomeThingHandler {
private Logger logger = LoggerFactory.getLogger(TadoZoneHandler.class);
+ private final TadoStateDescriptionProvider stateDescriptionProvider;
+
private TadoZoneConfig configuration;
private ScheduledFuture<?> refreshTimer;
private ScheduledFuture<?> scheduledHvacChange;
private GenericZoneCapabilities capabilities;
TadoHvacChange pendingHvacChange;
- public TadoZoneHandler(Thing thing) {
+ public TadoZoneHandler(Thing thing, TadoStateDescriptionProvider stateDescriptionProvider) {
super(thing);
+ this.stateDescriptionProvider = stateDescriptionProvider;
}
public long getZoneId() {
}
public Overlay setOverlay(Overlay overlay) throws IOException, ApiException {
- logger.debug("Setting overlay of home {} and zone {}", getHomeId(), getZoneId());
+ logger.debug("Setting overlay of home {} and zone {} with overlay: {}", getHomeId(), getZoneId(),
+ overlay.toString());
return getApi().updateZoneOverlay(getHomeId(), getZoneId(), overlay);
}
pendingHvacChange.withSwing(((OnOffType) command) == OnOffType.ON);
scheduleHvacChange();
break;
+ case TadoBindingConstants.CHANNEL_ZONE_LIGHT:
+ pendingHvacChange.withLight(((OnOffType) command) == OnOffType.ON);
+ scheduleHvacChange();
+ break;
case TadoBindingConstants.CHANNEL_ZONE_FAN_SPEED:
pendingHvacChange.withFanSpeed(((StringType) command).toFullString());
scheduleHvacChange();
break;
+ case TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL:
+ String fanLevelString = ((StringType) command).toFullString();
+ pendingHvacChange.withFanLevel(FanLevel.valueOf(fanLevelString.toUpperCase()));
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING:
+ String horizontalSwingString = ((StringType) command).toFullString();
+ pendingHvacChange.withHorizontalSwing(HorizontalSwing.valueOf(horizontalSwingString.toUpperCase()));
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING:
+ String verticalSwingString = ((StringType) command).toFullString();
+ pendingHvacChange.withVerticalSwing(VerticalSwing.valueOf(verticalSwingString.toUpperCase()));
+ scheduleHvacChange();
+ break;
case TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE:
String operationMode = ((StringType) command).toFullString();
pendingHvacChange.withOperationMode(OperationMode.valueOf(operationMode));
updateProperty(TadoBindingConstants.PROPERTY_ZONE_NAME, zoneDetails.getName());
updateProperty(TadoBindingConstants.PROPERTY_ZONE_TYPE, zoneDetails.getType().name());
this.capabilities = capabilities;
+ logger.debug("Got capabilities: {}", capabilities.toString());
} catch (IOException | ApiException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Could not connect to server due to " + e.getMessage());
updateState(TadoBindingConstants.CHANNEL_ZONE_TARGET_TEMPERATURE, state.getTargetTemperature());
updateState(TadoBindingConstants.CHANNEL_ZONE_FAN_SPEED, state.getFanSpeed());
updateState(TadoBindingConstants.CHANNEL_ZONE_SWING, state.getSwing());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_LIGHT, state.getLight());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL, state.getFanLevel());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING, state.getHorizontalSwing());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING, state.getVerticalSwing());
updateState(TadoBindingConstants.CHANNEL_ZONE_TIMER_DURATION, state.getRemainingTimerDuration());
updateState(TadoBindingConstants.CHANNEL_ZONE_OPEN_WINDOW_DETECTED, state.getOpenWindowDetected());
+ updateDynamicStateDescriptions(zoneState);
+
onSuccessfulOperation();
} catch (IOException | ApiException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
}
}
+ /**
+ * Update the dynamic state descriptions for any channels which support an unknown sub- range of enumerator setting
+ * values, based on the list of capabilities reported by the respective zone.
+ *
+ * Note: currently this only applies to A/C devices that support fanLevel, horizontalSwing, or verticalSwing.
+ *
+ * @param zoneState the current zone Thing's state
+ */
+ private void updateDynamicStateDescriptions(ZoneState zoneState) {
+ GenericZoneSetting setting = zoneState.getSetting();
+ if (setting.getType() != TadoSystemType.AIR_CONDITIONING) {
+ return;
+ }
+
+ AcModeCapabilities acCapabilities = TadoApiTypeUtils.getModeCapabilities(
+ (AirConditioningCapabilities) capabilities, ((CoolingZoneSetting) setting).getMode());
+
+ if (acCapabilities != null) {
+ Channel channel;
+
+ // update the options list of supported fan levels
+ channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL);
+ List<ACFanLevel> fanLevels = acCapabilities.getFanLevel();
+ if (channel != null && fanLevels != null) {
+ stateDescriptionProvider.setStateOptions(channel.getUID(),
+ fanLevels.stream().map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
+ }
+
+ // update the options list of supported horizontal swing settings
+ channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING);
+ List<ACHorizontalSwing> horizontalSwings = acCapabilities.getHorizontalSwing();
+ if (channel != null && horizontalSwings != null) {
+ stateDescriptionProvider.setStateOptions(channel.getUID(), horizontalSwings.stream()
+ .map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
+ }
+
+ // update the options list of supported vertical swing settings
+ channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING);
+ List<ACVerticalSwing> verticalSwings = acCapabilities.getVerticalSwing();
+ if (channel != null && verticalSwings != null) {
+ stateDescriptionProvider.setStateOptions(channel.getUID(), verticalSwings.stream()
+ .map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
+ }
+ }
+ }
+
private void scheduleZoneStateUpdate() {
if (refreshTimer == null || refreshTimer.isCancelled()) {
refreshTimer = scheduler.scheduleWithFixedDelay(new Runnable() {
channel-type.tado.atHome.description = ON if at home, OFF if away
channel-type.tado.currentTemperature.label = Temperature
channel-type.tado.currentTemperature.description = Current temperature
+
channel-type.tado.fanspeed.label = Fan Speed
channel-type.tado.fanspeed.description = AC fan speed (only if supported by AC)
channel-type.tado.fanspeed.state.option.LOW = Low
channel-type.tado.fanspeed.state.option.MIDDLE = Middle
channel-type.tado.fanspeed.state.option.HIGH = High
channel-type.tado.fanspeed.state.option.AUTO = Auto
+
+channel-type.tado.fanLevel.label = Fan Level
+channel-type.tado.fanLevel.description = AC fan speed (only if supported by AC)
+channel-type.tado.fanLevel.state.option.SILENT = Silent fan
+channel-type.tado.fanLevel.state.option.LEVEL1 = Fan level 1
+channel-type.tado.fanLevel.state.option.LEVEL2 = Fan level 2
+channel-type.tado.fanLevel.state.option.LEVEL3 = Fan level 3
+channel-type.tado.fanLevel.state.option.LEVEL4 = Fan level 4
+channel-type.tado.fanLevel.state.option.LEVEL5 = Fan level 5
+channel-type.tado.fanLevel.state.option.AUTO = Auto fan speed
+
channel-type.tado.heatingPower.label = Heating Power
channel-type.tado.heatingPower.description = Current heating power
channel-type.tado.homePresence.label = At Home
channel-type.tado.overlayExpiry.state.pattern = %1$tF %1$tR
channel-type.tado.swing.label = Swing
channel-type.tado.swing.description = State of AC swing (only if supported by AC)
+
+channel-type.tado.light.label = Light
+channel-type.tado.light.description = State of control panel light (only if supported by AC)
+
+channel-type.tado.horizontalSwing.label = Horizontal Swing
+channel-type.tado.horizontalSwing.description = State of AC Horizontal swing (only if supported by AC)
+channel-type.tado.horizontalSwing.state.option.AUTO = AUTO
+channel-type.tado.horizontalSwing.state.option.LEFT = LEFT
+channel-type.tado.horizontalSwing.state.option.MID_LEFT = MID-LEFT
+channel-type.tado.horizontalSwing.state.option.MID = MID
+channel-type.tado.horizontalSwing.state.option.MID_RIGHT = MID-RIGHT
+channel-type.tado.horizontalSwing.state.option.RIGHT = RIGHT
+channel-type.tado.horizontalSwing.state.option.ON = ON
+channel-type.tado.horizontalSwing.state.option.OFF = OFF
+
+channel-type.tado.verticalSwing.label = Vertical Swing
+channel-type.tado.verticalSwing.description = State of AC Vertical swing (only if supported by AC)
+channel-type.tado.verticalSwing.state.option.AUTO = AUTO
+channel-type.tado.verticalSwing.state.option.UP = UP
+channel-type.tado.verticalSwing.state.option.MID_UP = MID-UP
+channel-type.tado.verticalSwing.state.option.MID = MID
+channel-type.tado.verticalSwing.state.option.MID_DOWN = MID-DOWN
+channel-type.tado.verticalSwing.state.option.DOWN = DOWN
+channel-type.tado.verticalSwing.state.option.ON = ON
+channel-type.tado.verticalSwing.state.option.OFF = OFF
+
channel-type.tado.targetTemperature.label = Target Temperature
channel-type.tado.targetTemperature.description = Thermostat temperature setpoint
channel-type.tado.timerDuration.label = Timer Duration
<channel typeId="targetTemperature" id="targetTemperature"></channel>
<channel typeId="fanspeed" id="fanspeed"></channel>
<channel typeId="swing" id="swing"></channel>
+ <channel typeId="light" id="light"></channel>
+ <channel typeId="fanLevel" id="fanLevel"></channel>
+ <channel typeId="horizontalSwing" id="horizontalSwing"></channel>
+ <channel typeId="verticalSwing" id="verticalSwing"></channel>
<channel typeId="overlayExpiry" id="overlayExpiry"></channel>
<channel typeId="timerDuration" id="timerDuration"></channel>
<description>State of AC swing (only if supported by AC)</description>
</channel-type>
+ <channel-type id="light">
+ <item-type>Switch</item-type>
+ <label>Light</label>
+ <description>State of control panel light (only if supported by AC)</description>
+ </channel-type>
+
+ <channel-type id="fanLevel">
+ <item-type>String</item-type>
+ <label>Fan Speed</label>
+ <description>AC fan level (only if supported by AC)</description>
+ <state readOnly="false">
+ <options>
+ <option value="SILENT">SILENT</option>
+ <option value="LEVEL1">LEVEL1</option>
+ <option value="LEVEL2">LEVEL2</option>
+ <option value="LEVEL3">LEVEL3</option>
+ <option value="LEVEL4">LEVEL4</option>
+ <option value="LEVEL5">LEVEL5</option>
+ <option value="AUTO">AUTO</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="horizontalSwing">
+ <item-type>String</item-type>
+ <label>Horizontal Swing</label>
+ <description>State of AC horizontal swing (only if supported by AC)</description>
+ <state readOnly="false">
+ <options>
+ <option value="AUTO">AUTO</option>
+ <option value="LEFT">LEFT</option>
+ <option value="MID_LEFT">MID-LEFT</option>
+ <option value="MID">MID</option>
+ <option value="MID_RIGHT">MID-RIGHT</option>
+ <option value="RIGHT">RIGHT</option>
+ <option value="ON">ON</option>
+ <option value="OFF">OFF</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="verticalSwing">
+ <item-type>String</item-type>
+ <label>Vertical Swing</label>
+ <description>State of AC vertical swing (only if supported by AC)</description>
+ <state readOnly="false">
+ <options>
+ <option value="AUTO">AUTO</option>
+ <option value="UP">UP</option>
+ <option value="MID_UP">UP</option>
+ <option value="MID">MID</option>
+ <option value="MID_DOWN">MID-DOWN</option>
+ <option value="DOWN">DOWN</option>
+ <option value="ON">ON</option>
+ <option value="OFF">OFF</option>
+ </options>
+ </state>
+ </channel-type>
+
<channel-type id="operationMode">
<item-type>String</item-type>
<label>Zone Operation Mode</label>