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