2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.tado.internal.adapter;
15 import java.math.BigDecimal;
16 import java.time.OffsetDateTime;
18 import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
19 import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
20 import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
21 import org.openhab.binding.tado.internal.api.model.AcPowerDataPoint;
22 import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
23 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
24 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
25 import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
26 import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
27 import org.openhab.binding.tado.internal.api.model.Overlay;
28 import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
29 import org.openhab.binding.tado.internal.api.model.Power;
30 import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
31 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
32 import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
33 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
34 import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
35 import org.openhab.binding.tado.internal.api.model.ZoneState;
36 import org.openhab.core.library.types.DateTimeType;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.QuantityType;
40 import org.openhab.core.library.types.StringType;
41 import org.openhab.core.library.unit.ImperialUnits;
42 import org.openhab.core.library.unit.SIUnits;
43 import org.openhab.core.types.State;
44 import org.openhab.core.types.UnDefType;
47 * Adapter from API-level zone state to the binding's item-based zone state.
49 * @author Dennis Frommknecht - Initial contribution
50 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
53 public class TadoZoneStateAdapter {
54 private ZoneState zoneState;
55 private TemperatureUnit temperatureUnit;
57 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
58 this.zoneState = zoneState;
59 this.temperatureUnit = temperatureUnit;
62 public State getInsideTemperature() {
63 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
64 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
67 public DecimalType getHumidity() {
68 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
69 return sensorDataPoints.getHumidity() != null ? toDecimalType(sensorDataPoints.getHumidity().getPercentage())
73 public DecimalType getHeatingPower() {
74 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
75 return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
79 public OnOffType getAcPower() {
80 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
81 AcPowerDataPoint acPower = dataPoints.getAcPower();
82 if (acPower != null) {
83 String acPowerValue = acPower.getValue();
84 if (acPowerValue != null) {
85 return OnOffType.from(acPowerValue);
91 public StringType getMode() {
92 GenericZoneSetting setting = zoneState.getSetting();
95 return StringType.valueOf(HvacMode.OFF.name());
96 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
97 return StringType.valueOf(HvacMode.HEAT.name());
99 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
103 public State getTargetTemperature() {
104 switch (zoneState.getSetting().getType()) {
106 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
108 case AIR_CONDITIONING:
109 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
112 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
115 return UnDefType.UNDEF;
119 public State getFanSpeed() {
120 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
121 CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
122 return setting.getFanSpeed() != null ? StringType.valueOf(setting.getFanSpeed().getValue())
125 return UnDefType.UNDEF;
129 public State getSwing() {
130 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
131 CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
132 if (setting.getSwing() == null) {
133 return UnDefType.NULL;
134 } else if (setting.getSwing() == Power.ON) {
137 return OnOffType.OFF;
140 return UnDefType.UNDEF;
144 public StringType getOperationMode() {
145 Overlay overlay = zoneState.getOverlay();
146 if (overlay != null) {
147 switch (overlay.getTermination().getType()) {
149 return new StringType(OperationMode.MANUAL.name());
151 return new StringType(OperationMode.TIMER.name());
153 return new StringType(OperationMode.UNTIL_CHANGE.name());
157 return new StringType(OperationMode.SCHEDULE.name());
160 public DecimalType getRemainingTimerDuration() {
161 Overlay overlay = zoneState.getOverlay();
162 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
163 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
165 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
166 // If there's a timer overlay, then the timer should never be smaller than 1
167 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
170 return new DecimalType();
173 public State getOverlayExpiration() {
174 Overlay overlay = zoneState.getOverlay();
175 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
176 return UnDefType.UNDEF;
179 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
182 public boolean isPowerOn() {
183 Power power = Power.OFF;
184 GenericZoneSetting setting = zoneState.getSetting();
186 switch (setting.getType()) {
188 power = ((HeatingZoneSetting) setting).getPower();
190 case AIR_CONDITIONING:
191 power = ((CoolingZoneSetting) setting).getPower();
194 power = ((HotWaterZoneSetting) setting).getPower();
198 return power.getValue().equals("ON");
201 private static DecimalType toDecimalType(double value) {
202 BigDecimal decimal = new BigDecimal(value).setScale(2, BigDecimal.ROUND_HALF_UP);
203 return new DecimalType(decimal);
206 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
207 return new DateTimeType(offsetDateTime.toZonedDateTime());
210 private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
211 if (temperature == null) {
212 return UnDefType.NULL;
215 return temperatureUnit == TemperatureUnit.FAHRENHEIT
216 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
217 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
220 private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
221 if (temperature == null) {
222 return UnDefType.NULL;
225 return temperatureUnit == TemperatureUnit.FAHRENHEIT
226 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
227 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
230 public State getOpenWindowDetected() {
231 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
232 if (openWindowDetected != null) {
233 return OnOffType.from(openWindowDetected);
235 return OnOffType.OFF;