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.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
20 import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
21 import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
22 import org.openhab.binding.tado.internal.api.model.ACFanLevel;
23 import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
24 import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
25 import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
26 import org.openhab.binding.tado.internal.api.model.AcPowerDataPoint;
27 import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
28 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
29 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
30 import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
31 import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
32 import org.openhab.binding.tado.internal.api.model.Overlay;
33 import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
34 import org.openhab.binding.tado.internal.api.model.Power;
35 import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
36 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
37 import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
38 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
39 import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
40 import org.openhab.binding.tado.internal.api.model.ZoneState;
41 import org.openhab.core.library.types.DateTimeType;
42 import org.openhab.core.library.types.DecimalType;
43 import org.openhab.core.library.types.OnOffType;
44 import org.openhab.core.library.types.QuantityType;
45 import org.openhab.core.library.types.StringType;
46 import org.openhab.core.library.unit.ImperialUnits;
47 import org.openhab.core.library.unit.SIUnits;
48 import org.openhab.core.types.State;
49 import org.openhab.core.types.UnDefType;
52 * Adapter from API-level zone state to the binding's item-based zone state.
54 * @author Dennis Frommknecht - Initial contribution
55 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
58 public class TadoZoneStateAdapter {
59 private ZoneState zoneState;
60 private TemperatureUnit temperatureUnit;
62 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
63 this.zoneState = zoneState;
64 this.temperatureUnit = temperatureUnit;
67 public State getInsideTemperature() {
68 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
69 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
72 public DecimalType getHumidity() {
73 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
74 return sensorDataPoints.getHumidity() != null ? toDecimalType(sensorDataPoints.getHumidity().getPercentage())
78 public DecimalType getHeatingPower() {
79 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
80 return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
84 public OnOffType getAcPower() {
85 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
86 AcPowerDataPoint acPower = dataPoints.getAcPower();
87 if (acPower != null) {
88 String acPowerValue = acPower.getValue();
89 if (acPowerValue != null) {
90 return OnOffType.from(acPowerValue);
96 public StringType getMode() {
97 GenericZoneSetting setting = zoneState.getSetting();
100 return StringType.valueOf(HvacMode.OFF.name());
101 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
102 return StringType.valueOf(HvacMode.HEAT.name());
104 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
108 public State getTargetTemperature() {
109 switch (zoneState.getSetting().getType()) {
111 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
113 case AIR_CONDITIONING:
114 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
117 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
120 return UnDefType.UNDEF;
124 public State getFanSpeed() {
125 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
126 AcFanSpeed result = ((CoolingZoneSetting) zoneState.getSetting()).getFanSpeed();
127 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
129 return UnDefType.UNDEF;
132 public State getSwing() {
133 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
134 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getSwing();
135 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
137 return UnDefType.UNDEF;
140 public State getFanLevel() {
141 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
142 ACFanLevel result = ((CoolingZoneSetting) zoneState.getSetting()).getFanLevel();
143 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
145 return UnDefType.UNDEF;
148 public State getHorizontalSwing() {
149 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
150 ACHorizontalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getHorizontalSwing();
151 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
153 return UnDefType.UNDEF;
156 public State getVerticalSwing() {
157 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
158 ACVerticalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getVerticalSwing();
159 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
161 return UnDefType.UNDEF;
164 public StringType getOperationMode() {
165 Overlay overlay = zoneState.getOverlay();
166 if (overlay != null) {
167 switch (overlay.getTermination().getType()) {
169 return new StringType(OperationMode.MANUAL.name());
171 return new StringType(OperationMode.TIMER.name());
173 return new StringType(OperationMode.UNTIL_CHANGE.name());
177 return new StringType(OperationMode.SCHEDULE.name());
180 public DecimalType getRemainingTimerDuration() {
181 Overlay overlay = zoneState.getOverlay();
182 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
183 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
185 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
186 // If there's a timer overlay, then the timer should never be smaller than 1
187 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
190 return new DecimalType();
193 public State getOverlayExpiration() {
194 Overlay overlay = zoneState.getOverlay();
195 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
196 return UnDefType.UNDEF;
199 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
202 public boolean isPowerOn() {
203 Power power = Power.OFF;
204 GenericZoneSetting setting = zoneState.getSetting();
206 switch (setting.getType()) {
208 power = ((HeatingZoneSetting) setting).getPower();
210 case AIR_CONDITIONING:
211 power = ((CoolingZoneSetting) setting).getPower();
214 power = ((HotWaterZoneSetting) setting).getPower();
218 return power.getValue().equals("ON");
221 private static DecimalType toDecimalType(double value) {
222 BigDecimal decimal = new BigDecimal(value).setScale(2, RoundingMode.HALF_UP);
223 return new DecimalType(decimal);
226 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
227 return new DateTimeType(offsetDateTime.toZonedDateTime());
230 private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
231 if (temperature == null) {
232 return UnDefType.NULL;
235 return temperatureUnit == TemperatureUnit.FAHRENHEIT
236 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
237 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
240 private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
241 if (temperature == null) {
242 return UnDefType.NULL;
245 return temperatureUnit == TemperatureUnit.FAHRENHEIT
246 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
247 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
250 public State getOpenWindowDetected() {
251 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
252 if (openWindowDetected != null) {
253 return OnOffType.from(openWindowDetected);
255 return OnOffType.OFF;
258 public State getLight() {
259 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
260 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getLight();
261 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
263 return UnDefType.UNDEF;