2 * Copyright (c) 2010-2023 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.tellstick.internal.handler;
15 import static org.openhab.binding.tellstick.internal.TellstickBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.time.ZoneId;
19 import java.time.ZonedDateTime;
20 import java.util.Calendar;
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;
59 * Handler for telldus and tellstick devices. This sends the commands to the correct bridge.
61 * @author Jarle Hjortland - Initial contribution
63 public class TelldusDevicesHandler extends BaseThingHandler implements DeviceStatusListener {
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;
84 public TelldusDevicesHandler(Thing 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);
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.");
109 Device dev = getDevice(bridgeHandler, deviceId);
112 logger.warn("Device not found. Can't send command to device '{}'", deviceId);
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);
126 if (channelUID.getId().equals(CHANNEL_DIMMER) || channelUID.getId().equals(CHANNEL_STATE)) {
128 if (dev.getDeviceType() == DeviceType.DEVICE) {
129 getTellstickBridgeHandler().getController().handleSendEvent(dev, resend, isDimmer, command);
131 logger.warn("{} is not an updateable device. Read-only", dev);
133 } catch (TellstickException e) {
134 logger.debug("Failed to send command to tellstick", e);
135 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
138 logger.warn("Setting of channel {} not possible. Read-only", channelUID);
142 private void refreshDevice(Device dev) {
143 if (deviceId != null && isSensor()) {
144 updateSensorStates(dev);
145 } else if (deviceId != null) {
146 updateDeviceState(dev);
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;
158 final BigDecimal repeatCount = (BigDecimal) config.get(TellstickBindingConstants.DEVICE_RESEND_COUNT);
159 if (repeatCount != null) {
160 resend = repeatCount.intValue();
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());
169 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge defined");
172 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
173 "Missing serialNumber configuration");
178 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
179 logger.debug("device: {} bridgeStatusChanged: {}", deviceId, bridgeStatusInfo);
180 if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
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);
192 if (dev.getName() != null) {
193 config.put(TellstickBindingConstants.DEVICE_NAME, dev.getName());
195 if (dev.getProtocol() != null) {
196 config.put(TellstickBindingConstants.DEVICE_PROTOCOL, dev.getProtocol());
198 if (dev.getModel() != null) {
199 config.put(TellstickBindingConstants.DEVICE_MODEL, dev.getModel());
201 updateConfiguration(config);
203 updateStatus(ThingStatus.ONLINE);
206 "Could not find {}, please make sure it is defined and that telldus service is running",
208 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
212 } catch (Exception e) {
213 logger.warn("Failed to init device {}", deviceId, e);
214 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR);
217 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
222 public void dispose() {
223 TelldusBridgeHandler bridgeHandler = getTellstickBridgeHandler();
224 if (bridgeHandler != null) {
225 bridgeHandler.unregisterDeviceStatusListener(this);
230 private Device getDevice(TelldusBridgeHandler tellHandler, String deviceId) {
232 if (deviceId != null) {
234 dev = tellHandler.getSensor(deviceId);
236 dev = tellHandler.getDevice(deviceId);
237 updateDeviceState(dev);
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));
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));
256 } else if (dev instanceof TellstickNetSensor) {
257 if (((TellstickNetSensor) dev).getOnline()) {
258 updateStatus(ThingStatus.ONLINE);
260 updateStatus(ThingStatus.OFFLINE);
262 for (DataTypeValue type : ((TellstickNetSensor) dev).getData()) {
263 updateSensorDataState(type);
265 } else if (dev instanceof TellstickLocalSensorDTO) {
266 for (LocalDataTypeValueDTO type : ((TellstickLocalSensorDTO) dev).getData()) {
267 updateSensorDataState(type);
272 private synchronized TelldusBridgeHandler getTellstickBridgeHandler() {
273 if (this.bridgeHandler == null) {
274 logger.debug("No available bridge handler found for {} bridge {} .", deviceId, getBridge());
276 return this.bridgeHandler;
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());
296 logger.debug("Unhandled Device {}.", device.getDeviceType());
298 Calendar cal = Calendar.getInstance();
299 cal.setTimeInMillis(event.getTimestamp());
300 updateState(timestampChannel,
301 new DateTimeType(ZonedDateTime.ofInstant(cal.toInstant(), ZoneId.systemDefault())));
305 private void updateSensorDataState(DataType dataType, String data) {
308 updateState(humidityChannel, new QuantityType<>(new BigDecimal(data), HUMIDITY_UNIT));
311 updateState(tempChannel, new QuantityType<>(new BigDecimal(data), SIUnits.CELSIUS));
314 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(data), RAIN_UNIT));
317 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(data), RAIN_UNIT));
320 updateState(windAverageChannel, new QuantityType<>(new BigDecimal(data), WIND_SPEED_UNIT_MS));
323 updateState(windDirectionChannel, new QuantityType<>(new BigDecimal(data), WIND_DIRECTION_UNIT));
326 updateState(windGuestChannel, new QuantityType<>(new BigDecimal(data), WIND_SPEED_UNIT_MS));
332 private void updateSensorDataState(DataTypeValue dataType) {
333 switch (dataType.getName()) {
335 updateState(humidityChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), HUMIDITY_UNIT));
338 updateState(tempChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), SIUnits.CELSIUS));
341 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
344 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
347 updateState(windAverageChannel,
348 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
351 updateState(windDirectionChannel,
352 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_DIRECTION_UNIT));
355 updateState(windGuestChannel,
356 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
359 if (dataType.getUnit() != null && dataType.getUnit().equals("A")) {
360 updateState(ampereChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), ELECTRIC_UNIT));
362 updateState(wattChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), POWER_UNIT));
366 updateState(luxChannel, new QuantityType<>(new DecimalType(dataType.getValue()), LUX_UNIT));
372 private void updateSensorDataState(LocalDataTypeValueDTO dataType) {
373 switch (dataType.getName()) {
375 updateState(humidityChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), HUMIDITY_UNIT));
378 updateState(tempChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), SIUnits.CELSIUS));
381 updateState(rainRateChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
384 updateState(raintTotChannel, new QuantityType<>(new BigDecimal(dataType.getValue()), RAIN_UNIT));
387 updateState(windAverageChannel,
388 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
391 updateState(windDirectionChannel,
392 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_DIRECTION_UNIT));
395 updateState(windGuestChannel,
396 new QuantityType<>(new BigDecimal(dataType.getValue()), WIND_SPEED_UNIT_MS));
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));
406 updateState(luxChannel, new QuantityType<>(new DecimalType(dataType.getValue()), LUX_UNIT));
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();
418 if (bridgeHandler != null && bridgeHandler.getController() != null) {
419 st = bridgeHandler.getController().calcState(device);
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));
427 updateStatus(ThingStatus.ONLINE);
429 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
432 updateStatus(ThingStatus.REMOVED);
437 public void onDeviceRemoved(Bridge bridge, Device device) {
438 if (device.getUUId().equals(deviceId)) {
439 updateStatus(ThingStatus.REMOVED);
444 public void onDeviceAdded(Bridge bridge, Device device) {