added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
101
bundles/org.openhab.binding.tado/src/main/api/pom.xml
Normal file
101
bundles/org.openhab.binding.tado/src/main/api/pom.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
|
||||
<groupId>org.openhab.binding.tado</groupId>
|
||||
<artifactId>api-client</artifactId>
|
||||
<version>1.3.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-codegen-maven-plugin</artifactId>
|
||||
<version>2.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>generate</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<inputSpec>${project.basedir}/tado-api.yaml</inputSpec>
|
||||
<language>com.github.dfrommi.swagger.OpenHABClientGenerator</language>
|
||||
|
||||
<apiPackage>org.openhab.binding.tado.internal.api.client</apiPackage>
|
||||
<modelPackage>org.openhab.binding.tado.internal.api.model</modelPackage>
|
||||
<invokerPackage>org.openhab.binding.tado.internal.api</invokerPackage>
|
||||
|
||||
<configOptions>
|
||||
<artifactId>tado</artifactId>
|
||||
<artifactVersion>${project.version}</artifactVersion>
|
||||
</configOptions>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.dfrommi</groupId>
|
||||
<artifactId>swagger-codegen-openhab</artifactId>
|
||||
<version>0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>bundle-manifest</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>manifest</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
<outputDirectory>${project.basedir}/../../../lib</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-client</artifactId>
|
||||
<version>9.4.12.v20180830</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
1006
bundles/org.openhab.binding.tado/src/main/api/tado-api.yaml
Normal file
1006
bundles/org.openhab.binding.tado/src/main/api/tado-api.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.tado-${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-tado" description="Tado Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.tado/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 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.tado.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link TadoBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
* @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TadoBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "tado";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_HOME = new ThingTypeUID(BINDING_ID, "home");
|
||||
public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone");
|
||||
public static final ThingTypeUID THING_TYPE_MOBILE_DEVICE = new ThingTypeUID(BINDING_ID, "mobiledevice");
|
||||
|
||||
// List of all Channel IDs
|
||||
public static final String PROPERTY_HOME_TEMPERATURE_UNIT = "temperatureUnit";
|
||||
|
||||
public static enum TemperatureUnit {
|
||||
CELSIUS,
|
||||
FAHRENHEIT
|
||||
}
|
||||
|
||||
public static final String CHANNEL_ZONE_CURRENT_TEMPERATURE = "currentTemperature";
|
||||
public static final String CHANNEL_ZONE_HUMIDITY = "humidity";
|
||||
|
||||
public static final String CHANNEL_ZONE_HEATING_POWER = "heatingPower";
|
||||
// air conditioning power
|
||||
public static final String CHANNEL_ZONE_AC_POWER = "acPower";
|
||||
|
||||
public static final String CHANNEL_ZONE_HVAC_MODE = "hvacMode";
|
||||
|
||||
public static enum HvacMode {
|
||||
OFF,
|
||||
HEAT,
|
||||
COOL,
|
||||
DRY,
|
||||
FAN,
|
||||
AUTO
|
||||
}
|
||||
|
||||
public static final String CHANNEL_ZONE_TARGET_TEMPERATURE = "targetTemperature";
|
||||
|
||||
public static final String CHANNEL_ZONE_SWING = "swing";
|
||||
|
||||
public static final String CHANNEL_ZONE_FAN_SPEED = "fanspeed";
|
||||
|
||||
public static enum FanSpeed {
|
||||
LOW,
|
||||
MIDDLE,
|
||||
HIGH,
|
||||
AUTO
|
||||
}
|
||||
|
||||
public static final String CHANNEL_ZONE_OPERATION_MODE = "operationMode";
|
||||
|
||||
public static enum OperationMode {
|
||||
SCHEDULE,
|
||||
TIMER,
|
||||
MANUAL,
|
||||
UNTIL_CHANGE
|
||||
}
|
||||
|
||||
public static final String CHANNEL_ZONE_TIMER_DURATION = "timerDuration";
|
||||
public static final String CHANNEL_ZONE_OVERLAY_EXPIRY = "overlayExpiry";
|
||||
|
||||
// battery low alarm channel
|
||||
public static final String CHANNEL_ZONE_BATTERY_LOW_ALARM = "batteryLowAlarm";
|
||||
// open window detected channel
|
||||
public static final String CHANNEL_ZONE_OPEN_WINDOW_DETECTED = "openWindowDetected";
|
||||
|
||||
public static final String CHANNEL_MOBILE_DEVICE_AT_HOME = "atHome";
|
||||
|
||||
// Configuration
|
||||
public static final String CONFIG_ZONE_ID = "id";
|
||||
public static final String CONFIG_MOBILE_DEVICE_ID = "id";
|
||||
|
||||
// Properties
|
||||
public static final String PROPERTY_ZONE_NAME = "name";
|
||||
public static final String PROPERTY_ZONE_TYPE = "type";
|
||||
|
||||
public static enum ZoneType {
|
||||
HEATING,
|
||||
AIR_CONDITIONING,
|
||||
HOT_WATER
|
||||
}
|
||||
|
||||
public static final String PROPERTY_MOBILE_DEVICE_NAME = "name";
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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.tado.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
|
||||
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;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
|
||||
import org.openhab.binding.tado.internal.builder.TerminationConditionBuilder;
|
||||
import org.openhab.binding.tado.internal.builder.ZoneSettingsBuilder;
|
||||
import org.openhab.binding.tado.internal.builder.ZoneStateProvider;
|
||||
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
||||
/**
|
||||
* Builder for incremental creation of zone overlays.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoHvacChange {
|
||||
private TadoZoneHandler zoneHandler;
|
||||
|
||||
private boolean followSchedule = false;
|
||||
private TerminationConditionBuilder terminationConditionBuilder;
|
||||
private ZoneSettingsBuilder settingsBuilder;
|
||||
|
||||
public TadoHvacChange(Thing zoneThing) {
|
||||
if (!(zoneThing.getHandler() instanceof TadoZoneHandler)) {
|
||||
throw new IllegalArgumentException("TadoZoneThing expected, but instead got " + zoneThing);
|
||||
}
|
||||
|
||||
this.zoneHandler = (TadoZoneHandler) zoneThing.getHandler();
|
||||
this.terminationConditionBuilder = TerminationConditionBuilder.of(zoneHandler);
|
||||
this.settingsBuilder = ZoneSettingsBuilder.of(zoneHandler);
|
||||
}
|
||||
|
||||
public TadoHvacChange withOperationMode(OperationMode operationMode) {
|
||||
switch (operationMode) {
|
||||
case SCHEDULE:
|
||||
return followSchedule();
|
||||
case MANUAL:
|
||||
return activeForever();
|
||||
case TIMER:
|
||||
return activeFor(null);
|
||||
case UNTIL_CHANGE:
|
||||
return activeUntilChange();
|
||||
default:
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public TadoHvacChange followSchedule() {
|
||||
followSchedule = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange activeForever() {
|
||||
terminationConditionBuilder.withTerminationType(OverlayTerminationConditionType.MANUAL);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange activeUntilChange() {
|
||||
terminationConditionBuilder.withTerminationType(OverlayTerminationConditionType.TADO_MODE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange activeFor(Integer minutes) {
|
||||
terminationConditionBuilder.withTerminationType(OverlayTerminationConditionType.TIMER);
|
||||
terminationConditionBuilder.withTimerDurationInSeconds(minutes != null ? minutes * 60 : null);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange withTemperature(float temperatureValue) {
|
||||
settingsBuilder.withTemperature(temperatureValue, zoneHandler.getTemperatureUnit());
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange withHvacMode(HvacMode mode) {
|
||||
settingsBuilder.withMode(mode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange withHvacMode(String mode) {
|
||||
return withHvacMode(HvacMode.valueOf(mode.toUpperCase()));
|
||||
}
|
||||
|
||||
public TadoHvacChange withSwing(boolean swingOn) {
|
||||
settingsBuilder.withSwing(swingOn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange withFanSpeed(FanSpeed fanSpeed) {
|
||||
settingsBuilder.withFanSpeed(fanSpeed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TadoHvacChange withFanSpeed(String fanSpeed) {
|
||||
withFanSpeed(FanSpeed.valueOf(fanSpeed.toUpperCase()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void apply() throws IOException, ApiException {
|
||||
if (followSchedule) {
|
||||
zoneHandler.removeOverlay();
|
||||
} else {
|
||||
Overlay overlay = buildOverlay();
|
||||
zoneHandler.setOverlay(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
private Overlay buildOverlay() throws IOException, ApiException {
|
||||
ZoneStateProvider zoneStateProvider = new ZoneStateProvider(zoneHandler);
|
||||
OverlayTerminationCondition terminationCondition = terminationConditionBuilder.build(zoneStateProvider);
|
||||
GenericZoneSetting setting = settingsBuilder.build(zoneStateProvider, zoneHandler.getZoneCapabilities());
|
||||
|
||||
Overlay overlay = new Overlay();
|
||||
overlay.setTermination(terminationCondition);
|
||||
overlay.setSetting(setting);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* 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.tado.internal.adapter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
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.AcPowerDataPoint;
|
||||
import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
|
||||
import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.Overlay;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
|
||||
import org.openhab.binding.tado.internal.api.model.Power;
|
||||
import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
|
||||
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.ZoneState;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
/**
|
||||
* Adapter from API-level zone state to the binding's item-based zone state.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
* @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
|
||||
*
|
||||
*/
|
||||
public class TadoZoneStateAdapter {
|
||||
private ZoneState zoneState;
|
||||
private TemperatureUnit temperatureUnit;
|
||||
|
||||
public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
|
||||
this.zoneState = zoneState;
|
||||
this.temperatureUnit = temperatureUnit;
|
||||
}
|
||||
|
||||
public State getInsideTemperature() {
|
||||
SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
|
||||
return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
|
||||
}
|
||||
|
||||
public DecimalType getHumidity() {
|
||||
SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
|
||||
return sensorDataPoints.getHumidity() != null ? toDecimalType(sensorDataPoints.getHumidity().getPercentage())
|
||||
: null;
|
||||
}
|
||||
|
||||
public DecimalType getHeatingPower() {
|
||||
ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
|
||||
return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
|
||||
: DecimalType.ZERO;
|
||||
}
|
||||
|
||||
public OnOffType getAcPower() {
|
||||
ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
|
||||
AcPowerDataPoint acPower = dataPoints.getAcPower();
|
||||
if (acPower != null) {
|
||||
String acPowerValue = acPower.getValue();
|
||||
if (acPowerValue != null) {
|
||||
return OnOffType.from(acPowerValue);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public StringType getMode() {
|
||||
GenericZoneSetting setting = zoneState.getSetting();
|
||||
|
||||
if (!isPowerOn()) {
|
||||
return StringType.valueOf(HvacMode.OFF.name());
|
||||
} else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
|
||||
return StringType.valueOf(HvacMode.HEAT.name());
|
||||
} else {
|
||||
return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public State getTargetTemperature() {
|
||||
switch (zoneState.getSetting().getType()) {
|
||||
case HEATING:
|
||||
return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
|
||||
temperatureUnit);
|
||||
case AIR_CONDITIONING:
|
||||
return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
|
||||
temperatureUnit);
|
||||
case HOT_WATER:
|
||||
return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
|
||||
temperatureUnit);
|
||||
default:
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public StringType getOperationMode() {
|
||||
Overlay overlay = zoneState.getOverlay();
|
||||
if (overlay != null) {
|
||||
switch (overlay.getTermination().getType()) {
|
||||
case MANUAL:
|
||||
return new StringType(OperationMode.MANUAL.name());
|
||||
case TIMER:
|
||||
return new StringType(OperationMode.TIMER.name());
|
||||
case TADO_MODE:
|
||||
return new StringType(OperationMode.UNTIL_CHANGE.name());
|
||||
}
|
||||
}
|
||||
|
||||
return new StringType(OperationMode.SCHEDULE.name());
|
||||
}
|
||||
|
||||
public DecimalType getRemainingTimerDuration() {
|
||||
Overlay overlay = zoneState.getOverlay();
|
||||
if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
|
||||
TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
|
||||
|
||||
Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
|
||||
// If there's a timer overlay, then the timer should never be smaller than 1
|
||||
return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
|
||||
}
|
||||
|
||||
return new DecimalType();
|
||||
}
|
||||
|
||||
public State getOverlayExpiration() {
|
||||
Overlay overlay = zoneState.getOverlay();
|
||||
if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
return toDateTimeType(overlay.getTermination().getProjectedExpiry());
|
||||
}
|
||||
|
||||
public boolean isPowerOn() {
|
||||
Power power = Power.OFF;
|
||||
GenericZoneSetting setting = zoneState.getSetting();
|
||||
|
||||
switch (setting.getType()) {
|
||||
case HEATING:
|
||||
power = ((HeatingZoneSetting) setting).getPower();
|
||||
break;
|
||||
case AIR_CONDITIONING:
|
||||
power = ((CoolingZoneSetting) setting).getPower();
|
||||
break;
|
||||
case HOT_WATER:
|
||||
power = ((HotWaterZoneSetting) setting).getPower();
|
||||
break;
|
||||
}
|
||||
|
||||
return power.getValue().equals("ON");
|
||||
}
|
||||
|
||||
private static DecimalType toDecimalType(double value) {
|
||||
BigDecimal decimal = new BigDecimal(value).setScale(2, BigDecimal.ROUND_HALF_UP);
|
||||
return new DecimalType(decimal);
|
||||
}
|
||||
|
||||
private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
|
||||
return new DateTimeType(offsetDateTime.toZonedDateTime());
|
||||
}
|
||||
|
||||
private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
|
||||
if (temperature == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
return temperatureUnit == TemperatureUnit.FAHRENHEIT
|
||||
? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
|
||||
: new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
|
||||
}
|
||||
|
||||
private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
|
||||
if (temperature == null) {
|
||||
return UnDefType.NULL;
|
||||
}
|
||||
|
||||
return temperatureUnit == TemperatureUnit.FAHRENHEIT
|
||||
? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
|
||||
: new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
|
||||
}
|
||||
|
||||
public State getOpenWindowDetected() {
|
||||
Boolean openWindowDetected = zoneState.isOpenWindowDetected();
|
||||
if (openWindowDetected != null) {
|
||||
return OnOffType.from(openWindowDetected);
|
||||
}
|
||||
return OnOffType.OFF;
|
||||
}
|
||||
}
|
||||
@@ -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.tado.internal.api;
|
||||
|
||||
import org.openhab.binding.tado.internal.api.auth.Authorizer;
|
||||
import org.openhab.binding.tado.internal.api.auth.OAuthAuthorizer;
|
||||
import org.openhab.binding.tado.internal.api.client.HomeApi;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* Factory to create and configure {@link HomeApi} instances.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class HomeApiFactory {
|
||||
private static final String OAUTH_SCOPE = "home.user";
|
||||
private static final String OAUTH_CLIENT_ID = "public-api-preview";
|
||||
private static final String OAUTH_CLIENT_SECRET = "4HJGRffVR8xb3XdEUQpjgZ1VplJi6Xgw";
|
||||
|
||||
public HomeApi create(String username, String password) {
|
||||
Gson gson = GsonBuilderFactory.defaultGsonBuilder().create();
|
||||
Authorizer authorizer = new OAuthAuthorizer().passwordFlow(username, password).clientId(OAUTH_CLIENT_ID)
|
||||
.clientSecret(OAUTH_CLIENT_SECRET).scopes(OAUTH_SCOPE);
|
||||
return new HomeApi(gson, authorizer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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.tado.internal.api;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
|
||||
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;
|
||||
import org.openhab.binding.tado.internal.api.model.AirConditioningCapabilities;
|
||||
import org.openhab.binding.tado.internal.api.model.ManualTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionTemplate;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
|
||||
import org.openhab.binding.tado.internal.api.model.TadoModeTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.TimerTerminationConditionTemplate;
|
||||
|
||||
/**
|
||||
* Utility methods for the conversion of API types.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoApiTypeUtils {
|
||||
public static OverlayTerminationCondition getTerminationCondition(OverlayTerminationConditionType type,
|
||||
Integer timerDurationInSeconds) {
|
||||
switch (type) {
|
||||
case TIMER:
|
||||
return timerTermination(timerDurationInSeconds);
|
||||
case MANUAL:
|
||||
return manualTermination();
|
||||
case TADO_MODE:
|
||||
return tadoModeTermination();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static OverlayTerminationCondition cleanTerminationCondition(
|
||||
OverlayTerminationCondition terminationCondition) {
|
||||
Integer timerDuration = terminationCondition.getType() == OverlayTerminationConditionType.TIMER
|
||||
? ((TimerTerminationCondition) terminationCondition).getRemainingTimeInSeconds()
|
||||
: null;
|
||||
|
||||
return getTerminationCondition(terminationCondition.getType(), timerDuration);
|
||||
}
|
||||
|
||||
public static OverlayTerminationCondition terminationConditionTemplateToTerminationCondition(
|
||||
OverlayTerminationConditionTemplate template) {
|
||||
Integer timerDuration = template.getType() == OverlayTerminationConditionType.TIMER
|
||||
? ((TimerTerminationConditionTemplate) template).getDurationInSeconds()
|
||||
: null;
|
||||
|
||||
return getTerminationCondition(template.getType(), timerDuration);
|
||||
}
|
||||
|
||||
public static TimerTerminationCondition timerTermination(int durationInSeconds) {
|
||||
TimerTerminationCondition terminationCondition = new TimerTerminationCondition();
|
||||
terminationCondition.setType(OverlayTerminationConditionType.TIMER);
|
||||
terminationCondition.setDurationInSeconds(durationInSeconds);
|
||||
return terminationCondition;
|
||||
}
|
||||
|
||||
public static ManualTerminationCondition manualTermination() {
|
||||
ManualTerminationCondition terminationCondition = new ManualTerminationCondition();
|
||||
terminationCondition.setType(OverlayTerminationConditionType.MANUAL);
|
||||
return terminationCondition;
|
||||
}
|
||||
|
||||
public static TadoModeTerminationCondition tadoModeTermination() {
|
||||
TadoModeTerminationCondition terminationCondition = new TadoModeTerminationCondition();
|
||||
terminationCondition.setType(OverlayTerminationConditionType.TADO_MODE);
|
||||
return terminationCondition;
|
||||
}
|
||||
|
||||
public static TemperatureObject temperature(float degree, TemperatureUnit temperatureUnit) {
|
||||
TemperatureObject temperature = new TemperatureObject();
|
||||
if (temperatureUnit == TemperatureUnit.FAHRENHEIT) {
|
||||
temperature.setFahrenheit(degree);
|
||||
} else {
|
||||
temperature.setCelsius(degree);
|
||||
}
|
||||
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public static Float getTemperatureInUnit(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
|
||||
if (temperature == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return temperatureUnit == TemperatureUnit.FAHRENHEIT ? temperature.getFahrenheit() : temperature.getCelsius();
|
||||
}
|
||||
|
||||
public static AcMode getAcMode(HvacMode mode) {
|
||||
if (mode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case HEAT:
|
||||
return AcMode.HEAT;
|
||||
case COOL:
|
||||
return AcMode.COOL;
|
||||
case FAN:
|
||||
return AcMode.FAN;
|
||||
case DRY:
|
||||
return AcMode.DRY;
|
||||
case AUTO:
|
||||
return AcMode.AUTO;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static AcFanSpeed getAcFanSpeed(FanSpeed fanSpeed) {
|
||||
if (fanSpeed == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (fanSpeed) {
|
||||
case AUTO:
|
||||
return AcFanSpeed.AUTO;
|
||||
case HIGH:
|
||||
return AcFanSpeed.HIGH;
|
||||
case MIDDLE:
|
||||
return AcFanSpeed.MIDDLE;
|
||||
case LOW:
|
||||
return AcFanSpeed.LOW;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AcModeCapabilities getModeCapabilities(AirConditioningCapabilities capabilities, AcMode mode) {
|
||||
AcModeCapabilities modeCapabilities = null;
|
||||
|
||||
if (mode != null) {
|
||||
switch (mode) {
|
||||
case COOL:
|
||||
modeCapabilities = capabilities.getCOOL();
|
||||
break;
|
||||
case HEAT:
|
||||
modeCapabilities = capabilities.getHEAT();
|
||||
break;
|
||||
case DRY:
|
||||
modeCapabilities = capabilities.getDRY();
|
||||
break;
|
||||
case AUTO:
|
||||
modeCapabilities = capabilities.getAUTO();
|
||||
break;
|
||||
case FAN:
|
||||
modeCapabilities = capabilities.getFAN();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return modeCapabilities != null ? modeCapabilities : new AcModeCapabilities();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
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.AcFanSpeed;
|
||||
import org.openhab.binding.tado.internal.api.model.AcMode;
|
||||
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.IntRange;
|
||||
import org.openhab.binding.tado.internal.api.model.Power;
|
||||
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureRange;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class AirConditioningZoneSettingsBuilder extends ZoneSettingsBuilder {
|
||||
private static final AcMode DEFAULT_MODE = AcMode.COOL;
|
||||
private static final float DEFAULT_TEMPERATURE_C = 20.0f;
|
||||
private static final float DEFAULT_TEMPERATURE_F = 68.0f;
|
||||
|
||||
@Override
|
||||
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities genericCapabilities)
|
||||
throws IOException, ApiException {
|
||||
if (mode == HvacMode.OFF) {
|
||||
return coolingSetting(false);
|
||||
}
|
||||
|
||||
CoolingZoneSetting setting = coolingSetting(true);
|
||||
setting.setMode(getAcMode(mode));
|
||||
if (temperature != null) {
|
||||
setting.setTemperature(temperature(temperature, temperatureUnit));
|
||||
}
|
||||
|
||||
if (swing != null) {
|
||||
setting.setSwing(swing ? Power.ON : Power.OFF);
|
||||
}
|
||||
|
||||
if (fanSpeed != null) {
|
||||
setting.setFanSpeed(getAcFanSpeed(fanSpeed));
|
||||
}
|
||||
|
||||
addMissingSettingParts(zoneStateProvider, genericCapabilities, setting);
|
||||
|
||||
return setting;
|
||||
}
|
||||
|
||||
private void addMissingSettingParts(ZoneStateProvider zoneStateProvider,
|
||||
GenericZoneCapabilities genericCapabilities, CoolingZoneSetting setting) throws IOException, ApiException {
|
||||
if (setting.getMode() == null) {
|
||||
AcMode targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
|
||||
setting.setMode(targetMode);
|
||||
}
|
||||
|
||||
AcModeCapabilities capabilities = getModeCapabilities((AirConditioningCapabilities) genericCapabilities,
|
||||
setting.getMode());
|
||||
|
||||
if (capabilities.getTemperatures() != null && setting.getTemperature() == null) {
|
||||
TemperatureObject targetTemperature = getCurrentOrDefaultTemperature(zoneStateProvider,
|
||||
capabilities.getTemperatures());
|
||||
setting.setTemperature(targetTemperature);
|
||||
}
|
||||
|
||||
if (capabilities.getFanSpeeds() != null && !capabilities.getFanSpeeds().isEmpty()
|
||||
&& setting.getFanSpeed() == null) {
|
||||
AcFanSpeed fanSpeed = getCurrentOrDefaultFanSpeed(zoneStateProvider, capabilities.getFanSpeeds());
|
||||
setting.setFanSpeed(fanSpeed);
|
||||
}
|
||||
|
||||
if (capabilities.getSwings() != null && !capabilities.getSwings().isEmpty() && setting.getSwing() == null) {
|
||||
Power swing = getCurrentOrDefaultSwing(zoneStateProvider, capabilities.getSwings());
|
||||
setting.setSwing(swing);
|
||||
}
|
||||
}
|
||||
|
||||
private AcMode getCurrentOrDefaultAcMode(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
|
||||
CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
return zoneSetting.getMode() != null ? zoneSetting.getMode() : DEFAULT_MODE;
|
||||
}
|
||||
|
||||
private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider,
|
||||
TemperatureRange temperatureRanges) throws IOException, ApiException {
|
||||
CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
Float defaultTemperature = temperatureUnit == TemperatureUnit.FAHRENHEIT ? DEFAULT_TEMPERATURE_F
|
||||
: DEFAULT_TEMPERATURE_C;
|
||||
Float temperature = (zoneSetting != null && zoneSetting.getTemperature() != null)
|
||||
? getTemperatureInUnit(zoneSetting.getTemperature(), temperatureUnit)
|
||||
: defaultTemperature;
|
||||
IntRange temperatureRange = temperatureUnit == TemperatureUnit.FAHRENHEIT ? temperatureRanges.getFahrenheit()
|
||||
: temperatureRanges.getCelsius();
|
||||
|
||||
Float finalTemperature = temperatureRange.getMax() >= temperature && temperatureRange.getMin() <= temperature
|
||||
? temperature
|
||||
: temperatureRange.getMax();
|
||||
|
||||
return temperature(finalTemperature, temperatureUnit);
|
||||
}
|
||||
|
||||
private AcFanSpeed getCurrentOrDefaultFanSpeed(ZoneStateProvider zoneStateProvider, List<AcFanSpeed> fanSpeeds)
|
||||
throws IOException, ApiException {
|
||||
CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
if (zoneSetting.getFanSpeed() != null && fanSpeeds.contains(zoneSetting.getFanSpeed())) {
|
||||
return zoneSetting.getFanSpeed();
|
||||
}
|
||||
|
||||
return fanSpeeds.get(0);
|
||||
}
|
||||
|
||||
private Power getCurrentOrDefaultSwing(ZoneStateProvider zoneStateProvider, List<Power> swings)
|
||||
throws IOException, ApiException {
|
||||
CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
if (zoneSetting.getSwing() != null && swings.contains(zoneSetting.getSwing())) {
|
||||
return zoneSetting.getSwing();
|
||||
}
|
||||
|
||||
return swings.get(0);
|
||||
}
|
||||
|
||||
private CoolingZoneSetting coolingSetting(boolean powerOn) {
|
||||
CoolingZoneSetting setting = new CoolingZoneSetting();
|
||||
setting.setType(TadoSystemType.AIR_CONDITIONING);
|
||||
setting.setPower(powerOn ? Power.ON : Power.OFF);
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.temperature;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
|
||||
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;
|
||||
import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.Power;
|
||||
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
|
||||
/**
|
||||
* Builder for incremental creation of heating zone settings.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class HeatingZoneSettingsBuilder extends ZoneSettingsBuilder {
|
||||
private static final float DEFAULT_TEMPERATURE_C = 22.0f;
|
||||
private static final float DEFAULT_TEMPERATURE_F = 72.0f;
|
||||
|
||||
@Override
|
||||
public ZoneSettingsBuilder withSwing(boolean swingOn) {
|
||||
throw new IllegalArgumentException("Heating zones don't support SWING");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZoneSettingsBuilder withFanSpeed(FanSpeed fanSpeed) {
|
||||
throw new IllegalArgumentException("Heating zones don't support FAN SPEED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
|
||||
throws IOException, ApiException {
|
||||
if (mode == HvacMode.OFF) {
|
||||
return heatingSetting(false);
|
||||
}
|
||||
|
||||
HeatingZoneSetting setting = heatingSetting(true);
|
||||
|
||||
if (temperature != null) {
|
||||
setting.setTemperature(temperature(temperature, temperatureUnit));
|
||||
}
|
||||
|
||||
addMissingSettingParts(setting, zoneStateProvider);
|
||||
|
||||
return setting;
|
||||
}
|
||||
|
||||
private void addMissingSettingParts(HeatingZoneSetting setting, ZoneStateProvider zoneStateProvider)
|
||||
throws IOException, ApiException {
|
||||
if (setting.getTemperature() == null) {
|
||||
TemperatureObject temperatureObject = getCurrentOrDefaultTemperature(zoneStateProvider);
|
||||
setting.setTemperature(temperatureObject);
|
||||
}
|
||||
}
|
||||
|
||||
private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider)
|
||||
throws IOException, ApiException {
|
||||
HeatingZoneSetting zoneSetting = (HeatingZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
if (zoneSetting != null && zoneSetting.getTemperature() != null) {
|
||||
return truncateTemperature(zoneSetting.getTemperature());
|
||||
}
|
||||
|
||||
return buildDefaultTemperatureObject(DEFAULT_TEMPERATURE_C, DEFAULT_TEMPERATURE_F);
|
||||
}
|
||||
|
||||
private HeatingZoneSetting heatingSetting(boolean powerOn) {
|
||||
HeatingZoneSetting setting = new HeatingZoneSetting();
|
||||
setting.setType(TadoSystemType.HEATING);
|
||||
setting.setPower(powerOn ? Power.ON : Power.OFF);
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.temperature;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
|
||||
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;
|
||||
import org.openhab.binding.tado.internal.api.model.HotWaterCapabilities;
|
||||
import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.Power;
|
||||
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
|
||||
/**
|
||||
* Builder for incremental creation of hot water zone settings.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class HotWaterZoneSettingsBuilder extends ZoneSettingsBuilder {
|
||||
private static final float DEFAULT_TEMPERATURE_C = 50.0f;
|
||||
private static final float DEFAULT_TEMPERATURE_F = 122.0f;
|
||||
|
||||
@Override
|
||||
public ZoneSettingsBuilder withSwing(boolean swingOn) {
|
||||
throw new IllegalArgumentException("Hot Water zones don't support SWING");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZoneSettingsBuilder withFanSpeed(FanSpeed fanSpeed) {
|
||||
throw new IllegalArgumentException("Hot Water zones don't support FAN SPEED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
|
||||
throws IOException, ApiException {
|
||||
if (mode == HvacMode.OFF) {
|
||||
return hotWaterSetting(false);
|
||||
}
|
||||
|
||||
HotWaterZoneSetting setting = hotWaterSetting(true);
|
||||
|
||||
if (temperature != null) {
|
||||
setting.setTemperature(temperature(temperature, temperatureUnit));
|
||||
}
|
||||
|
||||
addMissingSettingParts(setting, zoneStateProvider, (HotWaterCapabilities) capabilities);
|
||||
|
||||
return setting;
|
||||
}
|
||||
|
||||
private void addMissingSettingParts(HotWaterZoneSetting setting, ZoneStateProvider zoneStateProvider,
|
||||
HotWaterCapabilities capabilities) throws IOException, ApiException {
|
||||
if (capabilities.isCanSetTemperature() && setting.getTemperature() == null) {
|
||||
TemperatureObject temperatureObject = getCurrentOrDefaultTemperature(zoneStateProvider);
|
||||
setting.setTemperature(temperatureObject);
|
||||
}
|
||||
}
|
||||
|
||||
private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider)
|
||||
throws IOException, ApiException {
|
||||
HotWaterZoneSetting zoneSetting = (HotWaterZoneSetting) zoneStateProvider.getZoneState().getSetting();
|
||||
|
||||
if (zoneSetting != null && zoneSetting.getTemperature() != null) {
|
||||
return truncateTemperature(zoneSetting.getTemperature());
|
||||
}
|
||||
|
||||
return buildDefaultTemperatureObject(DEFAULT_TEMPERATURE_C, DEFAULT_TEMPERATURE_F);
|
||||
}
|
||||
|
||||
private HotWaterZoneSetting hotWaterSetting(boolean powerOn) {
|
||||
HotWaterZoneSetting setting = new HotWaterZoneSetting();
|
||||
setting.setType(TadoSystemType.HOT_WATER);
|
||||
setting.setPower(powerOn ? Power.ON : Power.OFF);
|
||||
return setting;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
|
||||
import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
|
||||
import org.openhab.binding.tado.internal.api.model.ZoneState;
|
||||
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
|
||||
|
||||
/**
|
||||
* Builder for creation of overlay termination conditions.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TerminationConditionBuilder {
|
||||
private TadoZoneHandler zoneHandler;
|
||||
|
||||
private OverlayTerminationConditionType terminationType = null;
|
||||
private Integer timerDurationInSeconds = null;
|
||||
|
||||
protected TerminationConditionBuilder(TadoZoneHandler zoneHandler) {
|
||||
this.zoneHandler = zoneHandler;
|
||||
}
|
||||
|
||||
public static TerminationConditionBuilder of(TadoZoneHandler zoneHandler) {
|
||||
return new TerminationConditionBuilder(zoneHandler);
|
||||
}
|
||||
|
||||
public TerminationConditionBuilder withTerminationType(OverlayTerminationConditionType terminationType) {
|
||||
this.terminationType = terminationType;
|
||||
if (terminationType != OverlayTerminationConditionType.TIMER) {
|
||||
timerDurationInSeconds = null;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public TerminationConditionBuilder withTimerDurationInSeconds(Integer timerDurationInSeconds) {
|
||||
this.terminationType = OverlayTerminationConditionType.TIMER;
|
||||
this.timerDurationInSeconds = timerDurationInSeconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OverlayTerminationCondition build(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
|
||||
OverlayTerminationCondition terminationCondition = null;
|
||||
|
||||
if (terminationType != null) {
|
||||
if (terminationType != OverlayTerminationConditionType.TIMER || timerDurationInSeconds != null) {
|
||||
terminationCondition = getTerminationCondition(terminationType, timerDurationInSeconds);
|
||||
} else {
|
||||
terminationCondition = getCurrentOrDefaultTimerTermination(zoneStateProvider);
|
||||
}
|
||||
} else {
|
||||
ZoneState zoneState = zoneStateProvider.getZoneState();
|
||||
if (zoneState.getOverlay() != null) {
|
||||
terminationCondition = cleanTerminationCondition(zoneState.getOverlay().getTermination());
|
||||
} else {
|
||||
// Default zone termination condition
|
||||
terminationCondition = getDefaultTerminationCondition();
|
||||
}
|
||||
}
|
||||
return terminationCondition;
|
||||
}
|
||||
|
||||
private OverlayTerminationCondition getDefaultTerminationCondition() throws IOException, ApiException {
|
||||
OverlayTerminationCondition defaultTerminationCondition = zoneHandler.getDefaultTerminationCondition();
|
||||
return defaultTerminationCondition != null ? defaultTerminationCondition : manualTermination();
|
||||
}
|
||||
|
||||
private TimerTerminationCondition getCurrentOrDefaultTimerTermination(ZoneStateProvider zoneStateProvider)
|
||||
throws IOException, ApiException {
|
||||
// Timer without duration
|
||||
int duration = zoneHandler.getFallbackTimerDuration() * 60;
|
||||
|
||||
ZoneState zoneState = zoneStateProvider.getZoneState();
|
||||
|
||||
// If timer is currently running, use its time
|
||||
if (zoneState.getOverlay() != null
|
||||
&& zoneState.getOverlay().getTermination().getType() == OverlayTerminationConditionType.TIMER) {
|
||||
duration = ((TimerTerminationCondition) zoneState.getOverlay().getTermination()).getDurationInSeconds();
|
||||
}
|
||||
|
||||
return timerTermination(duration);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
|
||||
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.GenericZoneCapabilities;
|
||||
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
|
||||
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
|
||||
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
|
||||
|
||||
/**
|
||||
* Base class for zone settings builder.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public abstract class ZoneSettingsBuilder {
|
||||
public static ZoneSettingsBuilder of(TadoZoneHandler zoneHandler) {
|
||||
switch (zoneHandler.getZoneType()) {
|
||||
case HEATING:
|
||||
return new HeatingZoneSettingsBuilder();
|
||||
case AIR_CONDITIONING:
|
||||
return new AirConditioningZoneSettingsBuilder();
|
||||
case HOT_WATER:
|
||||
return new HotWaterZoneSettingsBuilder();
|
||||
default:
|
||||
throw new IllegalArgumentException("Zone type " + zoneHandler.getZoneType() + " unknown");
|
||||
}
|
||||
}
|
||||
|
||||
protected HvacMode mode = null;
|
||||
protected Float temperature = null;
|
||||
protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS;
|
||||
protected Boolean swing = null;
|
||||
protected FanSpeed fanSpeed = null;
|
||||
|
||||
public ZoneSettingsBuilder withMode(HvacMode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZoneSettingsBuilder withTemperature(Float temperature, TemperatureUnit temperatureUnit) {
|
||||
this.temperature = temperature;
|
||||
this.temperatureUnit = temperatureUnit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZoneSettingsBuilder withSwing(boolean swingOn) {
|
||||
this.swing = swingOn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ZoneSettingsBuilder withFanSpeed(FanSpeed fanSpeed) {
|
||||
this.fanSpeed = fanSpeed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities capabilities)
|
||||
throws IOException, ApiException;
|
||||
|
||||
protected TemperatureObject truncateTemperature(TemperatureObject temperature) {
|
||||
if (temperature == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TemperatureObject temperatureObject = new TemperatureObject();
|
||||
if (temperatureUnit == TemperatureUnit.FAHRENHEIT) {
|
||||
temperatureObject.setFahrenheit(temperature.getFahrenheit());
|
||||
} else {
|
||||
temperatureObject.setCelsius(temperature.getCelsius());
|
||||
}
|
||||
|
||||
return temperatureObject;
|
||||
}
|
||||
|
||||
protected TemperatureObject buildDefaultTemperatureObject(float temperatureCelsius, float temperatureFahrenheit) {
|
||||
TemperatureObject temperatureObject = new TemperatureObject();
|
||||
|
||||
if (temperatureUnit == TemperatureUnit.FAHRENHEIT) {
|
||||
temperatureObject.setFahrenheit(temperatureFahrenheit);
|
||||
} else {
|
||||
temperatureObject.setCelsius(temperatureCelsius);
|
||||
}
|
||||
|
||||
return temperatureObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.tado.internal.builder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.model.ZoneState;
|
||||
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
|
||||
|
||||
/**
|
||||
* Wrapper for zone state to support lazy loading.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class ZoneStateProvider {
|
||||
private TadoZoneHandler zoneHandler;
|
||||
private ZoneState zoneState;
|
||||
|
||||
public ZoneStateProvider(TadoZoneHandler zoneHandler) {
|
||||
this.zoneHandler = zoneHandler;
|
||||
}
|
||||
|
||||
ZoneState getZoneState() throws IOException, ApiException {
|
||||
if (this.zoneState == null) {
|
||||
ZoneState retrievedZoneState = zoneHandler.getZoneState();
|
||||
// empty zone state behaves like a NULL object
|
||||
this.zoneState = retrievedZoneState != null ? retrievedZoneState : new ZoneState();
|
||||
}
|
||||
|
||||
return this.zoneState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.tado.internal.config;
|
||||
|
||||
/**
|
||||
* Holder-object for home configuration
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoHomeConfig {
|
||||
public String username;
|
||||
public String password;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.tado.internal.config;
|
||||
|
||||
/**
|
||||
* Holder-object for mobile device configuration
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoMobileDeviceConfig {
|
||||
public int id;
|
||||
public int refreshInterval;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.tado.internal.config;
|
||||
|
||||
/**
|
||||
* Holder-object for zone configuration
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoZoneConfig {
|
||||
public long id;
|
||||
public int refreshInterval;
|
||||
public int fallbackTimerDuration;
|
||||
public int hvacChangeDebounce;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 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.tado.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.tado.internal.TadoBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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 java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants;
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.model.MobileDevice;
|
||||
import org.openhab.binding.tado.internal.api.model.Zone;
|
||||
import org.openhab.binding.tado.internal.handler.TadoHomeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Discovery service for zones and mobile devices.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoDiscoveryService extends AbstractDiscoveryService {
|
||||
private static final int TIMEOUT = 5;
|
||||
private static final long REFRESH = 600;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TadoDiscoveryService.class);
|
||||
|
||||
private ScheduledFuture<?> discoveryFuture;
|
||||
|
||||
public static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_ZONE, THING_TYPE_MOBILE_DEVICE).collect(Collectors.toSet()));
|
||||
|
||||
private TadoHomeHandler homeHandler;
|
||||
|
||||
public TadoDiscoveryService(TadoHomeHandler tadoHomeHandler) {
|
||||
super(DISCOVERABLE_THING_TYPES_UIDS, TIMEOUT);
|
||||
this.homeHandler = tadoHomeHandler;
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
super.activate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
if (homeHandler.getHomeId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
discoverZones();
|
||||
discoverMobileDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
logger.debug("Start Tado background discovery");
|
||||
if (discoveryFuture == null || discoveryFuture.isCancelled()) {
|
||||
logger.debug("Start Scan");
|
||||
discoveryFuture = scheduler.scheduleWithFixedDelay(this::startScan, 30, REFRESH, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
logger.debug("Stop Tado background discovery");
|
||||
if (discoveryFuture != null && !discoveryFuture.isCancelled()) {
|
||||
discoveryFuture.cancel(true);
|
||||
discoveryFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverZones() {
|
||||
Long homeId = homeHandler.getHomeId();
|
||||
try {
|
||||
List<Zone> zoneList = homeHandler.getApi().listZones(homeId);
|
||||
|
||||
if (zoneList != null) {
|
||||
for (Zone zone : zoneList) {
|
||||
notifyZoneDiscovery(homeId, zone);
|
||||
}
|
||||
}
|
||||
} catch (IOException | ApiException e) {
|
||||
logger.debug("Could not discover tado zones: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyZoneDiscovery(Long homeId, Zone zone) {
|
||||
Integer zoneId = zone.getId();
|
||||
|
||||
ThingUID bridgeUID = this.homeHandler.getThing().getUID();
|
||||
ThingUID uid = new ThingUID(TadoBindingConstants.THING_TYPE_ZONE, bridgeUID, zoneId.toString());
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(CONFIG_ZONE_ID, zoneId);
|
||||
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(zone.getName())
|
||||
.withProperties(properties).build();
|
||||
|
||||
thingDiscovered(result);
|
||||
|
||||
logger.debug("Discovered zone '{}' with id {} ({})", zone.getName(), zoneId.toString(), uid);
|
||||
}
|
||||
|
||||
private void discoverMobileDevices() {
|
||||
Long homeId = homeHandler.getHomeId();
|
||||
try {
|
||||
List<MobileDevice> mobileDeviceList = homeHandler.getApi().listMobileDevices(homeId);
|
||||
|
||||
if (mobileDeviceList != null) {
|
||||
for (MobileDevice mobileDevice : mobileDeviceList) {
|
||||
if (mobileDevice.getSettings().isGeoTrackingEnabled()) {
|
||||
notifyMobileDeviceDiscovery(homeId, mobileDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | ApiException e) {
|
||||
logger.debug("Could not discover tado zones: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyMobileDeviceDiscovery(Long homeId, MobileDevice device) {
|
||||
ThingUID bridgeUID = this.homeHandler.getThing().getUID();
|
||||
ThingUID uid = new ThingUID(TadoBindingConstants.THING_TYPE_MOBILE_DEVICE, bridgeUID,
|
||||
device.getId().toString());
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(CONFIG_MOBILE_DEVICE_ID, device.getId());
|
||||
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withBridge(bridgeUID).withLabel(device.getName())
|
||||
.withProperties(properties).build();
|
||||
|
||||
thingDiscovered(result);
|
||||
|
||||
logger.debug("Discovered mobile device '{}' with id {} ({})", device.getName(), device.getId().toString(), uid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.tado.internal.handler;
|
||||
|
||||
import org.openhab.binding.tado.internal.api.client.HomeApi;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
|
||||
/**
|
||||
* Common base class for home-based thing-handler.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public abstract class BaseHomeThingHandler extends BaseThingHandler {
|
||||
|
||||
public BaseHomeThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
public Long getHomeId() {
|
||||
TadoHomeHandler handler = getHomeHandler();
|
||||
return handler != null ? handler.getHomeId() : new Long(0);
|
||||
}
|
||||
|
||||
protected TadoHomeHandler getHomeHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
return bridge != null ? (TadoHomeHandler) bridge.getHandler() : null;
|
||||
}
|
||||
|
||||
protected HomeApi getApi() {
|
||||
TadoHomeHandler handler = getHomeHandler();
|
||||
return handler != null ? handler.getApi() : null;
|
||||
}
|
||||
|
||||
protected void onSuccessfulOperation() {
|
||||
// update without error -> we're back online
|
||||
if (getThing().getStatus() == ThingStatus.OFFLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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.tado.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.model.ControlDevice;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The {@link TadoBatteryChecker} checks the battery state of Tado control
|
||||
* devices.
|
||||
*
|
||||
* @author Andrew Fiddian-Green - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class TadoBatteryChecker {
|
||||
private final Logger logger = LoggerFactory.getLogger(TadoBatteryChecker.class);
|
||||
|
||||
private Map<Long, State> zoneList = new HashMap<>();
|
||||
private Date refreshTime = new Date();
|
||||
private TadoHomeHandler homeHandler;
|
||||
|
||||
public TadoBatteryChecker(TadoHomeHandler homeHandler) {
|
||||
this.homeHandler = homeHandler;
|
||||
}
|
||||
|
||||
private synchronized void refreshZoneList() {
|
||||
Date now = new Date();
|
||||
if (homeHandler != null && (now.after(refreshTime) || zoneList.isEmpty())) {
|
||||
// be frugal, we only need to refresh the battery state hourly
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(now);
|
||||
calendar.add(Calendar.HOUR, 1);
|
||||
refreshTime = calendar.getTime();
|
||||
|
||||
Long homeId = homeHandler.getHomeId();
|
||||
if (homeId != null) {
|
||||
logger.debug("Fetching (battery state) zone list for HomeId {}", homeId);
|
||||
zoneList.clear();
|
||||
try {
|
||||
homeHandler.getApi().listZones(homeId).forEach(zone -> {
|
||||
boolean batteryLow = !zone.getDevices().stream().map(ControlDevice::getBatteryState)
|
||||
.filter(Objects::nonNull).allMatch(s -> s.equals("NORMAL"));
|
||||
zoneList.put(Long.valueOf(zone.getId()), OnOffType.from(batteryLow));
|
||||
});
|
||||
} catch (IOException | ApiException e) {
|
||||
logger.debug("Fetch (battery state) zone list exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public State getBatteryLowAlarm(long zoneId) {
|
||||
refreshZoneList();
|
||||
return zoneList.getOrDefault(zoneId, UnDefType.UNDEF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.tado.internal.handler;
|
||||
|
||||
import static org.openhab.binding.tado.internal.TadoBindingConstants.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.tado.internal.discovery.TadoDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
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.Component;
|
||||
|
||||
/**
|
||||
* The {@link TadoHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
@Component(configurationPid = "binding.tado", service = ThingHandlerFactory.class)
|
||||
public class TadoHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(new HashSet<>(Arrays.asList(THING_TYPE_HOME, THING_TYPE_ZONE, THING_TYPE_MOBILE_DEVICE)));
|
||||
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_HOME)) {
|
||||
TadoHomeHandler tadoHomeHandler = new TadoHomeHandler((Bridge) thing);
|
||||
registerTadoDiscoveryService(tadoHomeHandler);
|
||||
return tadoHomeHandler;
|
||||
} else if (thingTypeUID.equals(THING_TYPE_ZONE)) {
|
||||
return new TadoZoneHandler(thing);
|
||||
} else if (thingTypeUID.equals(THING_TYPE_MOBILE_DEVICE)) {
|
||||
return new TadoMobileDeviceHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void registerTadoDiscoveryService(TadoHomeHandler tadoHomeHandler) {
|
||||
TadoDiscoveryService discoveryService = new TadoDiscoveryService(tadoHomeHandler);
|
||||
ServiceRegistration<?> serviceRegistration = bundleContext.registerService(DiscoveryService.class.getName(),
|
||||
discoveryService, new Hashtable<>());
|
||||
discoveryService.activate();
|
||||
this.discoveryServiceRegs.put(tadoHomeHandler.getThing().getUID(), serviceRegistration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof TadoHomeHandler) {
|
||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
TadoDiscoveryService service = (TadoDiscoveryService) bundleContext
|
||||
.getService(serviceReg.getReference());
|
||||
serviceReg.unregister();
|
||||
if (service != null) {
|
||||
service.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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.tado.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.HomeApiFactory;
|
||||
import org.openhab.binding.tado.internal.api.client.HomeApi;
|
||||
import org.openhab.binding.tado.internal.api.model.HomeInfo;
|
||||
import org.openhab.binding.tado.internal.api.model.User;
|
||||
import org.openhab.binding.tado.internal.config.TadoHomeConfig;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link TadoHomeHandler} is the bridge of all home-based things.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoHomeHandler extends BaseBridgeHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(TadoHomeHandler.class);
|
||||
|
||||
private TadoHomeConfig configuration;
|
||||
private HomeApi api;
|
||||
private Long homeId;
|
||||
|
||||
private TadoBatteryChecker batteryChecker;
|
||||
|
||||
private ScheduledFuture<?> initializationFuture;
|
||||
|
||||
public TadoHomeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
batteryChecker = new TadoBatteryChecker(this);
|
||||
}
|
||||
|
||||
public TemperatureUnit getTemperatureUnit() {
|
||||
String temperatureUnitStr = this.thing.getProperties().get(TadoBindingConstants.PROPERTY_HOME_TEMPERATURE_UNIT);
|
||||
return TemperatureUnit.valueOf(temperatureUnitStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
configuration = getConfigAs(TadoHomeConfig.class);
|
||||
api = new HomeApiFactory().create(configuration.username, configuration.password);
|
||||
|
||||
if (this.initializationFuture == null || this.initializationFuture.isDone()) {
|
||||
initializationFuture = scheduler.scheduleWithFixedDelay(this::initializeBridgeStatusAndPropertiesIfOffline,
|
||||
0, 300, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBridgeStatusAndPropertiesIfOffline() {
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get user info to verify successful authentication and connection to server
|
||||
User user = api.showUser();
|
||||
if (user == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Cannot connect to server. Username and/or password might be invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.getHomes().isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"User does not have access to any home");
|
||||
return;
|
||||
}
|
||||
|
||||
homeId = user.getHomes().get(0).getId().longValue();
|
||||
|
||||
HomeInfo homeInfo = api.showHome(homeId);
|
||||
TemperatureUnit temperatureUnit = org.openhab.binding.tado.internal.api.model.TemperatureUnit.FAHRENHEIT == homeInfo
|
||||
.getTemperatureUnit() ? TemperatureUnit.FAHRENHEIT : TemperatureUnit.CELSIUS;
|
||||
updateProperty(TadoBindingConstants.PROPERTY_HOME_TEMPERATURE_UNIT, temperatureUnit.name());
|
||||
} catch (IOException | ApiException e) {
|
||||
logger.debug("Error accessing tado server: {}", e.getMessage(), e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to server due to " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (this.initializationFuture != null || !this.initializationFuture.isDone()) {
|
||||
this.initializationFuture.cancel(true);
|
||||
this.initializationFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
public HomeApi getApi() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public Long getHomeId() {
|
||||
return homeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
// Nothing to do for a bridge
|
||||
}
|
||||
|
||||
public State getBatteryLowAlarm(long zoneId) {
|
||||
return batteryChecker.getBatteryLowAlarm(zoneId);
|
||||
}
|
||||
}
|
||||
@@ -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.tado.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants;
|
||||
import org.openhab.binding.tado.internal.api.ApiException;
|
||||
import org.openhab.binding.tado.internal.api.model.MobileDevice;
|
||||
import org.openhab.binding.tado.internal.config.TadoMobileDeviceConfig;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link TadoMobileDeviceHandler} is responsible for handling commands of mobile devices and update their state.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
*/
|
||||
public class TadoMobileDeviceHandler extends BaseHomeThingHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(TadoMobileDeviceHandler.class);
|
||||
|
||||
private TadoMobileDeviceConfig configuration;
|
||||
private ScheduledFuture<?> refreshTimer;
|
||||
|
||||
public TadoMobileDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command == RefreshType.REFRESH) {
|
||||
logger.debug("Refreshing {}", channelUID);
|
||||
updateState();
|
||||
} else {
|
||||
logger.warn("This Thing is read-only and can only handle REFRESH command");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
configuration = getConfigAs(TadoMobileDeviceConfig.class);
|
||||
|
||||
if (configuration.refreshInterval <= 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh interval of zone "
|
||||
+ configuration.id + " of home " + getHomeId() + " must be greater than zero");
|
||||
return;
|
||||
}
|
||||
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
bridgeStatusChanged(bridge.getStatusInfo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
MobileDevice device = getMobileDevice();
|
||||
updateProperty(TadoBindingConstants.PROPERTY_MOBILE_DEVICE_NAME, device.getName());
|
||||
|
||||
if (!device.getSettings().isGeoTrackingEnabled()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Geotracking is disabled on mobile device " + device.getName());
|
||||
return;
|
||||
}
|
||||
} catch (IOException | ApiException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to server due to " + e.getMessage());
|
||||
cancelScheduledStateUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleZoneStateUpdate();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
cancelScheduledStateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
try {
|
||||
MobileDevice device = getMobileDevice();
|
||||
updateState(TadoBindingConstants.CHANNEL_MOBILE_DEVICE_AT_HOME,
|
||||
device.getLocation().isAtHome() ? OnOffType.ON : OnOffType.OFF);
|
||||
} catch (IOException | ApiException e) {
|
||||
logger.debug("Status update of mobile device with id {} failed: {}", configuration.id, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private MobileDevice getMobileDevice() throws IOException, ApiException {
|
||||
MobileDevice device = null;
|
||||
|
||||
try {
|
||||
device = getApi().listMobileDevices(getHomeId()).stream().filter(m -> m.getId() == configuration.id)
|
||||
.findFirst().orElse(null);
|
||||
} catch (IOException | ApiException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to server due to " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (device == null) {
|
||||
String message = "Mobile device with id " + configuration.id + " unknown or does not belong to home "
|
||||
+ getHomeId();
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message);
|
||||
throw new IOException(message);
|
||||
}
|
||||
|
||||
onSuccessfulOperation();
|
||||
return device;
|
||||
}
|
||||
|
||||
private void scheduleZoneStateUpdate() {
|
||||
if (refreshTimer == null || refreshTimer.isCancelled()) {
|
||||
refreshTimer = scheduler.scheduleWithFixedDelay(this::updateState, 5, configuration.refreshInterval,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelScheduledStateUpdate() {
|
||||
if (refreshTimer != null) {
|
||||
refreshTimer.cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* 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.tado.internal.handler;
|
||||
|
||||
import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.terminationConditionTemplateToTerminationCondition;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.quantity.Temperature;
|
||||
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
|
||||
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
|
||||
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.client.HomeApi;
|
||||
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
|
||||
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.Zone;
|
||||
import org.openhab.binding.tado.internal.api.model.ZoneState;
|
||||
import org.openhab.binding.tado.internal.config.TadoZoneConfig;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
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.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link TadoZoneHandler} is responsible for handling commands of zones and update their state.
|
||||
*
|
||||
* @author Dennis Frommknecht - Initial contribution
|
||||
* @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
|
||||
*
|
||||
*/
|
||||
public class TadoZoneHandler extends BaseHomeThingHandler {
|
||||
private Logger logger = LoggerFactory.getLogger(TadoZoneHandler.class);
|
||||
|
||||
private TadoZoneConfig configuration;
|
||||
private ScheduledFuture<?> refreshTimer;
|
||||
private ScheduledFuture<?> scheduledHvacChange;
|
||||
private GenericZoneCapabilities capabilities;
|
||||
TadoHvacChange pendingHvacChange;
|
||||
|
||||
public TadoZoneHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
public long getZoneId() {
|
||||
return this.configuration.id;
|
||||
}
|
||||
|
||||
public int getFallbackTimerDuration() {
|
||||
return this.configuration.fallbackTimerDuration;
|
||||
}
|
||||
|
||||
public ZoneType getZoneType() {
|
||||
String zoneTypeStr = this.thing.getProperties().get(TadoBindingConstants.PROPERTY_ZONE_TYPE);
|
||||
return ZoneType.valueOf(zoneTypeStr);
|
||||
}
|
||||
|
||||
public OverlayTerminationCondition getDefaultTerminationCondition() throws IOException, ApiException {
|
||||
OverlayTemplate overlayTemplate = getApi().showZoneDefaultOverlay(getHomeId(), getZoneId());
|
||||
return terminationConditionTemplateToTerminationCondition(overlayTemplate.getTerminationCondition());
|
||||
}
|
||||
|
||||
public ZoneState getZoneState() throws IOException, ApiException {
|
||||
HomeApi api = getApi();
|
||||
return api != null ? api.showZoneState(getHomeId(), getZoneId()) : null;
|
||||
}
|
||||
|
||||
public GenericZoneCapabilities getZoneCapabilities() {
|
||||
return this.capabilities;
|
||||
}
|
||||
|
||||
public TemperatureUnit getTemperatureUnit() {
|
||||
return getHomeHandler().getTemperatureUnit();
|
||||
}
|
||||
|
||||
public Overlay setOverlay(Overlay overlay) throws IOException, ApiException {
|
||||
logger.debug("Setting overlay of home {} and zone {}", getHomeId(), getZoneId());
|
||||
return getApi().updateZoneOverlay(getHomeId(), getZoneId(), overlay);
|
||||
}
|
||||
|
||||
public void removeOverlay() throws IOException, ApiException {
|
||||
logger.debug("Removing overlay of home {} and zone {}", getHomeId(), getZoneId());
|
||||
getApi().deleteZoneOverlay(getHomeId(), getZoneId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
String id = channelUID.getId();
|
||||
|
||||
if (command == RefreshType.REFRESH) {
|
||||
updateZoneState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case TadoBindingConstants.CHANNEL_ZONE_HVAC_MODE:
|
||||
pendingHvacChange.withHvacMode(((StringType) command).toFullString());
|
||||
scheduleHvacChange();
|
||||
break;
|
||||
case TadoBindingConstants.CHANNEL_ZONE_TARGET_TEMPERATURE:
|
||||
QuantityType<Temperature> state = (QuantityType<Temperature>) command;
|
||||
QuantityType<Temperature> stateInTargetUnit = getTemperatureUnit() == TemperatureUnit.FAHRENHEIT
|
||||
? state.toUnit(ImperialUnits.FAHRENHEIT)
|
||||
: state.toUnit(SIUnits.CELSIUS);
|
||||
|
||||
if (stateInTargetUnit != null) {
|
||||
pendingHvacChange.withTemperature(stateInTargetUnit.floatValue());
|
||||
scheduleHvacChange();
|
||||
}
|
||||
|
||||
break;
|
||||
case TadoBindingConstants.CHANNEL_ZONE_SWING:
|
||||
pendingHvacChange.withSwing(((OnOffType) command) == OnOffType.ON);
|
||||
scheduleHvacChange();
|
||||
break;
|
||||
case TadoBindingConstants.CHANNEL_ZONE_FAN_SPEED:
|
||||
pendingHvacChange.withFanSpeed(((StringType) command).toFullString());
|
||||
scheduleHvacChange();
|
||||
break;
|
||||
case TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE:
|
||||
String operationMode = ((StringType) command).toFullString();
|
||||
pendingHvacChange.withOperationMode(OperationMode.valueOf(operationMode));
|
||||
scheduleHvacChange();
|
||||
break;
|
||||
case TadoBindingConstants.CHANNEL_ZONE_TIMER_DURATION:
|
||||
pendingHvacChange.activeFor(((DecimalType) command).intValue());
|
||||
scheduleHvacChange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
configuration = getConfigAs(TadoZoneConfig.class);
|
||||
|
||||
if (configuration.refreshInterval <= 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh interval of zone "
|
||||
+ getZoneId() + " of home " + getHomeId() + " must be greater than zero");
|
||||
return;
|
||||
} else if (configuration.fallbackTimerDuration <= 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Fallback timer duration of zone "
|
||||
+ getZoneId() + " of home " + getHomeId() + " must be greater than zero");
|
||||
return;
|
||||
} else if (configuration.hvacChangeDebounce <= 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "HVAC change debounce of zone "
|
||||
+ getZoneId() + " of home " + getHomeId() + " must be greater than zero");
|
||||
return;
|
||||
}
|
||||
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
bridgeStatusChanged(bridge.getStatusInfo());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
|
||||
try {
|
||||
Zone zoneDetails = getApi().showZoneDetails(getHomeId(), getZoneId());
|
||||
GenericZoneCapabilities capabilities = getApi().showZoneCapabilities(getHomeId(), getZoneId());
|
||||
|
||||
if (zoneDetails == null || capabilities == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Can not access zone " + getZoneId() + " of home " + getHomeId());
|
||||
return;
|
||||
}
|
||||
|
||||
updateProperty(TadoBindingConstants.PROPERTY_ZONE_NAME, zoneDetails.getName());
|
||||
updateProperty(TadoBindingConstants.PROPERTY_ZONE_TYPE, zoneDetails.getType().name());
|
||||
this.capabilities = capabilities;
|
||||
} catch (IOException | ApiException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to server due to " + e.getMessage());
|
||||
cancelScheduledZoneStateUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleZoneStateUpdate();
|
||||
pendingHvacChange = new TadoHvacChange(getThing());
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
cancelScheduledZoneStateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateZoneState(boolean forceUpdate) {
|
||||
// No update during HVAC change debounce
|
||||
if (!forceUpdate && scheduledHvacChange != null && !scheduledHvacChange.isDone()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ZoneState zoneState = getZoneState();
|
||||
|
||||
if (zoneState == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Updating state of home {} and zone {}", getHomeId(), getZoneId());
|
||||
|
||||
TadoZoneStateAdapter state = new TadoZoneStateAdapter(zoneState, getTemperatureUnit());
|
||||
updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_CURRENT_TEMPERATURE, state.getInsideTemperature());
|
||||
updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_HUMIDITY, state.getHumidity());
|
||||
|
||||
updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_HEATING_POWER, state.getHeatingPower());
|
||||
updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_AC_POWER, state.getAcPower());
|
||||
|
||||
updateState(TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE, state.getOperationMode());
|
||||
|
||||
updateState(TadoBindingConstants.CHANNEL_ZONE_HVAC_MODE, state.getMode());
|
||||
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_TIMER_DURATION, state.getRemainingTimerDuration());
|
||||
|
||||
updateState(TadoBindingConstants.CHANNEL_ZONE_OVERLAY_EXPIRY, state.getOverlayExpiration());
|
||||
|
||||
updateState(TadoBindingConstants.CHANNEL_ZONE_OPEN_WINDOW_DETECTED, state.getOpenWindowDetected());
|
||||
|
||||
onSuccessfulOperation();
|
||||
} catch (IOException | ApiException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Could not connect to server due to " + e.getMessage());
|
||||
}
|
||||
|
||||
TadoHomeHandler home = getHomeHandler();
|
||||
if (home != null) {
|
||||
updateState(TadoBindingConstants.CHANNEL_ZONE_BATTERY_LOW_ALARM, home.getBatteryLowAlarm(getZoneId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleZoneStateUpdate() {
|
||||
if (refreshTimer == null || refreshTimer.isCancelled()) {
|
||||
refreshTimer = scheduler.scheduleWithFixedDelay(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateZoneState(false);
|
||||
}
|
||||
}, 5, configuration.refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelScheduledZoneStateUpdate() {
|
||||
if (refreshTimer != null) {
|
||||
refreshTimer.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleHvacChange() {
|
||||
if (scheduledHvacChange != null) {
|
||||
scheduledHvacChange.cancel(false);
|
||||
}
|
||||
|
||||
scheduledHvacChange = scheduler.schedule(() -> {
|
||||
try {
|
||||
TadoHvacChange change = this.pendingHvacChange;
|
||||
this.pendingHvacChange = new TadoHvacChange(getThing());
|
||||
change.apply();
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
} catch (ApiException e) {
|
||||
logger.warn("Could not apply HVAC change on home {} and zone {}: {}", getHomeId(), getZoneId(),
|
||||
e.getMessage(), e);
|
||||
} finally {
|
||||
updateZoneState(true);
|
||||
}
|
||||
}, configuration.hvacChangeDebounce, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void updateStateIfNotNull(String channelID, State state) {
|
||||
if (state != null) {
|
||||
updateState(channelID, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="tado" 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>tado° Binding</name>
|
||||
<description>Binding for tado° devices</description>
|
||||
<author>Dennis Frommknecht</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,227 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="tado"
|
||||
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">
|
||||
|
||||
<!-- my.tado API Gateway -->
|
||||
<bridge-type id="home">
|
||||
<label>Tado Home</label>
|
||||
<description>The user's tado home</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="username" type="text" required="true">
|
||||
<label>User Name</label>
|
||||
<description>User name of tado login used for API access</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="password" type="text" required="true">
|
||||
<label>Password</label>
|
||||
<description>Password of tado login used for API access</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="zone">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="home"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Zone</label>
|
||||
<description>A zone of a home</description>
|
||||
|
||||
<channels>
|
||||
<channel typeId="currentTemperature" id="currentTemperature"></channel>
|
||||
<channel typeId="humidity" id="humidity"></channel>
|
||||
|
||||
<channel typeId="heatingPower" id="heatingPower"></channel>
|
||||
|
||||
<channel typeId="hvacMode" id="hvacMode"></channel>
|
||||
<channel typeId="targetTemperature" id="targetTemperature"></channel>
|
||||
<channel typeId="fanspeed" id="fanspeed"></channel>
|
||||
<channel typeId="swing" id="swing"></channel>
|
||||
|
||||
<channel typeId="overlayExpiry" id="overlayExpiry"></channel>
|
||||
<channel typeId="timerDuration" id="timerDuration"></channel>
|
||||
|
||||
<channel typeId="operationMode" id="operationMode"></channel>
|
||||
|
||||
<channel typeId="system.low-battery" id="batteryLowAlarm">
|
||||
<label>Battery Low Alarm</label>
|
||||
<description>ON if one or more devices in the zone have a low battery</description>
|
||||
</channel>
|
||||
|
||||
<channel typeId="acPower" id="acPower"></channel>
|
||||
<channel typeId="openWindowDetected" id="openWindowDetected"></channel>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>Zone Id</label>
|
||||
<description>Id of the zone</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval of home data</description>
|
||||
<default>30</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="fallbackTimerDuration" type="integer">
|
||||
<label>Fallback Timer Duration</label>
|
||||
<description>Timer duration used if no other duration can be determined</description>
|
||||
<default>30</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="hvacChangeDebounce" type="integer">
|
||||
<label>HVAC Change Debounce Delay</label>
|
||||
<description>Duration in seconds to combine multiple HVAC changes into one.</description>
|
||||
<default>5</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="mobiledevice">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="home"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Mobile Device</label>
|
||||
<description>Mobile device of a home</description>
|
||||
|
||||
<channels>
|
||||
<channel typeId="atHome" id="atHome"></channel>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>Mobile Device Id</label>
|
||||
<description>Id of the mobile device</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Refresh interval of location state</description>
|
||||
<default>60</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="currentTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Current temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state readOnly="true" pattern="%.1f %unit%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="humidity">
|
||||
<item-type>Number</item-type>
|
||||
<label>Humidity</label>
|
||||
<description>Current humidity in %</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="true" pattern="%.1f %%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatingPower">
|
||||
<item-type>Number</item-type>
|
||||
<label>Heating Power</label>
|
||||
<description>Current heating power</description>
|
||||
<state readOnly="true" pattern="%.0f %%"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="hvacMode">
|
||||
<item-type>String</item-type>
|
||||
<label>HVAC Mode</label>
|
||||
<description>Mode of the device (OFF, HEAT, COOL, DRY, FAN, AUTO - if supported)</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="OFF">Off</option>
|
||||
<option value="HEAT">Heat</option>
|
||||
<option value="COOL">Cool</option>
|
||||
<option value="DRY">Dry</option>
|
||||
<option value="FAN">Fan</option>
|
||||
<option value="AUTO">Auto</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="targetTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Target Temperature</label>
|
||||
<description>Thermostat temperature setpoint</description>
|
||||
<category>Temperature</category>
|
||||
<state step="0.1" pattern="%.1f %unit%" readOnly="false"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fanspeed">
|
||||
<item-type>String</item-type>
|
||||
<label>Fan Speed</label>
|
||||
<description>AC fan speed (only if supported by AC)</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="LOW">Low</option>
|
||||
<option value="MIDDLE">Middle</option>
|
||||
<option value="HIGH">High</option>
|
||||
<option value="AUTO">Auto</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="swing">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Swing</label>
|
||||
<description>State of AC swing (only if supported by AC)</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="operationMode">
|
||||
<item-type>String</item-type>
|
||||
<label>Zone Operation Mode</label>
|
||||
<description>Active operation mode (schedule, manual, timer or until next change)</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="SCHEDULE">Schedule</option>
|
||||
<option value="MANUAL">Manual</option>
|
||||
<option value="UNTIL_CHANGE">Until change</option>
|
||||
<option value="TIMER">Timer</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="timerDuration">
|
||||
<item-type>Number</item-type>
|
||||
<label>Timer Duration</label>
|
||||
<description>Total duration of a timer</description>
|
||||
<state min="0" step="1" pattern="%d min" readOnly="false"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="overlayExpiry">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Overlay End Time</label>
|
||||
<description>Time until when the overlay is active. Null if no overlay is set or overlay type is manual.</description>
|
||||
<state readOnly="true" pattern="%1$tF %1$tR"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="atHome">
|
||||
<item-type>Switch</item-type>
|
||||
<label>At Home</label>
|
||||
<description>ON if at home, OFF if away</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="acPower">
|
||||
<item-type>Switch</item-type>
|
||||
<label>AirCon Power State</label>
|
||||
<description>Indicates if the air-conditioning is Off or On</description>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="openWindowDetected">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Open Window Detected</label>
|
||||
<description>Indicates if an open window has been detected</description>
|
||||
<category>window</category>
|
||||
<state readOnly="true"></state>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user