2 * Copyright (c) 2010-2024 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.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
22 import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
23 import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
24 import org.openhab.binding.tado.swagger.codegen.api.model.ACFanLevel;
25 import org.openhab.binding.tado.swagger.codegen.api.model.ACHorizontalSwing;
26 import org.openhab.binding.tado.swagger.codegen.api.model.ACVerticalSwing;
27 import org.openhab.binding.tado.swagger.codegen.api.model.AcFanSpeed;
28 import org.openhab.binding.tado.swagger.codegen.api.model.AcPowerDataPoint;
29 import org.openhab.binding.tado.swagger.codegen.api.model.ActivityDataPoints;
30 import org.openhab.binding.tado.swagger.codegen.api.model.CoolingZoneSetting;
31 import org.openhab.binding.tado.swagger.codegen.api.model.GenericZoneSetting;
32 import org.openhab.binding.tado.swagger.codegen.api.model.HeatingZoneSetting;
33 import org.openhab.binding.tado.swagger.codegen.api.model.HotWaterZoneSetting;
34 import org.openhab.binding.tado.swagger.codegen.api.model.Overlay;
35 import org.openhab.binding.tado.swagger.codegen.api.model.OverlayTerminationConditionType;
36 import org.openhab.binding.tado.swagger.codegen.api.model.PercentageDataPoint;
37 import org.openhab.binding.tado.swagger.codegen.api.model.Power;
38 import org.openhab.binding.tado.swagger.codegen.api.model.SensorDataPoints;
39 import org.openhab.binding.tado.swagger.codegen.api.model.TadoSystemType;
40 import org.openhab.binding.tado.swagger.codegen.api.model.TemperatureDataPoint;
41 import org.openhab.binding.tado.swagger.codegen.api.model.TemperatureObject;
42 import org.openhab.binding.tado.swagger.codegen.api.model.TimerTerminationCondition;
43 import org.openhab.binding.tado.swagger.codegen.api.model.ZoneState;
44 import org.openhab.core.library.types.DateTimeType;
45 import org.openhab.core.library.types.DecimalType;
46 import org.openhab.core.library.types.OnOffType;
47 import org.openhab.core.library.types.QuantityType;
48 import org.openhab.core.library.types.StringType;
49 import org.openhab.core.library.unit.ImperialUnits;
50 import org.openhab.core.library.unit.SIUnits;
51 import org.openhab.core.library.unit.Units;
52 import org.openhab.core.types.State;
53 import org.openhab.core.types.UnDefType;
56 * Adapter from API-level zone state to the binding's item-based zone state.
58 * @author Dennis Frommknecht - Initial contribution
59 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
63 public class TadoZoneStateAdapter {
64 private final ZoneState zoneState;
65 private final TemperatureUnit temperatureUnit;
67 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
68 this.zoneState = zoneState;
69 this.temperatureUnit = temperatureUnit;
72 public State getInsideTemperature() {
73 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
74 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
77 public State getHumidity() {
78 PercentageDataPoint humidity = zoneState.getSensorDataPoints().getHumidity();
79 return humidity != null ? new QuantityType<>(humidity.getPercentage(), Units.PERCENT) : UnDefType.UNDEF;
82 public State getHeatingPower() {
83 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
84 return dataPoints.getHeatingPower() != null
85 ? new QuantityType<>(dataPoints.getHeatingPower().getPercentage().doubleValue(), Units.PERCENT)
89 public State getAcPower() {
90 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
91 AcPowerDataPoint acPower = dataPoints.getAcPower();
92 if (acPower != null) {
93 String acPowerValue = acPower.getValue();
94 if (acPowerValue != null) {
95 return OnOffType.from(acPowerValue);
98 return UnDefType.UNDEF;
101 public StringType getMode() {
102 GenericZoneSetting setting = zoneState.getSetting();
105 return StringType.valueOf(HvacMode.OFF.name());
106 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
107 return StringType.valueOf(HvacMode.HEAT.name());
109 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
113 public State getTargetTemperature() {
115 return UnDefType.UNDEF;
117 switch (zoneState.getSetting().getType()) {
119 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
121 case AIR_CONDITIONING:
122 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
125 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
128 return UnDefType.UNDEF;
132 public State getFanSpeed() {
133 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
134 AcFanSpeed result = ((CoolingZoneSetting) zoneState.getSetting()).getFanSpeed();
135 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
137 return UnDefType.UNDEF;
140 public State getSwing() {
141 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
142 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getSwing();
143 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
145 return UnDefType.UNDEF;
148 public State getFanLevel() {
149 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
150 ACFanLevel result = ((CoolingZoneSetting) zoneState.getSetting()).getFanLevel();
151 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
153 return UnDefType.UNDEF;
156 public State getHorizontalSwing() {
157 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
158 ACHorizontalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getHorizontalSwing();
159 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
161 return UnDefType.UNDEF;
164 public State getVerticalSwing() {
165 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
166 ACVerticalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getVerticalSwing();
167 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
169 return UnDefType.UNDEF;
172 public StringType getOperationMode() {
173 Overlay overlay = zoneState.getOverlay();
174 if (overlay != null) {
175 switch (overlay.getTermination().getType()) {
177 return new StringType(OperationMode.MANUAL.name());
179 return new StringType(OperationMode.TIMER.name());
181 return new StringType(OperationMode.UNTIL_CHANGE.name());
185 return new StringType(OperationMode.SCHEDULE.name());
188 public DecimalType getRemainingTimerDuration() {
189 Overlay overlay = zoneState.getOverlay();
190 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
191 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
193 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
194 // If there's a timer overlay, then the timer should never be smaller than 1
195 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
198 return new DecimalType();
201 public State getOverlayExpiration() {
202 Overlay overlay = zoneState.getOverlay();
203 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
204 return UnDefType.UNDEF;
207 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
210 public boolean isPowerOn() {
211 Power power = Power.OFF;
212 GenericZoneSetting setting = zoneState.getSetting();
214 switch (setting.getType()) {
216 power = ((HeatingZoneSetting) setting).getPower();
218 case AIR_CONDITIONING:
219 power = ((CoolingZoneSetting) setting).getPower();
222 power = ((HotWaterZoneSetting) setting).getPower();
226 return "ON".equals(power.getValue());
229 private static DecimalType toDecimalType(double value) {
230 BigDecimal decimal = new BigDecimal(value).setScale(2, RoundingMode.HALF_UP);
231 return new DecimalType(decimal);
234 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
235 return new DateTimeType(offsetDateTime.toZonedDateTime());
238 private static State toTemperatureState(@Nullable TemperatureObject temperature, TemperatureUnit temperatureUnit) {
239 if (temperature == null || (temperature.getCelsius() == null && temperature.getFahrenheit() == null)) {
240 return UnDefType.UNDEF;
242 return temperatureUnit == TemperatureUnit.FAHRENHEIT
243 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
244 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
247 private static State toTemperatureState(@Nullable TemperatureDataPoint temperature,
248 TemperatureUnit temperatureUnit) {
249 if (temperature == null || (temperature.getCelsius() == null && temperature.getFahrenheit() == null)) {
250 return UnDefType.UNDEF;
252 return temperatureUnit == TemperatureUnit.FAHRENHEIT
253 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
254 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
257 public State getOpenWindowDetected() {
258 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
259 if (openWindowDetected != null) {
260 return OnOffType.from(openWindowDetected);
262 return OnOffType.OFF;
265 public State getLight() {
266 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
267 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getLight();
268 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
270 return UnDefType.UNDEF;