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