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.builder;
15 import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.*;
17 import java.io.IOException;
18 import java.util.List;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
22 import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
23 import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
24 import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
25 import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
26 import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
27 import org.openhab.binding.tado.internal.api.ApiException;
28 import org.openhab.binding.tado.internal.api.TadoApiTypeUtils;
29 import org.openhab.binding.tado.internal.api.model.ACFanLevel;
30 import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
31 import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
32 import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
33 import org.openhab.binding.tado.internal.api.model.AcMode;
34 import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
35 import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
36 import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
37 import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
38 import org.openhab.binding.tado.internal.api.model.IntRange;
39 import org.openhab.binding.tado.internal.api.model.Power;
40 import org.openhab.binding.tado.internal.api.model.TadoSystemType;
41 import org.openhab.binding.tado.internal.api.model.TemperatureObject;
42 import org.openhab.binding.tado.internal.api.model.TemperatureRange;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
49 * @author Dennis Frommknecht - Initial contribution
52 public class AirConditioningZoneSettingsBuilder extends ZoneSettingsBuilder {
53 private static final AcMode DEFAULT_MODE = AcMode.COOL;
54 private static final float DEFAULT_TEMPERATURE_C = 20.0f;
55 private static final float DEFAULT_TEMPERATURE_F = 68.0f;
57 private static final String STATE_VALUE_NOT_SUPPORTED = "Your a/c unit does not support '{}:{}' when in state '{}:{}', (supported values: [{}]).";
58 private Logger logger = LoggerFactory.getLogger(AirConditioningZoneSettingsBuilder.class);
61 public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities genericCapabilities)
62 throws IOException, ApiException {
63 if (mode == HvacMode.OFF) {
64 return coolingSetting(false);
67 CoolingZoneSetting newSetting = coolingSetting(true);
70 HvacMode mode = this.mode;
72 targetMode = getAcMode(mode);
73 newSetting.setMode(targetMode);
75 // if mode not changing, so the reference is the current (or default) mode
76 targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
79 Float temperature = this.temperature;
80 if (temperature != null) {
81 newSetting.setTemperature(temperature(temperature, temperatureUnit));
84 Boolean swing = this.swing;
86 newSetting.setSwing(swing.booleanValue() ? Power.ON : Power.OFF);
89 Boolean light = this.light;
91 newSetting.setLight(light.booleanValue() ? Power.ON : Power.OFF);
94 FanSpeed fanSpeed = this.fanSpeed;
95 if (fanSpeed != null) {
96 newSetting.setFanSpeed(getAcFanSpeed(fanSpeed));
100 * In the latest API release Tado introduced extra AC settings that have an open ended list of possible
101 * supported state values. And for any particular device, its specific list of supported values is available
102 * via its 'capabilities' structure. So before setting a new value, we check if the respective new value is in
103 * the capabilities list that corresponds to the target AC mode. And if not, a warning message is logged.
105 AcModeCapabilities targetModeCapabilities = TadoApiTypeUtils.getModeCapabilities(targetMode,
106 genericCapabilities);
108 FanLevel fanLevel = this.fanLevel;
109 if (fanLevel != null) {
110 ACFanLevel targetFanLevel = getFanLevel(fanLevel);
111 List<ACFanLevel> targetFanLevels = targetModeCapabilities.getFanLevel();
112 if (targetFanLevels != null && targetFanLevels.contains(targetFanLevel)) {
113 newSetting.setFanLevel(targetFanLevel);
115 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetFanLevel.getClass().getSimpleName(), targetFanLevel,
116 targetMode.getClass().getSimpleName(), targetMode, targetFanLevels);
120 HorizontalSwing horizontalSwing = this.horizontalSwing;
121 if (horizontalSwing != null) {
122 ACHorizontalSwing targetHorizontalSwing = getHorizontalSwing(horizontalSwing);
123 List<ACHorizontalSwing> targetHorizontalSwings = targetModeCapabilities.getHorizontalSwing();
124 if (targetHorizontalSwings != null && targetHorizontalSwings.contains(targetHorizontalSwing)) {
125 newSetting.setHorizontalSwing(targetHorizontalSwing);
127 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetHorizontalSwing.getClass().getSimpleName(),
128 targetHorizontalSwing, targetMode.getClass().getSimpleName(), targetMode,
129 targetHorizontalSwings);
133 VerticalSwing verticalSwing = this.verticalSwing;
134 if (verticalSwing != null) {
135 ACVerticalSwing targetVerticalSwing = getVerticalSwing(verticalSwing);
136 List<ACVerticalSwing> targetVerticalSwings = targetModeCapabilities.getVerticalSwing();
137 if (targetVerticalSwings != null && targetVerticalSwings.contains(targetVerticalSwing)) {
138 newSetting.setVerticalSwing(targetVerticalSwing);
140 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetVerticalSwing.getClass().getSimpleName(),
141 targetVerticalSwing, targetMode.getClass().getSimpleName(), targetMode, targetVerticalSwings);
145 addMissingSettingParts(zoneStateProvider, genericCapabilities, newSetting);
150 private void addMissingSettingParts(ZoneStateProvider zoneStateProvider,
151 GenericZoneCapabilities genericCapabilities, CoolingZoneSetting newSetting)
152 throws IOException, ApiException {
153 if (newSetting.getMode() == null) {
154 AcMode targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
155 newSetting.setMode(targetMode);
158 AcModeCapabilities targetCapabilities = getModeCapabilities(newSetting.getMode(), genericCapabilities);
160 TemperatureRange temperatures = targetCapabilities.getTemperatures();
161 if (temperatures != null && newSetting.getTemperature() == null) {
162 newSetting.setTemperature(getCurrentOrDefaultTemperature(zoneStateProvider, temperatures));
165 List<AcFanSpeed> fanSpeeds = targetCapabilities.getFanSpeeds();
166 if (fanSpeeds != null && !fanSpeeds.isEmpty() && newSetting.getFanSpeed() == null) {
167 newSetting.setFanSpeed(getCurrentOrDefaultFanSpeed(zoneStateProvider, fanSpeeds));
170 List<Power> swings = targetCapabilities.getSwings();
171 if (swings != null && !swings.isEmpty() && newSetting.getSwing() == null) {
172 newSetting.setSwing(getCurrentOrDefaultSwing(zoneStateProvider, swings));
175 List<ACFanLevel> fanLevels = targetCapabilities.getFanLevel();
176 if (fanLevels != null && !fanLevels.isEmpty() && newSetting.getFanLevel() == null) {
177 newSetting.setFanLevel(getCurrentOrDefaultFanLevel(zoneStateProvider, fanLevels));
180 List<ACHorizontalSwing> horizontalSwings = targetCapabilities.getHorizontalSwing();
181 if (horizontalSwings != null && !horizontalSwings.isEmpty() && newSetting.getHorizontalSwing() == null) {
182 newSetting.setHorizontalSwing(getCurrentOrDefaultHorizontalSwing(zoneStateProvider, horizontalSwings));
185 List<ACVerticalSwing> verticalSwings = targetCapabilities.getVerticalSwing();
186 if (verticalSwings != null && !verticalSwings.isEmpty() && newSetting.getVerticalSwing() == null) {
187 newSetting.setVerticalSwing(getCurrentOrDefaultVerticalSwing(zoneStateProvider, verticalSwings));
190 List<Power> lights = targetCapabilities.getLight();
191 if (lights != null && !lights.isEmpty() && newSetting.getLight() == null) {
192 newSetting.setLight(getCurrentOrDefaultLight(zoneStateProvider, lights));
196 private AcMode getCurrentOrDefaultAcMode(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
197 AcMode acMode = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getMode();
198 return acMode != null ? acMode : DEFAULT_MODE;
201 private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider,
202 TemperatureRange temperatureRanges) throws IOException, ApiException {
203 CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
205 Float defaultTemperature = temperatureUnit == TemperatureUnit.FAHRENHEIT ? DEFAULT_TEMPERATURE_F
206 : DEFAULT_TEMPERATURE_C;
207 Float temperature = (zoneSetting != null && zoneSetting.getTemperature() != null)
208 ? getTemperatureInUnit(zoneSetting.getTemperature(), temperatureUnit)
209 : defaultTemperature;
210 IntRange temperatureRange = temperatureUnit == TemperatureUnit.FAHRENHEIT ? temperatureRanges.getFahrenheit()
211 : temperatureRanges.getCelsius();
213 Float finalTemperature = temperatureRange.getMax() >= temperature && temperatureRange.getMin() <= temperature
215 : temperatureRange.getMax();
217 return temperature(finalTemperature, temperatureUnit);
220 private AcFanSpeed getCurrentOrDefaultFanSpeed(ZoneStateProvider zoneStateProvider, List<AcFanSpeed> fanSpeeds)
221 throws IOException, ApiException {
222 AcFanSpeed fanSpeed = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanSpeed();
223 return (fanSpeed != null) && fanSpeeds.contains(fanSpeed) ? fanSpeed : fanSpeeds.get(0);
226 private Power getCurrentOrDefaultSwing(ZoneStateProvider zoneStateProvider, List<Power> swings)
227 throws IOException, ApiException {
228 Power swing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getSwing();
229 return (swing != null) && swings.contains(swing) ? swing : swings.get(0);
232 private ACFanLevel getCurrentOrDefaultFanLevel(ZoneStateProvider zoneStateProvider, List<ACFanLevel> fanLevels)
233 throws IOException, ApiException {
234 ACFanLevel fanLevel = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanLevel();
235 return (fanLevel != null) && fanLevels.contains(fanLevel) ? fanLevel : fanLevels.get(0);
238 private ACVerticalSwing getCurrentOrDefaultVerticalSwing(ZoneStateProvider zoneStateProvider,
239 List<ACVerticalSwing> vertSwings) throws IOException, ApiException {
240 ACVerticalSwing vertSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
242 return (vertSwing != null) && vertSwings.contains(vertSwing) ? vertSwing : vertSwings.get(0);
245 private ACHorizontalSwing getCurrentOrDefaultHorizontalSwing(ZoneStateProvider zoneStateProvider,
246 List<ACHorizontalSwing> horzSwings) throws IOException, ApiException {
247 ACHorizontalSwing horzSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
248 .getHorizontalSwing();
249 return (horzSwing != null) && horzSwings.contains(horzSwing) ? horzSwing : horzSwings.get(0);
252 private Power getCurrentOrDefaultLight(ZoneStateProvider zoneStateProvider, List<Power> lights)
253 throws IOException, ApiException {
254 Power light = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getLight();
255 return (light != null) && lights.contains(light) ? light : lights.get(0);
258 private CoolingZoneSetting coolingSetting(boolean powerOn) {
259 CoolingZoneSetting setting = new CoolingZoneSetting();
260 setting.setType(TadoSystemType.AIR_CONDITIONING);
261 setting.setPower(powerOn ? Power.ON : Power.OFF);