2 * Copyright (c) 2010-2021 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.core;
15 import java.math.BigDecimal;
16 import java.util.Collections;
17 import java.util.SortedMap;
18 import java.util.TreeMap;
20 import org.openhab.binding.tellstick.internal.TelldusBindingException;
21 import org.openhab.binding.tellstick.internal.handler.TelldusDeviceController;
22 import org.openhab.core.library.types.IncreaseDecreaseType;
23 import org.openhab.core.library.types.OnOffType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.types.Command;
26 import org.openhab.core.types.State;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.tellstick.JNA;
30 import org.tellstick.device.TellstickDevice;
31 import org.tellstick.device.TellstickDeviceEvent;
32 import org.tellstick.device.TellstickException;
33 import org.tellstick.device.TellstickSensorEvent;
34 import org.tellstick.device.iface.Device;
35 import org.tellstick.device.iface.DeviceChangeListener;
36 import org.tellstick.device.iface.DimmableDevice;
37 import org.tellstick.device.iface.SensorListener;
38 import org.tellstick.device.iface.SwitchableDevice;
41 * Device controller for telldus core (Basic and Duo).
42 * This communicates with the telldus DLL using the javatellstick
45 * @author Jarle Hjortland, Elias Gabrielsson - Initial contribution
47 public class TelldusCoreDeviceController implements DeviceChangeListener, SensorListener, TelldusDeviceController {
48 private final Logger logger = LoggerFactory.getLogger(TelldusCoreDeviceController.class);
49 private long lastSend = 0;
50 long resendInterval = 100;
51 public static final long DEFAULT_INTERVAL_BETWEEN_SEND = 250;
53 private TelldusCoreWorker telldusCoreWorker;
54 private Thread workerThread;
55 private SortedMap<Device, TelldusCoreSendEvent> messageQue;
57 public TelldusCoreDeviceController(long resendInterval) {
58 this.resendInterval = resendInterval;
59 messageQue = Collections.synchronizedSortedMap(new TreeMap<>());
60 telldusCoreWorker = new TelldusCoreWorker(messageQue);
61 workerThread = new Thread(telldusCoreWorker);
65 public void dispose() {
66 workerThread.interrupt();
70 public void handleSendEvent(Device device, int resendCount, boolean isDimmer, Command command)
71 throws TellstickException {
72 if (!workerThread.isAlive()) {
76 Long eventTime = System.currentTimeMillis();
77 synchronized (messageQue) {
78 messageQue.put(device, new TelldusCoreSendEvent(device, resendCount, isDimmer, command, eventTime));
84 public State calcState(Device dev) {
85 TellstickDevice device = (TellstickDevice) dev;
87 switch (device.getStatus()) {
88 case JNA.CLibrary.TELLSTICK_TURNON:
91 case JNA.CLibrary.TELLSTICK_TURNOFF:
94 case JNA.CLibrary.TELLSTICK_DIM:
95 BigDecimal dimValue = new BigDecimal(device.getData());
96 if (dimValue.intValue() == 0) {
98 } else if (dimValue.intValue() >= 255) {
105 logger.warn("Could not handle {} for {}", device.getStatus(), device);
111 public BigDecimal calcDimValue(Device device) {
112 BigDecimal dimValue = new BigDecimal(0);
113 switch (((TellstickDevice) device).getStatus()) {
114 case JNA.CLibrary.TELLSTICK_TURNON:
115 dimValue = new BigDecimal(100);
117 case JNA.CLibrary.TELLSTICK_TURNOFF:
119 case JNA.CLibrary.TELLSTICK_DIM:
120 dimValue = new BigDecimal(((TellstickDevice) device).getData());
121 dimValue = dimValue.multiply(new BigDecimal(100));
122 dimValue = dimValue.divide(new BigDecimal(255), 0, BigDecimal.ROUND_HALF_UP);
125 logger.warn("Could not handle {} for {}", ((TellstickDevice) device).getStatus(), device);
130 public long getLastSend() {
134 public void setLastSend(long currentTimeMillis) {
135 lastSend = currentTimeMillis;
139 public void onRequest(TellstickSensorEvent newDevices) {
140 setLastSend(newDevices.getTimestamp());
144 public void onRequest(TellstickDeviceEvent newDevices) {
145 setLastSend(newDevices.getTimestamp());
148 private void sendEvent(Device device, int resendCount, boolean isdimmer, Command command)
149 throws TellstickException {
150 for (int i = 0; i < resendCount; i++) {
151 checkLastAndWait(resendInterval);
152 logger.debug("Send {} to {} times={}", command, device, i);
153 if (device instanceof DimmableDevice) {
154 if (command == OnOffType.ON) {
156 } else if (command == OnOffType.OFF) {
158 } else if (command instanceof PercentType) {
159 dim(device, (PercentType) command);
160 } else if (command instanceof IncreaseDecreaseType) {
161 increaseDecrease(device, ((IncreaseDecreaseType) command));
163 } else if (device instanceof SwitchableDevice) {
164 if (command == OnOffType.ON) {
166 logger.debug("Turn off first in case it is allready on");
168 checkLastAndWait(resendInterval);
171 } else if (command == OnOffType.OFF) {
175 logger.warn("Cannot send to {}", device);
180 private void increaseDecrease(Device dev, IncreaseDecreaseType increaseDecreaseType) throws TellstickException {
181 String strValue = ((TellstickDevice) dev).getData();
183 if (strValue != null) {
184 value = Double.valueOf(strValue);
186 int percent = (int) Math.round((value / 255) * 100);
187 if (IncreaseDecreaseType.INCREASE == increaseDecreaseType) {
188 percent = Math.min(percent + 10, 100);
189 } else if (IncreaseDecreaseType.DECREASE == increaseDecreaseType) {
190 percent = Math.max(percent - 10, 0);
193 dim(dev, new PercentType(percent));
196 private void dim(Device dev, PercentType command) throws TellstickException {
197 double value = command.doubleValue();
199 // 0 means OFF and 100 means ON
200 if (value == 0 && dev instanceof SwitchableDevice) {
201 ((SwitchableDevice) dev).off();
202 } else if (value == 100 && dev instanceof SwitchableDevice) {
203 ((SwitchableDevice) dev).on();
204 } else if (dev instanceof DimmableDevice) {
205 long tdVal = Math.round((value / 100) * 255);
206 ((DimmableDevice) dev).dim((int) tdVal);
208 throw new TelldusBindingException("Cannot send DIM to " + dev);
212 private void turnOff(Device dev) throws TellstickException {
213 if (dev instanceof SwitchableDevice) {
214 ((SwitchableDevice) dev).off();
216 throw new TelldusBindingException("Cannot send OFF to " + dev);
220 private void turnOn(Device dev) throws TellstickException {
221 if (dev instanceof SwitchableDevice) {
222 ((SwitchableDevice) dev).on();
224 throw new TelldusBindingException("Cannot send ON to " + dev);
228 private void checkLastAndWait(long resendInterval) {
229 while ((System.currentTimeMillis() - lastSend) < resendInterval) {
230 logger.debug("Wait for {} millisec", resendInterval);
232 Thread.sleep(resendInterval);
233 } catch (InterruptedException e) {
234 logger.error("Failed to sleep", e);
237 lastSend = System.currentTimeMillis();
241 * This class is a worker which execute the commands sent to the TelldusCoreDeviceController.
242 * This enables separation between Telldus Core and openHAB for preventing latency on the bus.
243 * The Tellstick have an send pace of 4 Hz which is far slower then the bus itself.
245 * @author Elias Gabrielsson
248 private class TelldusCoreWorker implements Runnable {
249 private SortedMap<Device, TelldusCoreSendEvent> messageQue;
251 public TelldusCoreWorker(SortedMap<Device, TelldusCoreSendEvent> messageQue) {
252 this.messageQue = messageQue;
257 while (!Thread.currentThread().isInterrupted()) {
259 TelldusCoreSendEvent sendEvent;
261 synchronized (messageQue) {
262 while (messageQue.isEmpty()) {
265 sendEvent = messageQue.remove(messageQue.firstKey());
269 sendEvent(sendEvent.getDevice(), sendEvent.getResendCount(), sendEvent.getDimmer(),
270 sendEvent.getCommand());
271 } catch (TellstickException e) {
272 logger.error("Failed to send msg:{} to {}", sendEvent.getCommand(), sendEvent.getDevice(), e);
275 } catch (InterruptedException ie) {
283 * This is a wrapper class to enable queuing of send events between the controller and the working thread.
285 * @author Elias Gabrielsson
288 private class TelldusCoreSendEvent implements Comparable<TelldusCoreSendEvent> {
289 private Device device;
290 private int resendCount;
291 private boolean isDimmer;
292 private Command command;
293 private Long eventTime;
295 public TelldusCoreSendEvent(Device device, int resendCount, boolean isDimmer, Command command, Long eventTime) {
296 this.device = device;
297 this.resendCount = resendCount;
298 this.isDimmer = isDimmer;
299 this.command = command;
300 this.eventTime = eventTime;
303 public Device getDevice() {
307 public int getResendCount() {
311 public boolean getDimmer() {
315 public Command getCommand() {
319 public Long getEventTime() {
324 public int compareTo(TelldusCoreSendEvent o) {
325 return eventTime.compareTo(o.getEventTime());