2 * Copyright (c) 2010-2023 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.somfytahoma.internal.handler;
15 import static org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants.*;
18 import java.util.concurrent.TimeUnit;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaState;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.types.Command;
27 import org.openhab.core.types.RefreshType;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * The {@link SomfyTahomaWaterHeatingSystemHandler} is responsible for handling commands,
33 * which are sent to one of the channels of the Water Heating system thing.
35 * @author Benjamin Lafois - Initial contribution
38 public class SomfyTahomaWaterHeatingSystemHandler extends SomfyTahomaBaseThingHandler {
40 private final Logger logger = LoggerFactory.getLogger(SomfyTahomaWaterHeatingSystemHandler.class);
42 private boolean boostMode = false;
43 private boolean awayMode = false;
45 public SomfyTahomaWaterHeatingSystemHandler(Thing thing) {
48 stateNames.put(MIDDLEWATER_TEMPERATURE, MIDDLE_WATER_TEMPERATURE_STATE);
49 stateNames.put(TARGET_TEMPERATURE, TARGET_TEMPERATURE_STATE);
50 stateNames.put(WATER_HEATER_MODE, WATER_HEATER_MODE_STATE);
52 stateNames.put(BOOST_MODE_DURATION, BOOST_MODE_DURATION_STATE);
53 // override state type because the cloud sends consumption in percent
54 cacheStateType(BOOST_MODE_DURATION_STATE, TYPE_DECIMAL);
56 stateNames.put(AWAY_MODE_DURATION, AWAY_MODE_DURATION_STATE);
57 // override state type because the cloud sends consumption in percent
58 cacheStateType(AWAY_MODE_DURATION_STATE, TYPE_DECIMAL);
60 stateNames.put(HEAT_PUMP_OPERATING_TIME, HEAT_PUMP_OPERATING_TIME_STATE);
61 // override state type because the cloud sends consumption in percent
62 cacheStateType(HEAT_PUMP_OPERATING_TIME_STATE, TYPE_DECIMAL);
64 stateNames.put(ELECTRIC_BOOSTER_OPERATING_TIME, ELECTRIC_BOOSTER_OPERATING_TIME_STATE);
65 // override state type because the cloud sends consumption in percent
66 cacheStateType(ELECTRIC_BOOSTER_OPERATING_TIME_STATE, TYPE_DECIMAL);
68 stateNames.put(POWER_HEAT_PUMP, POWER_HEAT_PUMP_STATE);
69 // override state type because the cloud sends consumption in percent
70 cacheStateType(POWER_HEAT_PUMP_STATE, TYPE_DECIMAL);
72 stateNames.put(POWER_HEAT_ELEC, POWER_HEAT_ELEC_STATE);
73 // override state type because the cloud sends consumption in percent
74 cacheStateType(POWER_HEAT_ELEC_STATE, TYPE_DECIMAL);
78 public void updateThingChannels(SomfyTahomaState state) {
79 if (OPERATING_MODE_STATE.equals(state.getName()) && state.getValue() instanceof Map) {
80 logger.debug("Operating Mode State: {} {}", state.getValue().getClass().getName(), state.getValue());
82 Map<String, String> data = (Map<String, String>) state.getValue();
84 Object relaunchValue = data.get("relaunch");
85 if (relaunchValue != null) {
86 this.boostMode = "on".equalsIgnoreCase(relaunchValue.toString());
87 logger.debug("Boost Value: {}", this.boostMode);
88 updateState(BOOST_MODE, OnOffType.from(this.boostMode));
91 Object awayValue = data.get("absence");
92 if (awayValue != null) {
93 this.awayMode = "on".equalsIgnoreCase(awayValue.toString());
94 logger.debug("Away Value: {}", this.awayMode);
95 updateState(AWAY_MODE, OnOffType.from(this.awayMode));
97 } else if (TARGET_TEMPERATURE_STATE.equals(state.getName())) {
98 logger.debug("Target Temperature: {}", state.getValue());
104 temp = Double.parseDouble(state.getValue().toString());
109 } else if (temp == 54.5) {
111 } else if (temp == 62) {
115 updateState(SHOWERS, new DecimalType(v));
116 } catch (NumberFormatException e) {
117 logger.warn("Unexpected pre-defined value for Target State Temperature: {}", state.getValue());
123 super.updateThingChannels(state);
126 private void sendOperatingMode() {
127 sendCommand(COMMAND_SET_CURRENT_OPERATING_MODE, String.format("[ { \"relaunch\":\"%s\", \"absence\":\"%s\"} ]",
128 (this.boostMode ? "on" : "off"), (this.awayMode ? "on" : "off")));
131 private void sendBoostDuration(int duration) {
132 sendCommand(COMMAND_SET_BOOST_MODE_DURATION, "[ " + duration + " ]");
136 public void handleCommand(ChannelUID channelUID, Command command) {
137 super.handleCommand(channelUID, command);
139 if (command instanceof RefreshType) {
142 logger.debug("Command received: {}/{}", channelUID.getId(), command.toString());
144 if (BOOST_MODE_DURATION.equals(channelUID.getId())) {
147 duration = Integer.parseInt(command.toString());
148 } catch (NumberFormatException e) {
149 logger.debug("Invalid value received for boost mode duration: {}", command);
153 this.boostMode = false;
155 } else if (duration > 0 && duration < 8) {
156 this.boostMode = true;
158 sendBoostDuration(duration);
160 } else if (WATER_HEATER_MODE.equals(channelUID.getId())) {
161 sendCommand(COMMAND_SET_WATER_HEATER_MODE, "[ \"" + command.toString() + "\" ]");
162 } else if (AWAY_MODE_DURATION.equals(channelUID.getId())) {
163 sendCommand(COMMAND_SET_AWAY_MODE_DURATION, "[ \"" + command.toString() + "\" ]");
164 } else if (BOOST_MODE.equals(channelUID.getId()) && command instanceof OnOffType) {
165 if (command == OnOffType.ON) {
166 if (this.boostMode) {
169 this.boostMode = true;
171 scheduler.execute(() -> {
172 sendBoostDuration(1); // by default, boost for 1 day
175 scheduler.schedule(() -> {
176 sendCommand(COMMAND_REFRESH_DHWMODE, "[ ]");
177 }, 1, TimeUnit.SECONDS);
179 scheduler.schedule(() -> {
181 }, 2, TimeUnit.SECONDS);
183 scheduler.schedule(() -> {
184 sendCommand(COMMAND_REFRESH_BOOST_MODE_DURATION, "[ ]");
185 }, 3, TimeUnit.SECONDS);
188 this.boostMode = false;
191 } else if (AWAY_MODE.equals(channelUID.getId()) && command instanceof OnOffType) {
192 if (command == OnOffType.ON) {
193 this.boostMode = false;
194 this.awayMode = true;
196 this.awayMode = false;
199 } else if (SHOWERS.equals(channelUID.getId())) {
202 showers = Integer.parseInt(command.toString());
203 } catch (NumberFormatException e) {
204 logger.info("Received an invalid value for desired number of showers: {}", command);
222 sendCommand(COMMAND_SET_TARGET_TEMPERATURE, "[ " + value.toString() + " ]");