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.CommandMessage;
46 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
47 import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
48 import com.digitaldan.jomnilinkII.MessageTypes.properties.ThermostatProperties;
49 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedThermostatStatus;
50 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
51 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
54 * The {@link ThermostatHandler} defines some methods that are used to
55 * interface with an OmniLink Thermostat. This by extension also defines the
56 * Thermostat thing that openHAB will be able to pick up and interface with.
58 * @author Craig Hamilton - Initial contribution
59 * @author Ethan Dye - openHAB3 rewrite
62 public class ThermostatHandler extends AbstractOmnilinkStatusHandler<ExtendedThermostatStatus> {
63 private final Logger logger = LoggerFactory.getLogger(ThermostatHandler.class);
64 private final int thingID = getThingNumber();
65 public @Nullable String number;
67 private enum ThermostatStatus {
73 private final int bit;
74 private final int modeValue;
76 private ThermostatStatus(int bit, int modeValue) {
78 this.modeValue = modeValue;
82 public ThermostatHandler(Thing thing) {
87 public void initialize() {
89 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
90 if (bridgeHandler != null) {
91 updateThermostatProperties(bridgeHandler);
93 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
94 "Received null bridge while initializing Thermostat!");
98 private void updateThermostatProperties(OmnilinkBridgeHandler bridgeHandler) {
99 final List<AreaProperties> areas = getAreaProperties();
101 for (AreaProperties areaProperties : areas) {
102 int areaFilter = bitFilterForArea(areaProperties);
104 ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
105 .builder(bridgeHandler, ObjectPropertyRequests.THERMOSTAT, thingID, 0).selectNamed()
106 .areaFilter(areaFilter).build();
108 for (ThermostatProperties thermostatProperties : objectPropertyRequest) {
109 Map<String, String> properties = editProperties();
110 properties.put(THING_PROPERTIES_NAME, thermostatProperties.getName());
111 properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
112 updateProperties(properties);
119 @SuppressWarnings("unchecked")
120 public void handleCommand(ChannelUID channelUID, Command command) {
121 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
122 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
123 Optional<TemperatureFormat> temperatureFormat = Optional.empty();
125 if (command instanceof RefreshType) {
126 retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
127 ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
131 if (!(command instanceof DecimalType) && !(command instanceof QuantityType)) {
132 logger.debug("Invalid command: {}, must be DecimalType or QuantityType", command);
135 if (bridgeHandler != null) {
136 temperatureFormat = bridgeHandler.getTemperatureFormat();
137 if (!temperatureFormat.isPresent()) {
138 logger.warn("Receieved null temperature format!");
142 logger.warn("Could not connect to Bridge, failed to get temperature format!");
146 switch (channelUID.getId()) {
147 case CHANNEL_THERMO_SYSTEM_MODE:
148 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_SYSTEM_MODE, ((DecimalType) command).intValue(),
151 case CHANNEL_THERMO_FAN_MODE:
152 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_FAN_MODE, ((DecimalType) command).intValue(),
155 case CHANNEL_THERMO_HOLD_STATUS:
156 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HOLD_MODE, ((DecimalType) command).intValue(),
159 case CHANNEL_THERMO_HEAT_SETPOINT:
160 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HEAT_POINT,
161 temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
164 case CHANNEL_THERMO_COOL_SETPOINT:
165 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_COOL_POINT,
166 temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
169 case CHANNEL_THERMO_HUMIDIFY_SETPOINT:
170 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HUMDIFY_POINT,
171 TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
174 case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT:
175 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_DEHUMIDIFY_POINT,
176 TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
180 logger.warn("Unknown channel for Thermostat thing: {}", channelUID);
185 protected void updateChannels(ExtendedThermostatStatus status) {
186 logger.debug("updateChannels called for Thermostat status: {}", status);
187 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
189 // Thermostat communication status
190 BigInteger thermostatAlarms = BigInteger.valueOf(status.getStatus());
191 updateState(CHANNEL_THERMO_COMM_FAILURE,
192 thermostatAlarms.testBit(0) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
193 updateState(CHANNEL_THERMO_FREEZE_ALARM,
194 thermostatAlarms.testBit(1) ? OpenClosedType.CLOSED : OpenClosedType.OPEN);
196 // Thermostat operation status
197 BigInteger thermostatStatus = BigInteger.valueOf(status.getExtendedStatus());
198 if (thermostatStatus.testBit(ThermostatStatus.HEATING.bit)) {
199 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HEATING.modeValue));
200 } else if (thermostatStatus.testBit(ThermostatStatus.COOLING.bit)) {
201 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.COOLING.modeValue));
202 } else if (thermostatStatus.testBit(ThermostatStatus.HUMIDIFYING.bit)) {
203 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.HUMIDIFYING.modeValue));
204 } else if (thermostatStatus.testBit(ThermostatStatus.DEHUMIDIFYING.bit)) {
205 updateState(CHANNEL_THERMO_STATUS, new DecimalType(ThermostatStatus.DEHUMIDIFYING.modeValue));
207 updateState(CHANNEL_THERMO_STATUS, new DecimalType(0));
210 // Thermostat temperature status
211 if (bridgeHandler != null) {
212 Optional<TemperatureFormat> temperatureFormat = bridgeHandler.getTemperatureFormat();
213 if (temperatureFormat.isPresent()) {
214 updateState(CHANNEL_THERMO_CURRENT_TEMP, new QuantityType<>(
215 temperatureFormat.get().omniToFormat(status.getCurrentTemperature()),
216 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
217 updateState(CHANNEL_THERMO_OUTDOOR_TEMP, new QuantityType<>(
218 temperatureFormat.get().omniToFormat(status.getOutdoorTemperature()),
219 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
220 updateState(CHANNEL_THERMO_COOL_SETPOINT, new QuantityType<>(
221 temperatureFormat.get().omniToFormat(status.getCoolSetpoint()),
222 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
223 updateState(CHANNEL_THERMO_HEAT_SETPOINT, new QuantityType<>(
224 temperatureFormat.get().omniToFormat(status.getHeatSetpoint()),
225 temperatureFormat.get().getFormatNumber() == 1 ? ImperialUnits.FAHRENHEIT : SIUnits.CELSIUS));
227 logger.warn("Receieved null temperature format, could not update Thermostat channels!");
230 logger.warn("Could not connect to Bridge, failed to get temperature format!");
234 // Thermostat humidity status
235 updateState(CHANNEL_THERMO_HUMIDITY, new QuantityType<>(
236 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getCurrentHumidity()), Units.PERCENT));
237 updateState(CHANNEL_THERMO_HUMIDIFY_SETPOINT, new QuantityType<>(
238 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getHumidifySetpoint()), Units.PERCENT));
239 updateState(CHANNEL_THERMO_DEHUMIDIFY_SETPOINT, new QuantityType<>(
240 TemperatureFormat.FAHRENHEIT.omniToFormat(status.getDehumidifySetpoint()), Units.PERCENT));
242 // Thermostat mode, fan, and hold status
243 updateState(CHANNEL_THERMO_SYSTEM_MODE, new DecimalType(status.getSystemMode()));
244 updateState(CHANNEL_THERMO_FAN_MODE, new DecimalType(status.getFanMode()));
245 updateState(CHANNEL_THERMO_HOLD_STATUS,
246 new DecimalType(status.getHoldStatus() > 2 ? 1 : status.getHoldStatus()));
250 protected Optional<ExtendedThermostatStatus> retrieveStatus() {
252 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
253 if (bridgeHandler != null) {
254 ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_THERMO, thingID, thingID,
256 return Optional.of((ExtendedThermostatStatus) objStatus.getStatuses()[0]);
258 logger.debug("Received null bridge while updating Thermostat status!");
259 return Optional.empty();
261 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
262 logger.debug("Received exception while refreshing Thermostat status: {}", e.getMessage());
263 return Optional.empty();