2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ojelectronics.internal;
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;
23 import java.util.function.Consumer;
25 import javax.measure.quantity.Temperature;
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;
48 * The {@link ThermostatHandler} is responsible for handling commands, which are
49 * sent to one of the channels.
51 * @author Christian Kittel - Initial contribution
54 public class ThermostatHandler extends BaseThingHandler {
56 private static final Map<Integer, String> REGULATION_MODES = createRegulationMap();
57 private static final Map<String, Integer> REVERSE_REGULATION_MODES = createRegulationReverseMap();
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;
65 private LinkedList<AbstractMap.SimpleImmutableEntry<String, Command>> updatedValues = new LinkedList<>();
66 private @Nullable Thermostat currentThermostat;
69 * Creates a new instance of {@link ThermostatHandler}
72 * @param timeZoneProvider Time zone
74 public ThermostatHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
76 serialNumber = getConfigAs(OJElectronicsThermostatConfiguration.class).serialNumber;
77 this.timeZoneProvider = timeZoneProvider;
81 * Gets the thing's serial number.
83 * @return serial number
85 public String getSerialNumber() {
90 * Handles commands to this thing.
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);
103 synchronized (this) {
104 updatedValues.add(new AbstractMap.SimpleImmutableEntry<String, Command>(channelUID.getId(), command));
110 * Initializes the thing handler.
113 public void initialize() {
114 updateStatus(ThingStatus.ONLINE);
118 * Sets the values after refreshing the thermostats values
120 * @param thermostat thermostat values
122 public void handleThermostatRefresh(Thermostat thermostat) {
123 currentThermostat = thermostat;
124 channelrefreshActions.forEach((channelUID, action) -> action.accept(thermostat));
128 * Gets a {@link Thermostat} with changed values or null if nothing has changed
130 * @return The changed {@link Thermostat}
132 public @Nullable Thermostat tryHandleAndGetUpdatedThermostat() {
133 final LinkedList<SimpleImmutableEntry<String, Command>> updatedValues = this.updatedValues;
134 if (updatedValues.isEmpty()) {
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());
146 return currentThermostat;
149 private void updateManualSetpoint(Thermostat thermostat) {
150 updateState(BindingConstants.CHANNEL_OWD5_MANUALSETPOINT,
151 new QuantityType<Temperature>(thermostat.manualModeSetpoint / (double) 100, SIUnits.CELSIUS));
154 private void updateManualSetpoint(Command command) {
155 if (command instanceof QuantityType<?>) {
156 currentThermostat.manualModeSetpoint = (int) (((QuantityType<?>) command).floatValue() * 100);
158 logger.warn("Unable to set value {}", command);
162 private void updateBoostEndTime(Thermostat thermostat) {
163 updateState(BindingConstants.CHANNEL_OWD5_BOOSTENDTIME, new DateTimeType(
164 ZonedDateTime.ofInstant(thermostat.boostEndTime.toInstant(), timeZoneProvider.getTimeZone())));
167 private void updateBoostEndTime(Command command) {
168 if (command instanceof DateTimeType) {
169 currentThermostat.boostEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant());
171 logger.warn("Unable to set value {}", command);
175 private void updateComfortEndTime(Thermostat thermostat) {
176 updateState(BindingConstants.CHANNEL_OWD5_COMFORTENDTIME, new DateTimeType(
177 ZonedDateTime.ofInstant(thermostat.comfortEndTime.toInstant(), timeZoneProvider.getTimeZone())));
180 private void updateComfortEndTime(Command command) {
181 if (command instanceof DateTimeType) {
182 currentThermostat.comfortEndTime = Date.from(((DateTimeType) command).getZonedDateTime().toInstant());
184 logger.warn("Unable to set value {}", command);
188 private void updateComfortSetpoint(Thermostat thermostat) {
189 updateState(BindingConstants.CHANNEL_OWD5_COMFORTSETPOINT,
190 new QuantityType<Temperature>(thermostat.comfortSetpoint / (double) 100, SIUnits.CELSIUS));
193 private void updateComfortSetpoint(Command command) {
194 if (command instanceof QuantityType<?>) {
195 currentThermostat.comfortSetpoint = (int) (((QuantityType<?>) command).floatValue() * 100);
197 logger.warn("Unable to set value {}", command);
201 private void updateRegulationMode(Thermostat thermostat) {
202 updateState(BindingConstants.CHANNEL_OWD5_REGULATIONMODE,
203 StringType.valueOf(getRegulationMode(thermostat.regulationMode)));
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());
210 currentThermostat.regulationMode = mode;
213 logger.warn("Unable to set value {}", command);
217 private void updateThermostatName(Thermostat thermostat) {
218 updateState(BindingConstants.CHANNEL_OWD5_THERMOSTATNAME, StringType.valueOf(thermostat.thermostatName));
221 private void updateFloorTemperature(Thermostat thermostat) {
222 updateState(BindingConstants.CHANNEL_OWD5_FLOORTEMPERATURE,
223 new QuantityType<Temperature>(thermostat.floorTemperature / (double) 100, SIUnits.CELSIUS));
226 private void updateRoomTemperature(Thermostat thermostat) {
227 updateState(BindingConstants.CHANNEL_OWD5_ROOMTEMPERATURE,
228 new QuantityType<Temperature>(thermostat.roomTemperature / (double) 100, SIUnits.CELSIUS));
231 private void updateHeating(Thermostat thermostat) {
232 updateState(BindingConstants.CHANNEL_OWD5_HEATING,
233 thermostat.heating ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
236 private void updateOnline(Thermostat thermostat) {
237 updateState(BindingConstants.CHANNEL_OWD5_ONLINE,
238 thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
241 private void updateGroupId(Thermostat thermostat) {
242 updateState(BindingConstants.CHANNEL_OWD5_GROUPID, new DecimalType(thermostat.groupId));
245 private void updateGroupName(Thermostat thermostat) {
246 updateState(BindingConstants.CHANNEL_OWD5_GROUPNAME, StringType.valueOf(thermostat.groupName));
249 private void updateVacationEnabled(Thermostat thermostat) {
250 updateState(BindingConstants.CHANNEL_OWD5_VACATIONENABLED,
251 thermostat.online ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
254 private void updateVacationBeginDay(Thermostat thermostat) {
255 updateState(BindingConstants.CHANNEL_OWD5_VACATIONBEGINDAY,
257 ZonedDateTime.ofInstant(thermostat.vacationBeginDay.toInstant(), timeZoneProvider.getTimeZone())
258 .truncatedTo(ChronoUnit.DAYS)));
261 private void updateVacationBeginDay(Command command) {
262 if (command instanceof DateTimeType) {
263 currentThermostat.vacationBeginDay = Date
264 .from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
266 logger.warn("Unable to set value {}", command);
270 private void updateVacationEndDay(Thermostat thermostat) {
271 updateState(BindingConstants.CHANNEL_OWD5_VACATIONENDDAY,
273 ZonedDateTime.ofInstant(thermostat.vacationEndDay.toInstant(), timeZoneProvider.getTimeZone())
274 .truncatedTo(ChronoUnit.DAYS)));
277 private void updateVacationEndDay(Command command) {
278 if (command instanceof DateTimeType) {
279 currentThermostat.vacationEndDay = Date
280 .from(((DateTimeType) command).getZonedDateTime().toInstant().truncatedTo(ChronoUnit.DAYS));
282 logger.warn("Unable to set value {}", command);
286 private @Nullable String getRegulationMode(int regulationMode) {
287 return REGULATION_MODES.get(regulationMode);
290 private static Map<Integer, String> createRegulationMap() {
291 HashMap<Integer, String> map = new HashMap<>();
293 map.put(2, "comfort");
294 map.put(3, "manual");
295 map.put(4, "vacation");
296 map.put(6, "frostProtection");
302 private static Map<String, Integer> createRegulationReverseMap() {
303 HashMap<String, Integer> map = new HashMap<>();
305 map.put("comfort", 2);
306 map.put("manual", 3);
307 map.put("vacation", 4);
308 map.put("frostprotection", 6);
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);
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);