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.nest.internal.wwn.handler;
15 import static org.openhab.binding.nest.internal.wwn.WWNBindingConstants.*;
16 import static org.openhab.core.library.unit.SIUnits.CELSIUS;
17 import static org.openhab.core.thing.Thing.PROPERTY_FIRMWARE_VERSION;
18 import static org.openhab.core.types.RefreshType.REFRESH;
20 import java.math.BigDecimal;
21 import java.math.RoundingMode;
23 import javax.measure.Unit;
24 import javax.measure.quantity.Temperature;
25 import javax.measure.quantity.Time;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.nest.internal.wwn.dto.WWNThermostat;
30 import org.openhab.binding.nest.internal.wwn.dto.WWNThermostat.Mode;
31 import org.openhab.core.library.types.OnOffType;
32 import org.openhab.core.library.types.QuantityType;
33 import org.openhab.core.library.types.StringType;
34 import org.openhab.core.library.unit.Units;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.types.Command;
39 import org.openhab.core.types.State;
40 import org.openhab.core.types.UnDefType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * The {@link WWNThermostatHandler} is responsible for handling commands, which are
46 * sent to one of the channels for the thermostat.
48 * @author David Bennett - Initial contribution
49 * @author Wouter Born - Handle channel refresh command
52 public class WWNThermostatHandler extends WWNBaseHandler<WWNThermostat> {
53 private final Logger logger = LoggerFactory.getLogger(WWNThermostatHandler.class);
55 public WWNThermostatHandler(Thing thing) {
56 super(thing, WWNThermostat.class);
60 protected State getChannelState(ChannelUID channelUID, WWNThermostat thermostat) {
61 switch (channelUID.getId()) {
62 case CHANNEL_CAN_COOL:
63 return getAsOnOffTypeOrNull(thermostat.isCanCool());
64 case CHANNEL_CAN_HEAT:
65 return getAsOnOffTypeOrNull(thermostat.isCanHeat());
66 case CHANNEL_ECO_MAX_SET_POINT:
67 return getAsQuantityTypeOrNull(thermostat.getEcoTemperatureHigh(), thermostat.getTemperatureUnit());
68 case CHANNEL_ECO_MIN_SET_POINT:
69 return getAsQuantityTypeOrNull(thermostat.getEcoTemperatureLow(), thermostat.getTemperatureUnit());
70 case CHANNEL_FAN_TIMER_ACTIVE:
71 return getAsOnOffTypeOrNull(thermostat.isFanTimerActive());
72 case CHANNEL_FAN_TIMER_DURATION:
73 return getAsQuantityTypeOrNull(thermostat.getFanTimerDuration(), Units.MINUTE);
74 case CHANNEL_FAN_TIMER_TIMEOUT:
75 return getAsDateTimeTypeOrNull(thermostat.getFanTimerTimeout());
77 return getAsOnOffTypeOrNull(thermostat.isHasFan());
78 case CHANNEL_HAS_LEAF:
79 return getAsOnOffTypeOrNull(thermostat.isHasLeaf());
80 case CHANNEL_HUMIDITY:
81 return getAsQuantityTypeOrNull(thermostat.getHumidity(), Units.PERCENT);
82 case CHANNEL_LAST_CONNECTION:
83 return getAsDateTimeTypeOrNull(thermostat.getLastConnection());
85 return getAsOnOffTypeOrNull(thermostat.isLocked());
86 case CHANNEL_LOCKED_MAX_SET_POINT:
87 return getAsQuantityTypeOrNull(thermostat.getLockedTempMax(), thermostat.getTemperatureUnit());
88 case CHANNEL_LOCKED_MIN_SET_POINT:
89 return getAsQuantityTypeOrNull(thermostat.getLockedTempMin(), thermostat.getTemperatureUnit());
90 case CHANNEL_MAX_SET_POINT:
91 return getAsQuantityTypeOrNull(thermostat.getTargetTemperatureHigh(), thermostat.getTemperatureUnit());
92 case CHANNEL_MIN_SET_POINT:
93 return getAsQuantityTypeOrNull(thermostat.getTargetTemperatureLow(), thermostat.getTemperatureUnit());
95 return getAsStringTypeOrNull(thermostat.getMode());
96 case CHANNEL_PREVIOUS_MODE:
97 Mode previousMode = thermostat.getPreviousHvacMode() != null ? thermostat.getPreviousHvacMode()
98 : thermostat.getMode();
99 return getAsStringTypeOrNull(previousMode);
101 return getAsStringTypeOrNull(thermostat.getHvacState());
102 case CHANNEL_SET_POINT:
103 return getAsQuantityTypeOrNull(thermostat.getTargetTemperature(), thermostat.getTemperatureUnit());
104 case CHANNEL_SUNLIGHT_CORRECTION_ACTIVE:
105 return getAsOnOffTypeOrNull(thermostat.isSunlightCorrectionActive());
106 case CHANNEL_SUNLIGHT_CORRECTION_ENABLED:
107 return getAsOnOffTypeOrNull(thermostat.isSunlightCorrectionEnabled());
108 case CHANNEL_TEMPERATURE:
109 return getAsQuantityTypeOrNull(thermostat.getAmbientTemperature(), thermostat.getTemperatureUnit());
110 case CHANNEL_TIME_TO_TARGET:
111 return getAsQuantityTypeOrNull(thermostat.getTimeToTarget(), Units.MINUTE);
112 case CHANNEL_USING_EMERGENCY_HEAT:
113 return getAsOnOffTypeOrNull(thermostat.isUsingEmergencyHeat());
115 logger.error("Unsupported channelId '{}'", channelUID.getId());
116 return UnDefType.UNDEF;
121 * Handle the command to do things to the thermostat, this will change the
122 * value of a channel by sending the request to Nest.
125 @SuppressWarnings("unchecked")
126 public void handleCommand(ChannelUID channelUID, Command command) {
127 if (REFRESH.equals(command)) {
128 WWNThermostat lastUpdate = getLastUpdate();
129 if (lastUpdate != null) {
130 updateState(channelUID, getChannelState(channelUID, lastUpdate));
132 } else if (CHANNEL_FAN_TIMER_ACTIVE.equals(channelUID.getId())) {
133 if (command instanceof OnOffType) {
134 // Update fan timer active to the command value
135 addUpdateRequest("fan_timer_active", command == OnOffType.ON);
137 } else if (CHANNEL_FAN_TIMER_DURATION.equals(channelUID.getId())) {
138 if (command instanceof QuantityType) {
139 // Update fan timer duration to the command value
140 QuantityType<Time> minuteQuantity = ((QuantityType<Time>) command).toUnit(Units.MINUTE);
141 if (minuteQuantity != null) {
142 addUpdateRequest("fan_timer_duration", minuteQuantity.intValue());
145 } else if (CHANNEL_MAX_SET_POINT.equals(channelUID.getId())) {
146 if (command instanceof QuantityType) {
147 // Update maximum set point to the command value
148 addTemperatureUpdateRequest("target_temperature_high_c", "target_temperature_high_f",
149 (QuantityType<Temperature>) command);
151 } else if (CHANNEL_MIN_SET_POINT.equals(channelUID.getId())) {
152 if (command instanceof QuantityType) {
153 // Update minimum set point to the command value
154 addTemperatureUpdateRequest("target_temperature_low_c", "target_temperature_low_f",
155 (QuantityType<Temperature>) command);
157 } else if (CHANNEL_MODE.equals(channelUID.getId())) {
158 if (command instanceof StringType) {
159 // Update the HVAC mode to the command value
160 addUpdateRequest("hvac_mode", Mode.valueOf(((StringType) command).toString()));
162 } else if (CHANNEL_SET_POINT.equals(channelUID.getId())) {
163 if (command instanceof QuantityType) {
164 // Update set point to the command value
165 addTemperatureUpdateRequest("target_temperature_c", "target_temperature_f",
166 (QuantityType<Temperature>) command);
171 private void addUpdateRequest(String field, Object value) {
172 addUpdateRequest(NEST_THERMOSTAT_UPDATE_PATH, field, value);
175 private void addTemperatureUpdateRequest(String celsiusField, String fahrenheitField,
176 QuantityType<Temperature> quantity) {
177 Unit<Temperature> unit = getTemperatureUnit(quantity.getUnit());
178 BigDecimal value = quantityToRoundedTemperature(quantity, unit);
180 addUpdateRequest(NEST_THERMOSTAT_UPDATE_PATH, unit == CELSIUS ? celsiusField : fahrenheitField, value);
184 private Unit<Temperature> getTemperatureUnit(Unit<Temperature> fallbackUnit) {
185 WWNThermostat lastUpdate = getLastUpdate();
186 if (lastUpdate != null && lastUpdate.getTemperatureUnit() != null) {
187 return lastUpdate.getTemperatureUnit();
193 private @Nullable BigDecimal quantityToRoundedTemperature(QuantityType<Temperature> quantity,
194 Unit<Temperature> unit) throws IllegalArgumentException {
195 QuantityType<Temperature> temparatureQuantity = quantity.toUnit(unit);
196 if (temparatureQuantity == null) {
200 BigDecimal value = temparatureQuantity.toBigDecimal();
201 BigDecimal increment = CELSIUS == unit ? new BigDecimal("0.5") : new BigDecimal("1");
202 BigDecimal divisor = value.divide(increment, 0, RoundingMode.HALF_UP);
203 return divisor.multiply(increment);
207 protected void update(@Nullable WWNThermostat oldThermostat, WWNThermostat thermostat) {
208 logger.debug("Updating {}", getThing().getUID());
210 updateLinkedChannels(oldThermostat, thermostat);
211 updateProperty(PROPERTY_FIRMWARE_VERSION, thermostat.getSoftwareVersion());
213 ThingStatus newStatus = thermostat.isOnline() == null ? ThingStatus.UNKNOWN
214 : thermostat.isOnline() ? ThingStatus.ONLINE : ThingStatus.OFFLINE;
215 if (newStatus != thing.getStatus()) {
216 updateStatus(newStatus);