]> git.basschouten.com Git - openhab-addons.git/blob
b7b6fcaa53e24b043e8bc9fca5491606cef5e0fc
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.tado.internal.builder;
14
15 import static org.openhab.binding.tado.internal.api.TadoApiTypeUtils.*;
16
17 import java.io.IOException;
18 import java.util.List;
19
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;
45
46 /**
47  *
48  *
49  * @author Dennis Frommknecht - Initial contribution
50  */
51 @NonNullByDefault
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;
56
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);
59
60     @Override
61     public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities genericCapabilities)
62             throws IOException, ApiException {
63         if (mode == HvacMode.OFF) {
64             return coolingSetting(false);
65         }
66
67         CoolingZoneSetting newSetting = coolingSetting(true);
68
69         AcMode targetMode;
70         HvacMode mode = this.mode;
71         if (mode != null) {
72             targetMode = getAcMode(mode);
73             newSetting.setMode(targetMode);
74         } else {
75             // if mode not changing, so the reference is the current (or default) mode
76             targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
77         }
78
79         Boolean swing = this.swing;
80         if (swing != null) {
81             newSetting.setSwing(swing.booleanValue() ? Power.ON : Power.OFF);
82         }
83
84         Boolean light = this.light;
85         if (light != null) {
86             newSetting.setLight(light.booleanValue() ? Power.ON : Power.OFF);
87         }
88
89         FanSpeed fanSpeed = this.fanSpeed;
90         if (fanSpeed != null) {
91             newSetting.setFanSpeed(getAcFanSpeed(fanSpeed));
92         }
93
94         /*
95          * In the latest API release Tado introduced extra AC settings that have an open ended list of possible
96          * supported state values. And for any particular device, its specific list of supported values is available
97          * via its 'capabilities' structure. So before setting a new value, we check if the respective new value is in
98          * the capabilities list that corresponds to the target AC mode. And if not, a warning message is logged.
99          */
100         AcModeCapabilities targetModeCapabilities = TadoApiTypeUtils.getModeCapabilities(targetMode,
101                 genericCapabilities);
102
103         Float temperature = this.temperature;
104         if (temperature != null) {
105             IntRange range = null;
106             boolean valid = false;
107             TemperatureRange caps = targetModeCapabilities.getTemperatures();
108             if (caps != null) {
109                 range = temperatureUnit == TemperatureUnit.CELSIUS ? caps.getCelsius() : caps.getFahrenheit();
110                 valid = (range != null) && (range.getMin() <= temperature) && (temperature <= range.getMax());
111             }
112             if (valid) {
113                 newSetting.setTemperature(temperature(temperature, temperatureUnit));
114             } else {
115                 logger.warn(STATE_VALUE_NOT_SUPPORTED, "Target Temperature", temperature,
116                         targetMode.getClass().getSimpleName(), targetMode,
117                         range == null ? "none" : String.format("%d..%d", range.getMin(), range.getMax()));
118             }
119         }
120
121         FanLevel fanLevel = this.fanLevel;
122         if (fanLevel != null) {
123             ACFanLevel targetFanLevel = getFanLevel(fanLevel);
124             List<ACFanLevel> targetFanLevels = targetModeCapabilities.getFanLevel();
125             if (targetFanLevels != null && targetFanLevels.contains(targetFanLevel)) {
126                 newSetting.setFanLevel(targetFanLevel);
127             } else {
128                 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetFanLevel.getClass().getSimpleName(), targetFanLevel,
129                         targetMode.getClass().getSimpleName(), targetMode, targetFanLevels);
130             }
131         }
132
133         HorizontalSwing horizontalSwing = this.horizontalSwing;
134         if (horizontalSwing != null) {
135             ACHorizontalSwing targetHorizontalSwing = getHorizontalSwing(horizontalSwing);
136             List<ACHorizontalSwing> targetHorizontalSwings = targetModeCapabilities.getHorizontalSwing();
137             if (targetHorizontalSwings != null && targetHorizontalSwings.contains(targetHorizontalSwing)) {
138                 newSetting.setHorizontalSwing(targetHorizontalSwing);
139             } else {
140                 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetHorizontalSwing.getClass().getSimpleName(),
141                         targetHorizontalSwing, targetMode.getClass().getSimpleName(), targetMode,
142                         targetHorizontalSwings);
143             }
144         }
145
146         VerticalSwing verticalSwing = this.verticalSwing;
147         if (verticalSwing != null) {
148             ACVerticalSwing targetVerticalSwing = getVerticalSwing(verticalSwing);
149             List<ACVerticalSwing> targetVerticalSwings = targetModeCapabilities.getVerticalSwing();
150             if (targetVerticalSwings != null && targetVerticalSwings.contains(targetVerticalSwing)) {
151                 newSetting.setVerticalSwing(targetVerticalSwing);
152             } else {
153                 logger.warn(STATE_VALUE_NOT_SUPPORTED, targetVerticalSwing.getClass().getSimpleName(),
154                         targetVerticalSwing, targetMode.getClass().getSimpleName(), targetMode, targetVerticalSwings);
155             }
156         }
157
158         addMissingSettingParts(zoneStateProvider, genericCapabilities, newSetting);
159
160         return newSetting;
161     }
162
163     private void addMissingSettingParts(ZoneStateProvider zoneStateProvider,
164             GenericZoneCapabilities genericCapabilities, CoolingZoneSetting newSetting)
165             throws IOException, ApiException {
166         if (newSetting.getMode() == null) {
167             AcMode targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
168             newSetting.setMode(targetMode);
169         }
170
171         AcModeCapabilities targetCapabilities = getModeCapabilities(newSetting.getMode(), genericCapabilities);
172
173         TemperatureRange temperatures = targetCapabilities.getTemperatures();
174         if (temperatures != null && newSetting.getTemperature() == null) {
175             newSetting.setTemperature(getCurrentOrDefaultTemperature(zoneStateProvider, temperatures));
176         }
177
178         List<AcFanSpeed> fanSpeeds = targetCapabilities.getFanSpeeds();
179         if (fanSpeeds != null && !fanSpeeds.isEmpty() && newSetting.getFanSpeed() == null) {
180             newSetting.setFanSpeed(getCurrentOrDefaultFanSpeed(zoneStateProvider, fanSpeeds));
181         }
182
183         List<Power> swings = targetCapabilities.getSwings();
184         if (swings != null && !swings.isEmpty() && newSetting.getSwing() == null) {
185             newSetting.setSwing(getCurrentOrDefaultSwing(zoneStateProvider, swings));
186         }
187
188         List<ACFanLevel> fanLevels = targetCapabilities.getFanLevel();
189         if (fanLevels != null && !fanLevels.isEmpty() && newSetting.getFanLevel() == null) {
190             newSetting.setFanLevel(getCurrentOrDefaultFanLevel(zoneStateProvider, fanLevels));
191         }
192
193         List<ACHorizontalSwing> horizontalSwings = targetCapabilities.getHorizontalSwing();
194         if (horizontalSwings != null && !horizontalSwings.isEmpty() && newSetting.getHorizontalSwing() == null) {
195             newSetting.setHorizontalSwing(getCurrentOrDefaultHorizontalSwing(zoneStateProvider, horizontalSwings));
196         }
197
198         List<ACVerticalSwing> verticalSwings = targetCapabilities.getVerticalSwing();
199         if (verticalSwings != null && !verticalSwings.isEmpty() && newSetting.getVerticalSwing() == null) {
200             newSetting.setVerticalSwing(getCurrentOrDefaultVerticalSwing(zoneStateProvider, verticalSwings));
201         }
202
203         List<Power> lights = targetCapabilities.getLight();
204         if (lights != null && !lights.isEmpty() && newSetting.getLight() == null) {
205             newSetting.setLight(getCurrentOrDefaultLight(zoneStateProvider, lights));
206         }
207     }
208
209     private AcMode getCurrentOrDefaultAcMode(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
210         AcMode acMode = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getMode();
211         return acMode != null ? acMode : DEFAULT_MODE;
212     }
213
214     private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider,
215             TemperatureRange temperatureRanges) throws IOException, ApiException {
216         CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
217
218         Float defaultTemperature = temperatureUnit == TemperatureUnit.FAHRENHEIT ? DEFAULT_TEMPERATURE_F
219                 : DEFAULT_TEMPERATURE_C;
220         Float temperature = (zoneSetting != null && zoneSetting.getTemperature() != null)
221                 ? getTemperatureInUnit(zoneSetting.getTemperature(), temperatureUnit)
222                 : defaultTemperature;
223         IntRange temperatureRange = temperatureUnit == TemperatureUnit.FAHRENHEIT ? temperatureRanges.getFahrenheit()
224                 : temperatureRanges.getCelsius();
225
226         Float finalTemperature = temperatureRange.getMax() >= temperature && temperatureRange.getMin() <= temperature
227                 ? temperature
228                 : temperatureRange.getMax();
229
230         return temperature(finalTemperature, temperatureUnit);
231     }
232
233     private AcFanSpeed getCurrentOrDefaultFanSpeed(ZoneStateProvider zoneStateProvider, List<AcFanSpeed> fanSpeeds)
234             throws IOException, ApiException {
235         AcFanSpeed fanSpeed = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanSpeed();
236         return (fanSpeed != null) && fanSpeeds.contains(fanSpeed) ? fanSpeed : fanSpeeds.get(0);
237     }
238
239     private Power getCurrentOrDefaultSwing(ZoneStateProvider zoneStateProvider, List<Power> swings)
240             throws IOException, ApiException {
241         Power swing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getSwing();
242         return (swing != null) && swings.contains(swing) ? swing : swings.get(0);
243     }
244
245     private ACFanLevel getCurrentOrDefaultFanLevel(ZoneStateProvider zoneStateProvider, List<ACFanLevel> fanLevels)
246             throws IOException, ApiException {
247         ACFanLevel fanLevel = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanLevel();
248         return (fanLevel != null) && fanLevels.contains(fanLevel) ? fanLevel : fanLevels.get(0);
249     }
250
251     private ACVerticalSwing getCurrentOrDefaultVerticalSwing(ZoneStateProvider zoneStateProvider,
252             List<ACVerticalSwing> vertSwings) throws IOException, ApiException {
253         ACVerticalSwing vertSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
254                 .getVerticalSwing();
255         return (vertSwing != null) && vertSwings.contains(vertSwing) ? vertSwing : vertSwings.get(0);
256     }
257
258     private ACHorizontalSwing getCurrentOrDefaultHorizontalSwing(ZoneStateProvider zoneStateProvider,
259             List<ACHorizontalSwing> horzSwings) throws IOException, ApiException {
260         ACHorizontalSwing horzSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
261                 .getHorizontalSwing();
262         return (horzSwing != null) && horzSwings.contains(horzSwing) ? horzSwing : horzSwings.get(0);
263     }
264
265     private Power getCurrentOrDefaultLight(ZoneStateProvider zoneStateProvider, List<Power> lights)
266             throws IOException, ApiException {
267         Power light = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getLight();
268         return (light != null) && lights.contains(light) ? light : lights.get(0);
269     }
270
271     private CoolingZoneSetting coolingSetting(boolean powerOn) {
272         CoolingZoneSetting setting = new CoolingZoneSetting();
273         setting.setType(TadoSystemType.AIR_CONDITIONING);
274         setting.setPower(powerOn ? Power.ON : Power.OFF);
275         return setting;
276     }
277 }