]> git.basschouten.com Git - openhab-addons.git/blob
f5daccab31857e66a43bc318b1ff01b10d2e0ee1
[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.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;
51
52 /**
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.
56  *
57  * @author Craig Hamilton - Initial contribution
58  * @author Ethan Dye - openHAB3 rewrite
59  */
60 @NonNullByDefault
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;
65
66     private enum ThermostatStatus {
67         HEATING(0, 1),
68         COOLING(1, 2),
69         HUMIDIFYING(2, 3),
70         DEHUMIDIFYING(3, 4);
71
72         private final int bit;
73         private final int modeValue;
74
75         private ThermostatStatus(int bit, int modeValue) {
76             this.bit = bit;
77             this.modeValue = modeValue;
78         }
79     }
80
81     public ThermostatHandler(Thing thing) {
82         super(thing);
83     }
84
85     @Override
86     public void initialize() {
87         super.initialize();
88         final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
89         if (bridgeHandler != null) {
90             updateThermostatProperties(bridgeHandler);
91         } else {
92             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
93                     "Received null bridge while initializing Thermostat!");
94         }
95     }
96
97     private void updateThermostatProperties(OmnilinkBridgeHandler bridgeHandler) {
98         final List<AreaProperties> areas = getAreaProperties();
99         if (areas != null) {
100             for (AreaProperties areaProperties : areas) {
101                 int areaFilter = bitFilterForArea(areaProperties);
102
103                 ObjectPropertyRequest<ThermostatProperties> objectPropertyRequest = ObjectPropertyRequest
104                         .builder(bridgeHandler, ObjectPropertyRequests.THERMOSTAT, thingID, 0).selectNamed()
105                         .areaFilter(areaFilter).build();
106
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);
112                 }
113             }
114         }
115     }
116
117     @Override
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();
123
124         if (command instanceof RefreshType) {
125             retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
126                     ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
127             return;
128         }
129
130         if (!(command instanceof DecimalType) && !(command instanceof QuantityType)) {
131             logger.debug("Invalid command: {}, must be DecimalType or QuantityType", command);
132             return;
133         }
134         if (bridgeHandler != null) {
135             temperatureFormat = bridgeHandler.getTemperatureFormat();
136             if (!temperatureFormat.isPresent()) {
137                 logger.warn("Receieved null temperature format!");
138                 return;
139             }
140         } else {
141             logger.warn("Could not connect to Bridge, failed to get temperature format!");
142             return;
143         }
144
145         switch (channelUID.getId()) {
146             case CHANNEL_THERMO_SYSTEM_MODE:
147                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_SYSTEM_MODE.getNumber(),
148                         ((DecimalType) command).intValue(), thingID);
149                 break;
150             case CHANNEL_THERMO_FAN_MODE:
151                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_FAN_MODE.getNumber(), ((DecimalType) command).intValue(),
152                         thingID);
153                 break;
154             case CHANNEL_THERMO_HOLD_STATUS:
155                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HOLD_MODE.getNumber(),
156                         ((DecimalType) command).intValue(), thingID);
157                 break;
158             case CHANNEL_THERMO_HEAT_SETPOINT:
159                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HEAT_LOW_POINT.getNumber(),
160                         temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
161                         thingID);
162                 break;
163             case CHANNEL_THERMO_COOL_SETPOINT:
164                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_COOL_HIGH_POINT.getNumber(),
165                         temperatureFormat.get().formatToOmni(((QuantityType<Temperature>) command).floatValue()),
166                         thingID);
167                 break;
168             case CHANNEL_THERMO_HUMIDIFY_SETPOINT:
169                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_HUMDIFY_POINT.getNumber(),
170                         TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
171                         thingID);
172                 break;
173             case CHANNEL_THERMO_DEHUMIDIFY_SETPOINT:
174                 sendOmnilinkCommand(OmniLinkCmd.CMD_THERMO_SET_DEHUMIDIFY_POINT.getNumber(),
175                         TemperatureFormat.FAHRENHEIT.formatToOmni(((QuantityType<Dimensionless>) command).floatValue()),
176                         thingID);
177                 break;
178             default:
179                 logger.warn("Unknown channel for Thermostat thing: {}", channelUID);
180         }
181     }
182
183     @Override
184     protected void updateChannels(ExtendedThermostatStatus status) {
185         logger.debug("updateChannels called for Thermostat status: {}", status);
186         final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
187
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);
194
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));
205         } else {
206             updateState(CHANNEL_THERMO_STATUS, new DecimalType(0));
207         }
208
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));
225             } else {
226                 logger.warn("Receieved null temperature format, could not update Thermostat channels!");
227             }
228         } else {
229             logger.warn("Could not connect to Bridge, failed to get temperature format!");
230             return;
231         }
232
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));
240
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()));
246     }
247
248     @Override
249     protected Optional<ExtendedThermostatStatus> retrieveStatus() {
250         try {
251             final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
252             if (bridgeHandler != null) {
253                 ObjectStatus objStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_THERMO, thingID, thingID,
254                         true);
255                 return Optional.of((ExtendedThermostatStatus) objStatus.getStatuses()[0]);
256             } else {
257                 logger.debug("Received null bridge while updating Thermostat status!");
258                 return Optional.empty();
259             }
260         } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
261             logger.debug("Received exception while refreshing Thermostat status: {}", e.getMessage());
262             return Optional.empty();
263         }
264     }
265 }