]> git.basschouten.com Git - openhab-addons.git/blob
95720a88208531e877a03bbc3dcfb6204ceb6407
[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.binding.ojelectronics.internal;
14
15 import java.time.ZonedDateTime;
16 import java.time.temporal.ChronoUnit;
17 import java.util.AbstractMap;
18 import java.util.AbstractMap.SimpleImmutableEntry;
19 import java.util.Date;
20 import java.util.HashMap;
21 import java.util.LinkedList;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.function.Consumer;
25
26 import javax.measure.quantity.Temperature;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.ojelectronics.internal.config.OJElectronicsThermostatConfiguration;
31 import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatModel;
32 import org.openhab.binding.ojelectronics.internal.models.thermostat.ThermostatRealTimeValuesModel;
33 import org.openhab.core.i18n.TimeZoneProvider;
34 import org.openhab.core.library.types.DateTimeType;
35 import org.openhab.core.library.types.DecimalType;
36 import org.openhab.core.library.types.OpenClosedType;
37 import org.openhab.core.library.types.QuantityType;
38 import org.openhab.core.library.types.StringType;
39 import org.openhab.core.library.unit.SIUnits;
40 import org.openhab.core.thing.Bridge;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.thing.binding.BaseThingHandler;
45 import org.openhab.core.thing.binding.BridgeHandler;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.RefreshType;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * The {@link ThermostatHandler} is responsible for handling commands, which are
53  * sent to one of the channels.
54  *
55  * @author Christian Kittel - Initial contribution
56  */
57 @NonNullByDefault
58 public class ThermostatHandler extends BaseThingHandler {
59
60     private static final Map<Integer, String> REGULATION_MODES = createRegulationMap();
61     private static final Map<String, Integer> REVERSE_REGULATION_MODES = createRegulationReverseMap();
62
63     private final String serialNumber;
64     private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class);
65     private final Map<String, Consumer<ThermostatModel>> channelRefreshActions = createChannelRefreshActionMap();
66     private final Map<String, Consumer<ThermostatRealTimeValuesModel>> channelRealTimeRefreshActions = createRealTimeChannelRefreshActionMap();
67     private final Map<String, Consumer<Command>> updateThermostatValueActions = createUpdateThermostatValueActionMap();
68     private final TimeZoneProvider timeZoneProvider;
69
70     private LinkedList<AbstractMap.SimpleImmutableEntry<String, Command>> updatedValues = new LinkedList<>();
71     private @Nullable ThermostatModel currentThermostat;
72
73     /**
74      * Creates a new instance of {@link ThermostatHandler}
75      *
76      * @param thing Thing
77      * @param timeZoneProvider Time zone
78      */
79     public ThermostatHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
80         super(thing);
81         serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber;
82         this.timeZoneProvider = timeZoneProvider;
83     }
84
85     /**
86      * Gets the thing's serial number.
87      *
88      * @return serial number
89      */
90     public String getSerialNumber() {
91         return serialNumber;
92     }
93
94     /**
95      * Handles commands to this thing.
96      */
97     @Override
98     public void handleCommand(ChannelUID channelUID, Command command) {
99         if (command instanceof RefreshType) {
100             final ThermostatModel thermostat = currentThermostat;
101             if (thermostat != null && channelRefreshActions.containsKey(channelUID.getId())) {
102                 final @Nullable Consumer<ThermostatModel> consumer = channelRefreshActions.get(channelUID.getId());
103                 if (consumer != null) {
104                     consumer.accept(thermostat);
105                 }
106             }
107         } else {
108             synchronized (this) {
109                 updatedValues.add(new AbstractMap.SimpleImmutableEntry<String, Command>(channelUID.getId(), command));
110
111                 BridgeHandler bridgeHandler = Objects.requireNonNull(getBridge()).getHandler();
112                 if (bridgeHandler != null) {
113                     ((OJCloudHandler) (bridgeHandler)).updateThinksChannelValuesToCloud();
114                 } else {
115                     currentThermostat = null;
116                     updateStatus(ThingStatus.OFFLINE);
117                 }
118             }
119         }
120     }
121
122     /**
123      * Initializes the thing handler.
124      */
125     @Override
126     public void initialize() {
127         @Nullable
128         Bridge bridge = getBridge();
129         if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE && currentThermostat == null) {
130             @Nullable
131             OJCloudHandler bridgeHandler = (OJCloudHandler) (bridge.getHandler());
132             if (bridgeHandler != null) {
133                 bridgeHandler.reInitialize();
134             }
135         }
136     }
137
138     /**
139      * Sets the values after refreshing the thermostats values
140      *
141      * @param thermostat thermostat values
142      */
143     public void handleThermostatRefresh(ThermostatModel thermostat) {
144         if (currentThermostat == null) {
145             updateStatus(ThingStatus.ONLINE);
146         }
147         currentThermostat = thermostat;
148         channelRefreshActions.forEach((channelUID, action) -> action.accept(thermostat));
149     }
150
151     /**
152      * Sets the values after refreshing the thermostats values
153      *
154      * @param thermostat thermostat values
155      */
156     public void handleThermostatRefresh(ThermostatRealTimeValuesModel thermostat) {
157         final ThermostatModel currentThermostat = this.currentThermostat;
158         if (currentThermostat != null) {
159             currentThermostat.heating = thermostat.heating;
160             currentThermostat.floorTemperature = thermostat.floorTemperature;
161             currentThermostat.action = thermostat.action;
162             currentThermostat.online = thermostat.online;
163             currentThermostat.roomTemperature = thermostat.roomTemperature;
164             channelRealTimeRefreshActions.forEach((channelUID, action) -> action.accept(thermostat));
165         }
166     }
167
168     /**
169      * Gets a {@link ThermostatModel} with changed values or null if nothing has changed
170      *
171      * @return The changed {@link ThermostatModel}
172      */
173     public @Nullable ThermostatModel tryHandleAndGetUpdatedThermostat() {
174         final LinkedList<SimpleImmutableEntry<String, Command>> updatedValues = this.updatedValues;
175         if (updatedValues.isEmpty()) {
176             return null;
177         }
178         this.updatedValues = new LinkedList<>();
179         updatedValues.forEach(item -> {
180             if (updateThermostatValueActions.containsKey(item.getKey())) {
181                 final @Nullable Consumer<Command> consumer = updateThermostatValueActions.get(item.getKey());
182                 if (consumer != null) {
183                     consumer.accept(item.getValue());
184                 }
185             }
186         });
187         return currentThermostat;
188     }
189
190     private ThermostatModel getCurrentThermostat() {
191         return Objects.requireNonNull(currentThermostat);
192     }
193
194     private void updateManualSetpoint(ThermostatModel thermostat) {
195         updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT,
196                 new QuantityType<Temperature>(thermostat.manualModeSetpoint / (double) 100, SIUnits.CELSIUS));
197     }
198
199     private void updateManualSetpoint(Command command) {
200         if (command instanceof QuantityType<?> quantityCommand) {
201             getCurrentThermostat().manualModeSetpoint = (int) (quantityCommand.floatValue() * 100);
202         } else {
203             logger.warn("Unable to set value {}", command);
204         }
205     }
206
207     private void updateBoostEndTime(ThermostatModel thermostat) {
208         updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, new DateTimeType(
209                 ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), timeZoneProvider.getTimeZone())));
210     }
211
212     private void updateBoostEndTime(Command command) {
213         if (command instanceof DateTimeType dateTimeCommand) {
214             getCurrentThermostat().boostEndTime = Date.from(dateTimeCommand.getZonedDateTime().toInstant());
215         } else {
216             logger.warn("Unable to set value {}", command);
217         }
218     }
219
220     private void updateComfortEndTime(ThermostatModel thermostat) {
221         updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType(
222                 ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), timeZoneProvider.getTimeZone())));
223     }
224
225     private void updateComfortEndTime(Command command) {
226         if (command instanceof DateTimeType dateTimeCommand) {
227             getCurrentThermostat().comfortEndTime = Objects
228                     .requireNonNull(Date.from(dateTimeCommand.getZonedDateTime().toInstant()));
229         } else {
230             logger.warn("Unable to set value {}", command);
231         }
232     }
233
234     private void updateComfortSetpoint(ThermostatModel thermostat) {
235         updateState(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT,
236                 new QuantityType<Temperature>(thermostat.comfortSetpoint / (double) 100, SIUnits.CELSIUS));
237     }
238
239     private void updateComfortSetpoint(Command command) {
240         if (command instanceof QuantityType<?> quantityCommand) {
241             getCurrentThermostat().comfortSetpoint = (int) (quantityCommand.floatValue() * 100);
242         } else {
243             logger.warn("Unable to set value {}", command);
244         }
245     }
246
247     private void updateRegulationMode(ThermostatModel thermostat) {
248         updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE,
249                 StringType.valueOf(getRegulationMode(thermostat.regulationMode)));
250     }
251
252     private void updateRegulationMode(Command command) {
253         if (command instanceof StringType && (REVERSE_REGULATION_MODES.containsKey(command.toString().toLowerCase()))) {
254             final @Nullable Integer mode = REVERSE_REGULATION_MODES.get(command.toString().toLowerCase());
255             if (mode != null) {
256                 getCurrentThermostat().regulationMode = mode;
257             }
258         } else {
259             logger.warn("Unable to set value {}", command);
260         }
261     }
262
263     private void updateThermostatName(ThermostatModel thermostat) {
264         updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName));
265     }
266
267     private void updateFloorTemperature(ThermostatModel thermostat) {
268         updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE,
269                 new QuantityType<Temperature>(thermostat.floorTemperature / (double) 100, SIUnits.CELSIUS));
270     }
271
272     private void updateFloorTemperature(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
273         updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, new QuantityType<Temperature>(
274                 thermostatRealTimeValues.floorTemperature / (double) 100, SIUnits.CELSIUS));
275     }
276
277     private void updateRoomTemperature(ThermostatModel thermostat) {
278         updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE,
279                 new QuantityType<Temperature>(thermostat.roomTemperature / (double) 100, SIUnits.CELSIUS));
280     }
281
282     private void updateRoomTemperature(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
283         updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, new QuantityType<Temperature>(
284                 thermostatRealTimeValues.roomTemperature / (double) 100, SIUnits.CELSIUS));
285     }
286
287     private void updateHeating(ThermostatModel thermostat) {
288         updateState(BindingConstants.CHANNEL_OWD5_HEATING,
289                 thermostat.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
290     }
291
292     private void updateHeating(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
293         updateState(BindingConstants.CHANNEL_OWD5_HEATING,
294                 thermostatRealTimeValues.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
295     }
296
297     private void updateOnline(ThermostatModel thermostat) {
298         updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
299                 thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
300     }
301
302     private void updateOnline(ThermostatRealTimeValuesModel thermostatRealTimeValues) {
303         updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
304                 thermostatRealTimeValues.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
305     }
306
307     private void updateGroupId(ThermostatModel thermostat) {
308         updateState(BindingConstants.CHANNEL_OWD5_GROUPID, new DecimalType(thermostat.groupId));
309     }
310
311     private void updateGroupName(ThermostatModel thermostat) {
312         updateState(BindingConstants.CHANNEL_OWD5_GROUPNAME, StringType.valueOf(thermostat.groupName));
313     }
314
315     private void updateVacationEnabled(ThermostatModel thermostat) {
316         updateState(BindingConstants.CHANNEL_OWD5_VACATIONENABLED,
317                 thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
318     }
319
320     private void updateVacationBeginDay(ThermostatModel thermostat) {
321         updateState(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY,
322                 new DateTimeType(
323                         ZonedDateTime.ofInstant(thermostat.vacationBeginDay.toInstant(), timeZoneProvider.getTimeZone())
324                                 .truncatedTo(ChronoUnit.DAYS)));
325     }
326
327     private void updateVacationBeginDay(Command command) {
328         if (command instanceof DateTimeType dateTimeCommand) {
329             getCurrentThermostat().vacationBeginDay = Date
330                     .from(dateTimeCommand.getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
331         } else {
332             logger.warn("Unable to set value {}", command);
333         }
334     }
335
336     private void updateVacationEndDay(ThermostatModel thermostat) {
337         updateState(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY,
338                 new DateTimeType(
339                         ZonedDateTime.ofInstant(thermostat.vacationEndDay.toInstant(), timeZoneProvider.getTimeZone())
340                                 .truncatedTo(ChronoUnit.DAYS)));
341     }
342
343     private void updateVacationEndDay(Command command) {
344         if (command instanceof DateTimeType dateTimeCommand) {
345             getCurrentThermostat().vacationEndDay = Date
346                     .from(dateTimeCommand.getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
347         } else {
348             logger.warn("Unable to set value {}", command);
349         }
350     }
351
352     private @Nullable String getRegulationMode(int regulationMode) {
353         return REGULATION_MODES.get(regulationMode);
354     }
355
356     private static Map<Integer, String> createRegulationMap() {
357         HashMap<Integer, String> map = new HashMap<>();
358         map.put(1, "auto");
359         map.put(2, "comfort");
360         map.put(3, "manual");
361         map.put(4, "vacation");
362         map.put(6, "frostProtection");
363         map.put(8, "boost");
364         map.put(9, "eco");
365         return map;
366     };
367
368     private static Map<String, Integer> createRegulationReverseMap() {
369         HashMap<String, Integer> map = new HashMap<>();
370         map.put("auto", 1);
371         map.put("comfort", 2);
372         map.put("manual", 3);
373         map.put("vacation", 4);
374         map.put("frostprotection", 6);
375         map.put("boost", 8);
376         map.put("eco", 9);
377         return map;
378     };
379
380     private Map<String, Consumer<ThermostatModel>> createChannelRefreshActionMap() {
381         HashMap<String, Consumer<ThermostatModel>> map = new HashMap<>();
382         map.put(BindingConstants.CHANNEL_OWD5_GROUPNAME, this::updateGroupName);
383         map.put(BindingConstants.CHANNEL_OWD5_GROUPID, this::updateGroupId);
384         map.put(BindingConstants.CHANNEL_OWD5_ONLINE, this::updateOnline);
385         map.put(BindingConstants.CHANNEL_OWD5_HEATING, this::updateHeating);
386         map.put(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, this::updateRoomTemperature);
387         map.put(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, this::updateFloorTemperature);
388         map.put(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, this::updateThermostatName);
389         map.put(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, this::updateRegulationMode);
390         map.put(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT, this::updateComfortSetpoint);
391         map.put(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, this::updateComfortEndTime);
392         map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime);
393         map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint);
394         map.put(BindingConstants.CHANNEL_OWD5_VACATIONENABLED, this::updateVacationEnabled);
395         map.put(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY, this::updateVacationBeginDay);
396         map.put(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY, this::updateVacationEndDay);
397         return map;
398     }
399
400     private Map<String, Consumer<ThermostatRealTimeValuesModel>> createRealTimeChannelRefreshActionMap() {
401         HashMap<String, Consumer<ThermostatRealTimeValuesModel>> map = new HashMap<>();
402         map.put(BindingConstants.CHANNEL_OWD5_ONLINE, this::updateOnline);
403         map.put(BindingConstants.CHANNEL_OWD5_HEATING, this::updateHeating);
404         map.put(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE, this::updateRoomTemperature);
405         map.put(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE, this::updateFloorTemperature);
406         return map;
407     }
408
409     private Map<String, Consumer<Command>> createUpdateThermostatValueActionMap() {
410         HashMap<String, Consumer<Command>> map = new HashMap<>();
411         map.put(BindingConstants.CHANNEL_OWD5_REGULATIONMODE, this::updateRegulationMode);
412         map.put(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT, this::updateManualSetpoint);
413         map.put(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, this::updateBoostEndTime);
414         map.put(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, this::updateComfortEndTime);
415         map.put(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT, this::updateComfortSetpoint);
416         map.put(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY, this::updateVacationBeginDay);
417         map.put(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY, this::updateVacationEndDay);
418         return map;
419     }
420 }