]> git.basschouten.com Git - openhab-addons.git/blob
7f615997d01f02ec8545bf107de97468ecfaf753
[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.broadlinkthermostat.internal.handler;
14
15 import static org.openhab.binding.broadlinkthermostat.internal.BroadlinkThermostatBindingConstants.*;
16
17 import java.io.IOException;
18 import java.time.LocalTime;
19 import java.time.ZonedDateTime;
20 import java.util.concurrent.TimeUnit;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.core.cache.ExpiringCache;
25 import org.openhab.core.library.types.DateTimeType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.library.unit.SIUnits;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.RefreshType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import com.github.mob41.blapi.FloureonDevice;
40 import com.github.mob41.blapi.dev.hysen.AdvancedStatusInfo;
41 import com.github.mob41.blapi.dev.hysen.BaseStatusInfo;
42 import com.github.mob41.blapi.dev.hysen.SensorControl;
43 import com.github.mob41.blapi.mac.Mac;
44 import com.github.mob41.blapi.pkt.cmd.hysen.SetTimeCommand;
45
46 /**
47  * The {@link FloureonThermostatHandler} is responsible for handling thermostats labeled as Floureon Thermostat.
48  *
49  * @author Florian Mueller - Initial contribution
50  */
51 @NonNullByDefault
52 public class FloureonThermostatHandler extends BroadlinkThermostatHandler {
53
54     private final Logger logger = LoggerFactory.getLogger(FloureonThermostatHandler.class);
55     private @Nullable FloureonDevice floureonDevice;
56
57     private static final long CACHE_EXPIRY = TimeUnit.SECONDS.toSeconds(3);
58     private final ExpiringCache<AdvancedStatusInfo> advancedStatusInfoExpiringCache = new ExpiringCache<>(CACHE_EXPIRY,
59             this::refreshAdvancedStatus);
60
61     /**
62      * Creates a new instance of this class for the {@link FloureonThermostatHandler}.
63      *
64      * @param thing the thing that should be handled, not null
65      */
66     public FloureonThermostatHandler(Thing thing) {
67         super(thing);
68     }
69
70     /**
71      * Initializes a new instance of a {@link FloureonThermostatHandler}.
72      */
73     @Override
74     public void initialize() {
75         super.initialize();
76         if (host != null && macAddress != null) {
77             try {
78                 blDevice = new FloureonDevice(host, new Mac(macAddress));
79                 this.floureonDevice = (FloureonDevice) blDevice;
80                 updateStatus(ThingStatus.ONLINE);
81             } catch (IOException e) {
82                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
83                         "Could not find broadlinkthermostat device at host" + host + "with MAC+" + macAddress + ": "
84                                 + e.getMessage());
85             }
86         }
87     }
88
89     @Override
90     public void handleCommand(ChannelUID channelUID, Command command) {
91         logger.debug("Command: {}", command.toFullString());
92         authenticate(false);
93
94         if (command == RefreshType.REFRESH) {
95             refreshData();
96             return;
97         }
98
99         switch (channelUID.getIdWithoutGroup()) {
100             case SETPOINT:
101                 handleSetpointCommand(channelUID, command);
102                 break;
103             case POWER:
104                 handlePowerCommand(channelUID, command);
105                 break;
106             case MODE:
107                 handleModeCommand(channelUID, command);
108                 break;
109             case SENSOR:
110                 handleSensorCommand(channelUID, command);
111                 break;
112             case REMOTE_LOCK:
113                 handleRemoteLockCommand(channelUID, command);
114                 break;
115             case TIME:
116                 handleSetTimeCommand(channelUID, command);
117                 break;
118             default:
119                 logger.warn("Channel {} does not support command {}", channelUID, command);
120         }
121     }
122
123     private void handlePowerCommand(ChannelUID channelUID, Command command) {
124         FloureonDevice floureonDevice = this.floureonDevice;
125         if (command instanceof OnOffType && floureonDevice != null) {
126             try {
127                 floureonDevice.setPower(command == OnOffType.ON);
128             } catch (Exception e) {
129                 logger.warn("Error while setting power of {} to {}: {}", thing.getUID(), command, e.getMessage());
130             }
131         } else {
132             logger.warn("Channel {} does not support command {}", channelUID, command);
133         }
134     }
135
136     private void handleModeCommand(ChannelUID channelUID, Command command) {
137         FloureonDevice floureonDevice = this.floureonDevice;
138         if (command instanceof StringType && floureonDevice != null) {
139             try {
140                 if (MODE_AUTO.equals(command.toFullString())) {
141                     floureonDevice.switchToAuto();
142                 } else {
143                     floureonDevice.switchToManual();
144                 }
145             } catch (Exception e) {
146                 logger.warn("Error while setting power off {} to {}: {}", thing.getUID(), command, e.getMessage());
147             }
148         } else {
149             logger.warn("Channel {} does not support command {}", channelUID, command);
150         }
151     }
152
153     private void handleSetpointCommand(ChannelUID channelUID, Command command) {
154         FloureonDevice floureonDevice = this.floureonDevice;
155         if (command instanceof QuantityType && floureonDevice != null) {
156             try {
157                 QuantityType<?> temperatureQuantityType = ((QuantityType<?>) command).toUnit(SIUnits.CELSIUS);
158                 if (temperatureQuantityType != null) {
159                     floureonDevice.setThermostatTemp(temperatureQuantityType.doubleValue());
160                 } else {
161                     logger.warn("Could not convert {} to °C", command);
162                 }
163             } catch (Exception e) {
164                 logger.warn("Error while setting setpoint of {} to {}: {}", thing.getUID(), command, e.getMessage());
165             }
166
167         } else {
168             logger.warn("Channel {} does not support command {}", channelUID, command);
169         }
170     }
171
172     private void handleSensorCommand(ChannelUID channelUID, Command command) {
173         FloureonDevice floureonDevice = this.floureonDevice;
174         if (command instanceof StringType && floureonDevice != null) {
175             try {
176                 BaseStatusInfo statusInfo = floureonDevice.getBasicStatus();
177                 if (SENSOR_INTERNAL.equals(command.toFullString())) {
178                     floureonDevice.setMode(statusInfo.getAutoMode(), statusInfo.getLoopMode(), SensorControl.INTERNAL);
179                 } else if (SENSOR_EXTERNAL.equals(command.toFullString())) {
180                     floureonDevice.setMode(statusInfo.getAutoMode(), statusInfo.getLoopMode(), SensorControl.EXTERNAL);
181                 } else {
182                     floureonDevice.setMode(statusInfo.getAutoMode(), statusInfo.getLoopMode(),
183                             SensorControl.INTERNAL_TEMP_EXTERNAL_LIMIT);
184                 }
185             } catch (Exception e) {
186                 logger.warn("Error while trying to set sensor mode {}: {}", command, e.getMessage());
187             }
188         } else {
189             logger.warn("Channel {} does not support command {}", channelUID, command);
190         }
191     }
192
193     private void handleRemoteLockCommand(ChannelUID channelUID, Command command) {
194         FloureonDevice floureonDevice = this.floureonDevice;
195         if (command instanceof OnOffType && floureonDevice != null) {
196             try {
197                 floureonDevice.setLock(command == OnOffType.ON);
198             } catch (Exception e) {
199                 logger.warn("Error while setting remote lock of {} to {}: {}", thing.getUID(), command, e.getMessage());
200             }
201         } else {
202             logger.warn("Channel {} does not support command {}", channelUID, command);
203         }
204     }
205
206     private void handleSetTimeCommand(ChannelUID channelUID, Command command) {
207         if (command instanceof DateTimeType) {
208             ZonedDateTime zonedDateTime = ((DateTimeType) command).getZonedDateTime();
209             try {
210                 new SetTimeCommand(tob(zonedDateTime.getHour()), tob(zonedDateTime.getMinute()),
211                         tob(zonedDateTime.getSecond()), tob(zonedDateTime.getDayOfWeek().getValue()))
212                                 .execute(floureonDevice);
213             } catch (Exception e) {
214                 logger.warn("Error while setting time of {} to {}: {}", thing.getUID(), command, e.getMessage());
215             }
216         } else {
217             logger.warn("Channel {} does not support command {}", channelUID, command);
218         }
219     }
220
221     @Nullable
222     private AdvancedStatusInfo refreshAdvancedStatus() {
223         if (ThingStatus.ONLINE != thing.getStatus()) {
224             return null;
225         }
226
227         FloureonDevice floureonDevice = this.floureonDevice;
228         if (floureonDevice != null) {
229             try {
230                 AdvancedStatusInfo advancedStatusInfo = floureonDevice.getAdvancedStatus();
231                 if (advancedStatusInfo == null) {
232                     logger.warn("Device {} did not return any data. Trying to reauthenticate...", thing.getUID());
233                     authenticate(true);
234                     advancedStatusInfo = floureonDevice.getAdvancedStatus();
235                 }
236                 if (advancedStatusInfo == null) {
237                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Device not responding.");
238                     return null;
239                 }
240                 return advancedStatusInfo;
241             } catch (Exception e) {
242                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
243                         "Error while retrieving data for " + thing.getUID() + ":  " + e.getMessage());
244             }
245         }
246         return null;
247     }
248
249     @Override
250     protected void refreshData() {
251
252         AdvancedStatusInfo advancedStatusInfo = advancedStatusInfoExpiringCache.getValue();
253         if (advancedStatusInfo == null) {
254             return;
255         }
256         logger.trace("Retrieved data from device {}: {}", thing.getUID(), advancedStatusInfo);
257         updateState(ROOM_TEMPERATURE, new QuantityType<>(advancedStatusInfo.getRoomTemp(), SIUnits.CELSIUS));
258         updateState(ROOM_TEMPERATURE_EXTERNAL_SENSOR,
259                 new QuantityType<>(advancedStatusInfo.getExternalTemp(), SIUnits.CELSIUS));
260         updateState(SETPOINT, new QuantityType<>(advancedStatusInfo.getThermostatTemp(), SIUnits.CELSIUS));
261         updateState(POWER, OnOffType.from(advancedStatusInfo.getPower()));
262         updateState(MODE, StringType.valueOf(advancedStatusInfo.getAutoMode() ? "auto" : "manual"));
263         updateState(SENSOR, StringType.valueOf(advancedStatusInfo.getSensorControl().name()));
264         updateState(TEMPERATURE_OFFSET, new QuantityType<>(advancedStatusInfo.getDif(), SIUnits.CELSIUS));
265         updateState(ACTIVE, OnOffType.from(advancedStatusInfo.getActive()));
266         updateState(REMOTE_LOCK, OnOffType.from(advancedStatusInfo.getRemoteLock()));
267         updateState(TIME, new DateTimeType(getTimestamp(advancedStatusInfo)));
268     }
269
270     private ZonedDateTime getTimestamp(AdvancedStatusInfo advancedStatusInfo) {
271         ZonedDateTime now = ZonedDateTime.now();
272         return now.with(
273                 LocalTime.of(advancedStatusInfo.getHour(), advancedStatusInfo.getMin(), advancedStatusInfo.getSec()));
274     }
275
276     private static byte tob(int in) {
277         return (byte) (in & 0xff);
278     }
279 }