2 * Copyright (c) 2010-2022 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.math.RoundingMode;
17 import java.time.OffsetDateTime;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
21 import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
22 import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
23 import org.openhab.binding.tado.internal.api.model.ACFanLevel;
24 import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
25 import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
26 import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
27 import org.openhab.binding.tado.internal.api.model.AcPowerDataPoint;
28 import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
29 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
30 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
31 import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
32 import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
33 import org.openhab.binding.tado.internal.api.model.Overlay;
34 import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
35 import org.openhab.binding.tado.internal.api.model.PercentageDataPoint;
36 import org.openhab.binding.tado.internal.api.model.Power;
37 import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
38 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
39 import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
40 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
41 import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
42 import org.openhab.binding.tado.internal.api.model.ZoneState;
43 import org.openhab.core.library.types.DateTimeType;
44 import org.openhab.core.library.types.DecimalType;
45 import org.openhab.core.library.types.OnOffType;
46 import org.openhab.core.library.types.QuantityType;
47 import org.openhab.core.library.types.StringType;
48 import org.openhab.core.library.unit.ImperialUnits;
49 import org.openhab.core.library.unit.SIUnits;
50 import org.openhab.core.types.State;
51 import org.openhab.core.types.UnDefType;
54 * Adapter from API-level zone state to the binding's item-based zone state.
56 * @author Dennis Frommknecht - Initial contribution
57 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
61 public class TadoZoneStateAdapter {
62 private final ZoneState zoneState;
63 private final TemperatureUnit temperatureUnit;
65 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
66 this.zoneState = zoneState;
67 this.temperatureUnit = temperatureUnit;
70 public State getInsideTemperature() {
71 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
72 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
75 public State getHumidity() {
76 PercentageDataPoint humidity = zoneState.getSensorDataPoints().getHumidity();
77 return humidity != null ? toDecimalType(humidity.getPercentage()) : UnDefType.UNDEF;
80 public DecimalType getHeatingPower() {
81 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
82 return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
86 public State getAcPower() {
87 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
88 AcPowerDataPoint acPower = dataPoints.getAcPower();
89 if (acPower != null) {
90 String acPowerValue = acPower.getValue();
91 if (acPowerValue != null) {
92 return OnOffType.from(acPowerValue);
95 return UnDefType.UNDEF;
98 public StringType getMode() {
99 GenericZoneSetting setting = zoneState.getSetting();
102 return StringType.valueOf(HvacMode.OFF.name());
103 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
104 return StringType.valueOf(HvacMode.HEAT.name());
106 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
110 public State getTargetTemperature() {
112 return UnDefType.UNDEF;
114 switch (zoneState.getSetting().getType()) {
116 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
118 case AIR_CONDITIONING:
119 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
122 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
125 return UnDefType.UNDEF;
129 public State getFanSpeed() {
130 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
131 AcFanSpeed result = ((CoolingZoneSetting) zoneState.getSetting()).getFanSpeed();
132 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
134 return UnDefType.UNDEF;
137 public State getSwing() {
138 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
139 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getSwing();
140 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
142 return UnDefType.UNDEF;
145 public State getFanLevel() {
146 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
147 ACFanLevel result = ((CoolingZoneSetting) zoneState.getSetting()).getFanLevel();
148 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
150 return UnDefType.UNDEF;
153 public State getHorizontalSwing() {
154 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
155 ACHorizontalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getHorizontalSwing();
156 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
158 return UnDefType.UNDEF;
161 public State getVerticalSwing() {
162 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
163 ACVerticalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getVerticalSwing();
164 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
166 return UnDefType.UNDEF;
169 public StringType getOperationMode() {
170 Overlay overlay = zoneState.getOverlay();
171 if (overlay != null) {
172 switch (overlay.getTermination().getType()) {
174 return new StringType(OperationMode.MANUAL.name());
176 return new StringType(OperationMode.TIMER.name());
178 return new StringType(OperationMode.UNTIL_CHANGE.name());
182 return new StringType(OperationMode.SCHEDULE.name());
185 public DecimalType getRemainingTimerDuration() {
186 Overlay overlay = zoneState.getOverlay();
187 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
188 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
190 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
191 // If there's a timer overlay, then the timer should never be smaller than 1
192 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
195 return new DecimalType();
198 public State getOverlayExpiration() {
199 Overlay overlay = zoneState.getOverlay();
200 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
201 return UnDefType.UNDEF;
204 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
207 public boolean isPowerOn() {
208 Power power = Power.OFF;
209 GenericZoneSetting setting = zoneState.getSetting();
211 switch (setting.getType()) {
213 power = ((HeatingZoneSetting) setting).getPower();
215 case AIR_CONDITIONING:
216 power = ((CoolingZoneSetting) setting).getPower();
219 power = ((HotWaterZoneSetting) setting).getPower();
223 return power.getValue().equals("ON");
226 private static DecimalType toDecimalType(double value) {
227 BigDecimal decimal = new BigDecimal(value).setScale(2, RoundingMode.HALF_UP);
228 return new DecimalType(decimal);
231 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
232 return new DateTimeType(offsetDateTime.toZonedDateTime());
235 private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
236 return temperatureUnit == TemperatureUnit.FAHRENHEIT
237 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
238 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
241 private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
242 return temperatureUnit == TemperatureUnit.FAHRENHEIT
243 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
244 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
247 public State getOpenWindowDetected() {
248 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
249 if (openWindowDetected != null) {
250 return OnOffType.from(openWindowDetected);
252 return OnOffType.OFF;
255 public State getLight() {
256 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
257 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getLight();
258 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
260 return UnDefType.UNDEF;