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.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.AcPowerDataPoint;
23 import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
24 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
25 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
26 import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
27 import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
28 import org.openhab.binding.tado.internal.api.model.Overlay;
29 import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
30 import org.openhab.binding.tado.internal.api.model.Power;
31 import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
32 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
33 import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
34 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
35 import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
36 import org.openhab.binding.tado.internal.api.model.ZoneState;
37 import org.openhab.core.library.types.DateTimeType;
38 import org.openhab.core.library.types.DecimalType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.QuantityType;
41 import org.openhab.core.library.types.StringType;
42 import org.openhab.core.library.unit.ImperialUnits;
43 import org.openhab.core.library.unit.SIUnits;
44 import org.openhab.core.types.State;
45 import org.openhab.core.types.UnDefType;
48 * Adapter from API-level zone state to the binding's item-based zone state.
50 * @author Dennis Frommknecht - Initial contribution
51 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
54 public class TadoZoneStateAdapter {
55 private ZoneState zoneState;
56 private TemperatureUnit temperatureUnit;
58 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
59 this.zoneState = zoneState;
60 this.temperatureUnit = temperatureUnit;
63 public State getInsideTemperature() {
64 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
65 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
68 public DecimalType getHumidity() {
69 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
70 return sensorDataPoints.getHumidity() != null ? toDecimalType(sensorDataPoints.getHumidity().getPercentage())
74 public DecimalType getHeatingPower() {
75 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
76 return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
80 public OnOffType getAcPower() {
81 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
82 AcPowerDataPoint acPower = dataPoints.getAcPower();
83 if (acPower != null) {
84 String acPowerValue = acPower.getValue();
85 if (acPowerValue != null) {
86 return OnOffType.from(acPowerValue);
92 public StringType getMode() {
93 GenericZoneSetting setting = zoneState.getSetting();
96 return StringType.valueOf(HvacMode.OFF.name());
97 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
98 return StringType.valueOf(HvacMode.HEAT.name());
100 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
104 public State getTargetTemperature() {
105 switch (zoneState.getSetting().getType()) {
107 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
109 case AIR_CONDITIONING:
110 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
113 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
116 return UnDefType.UNDEF;
120 public State getFanSpeed() {
121 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
122 CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
123 return setting.getFanSpeed() != null ? StringType.valueOf(setting.getFanSpeed().getValue())
126 return UnDefType.UNDEF;
130 public State getSwing() {
131 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
132 CoolingZoneSetting setting = (CoolingZoneSetting) zoneState.getSetting();
133 if (setting.getSwing() == null) {
134 return UnDefType.NULL;
135 } else if (setting.getSwing() == Power.ON) {
138 return OnOffType.OFF;
141 return UnDefType.UNDEF;
145 public StringType getOperationMode() {
146 Overlay overlay = zoneState.getOverlay();
147 if (overlay != null) {
148 switch (overlay.getTermination().getType()) {
150 return new StringType(OperationMode.MANUAL.name());
152 return new StringType(OperationMode.TIMER.name());
154 return new StringType(OperationMode.UNTIL_CHANGE.name());
158 return new StringType(OperationMode.SCHEDULE.name());
161 public DecimalType getRemainingTimerDuration() {
162 Overlay overlay = zoneState.getOverlay();
163 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
164 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
166 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
167 // If there's a timer overlay, then the timer should never be smaller than 1
168 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
171 return new DecimalType();
174 public State getOverlayExpiration() {
175 Overlay overlay = zoneState.getOverlay();
176 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
177 return UnDefType.UNDEF;
180 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
183 public boolean isPowerOn() {
184 Power power = Power.OFF;
185 GenericZoneSetting setting = zoneState.getSetting();
187 switch (setting.getType()) {
189 power = ((HeatingZoneSetting) setting).getPower();
191 case AIR_CONDITIONING:
192 power = ((CoolingZoneSetting) setting).getPower();
195 power = ((HotWaterZoneSetting) setting).getPower();
199 return power.getValue().equals("ON");
202 private static DecimalType toDecimalType(double value) {
203 BigDecimal decimal = new BigDecimal(value).setScale(2, RoundingMode.HALF_UP);
204 return new DecimalType(decimal);
207 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
208 return new DateTimeType(offsetDateTime.toZonedDateTime());
211 private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
212 if (temperature == null) {
213 return UnDefType.NULL;
216 return temperatureUnit == TemperatureUnit.FAHRENHEIT
217 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
218 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
221 private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
222 if (temperature == null) {
223 return UnDefType.NULL;
226 return temperatureUnit == TemperatureUnit.FAHRENHEIT
227 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
228 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
231 public State getOpenWindowDetected() {
232 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
233 if (openWindowDetected != null) {
234 return OnOffType.from(openWindowDetected);
236 return OnOffType.OFF;