]> git.basschouten.com Git - openhab-addons.git/blob
105f0b1bb0bd21144f7c0b2db384783270da4453
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.io.homekit.internal.accessories;
14
15 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.CURRENT_HEATING_COOLING_STATE;
16 import static org.openhab.io.homekit.internal.HomekitCharacteristicType.TARGET_HEATING_COOLING_STATE;
17
18 import java.math.BigDecimal;
19 import java.util.ArrayList;
20 import java.util.EnumMap;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Optional;
24 import java.util.concurrent.CompletableFuture;
25
26 import org.openhab.core.library.items.NumberItem;
27 import org.openhab.core.library.items.StringItem;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.io.homekit.internal.HomekitAccessoryUpdater;
31 import org.openhab.io.homekit.internal.HomekitCharacteristicType;
32 import org.openhab.io.homekit.internal.HomekitSettings;
33 import org.openhab.io.homekit.internal.HomekitTaggedItem;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import io.github.hapjava.accessories.ThermostatAccessory;
38 import io.github.hapjava.characteristics.HomekitCharacteristicChangeCallback;
39 import io.github.hapjava.characteristics.impl.thermostat.CurrentHeatingCoolingStateEnum;
40 import io.github.hapjava.characteristics.impl.thermostat.CurrentTemperatureCharacteristic;
41 import io.github.hapjava.characteristics.impl.thermostat.TargetHeatingCoolingStateEnum;
42 import io.github.hapjava.characteristics.impl.thermostat.TargetTemperatureCharacteristic;
43 import io.github.hapjava.characteristics.impl.thermostat.TemperatureDisplayUnitEnum;
44 import io.github.hapjava.services.impl.ThermostatService;
45
46 /**
47  * Implements Thermostat as a GroupedAccessory made up of multiple items:
48  * <ul>
49  * <li>Current Temperature: Number type</li>
50  * <li>Target Temperature: Number type</li>
51  * <li>Current Heating/Cooling Mode: String type (see HomekitSettings.thermostat*Mode)</li>
52  * <li>Target Heating/Cooling Mode: String type (see HomekitSettings.thermostat*Mode)</li>
53  * </ul>
54  *
55  * @author Andy Lintner - Initial contribution
56  */
57 class HomekitThermostatImpl extends AbstractHomekitAccessoryImpl implements ThermostatAccessory {
58     private final Logger logger = LoggerFactory.getLogger(HomekitThermostatImpl.class);
59     private final Map<CurrentHeatingCoolingStateEnum, String> currentHeatingCoolingStateMapping;
60     private final Map<TargetHeatingCoolingStateEnum, String> targetHeatingCoolingStateMapping;
61     private final List<CurrentHeatingCoolingStateEnum> customCurrentHeatingCoolingStateList;
62     private final List<TargetHeatingCoolingStateEnum> customTargetHeatingCoolingStateList;
63
64     public HomekitThermostatImpl(HomekitTaggedItem taggedItem, List<HomekitTaggedItem> mandatoryCharacteristics,
65             HomekitAccessoryUpdater updater, HomekitSettings settings) {
66         super(taggedItem, mandatoryCharacteristics, updater, settings);
67         currentHeatingCoolingStateMapping = new EnumMap<>(CurrentHeatingCoolingStateEnum.class);
68         currentHeatingCoolingStateMapping.put(CurrentHeatingCoolingStateEnum.OFF, settings.thermostatCurrentModeOff);
69         currentHeatingCoolingStateMapping.put(CurrentHeatingCoolingStateEnum.COOL,
70                 settings.thermostatCurrentModeCooling);
71         currentHeatingCoolingStateMapping.put(CurrentHeatingCoolingStateEnum.HEAT,
72                 settings.thermostatCurrentModeHeating);
73         targetHeatingCoolingStateMapping = new EnumMap<>(TargetHeatingCoolingStateEnum.class);
74         targetHeatingCoolingStateMapping.put(TargetHeatingCoolingStateEnum.OFF, settings.thermostatTargetModeOff);
75         targetHeatingCoolingStateMapping.put(TargetHeatingCoolingStateEnum.COOL, settings.thermostatTargetModeCool);
76         targetHeatingCoolingStateMapping.put(TargetHeatingCoolingStateEnum.HEAT, settings.thermostatTargetModeHeat);
77         targetHeatingCoolingStateMapping.put(TargetHeatingCoolingStateEnum.AUTO, settings.thermostatTargetModeAuto);
78         customCurrentHeatingCoolingStateList = new ArrayList<>();
79         customTargetHeatingCoolingStateList = new ArrayList<>();
80         updateMapping(CURRENT_HEATING_COOLING_STATE, currentHeatingCoolingStateMapping,
81                 customCurrentHeatingCoolingStateList);
82         updateMapping(TARGET_HEATING_COOLING_STATE, targetHeatingCoolingStateMapping,
83                 customTargetHeatingCoolingStateList);
84         this.getServices().add(new ThermostatService(this));
85     }
86
87     @Override
88     public CurrentHeatingCoolingStateEnum[] getCurrentHeatingCoolingStateValidValues() {
89         return customCurrentHeatingCoolingStateList.isEmpty()
90                 ? currentHeatingCoolingStateMapping.keySet().toArray(new CurrentHeatingCoolingStateEnum[0])
91                 : customCurrentHeatingCoolingStateList.toArray(new CurrentHeatingCoolingStateEnum[0]);
92     }
93
94     @Override
95     public TargetHeatingCoolingStateEnum[] getTargetHeatingCoolingStateValidValues() {
96         return customTargetHeatingCoolingStateList.isEmpty()
97                 ? targetHeatingCoolingStateMapping.keySet().toArray(new TargetHeatingCoolingStateEnum[0])
98                 : customTargetHeatingCoolingStateList.toArray(new TargetHeatingCoolingStateEnum[0]);
99     }
100
101     @Override
102     public CompletableFuture<CurrentHeatingCoolingStateEnum> getCurrentState() {
103         return CompletableFuture.completedFuture(getKeyFromMapping(CURRENT_HEATING_COOLING_STATE,
104                 currentHeatingCoolingStateMapping, CurrentHeatingCoolingStateEnum.OFF));
105     }
106
107     @Override
108     public CompletableFuture<Double> getCurrentTemperature() {
109         DecimalType state = getStateAs(HomekitCharacteristicType.CURRENT_TEMPERATURE, DecimalType.class);
110         return CompletableFuture
111                 .completedFuture(state != null ? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue())
112                         : getMinCurrentTemperature());
113     }
114
115     @Override
116     public double getMinCurrentTemperature() {
117         // Apple defines default values in Celsius. We need to convert them to Fahrenheit if openHAB is using Fahrenheit
118         // convertToCelsius and convertFromCelsius are only converting if useFahrenheit is set to true, so no additional
119         // check here needed
120
121         return HomekitCharacteristicFactory.convertToCelsius(
122                 getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MIN_VALUE,
123                         BigDecimal.valueOf(HomekitCharacteristicFactory
124                                 .convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
125                                         .doubleValue());
126     }
127
128     @Override
129     public double getMaxCurrentTemperature() {
130         return HomekitCharacteristicFactory.convertToCelsius(
131                 getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.MAX_VALUE,
132                         BigDecimal.valueOf(HomekitCharacteristicFactory
133                                 .convertFromCelsius(CurrentTemperatureCharacteristic.DEFAULT_MAX_VALUE)))
134                                         .doubleValue());
135     }
136
137     @Override
138     public double getMinStepCurrentTemperature() {
139         return getAccessoryConfiguration(HomekitCharacteristicType.CURRENT_TEMPERATURE, HomekitTaggedItem.STEP,
140                 BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_STEP)).doubleValue();
141     }
142
143     @Override
144     public CompletableFuture<TargetHeatingCoolingStateEnum> getTargetState() {
145         return CompletableFuture.completedFuture(getKeyFromMapping(TARGET_HEATING_COOLING_STATE,
146                 targetHeatingCoolingStateMapping, TargetHeatingCoolingStateEnum.OFF));
147     }
148
149     @Override
150     public CompletableFuture<TemperatureDisplayUnitEnum> getTemperatureDisplayUnit() {
151         return CompletableFuture
152                 .completedFuture(HomekitCharacteristicFactory.useFahrenheit() ? TemperatureDisplayUnitEnum.FAHRENHEIT
153                         : TemperatureDisplayUnitEnum.CELSIUS);
154     }
155
156     @Override
157     public void setTemperatureDisplayUnit(TemperatureDisplayUnitEnum value) {
158         // TODO: add support for display unit change
159     }
160
161     @Override
162     public CompletableFuture<Double> getTargetTemperature() {
163         DecimalType state = getStateAs(HomekitCharacteristicType.TARGET_TEMPERATURE, DecimalType.class);
164         return CompletableFuture.completedFuture(
165                 state != null ? HomekitCharacteristicFactory.convertToCelsius(state.doubleValue()) : 0.0);
166     }
167
168     @Override
169     public void setTargetState(TargetHeatingCoolingStateEnum mode) {
170         getItem(TARGET_HEATING_COOLING_STATE, StringItem.class)
171                 .ifPresent(item -> item.send(new StringType(targetHeatingCoolingStateMapping.get(mode))));
172     }
173
174     @Override
175     public void setTargetTemperature(Double value) {
176         final Optional<HomekitTaggedItem> characteristic = getCharacteristic(
177                 HomekitCharacteristicType.TARGET_TEMPERATURE);
178         if (characteristic.isPresent()) {
179             ((NumberItem) characteristic.get().getItem())
180                     .send(new DecimalType(BigDecimal.valueOf(HomekitCharacteristicFactory.convertFromCelsius(value))));
181         } else {
182             logger.warn("Missing mandatory characteristic {}", HomekitCharacteristicType.TARGET_TEMPERATURE);
183         }
184     }
185
186     @Override
187     public double getMinTargetTemperature() {
188         return HomekitCharacteristicFactory
189                 .convertToCelsius(
190                         getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE,
191                                 HomekitTaggedItem.MIN_VALUE,
192                                 BigDecimal.valueOf(HomekitCharacteristicFactory
193                                         .convertFromCelsius(TargetTemperatureCharacteristic.DEFAULT_MIN_VALUE)))
194                                                 .doubleValue());
195     }
196
197     @Override
198     public double getMaxTargetTemperature() {
199         return HomekitCharacteristicFactory
200                 .convertToCelsius(
201                         getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE,
202                                 HomekitTaggedItem.MAX_VALUE,
203                                 BigDecimal.valueOf(HomekitCharacteristicFactory
204                                         .convertFromCelsius(TargetTemperatureCharacteristic.DEFAULT_MAX_VALUE)))
205                                                 .doubleValue());
206     }
207
208     @Override
209     public double getMinStepTargetTemperature() {
210         return getAccessoryConfiguration(HomekitCharacteristicType.TARGET_TEMPERATURE, HomekitTaggedItem.STEP,
211                 BigDecimal.valueOf(TargetTemperatureCharacteristic.DEFAULT_STEP)).doubleValue();
212     }
213
214     @Override
215     public void subscribeCurrentState(HomekitCharacteristicChangeCallback callback) {
216         subscribe(CURRENT_HEATING_COOLING_STATE, callback);
217     }
218
219     @Override
220     public void subscribeCurrentTemperature(HomekitCharacteristicChangeCallback callback) {
221         subscribe(HomekitCharacteristicType.CURRENT_TEMPERATURE, callback);
222     }
223
224     @Override
225     public void subscribeTargetState(HomekitCharacteristicChangeCallback callback) {
226         subscribe(HomekitCharacteristicType.TARGET_HEATING_COOLING_STATE, callback);
227     }
228
229     @Override
230     public void subscribeTargetTemperature(HomekitCharacteristicChangeCallback callback) {
231         subscribe(HomekitCharacteristicType.TARGET_TEMPERATURE, callback);
232     }
233
234     @Override
235     public void subscribeTemperatureDisplayUnit(HomekitCharacteristicChangeCallback callback) {
236         // TODO: add support for display unit change
237     }
238
239     @Override
240     public void unsubscribeCurrentState() {
241         unsubscribe(CURRENT_HEATING_COOLING_STATE);
242     }
243
244     @Override
245     public void unsubscribeCurrentTemperature() {
246         unsubscribe(HomekitCharacteristicType.CURRENT_TEMPERATURE);
247     }
248
249     @Override
250     public void unsubscribeTemperatureDisplayUnit() {
251         // TODO: add support for display unit change
252     }
253
254     @Override
255     public void unsubscribeTargetState() {
256         unsubscribe(HomekitCharacteristicType.TARGET_HEATING_COOLING_STATE);
257     }
258
259     @Override
260     public void unsubscribeTargetTemperature() {
261         unsubscribe(HomekitCharacteristicType.TARGET_TEMPERATURE);
262     }
263 }