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.internal.api.model.ACFanLevel;
25 import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
26 import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
27 import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
28 import org.openhab.binding.tado.internal.api.model.AcPowerDataPoint;
29 import org.openhab.binding.tado.internal.api.model.ActivityDataPoints;
30 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
31 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
32 import org.openhab.binding.tado.internal.api.model.HeatingZoneSetting;
33 import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
34 import org.openhab.binding.tado.internal.api.model.Overlay;
35 import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
36 import org.openhab.binding.tado.internal.api.model.PercentageDataPoint;
37 import org.openhab.binding.tado.internal.api.model.Power;
38 import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
39 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
40 import org.openhab.binding.tado.internal.api.model.TemperatureDataPoint;
41 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
42 import org.openhab.binding.tado.internal.api.model.TimerTerminationCondition;
43 import org.openhab.binding.tado.internal.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.types.State;
52 import org.openhab.core.types.UnDefType;
55 * Adapter from API-level zone state to the binding's item-based zone state.
57 * @author Dennis Frommknecht - Initial contribution
58 * @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
62 public class TadoZoneStateAdapter {
63 private final ZoneState zoneState;
64 private final TemperatureUnit temperatureUnit;
66 public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
67 this.zoneState = zoneState;
68 this.temperatureUnit = temperatureUnit;
71 public State getInsideTemperature() {
72 SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
73 return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
76 public State getHumidity() {
77 PercentageDataPoint humidity = zoneState.getSensorDataPoints().getHumidity();
78 return humidity != null ? toDecimalType(humidity.getPercentage()) : UnDefType.UNDEF;
81 public DecimalType getHeatingPower() {
82 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
83 return dataPoints.getHeatingPower() != null ? toDecimalType(dataPoints.getHeatingPower().getPercentage())
87 public State getAcPower() {
88 ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
89 AcPowerDataPoint acPower = dataPoints.getAcPower();
90 if (acPower != null) {
91 String acPowerValue = acPower.getValue();
92 if (acPowerValue != null) {
93 return OnOffType.from(acPowerValue);
96 return UnDefType.UNDEF;
99 public StringType getMode() {
100 GenericZoneSetting setting = zoneState.getSetting();
103 return StringType.valueOf(HvacMode.OFF.name());
104 } else if (setting.getType() == TadoSystemType.HEATING || setting.getType() == TadoSystemType.HOT_WATER) {
105 return StringType.valueOf(HvacMode.HEAT.name());
107 return StringType.valueOf(((CoolingZoneSetting) setting).getMode().getValue());
111 public State getTargetTemperature() {
113 return UnDefType.UNDEF;
115 switch (zoneState.getSetting().getType()) {
117 return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
119 case AIR_CONDITIONING:
120 return toTemperatureState(((CoolingZoneSetting) zoneState.getSetting()).getTemperature(),
123 return toTemperatureState(((HotWaterZoneSetting) zoneState.getSetting()).getTemperature(),
126 return UnDefType.UNDEF;
130 public State getFanSpeed() {
131 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
132 AcFanSpeed result = ((CoolingZoneSetting) zoneState.getSetting()).getFanSpeed();
133 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
135 return UnDefType.UNDEF;
138 public State getSwing() {
139 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
140 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getSwing();
141 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
143 return UnDefType.UNDEF;
146 public State getFanLevel() {
147 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
148 ACFanLevel result = ((CoolingZoneSetting) zoneState.getSetting()).getFanLevel();
149 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
151 return UnDefType.UNDEF;
154 public State getHorizontalSwing() {
155 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
156 ACHorizontalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getHorizontalSwing();
157 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
159 return UnDefType.UNDEF;
162 public State getVerticalSwing() {
163 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
164 ACVerticalSwing result = ((CoolingZoneSetting) zoneState.getSetting()).getVerticalSwing();
165 return result != null ? StringType.valueOf(result.getValue()) : UnDefType.NULL;
167 return UnDefType.UNDEF;
170 public StringType getOperationMode() {
171 Overlay overlay = zoneState.getOverlay();
172 if (overlay != null) {
173 switch (overlay.getTermination().getType()) {
175 return new StringType(OperationMode.MANUAL.name());
177 return new StringType(OperationMode.TIMER.name());
179 return new StringType(OperationMode.UNTIL_CHANGE.name());
183 return new StringType(OperationMode.SCHEDULE.name());
186 public DecimalType getRemainingTimerDuration() {
187 Overlay overlay = zoneState.getOverlay();
188 if (overlay != null && overlay.getTermination().getType() == OverlayTerminationConditionType.TIMER) {
189 TimerTerminationCondition timerTerminationCondition = (TimerTerminationCondition) overlay.getTermination();
191 Integer remainingTimeInSeconds = timerTerminationCondition.getRemainingTimeInSeconds();
192 // If there's a timer overlay, then the timer should never be smaller than 1
193 return new DecimalType((int) Math.max(1.0, Math.round(remainingTimeInSeconds / 60.0)));
196 return new DecimalType();
199 public State getOverlayExpiration() {
200 Overlay overlay = zoneState.getOverlay();
201 if (overlay == null || overlay.getTermination().getProjectedExpiry() == null) {
202 return UnDefType.UNDEF;
205 return toDateTimeType(overlay.getTermination().getProjectedExpiry());
208 public boolean isPowerOn() {
209 Power power = Power.OFF;
210 GenericZoneSetting setting = zoneState.getSetting();
212 switch (setting.getType()) {
214 power = ((HeatingZoneSetting) setting).getPower();
216 case AIR_CONDITIONING:
217 power = ((CoolingZoneSetting) setting).getPower();
220 power = ((HotWaterZoneSetting) setting).getPower();
224 return "ON".equals(power.getValue());
227 private static DecimalType toDecimalType(double value) {
228 BigDecimal decimal = new BigDecimal(value).setScale(2, RoundingMode.HALF_UP);
229 return new DecimalType(decimal);
232 private static DateTimeType toDateTimeType(OffsetDateTime offsetDateTime) {
233 return new DateTimeType(offsetDateTime.toZonedDateTime());
236 private static State toTemperatureState(@Nullable TemperatureObject temperature, TemperatureUnit temperatureUnit) {
237 if (temperature == null || (temperature.getCelsius() == null && temperature.getFahrenheit() == null)) {
238 return UnDefType.UNDEF;
240 return temperatureUnit == TemperatureUnit.FAHRENHEIT
241 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
242 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
245 private static State toTemperatureState(@Nullable TemperatureDataPoint temperature,
246 TemperatureUnit temperatureUnit) {
247 if (temperature == null || (temperature.getCelsius() == null && temperature.getFahrenheit() == null)) {
248 return UnDefType.UNDEF;
250 return temperatureUnit == TemperatureUnit.FAHRENHEIT
251 ? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
252 : new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
255 public State getOpenWindowDetected() {
256 Boolean openWindowDetected = zoneState.isOpenWindowDetected();
257 if (openWindowDetected != null) {
258 return OnOffType.from(openWindowDetected);
260 return OnOffType.OFF;
263 public State getLight() {
264 if (zoneState.getSetting().getType() == TadoSystemType.AIR_CONDITIONING) {
265 Power result = ((CoolingZoneSetting) zoneState.getSetting()).getLight();
266 return result != null ? OnOffType.from(result == Power.ON) : UnDefType.NULL;
268 return UnDefType.UNDEF;