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