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