2 * Copyright (c) 2010-2021 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.omnilink.internal.handler;
15 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
17 import java.math.BigInteger;
18 import java.util.List;
20 import java.util.Optional;
22 import javax.measure.quantity.Dimensionless;
23 import javax.measure.quantity.Temperature;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
28 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
29 import org.openhab.core.library.types.DecimalType;
30 import org.openhab.core.library.types.OpenClosedType;
31 import org.openhab.core.library.types.QuantityType;
32 import org.openhab.core.library.unit.ImperialUnits;
33 import org.openhab.core.library.unit.SIUnits;
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.thing.ThingStatusDetail;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.RefreshType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 import com.digitaldan.jomnilinkII.Message;
45 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
46 import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
47 import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
48 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
49 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
50 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
53 * The {@link ThermostatHandler} defines some methods that are used to
54 * interface with an OmniLink Thermostat. This by extension also defines the
55 * Thermostat thing that openHAB will be able to pick up and interface with.
57 * @author Craig Hamilton - Initial contribution
58 * @author Ethan Dye - openHAB3 rewrite
61 public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThermostatStatus> {
62 private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class);
63 private final int thingID = getThingNumber();
64 public @Nullable String number;
66 private enum ThermostatStatus {
72 private final int bit;
73 private final int modeValue;
75 private ThermostatStatus(int bit, int modeValue) {
77 this.modeValue = modeValue;
81 public ThermostatHandler(Thing thing) {
86 public void initialize() {
88 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
89 if (bridgeHandler != null) {
90 updateThermostatProperties(bridgeHandler);
92 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
93 "Received null bridge while initializing Thermostat!");
97 private void updateThermostatProperties(OmnilinkBridgeHandler bridgeHandler) {
98 final List<AreaProperties> areas = getAreaProperties();
100 for (AreaProperties areaProperties : areas) {
101 int areaFilter = bitFilterForArea(areaProperties);
103 ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
104 .builder(bridgeHandler, ObjectPropertyRequests.THERMOSTAT, thingID, 0).selectNamed()
105 .areaFilter(areaFilter).build();
107 for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
108 Map<String, String> properties = editProperties();
109 properties.put(THING_PROPERTIES_NAME, thermostatProperties.getName());
110 properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
111 updateProperties(properties);
118 @SuppressWarnings("unchecked")
119 public void handleCommand(ChannelUID channelUID, Command command) {
120 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
121 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
122 Optional<TemperatureFormat> temperatureFormat = Optional.empty();
124 if (command instanceof RefreshType) {
125 retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
126 ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
130 if (!(command instanceof DecimalType) && !(command instanceof QuantityType)) {
131 logger.debug("Invalid command: {}, must be DecimalType or QuantityType", command);
134 if (bridgeHandler != null) {
135 temperatureFormat = bridgeHandler.getTemperatureFormat();
136 if (!temperatureFormat.isPresent()) {
137 logger.warn("Receieved null temperature format!");
141 logger.warn("Could not connect to Bridge, failed to get temperature format!");
145 switch (channelUID.getId()) {
146 case CHANNEL_THERMO_SYSTEM_MODE:
147 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_SYSTEM_MODE.getNumber(),
148 ((DecimalType) command).intValue(), thingID);
150 case CHANNEL_THERMO_FAN_MODE:
151 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_FAN_MODE.getNumber(), ((DecimalType) command).intValue(),
154 case CHANNEL_THERMO_HOLD_STATUS:
155 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HOLD_MODE.getNumber(),
156 ((DecimalType) command).intValue(), thingID);
158 case CHANNEL_THERMO_HEAT_SETPOINT:
159 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(),
160 temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
163 case CHANNEL_THERMO_COOL_SETPOINT:
164 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(),
165 temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).intValue()),
168 case CHANNEL_THERMO_HUMIDIFY_SETPOINT:
169 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HUMDIFY_POINT.getNumber(),
170 TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
173 case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT:
174 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_DEHUMIDIFY_POINT.getNumber(),
175 TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).intValue()),
179 logger.warn("Unknown channel for Thermostat thing: {}", channelUID);
184 protected void updateChannels(ExtendedThermostatStatus status) {
185 logger.debug("updateChannels called for Thermostat status: {}", status);
186 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
188 // Thermostat communication status
189 BigInteger thermostatAlarms = BigInteger.valueOf(status.getStatus());
190 updateState(CHANNEL_THERMO_COMM_FAILURE,
191 thermostatAlarms.testBit(0) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
192 updateState(CHANNEL_THERMO_FREEZE_ALARM,
193 thermostatAlarms.testBit(1) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
195 // Thermostat operation status
196 BigInteger thermostatStatus = BigInteger.valueOf(status.getExtendedStatus());
197 if (thermostatStatus.testBit(ThermostatStatus.HEATING.bit)) {
198 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HEATING.modeValue));
199 } else if (thermostatStatus.testBit(ThermostatStatus.COOLING.bit)) {
200 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.COOLING.modeValue));
201 } else if (thermostatStatus.testBit(ThermostatStatus.HUMIDIFYING.bit)) {
202 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HUMIDIFYING.modeValue));
203 } else if (thermostatStatus.testBit(ThermostatStatus.DEHUMIDIFYING.bit)) {
204 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.DEHUMIDIFYING.modeValue));
206 updateState(CHANNEL_THERMO_STATUS, new DecimalType(0));
209 // Thermostat temperature status
210 if (bridgeHandler != null) {
211 Optional<TemperatureFormat> temperatureFormat = bridgeHandler.getTemperatureFormat();
212 if (temperatureFormat.isPresent()) {
213 updateState(CHANNEL_THERMO_CURRENT_TEMP, new QuantityType<>(
214 temperatureFormat.get().omniToFormat(status.getCurrentTemperature()),
215 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
216 updateState(CHANNEL_THERMO_OUTDOOR_TEMP, new QuantityType<>(
217 temperatureFormat.get().omniToFormat(status.getOutdoorTemperature()),
218 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
219 updateState(CHANNEL_THERMO_COOL_SETPOINT, new QuantityType<>(
220 temperatureFormat.get().omniToFormat(status.getCoolSetpoint()),
221 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
222 updateState(CHANNEL_THERMO_HEAT_SETPOINT, new QuantityType<>(
223 temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
224 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
226 logger.warn("Receieved null temperature format, could not update Thermostat channels!");
229 logger.warn("Could not connect to Bridge, failed to get temperature format!");
233 // Thermostat humidity status
234 updateState(CHANNEL_THERMO_HUMIDITY, new QuantityType<>(
235 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getCurrentHumidity()), Units.PERCENT));
236 updateState(CHANNEL_THERMO_HUMIDIFY_SETPOINT, new QuantityType<>(
237 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getHumidifySetpoint()), Units.PERCENT));
238 updateState(CHANNEL_THERMO_DEHUMIDIFY_SETPOINT, new QuantityType<>(
239 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getDehumidifySetpoint()), Units.PERCENT));
241 // Thermostat mode, fan, and hold status
242 updateState(CHANNEL_THERMO_SYSTEM_MODE, new DecimalType(status.getSystemMode()));
243 updateState(CHANNEL_THERMO_FAN_MODE, new DecimalType(status.getFanMode()));
244 updateState(CHANNEL_THERMO_HOLD_STATUS,
245 new DecimalType(status.getHoldStatus() > 2 ? 1 : status.getHoldStatus()));
249 protected Optional<ExtendedThermostatStatus> retrieveStatus() {
251 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
252 if (bridgeHandler != null) {
253 ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_THERMO, thingID, thingID,
255 return Optional.of((ExtendedThermostatStatus) objStatus.getStatuses()[0]);
257 logger.debug("Received null bridge while updating Thermostat status!");
258 return Optional.empty();
260 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
261 logger.debug("Received exception while refreshing Thermostat status: {}", e.getMessage());
262 return Optional.empty();