]> git.basschouten.com Git - openhab-addons.git/blob
c3c2cdf9e2bec265aff995e58d9217c0a049d131
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.tellstick.internal.handler;
14
15 import static org.openhab.binding.tellstick.internal.TellstickBindingConstants.*;
16
17 import java.math.BigDecimal;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.util.Calendar;
21
22 import org.openhab.binding.tellstick.internal.TellstickBindingConstants;
23 import org.openhab.binding.tellstick.internal.live.xml.DataTypeValue;
24 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetSensor;
25 import org.openhab.binding.tellstick.internal.live.xml.TellstickNetSensorEvent;
26 import org.openhab.binding.tellstick.internal.local.dto.LocalDataTypeValueDTO;
27 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalSensorDTO;
28 import org.openhab.binding.tellstick.internal.local.dto.TellstickLocalSensorEventDTO;
29 import org.openhab.core.config.core.Configuration;
30 import org.openhab.core.library.types.DateTimeType;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.PercentType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.unit.SIUnits;
35 import org.openhab.core.library.unit.Units;
36 import org.openhab.core.thing.Bridge;
37 import org.openhab.core.thing.ChannelUID;
38 import org.openhab.core.thing.Thing;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingStatusInfo;
42 import org.openhab.core.thing.binding.BaseThingHandler;
43 import org.openhab.core.types.Command;
44 import org.openhab.core.types.RefreshType;
45 import org.openhab.core.types.State;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.tellstick.device.TellstickDeviceEvent;
49 import org.tellstick.device.TellstickException;
50 import org.tellstick.device.TellstickSensor;
51 import org.tellstick.device.TellstickSensorEvent;
52 import org.tellstick.device.iface.Device;
53 import org.tellstick.device.iface.DimmableDevice;
54 import org.tellstick.device.iface.TellstickEvent;
55 import org.tellstick.enums.DataType;
56 import org.tellstick.enums.DeviceType;
57
58 /**
59  * Handler for telldus and tellstick devices. This sends the commands to the correct bridge.
60  *
61  * @author Jarle Hjortland - Initial contribution
62  */
63 public class TelldusDevicesHandler extends BaseThingHandler implements DeviceStatusListener {
64
65     private Logger logger = LoggerFactory.getLogger(TelldusDevicesHandler.class);
66     private String deviceId;
67     private Boolean isDimmer = Boolean.FALSE;
68     private int resend = 1;
69     private TelldusBridgeHandler bridgeHandler = null;
70     private final ChannelUID stateChannel;
71     private final ChannelUID dimChannel;
72     private final ChannelUID humidityChannel;
73     private final ChannelUID tempChannel;
74     private final ChannelUID raintTotChannel;
75     private final ChannelUID rainRateChannel;
76     private final ChannelUID windAverageChannel;
77     private final ChannelUID windDirectionChannel;
78     private final ChannelUID windGuestChannel;
79     private final ChannelUID wattChannel;
80     private final ChannelUID ampereChannel;
81     private final ChannelUID luxChannel;
82     private final ChannelUID timestampChannel;
83
84     public TelldusDevicesHandler(Thing thing) {
85         super(thing);
86         stateChannel = new ChannelUID(getThing().getUID(), CHANNEL_STATE);
87         dimChannel = new ChannelUID(getThing().getUID(), CHANNEL_DIMMER);
88         humidityChannel = new ChannelUID(getThing().getUID(), CHANNEL_HUMIDITY);
89         tempChannel = new ChannelUID(getThing().getUID(), CHANNEL_TEMPERATURE);
90         raintTotChannel = new ChannelUID(getThing().getUID(), CHANNEL_RAINTOTAL);
91         rainRateChannel = new ChannelUID(getThing().getUID(), CHANNEL_RAINRATE);
92         windAverageChannel = new ChannelUID(getThing().getUID(), CHANNEL_WINDAVERAGE);
93         windDirectionChannel = new ChannelUID(getThing().getUID(), CHANNEL_WINDDIRECTION);
94         windGuestChannel = new ChannelUID(getThing().getUID(), CHANNEL_WINDGUST);
95         wattChannel = new ChannelUID(getThing().getUID(), CHANNEL_WATT);
96         ampereChannel = new ChannelUID(getThing().getUID(), CHANNEL_AMPERE);
97         timestampChannel = new ChannelUID(getThing().getUID(), CHANNEL_TIMESTAMP);
98         luxChannel = new ChannelUID(getThing().getUID(), CHANNEL_LUX);
99     }
100
101     @Override
102     public void handleCommand(ChannelUID channelUID, Command command) {
103         logger.debug("Handle event {} for {}", command, channelUID);
104         TelldusBridgeHandler bridgeHandler = getTellstickBridgeHandler();
105         if (bridgeHandler == null) {
106             logger.warn("Tellstick bridge handler not found. Cannot handle command without bridge.");
107             return;
108         }
109         Device dev = getDevice(bridgeHandler, deviceId);
110
111         if (dev == null) {
112             logger.warn("Device not found. Can't send command to device '{}'", deviceId);
113             return;
114         }
115         if (command instanceof RefreshType) {
116             Bridge bridge = getBridge();
117             if (bridge != null) {
118                 TelldusBridgeHandler localBridgeHandler = (TelldusBridgeHandler) bridge.getHandler();
119                 if (localBridgeHandler != null) {
120                     localBridgeHandler.handleCommand(channelUID, command);
121                     refreshDevice(dev);
122                     return;
123                 }
124             }
125         }
126         if (channelUID.getId().equals(CHANNEL_DIMMER) || channelUID.getId().equals(CHANNEL_STATE)) {
127             try {
128                 if (dev.getDeviceType() == DeviceType.DEVICE) {
129                     getTellstickBridgeHandler().getController().handleSendEvent(dev, resend, isDimmer, command);
130                 } else {
131                     logger.warn("{} is not an updateable device. Read-only", dev);
132                 }
133             } catch (TellstickException e) {
134                 logger.debug("Failed to send command to tellstick", e);
135                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
136             }
137         } else {
138             logger.warn("Setting of channel {} not possible. Read-only", channelUID);
139         }
140     }
141
142     private void refreshDevice(Device dev) {
143         if (deviceId != null && isSensor()) {
144             updateSensorStates(dev);
145         } else if (deviceId != null) {
146             updateDeviceState(dev);
147         }
148     }
149
150     @Override
151     public void initialize() {
152         Configuration config = getConfig();
153         logger.debug("Initialize TelldusDeviceHandler {}. class {}", config, config.getClass());
154         final Boolean isADimmer = (Boolean) config.get(TellstickBindingConstants.DEVICE_ISDIMMER);
155         if (isADimmer != null) {
156             isDimmer = isADimmer;
157         }
158         final BigDecimal repeatCount = (BigDecimal) config.get(TellstickBindingConstants.DEVICE_RESEND_COUNT);
159         if (repeatCount != null) {
160             resend = repeatCount.intValue();
161         }
162         final Object configDeviceId = config.get(TellstickBindingConstants.DEVICE_ID);
163         if (configDeviceId != null) {
164             deviceId = configDeviceId.toString();
165             Bridge bridge = getBridge();
166             if (bridge != null) {
167                 bridgeStatusChanged(bridge.getStatusInfo());
168             } else {
169                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge defined");
170             }
171         } else {
172             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
173                     "Missing serialNumber configuration");
174         }
175     }
176
177     @Override
178     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
179         logger.debug("device: {} bridgeStatusChanged: {}", deviceId, bridgeStatusInfo);
180         if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
181             try {
182                 Bridge localBridge = getBridge();
183                 if (localBridge != null) {
184                     TelldusBridgeHandler telldusBridgeHandler = (TelldusBridgeHandler) localBridge.getHandler();
185                     logger.debug("Init device {}, bridge:{}", deviceId, telldusBridgeHandler);
186                     if (telldusBridgeHandler != null) {
187                         this.bridgeHandler = telldusBridgeHandler;
188                         this.bridgeHandler.registerDeviceStatusListener(this);
189                         Configuration config = editConfiguration();
190                         Device dev = getDevice(telldusBridgeHandler, deviceId);
191                         if (dev != null) {
192                             if (dev.getName() != null) {
193                                 config.put(TellstickBindingConstants.DEVICE_NAME, dev.getName());
194                             }
195                             if (dev.getProtocol() != null) {
196                                 config.put(TellstickBindingConstants.DEVICE_PROTOCOL, dev.getProtocol());
197                             }
198                             if (dev.getModel() != null) {
199                                 config.put(TellstickBindingConstants.DEVICE_MODEL, dev.getModel());
200                             }
201                             updateConfiguration(config);
202
203                             updateStatus(ThingStatus.ONLINE);
204                         } else {
205                             logger.warn(
206                                     "Could not find {}, please make sure it is defined and that telldus service is running",
207                                     deviceId);
208                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
209                         }
210                     }
211                 }
212             } catch (Exception e) {
213                 logger.warn("Failed to init device {}", deviceId, e);
214                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR);
215             }
216         } else {
217             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
218         }
219     }
220
221     @Override
222     public void dispose() {
223         TelldusBridgeHandler bridgeHandler = getTellstickBridgeHandler();
224         if (bridgeHandler != null) {
225             bridgeHandler.unregisterDeviceStatusListener(this);
226         }
227         super.dispose();
228     }
229
230     private Device getDevice(TelldusBridgeHandler tellHandler, String deviceId) {
231         Device dev = null;
232         if (deviceId != null) {
233             if (isSensor()) {
234                 dev = tellHandler.getSensor(deviceId);
235             } else {
236                 dev = tellHandler.getDevice(deviceId);
237                 updateDeviceState(dev);
238             }
239         }
240         return dev;
241     }
242
243     private boolean isSensor() {
244         return (getThing().getThingTypeUID().equals(TellstickBindingConstants.SENSOR_THING_TYPE)
245                 || getThing().getThingTypeUID().equals(TellstickBindingConstants.RAINSENSOR_THING_TYPE)
246                 || getThing().getThingTypeUID().equals(TellstickBindingConstants.WINDSENSOR_THING_TYPE)
247                 || getThing().getThingTypeUID().equals(TellstickBindingConstants.POWERSENSOR_THING_TYPE));
248     }
249
250     private void updateSensorStates(Device dev) {
251         if (dev instanceof TellstickSensor) {
252             updateStatus(ThingStatus.ONLINE);
253             for (DataType type : ((TellstickSensor) dev).getData().keySet()) {
254                 updateSensorDataState(type, ((TellstickSensor) dev).getData(type));
255             }
256         } else if (dev instanceof TellstickNetSensor) {
257             if (((TellstickNetSensor) dev).getOnline()) {
258                 updateStatus(ThingStatus.ONLINE);
259             } else {
260                 updateStatus(ThingStatus.OFFLINE);
261             }
262             for (DataTypeValue type : ((TellstickNetSensor) dev).getData()) {
263                 updateSensorDataState(type);
264             }
265         } else if (dev instanceof TellstickLocalSensorDTO) {
266             for (LocalDataTypeValueDTO type : ((TellstickLocalSensorDTO) dev).getData()) {
267                 updateSensorDataState(type);
268             }
269         }
270     }
271
272     private synchronized TelldusBridgeHandler getTellstickBridgeHandler() {
273         if (this.bridgeHandler == null) {
274             logger.debug("No available bridge handler found for {} bridge {} .", deviceId, getBridge());
275         }
276         return this.bridgeHandler;
277     }
278
279     @Override
280     public void onDeviceStateChanged(Bridge bridge, Device device, TellstickEvent event) {
281         logger.debug("Updating states of ({} {} ({}) id: {} or {}", device.getDeviceType(), device.getName(),
282                 device.getUUId(), getThing().getUID(), deviceId);
283         if (device.getUUId().equals(deviceId)) {
284             if (event instanceof TellstickDeviceEvent) {
285                 updateDeviceState(device);
286             } else if (event instanceof TellstickNetSensorEvent) {
287                 TellstickNetSensorEvent sensorevent = (TellstickNetSensorEvent) event;
288                 updateSensorDataState(sensorevent.getDataTypeValue());
289             } else if (event instanceof TellstickLocalSensorEventDTO) {
290                 TellstickLocalSensorEventDTO sensorevent = (TellstickLocalSensorEventDTO) event;
291                 updateSensorDataState(sensorevent.getDataTypeValue());
292             } else if (event instanceof TellstickSensorEvent) {
293                 TellstickSensorEvent sensorevent = (TellstickSensorEvent) event;
294                 updateSensorDataState(sensorevent.getDataType(), sensorevent.getData());
295             } else {
296                 logger.debug("Unhandled Device {}.", device.getDeviceType());
297             }
298             Calendar cal = Calendar.getInstance();
299             cal.setTimeInMillis(event.getTimestamp());
300             updateState(timestampChannel,
301                     new DateTimeType(ZonedDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault())));
302         }
303     }
304
305     private void updateSensorDataState(DataType dataType, String data) {
306         switch (dataType) {
307             case HUMIDITY:
308                 updateState(humidityChannel, new QuantityType<>(new BigDecimal(data), HUMIDITY_UNIT));
309                 break;
310             case TEMPERATURE:
311                 updateState(tempChannel, new QuantityType<>(new BigDecimal(data), SIUnits.CELSIUS));
312                 break;
313             case RAINRATE:
314                 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(data), RAIN_UNIT));
315                 break;
316             case RAINTOTAL:
317                 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(data), RAIN_UNIT));
318                 break;
319             case WINDAVERAGE:
320                 updateState(windAverageChannel, new QuantityType<>(new BigDecimal(data), WIND_SPEED_UNIT_MS));
321                 break;
322             case WINDDIRECTION:
323                 updateState(windDirectionChannel, new QuantityType<>(new BigDecimal(data), WIND_DIRECTION_UNIT));
324                 break;
325             case WINDGUST:
326                 updateState(windGuestChannel, new QuantityType<>(new BigDecimal(data), WIND_SPEED_UNIT_MS));
327                 break;
328             default:
329         }
330     }
331
332     private void updateSensorDataState(DataTypeValue dataType) {
333         switch (dataType.getName()) {
334             case HUMIDITY:
335                 updateState(humidityChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), HUMIDITY_UNIT));
336                 break;
337             case TEMPERATURE:
338                 updateState(tempChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), SIUnits.CELSIUS));
339                 break;
340             case RAINRATE:
341                 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
342                 break;
343             case RAINTOTAL:
344                 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
345                 break;
346             case WINDAVERAGE:
347                 updateState(windAverageChannel,
348                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
349                 break;
350             case WINDDIRECTION:
351                 updateState(windDirectionChannel,
352                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_DIRECTION_UNIT));
353                 break;
354             case WINDGUST:
355                 updateState(windGuestChannel,
356                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
357                 break;
358             case WATT:
359                 if (dataType.getUnit() != null && dataType.getUnit().equals("A")) {
360                     updateState(ampereChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), ELECTRIC_UNIT));
361                 } else {
362                     updateState(wattChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), POWER_UNIT));
363                 }
364                 break;
365             case LUMINATION:
366                 updateState(luxChannel, new QuantityType<>(new DecimalType(dataType.getValue()), LUX_UNIT));
367                 break;
368             default:
369         }
370     }
371
372     private void updateSensorDataState(LocalDataTypeValueDTO dataType) {
373         switch (dataType.getName()) {
374             case HUMIDITY:
375                 updateState(humidityChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), HUMIDITY_UNIT));
376                 break;
377             case TEMPERATURE:
378                 updateState(tempChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), SIUnits.CELSIUS));
379                 break;
380             case RAINRATE:
381                 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
382                 break;
383             case RAINTOTAL:
384                 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
385                 break;
386             case WINDAVERAGE:
387                 updateState(windAverageChannel,
388                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
389                 break;
390             case WINDDIRECTION:
391                 updateState(windDirectionChannel,
392                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_DIRECTION_UNIT));
393                 break;
394             case WINDGUST:
395                 updateState(windGuestChannel,
396                         new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
397                 break;
398             case WATT:
399                 if (dataType.getScale() == 5) {
400                     updateState(ampereChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), ELECTRIC_UNIT));
401                 } else if (dataType.getScale() == 2) {
402                     updateState(wattChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), Units.WATT));
403                 }
404                 break;
405             case LUMINATION:
406                 updateState(luxChannel, new QuantityType<>(new DecimalType(dataType.getValue()), LUX_UNIT));
407                 break;
408             default:
409         }
410     }
411
412     private void updateDeviceState(Device device) {
413         if (device != null) {
414             logger.debug("Updating state of {} {} ({}) id: {}", device.getDeviceType(), device.getName(),
415                     device.getUUId(), getThing().getUID());
416             TelldusBridgeHandler bridgeHandler = getTellstickBridgeHandler();
417             State st = null;
418             if (bridgeHandler != null && bridgeHandler.getController() != null) {
419                 st = bridgeHandler.getController().calcState(device);
420             }
421             if (st != null && bridgeHandler != null) {
422                 BigDecimal dimValue = bridgeHandler.getController().calcDimValue(device);
423                 updateState(stateChannel, st);
424                 if (device instanceof DimmableDevice) {
425                     updateState(dimChannel, new PercentType(dimValue));
426                 }
427                 updateStatus(ThingStatus.ONLINE);
428             } else {
429                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
430             }
431         } else {
432             updateStatus(ThingStatus.REMOVED);
433         }
434     }
435
436     @Override
437     public void onDeviceRemoved(Bridge bridge, Device device) {
438         if (device.getUUId().equals(deviceId)) {
439             updateStatus(ThingStatus.REMOVED);
440         }
441     }
442
443     @Override
444     public void onDeviceAdded(Bridge bridge, Device device) {
445     }
446 }