]> git.basschouten.com Git - openhab-addons.git/blob
a66259caf663e91b0f365da310efb795c1f37568
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.omnilink.internal.handler;
14
15 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
16
17 import java.math.BigInteger;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21
22 import javax.measure.quantity.Dimensionless;
23 import javax.measure.quantity.Temperature;
24
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;
43
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;
52
53 /**
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.
57  *
58  * @author Craig Hamilton - Initial contribution
59  * @author Ethan Dye - openHAB3 rewrite
60  */
61 @NonNullByDefault
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;
66
67     private enum ThermostatStatus {
68         HEATING(0, 1),
69         COOLING(1, 2),
70         HUMIDIFYING(2, 3),
71         DEHUMIDIFYING(3, 4);
72
73         private final int bit;
74         private final int modeValue;
75
76         private ThermostatStatus(int bit, int modeValue) {
77             this.bit = bit;
78             this.modeValue = modeValue;
79         }
80     }
81
82     public ThermostatHandler(Thing thing) {
83         super(thing);
84     }
85
86     @Override
87     public void initialize() {
88         super.initialize();
89         final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
90         if (bridgeHandler != null) {
91             updateThermostatProperties(bridgeHandler);
92         } else {
93             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
94                     "Received null bridge while initializing Thermostat!");
95         }
96     }
97
98     private void updateThermostatProperties(OmnilinkBridgeHandler bridgeHandler) {
99         final List<AreaProperties> areas = getAreaProperties();
100         if (areas != null) {
101             for (AreaProperties areaProperties : areas) {
102                 int areaFilter = bitFilterForArea(areaProperties);
103
104                 ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
105                         .builder(bridgeHandler, ObjectPropertyRequests.THERMOSTAT, thingID, 0).selectNamed()
106                         .areaFilter(areaFilter).build();
107
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);
113                 }
114             }
115         }
116     }
117
118     @Override
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();
124
125         if (command instanceof RefreshType) {
126             retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
127                     ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
128             return;
129         }
130
131         if (!(command instanceof DecimalType) && !(command instanceof QuantityType)) {
132             logger.debug("Invalid command: {}, must be DecimalType or QuantityType", command);
133             return;
134         }
135         if (bridgeHandler != null) {
136             temperatureFormat = bridgeHandler.getTemperatureFormat();
137             if (!temperatureFormat.isPresent()) {
138                 logger.warn("Receieved null temperature format!");
139                 return;
140             }
141         } else {
142             logger.warn("Could not connect to Bridge, failed to get temperature format!");
143             return;
144         }
145
146         switch (channelUID.getId()) {
147             case CHANNEL_THERMO_SYSTEM_MODE:
148                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_SYSTEM_MODE, ((DecimalType) command).intValue(),
149                         thingID);
150                 break;
151             case CHANNEL_THERMO_FAN_MODE:
152                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_FAN_MODE, ((DecimalType) command).intValue(),
153                         thingID);
154                 break;
155             case CHANNEL_THERMO_HOLD_STATUS:
156                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HOLD_MODE, ((DecimalType) command).intValue(),
157                         thingID);
158                 break;
159             case CHANNEL_THERMO_HEAT_SETPOINT:
160                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HEAT_POINT,
161                         temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
162                         thingID);
163                 break;
164             case CHANNEL_THERMO_COOL_SETPOINT:
165                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_COOL_POINT,
166                         temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
167                         thingID);
168                 break;
169             case CHANNEL_THERMO_HUMIDIFY_SETPOINT:
170                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_HUMDIFY_POINT,
171                         TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
172                         thingID);
173                 break;
174             case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT:
175                 sendOmnilinkCommand(CommandMessage.CMD_THERMO_SET_DEHUMIDIFY_POINT,
176                         TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
177                         thingID);
178                 break;
179             default:
180                 logger.warn("Unknown channel for Thermostat thing: {}", channelUID);
181         }
182     }
183
184     @Override
185     protected void updateChannels(ExtendedThermostatStatus status) {
186         logger.debug("updateChannels called for Thermostat status: {}", status);
187         final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
188
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);
195
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));
206         } else {
207             updateState(CHANNEL_THERMO_STATUS, new DecimalType(0));
208         }
209
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));
226             } else {
227                 logger.warn("Receieved null temperature format, could not update Thermostat channels!");
228             }
229         } else {
230             logger.warn("Could not connect to Bridge, failed to get temperature format!");
231             return;
232         }
233
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));
241
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()));
247     }
248
249     @Override
250     protected Optional<ExtendedThermostatStatus> retrieveStatus() {
251         try {
252             final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
253             if (bridgeHandler != null) {
254                 ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_THERMO, thingID, thingID,
255                         true);
256                 return Optional.of((ExtendedThermostatStatus) objStatus.getStatuses()[0]);
257             } else {
258                 logger.debug("Received null bridge while updating Thermostat status!");
259                 return Optional.empty();
260             }
261         } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
262             logger.debug("Received exception while refreshing Thermostat status: {}", e.getMessage());
263             return Optional.empty();
264         }
265     }
266 }