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.core;
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.util.Collections;
18 import java.util.SortedMap;
19 import java.util.TreeMap;
21 import org.openhab.binding.tellstick.internal.TelldusBindingException;
22 import org.openhab.binding.tellstick.internal.handler.TelldusDeviceController;
23 import org.openhab.core.library.types.IncreaseDecreaseType;
24 import org.openhab.core.library.types.OnOffType;
25 import org.openhab.core.library.types.PercentType;
26 import org.openhab.core.types.Command;
27 import org.openhab.core.types.State;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30 import org.tellstick.JNA;
31 import org.tellstick.device.TellstickDevice;
32 import org.tellstick.device.TellstickDeviceEvent;
33 import org.tellstick.device.TellstickException;
34 import org.tellstick.device.TellstickSensorEvent;
35 import org.tellstick.device.iface.Device;
36 import org.tellstick.device.iface.DeviceChangeListener;
37 import org.tellstick.device.iface.DimmableDevice;
38 import org.tellstick.device.iface.SensorListener;
39 import org.tellstick.device.iface.SwitchableDevice;
42 * Device controller for telldus core (Basic and Duo).
43 * This communicates with the telldus DLL using the javatellstick
46 * @author Jarle Hjortland, Elias Gabrielsson - Initial contribution
48 public class TelldusCoreDeviceController implements DeviceChangeListener, SensorListener, TelldusDeviceController {
49 private final Logger logger = LoggerFactory.getLogger(TelldusCoreDeviceController.class);
50 private long lastSend = 0;
51 long resendInterval = 100;
52 public static final long DEFAULT_INTERVAL_BETWEEN_SEND = 250;
54 private TelldusCoreWorker telldusCoreWorker;
55 private Thread workerThread;
56 private SortedMap<Device, TelldusCoreSendEvent> messageQue;
58 public TelldusCoreDeviceController(long resendInterval) {
59 this.resendInterval = resendInterval;
60 messageQue = Collections.synchronizedSortedMap(new TreeMap<>());
61 telldusCoreWorker = new TelldusCoreWorker(messageQue);
62 workerThread = new Thread(telldusCoreWorker);
66 public void dispose() {
67 workerThread.interrupt();
71 public void handleSendEvent(Device device, int resendCount, boolean isDimmer, Command command)
72 throws TellstickException {
73 if (!workerThread.isAlive()) {
77 Long eventTime = System.currentTimeMillis();
78 synchronized (messageQue) {
79 messageQue.put(device, new TelldusCoreSendEvent(device, resendCount, isDimmer, command, eventTime));
85 public State calcState(Device dev) {
86 TellstickDevice device = (TellstickDevice) dev;
88 switch (device.getStatus()) {
89 case JNA.CLibrary.TELLSTICK_TURNON:
92 case JNA.CLibrary.TELLSTICK_TURNOFF:
95 case JNA.CLibrary.TELLSTICK_DIM:
96 BigDecimal dimValue = new BigDecimal(device.getData());
97 if (dimValue.intValue() == 0) {
99 } else if (dimValue.intValue() >= 255) {
106 logger.warn("Could not handle {} for {}", device.getStatus(), device);
112 public BigDecimal calcDimValue(Device device) {
113 BigDecimal dimValue = new BigDecimal(0);
114 switch (((TellstickDevice) device).getStatus()) {
115 case JNA.CLibrary.TELLSTICK_TURNON:
116 dimValue = new BigDecimal(100);
118 case JNA.CLibrary.TELLSTICK_TURNOFF:
120 case JNA.CLibrary.TELLSTICK_DIM:
121 dimValue = new BigDecimal(((TellstickDevice) device).getData());
122 dimValue = dimValue.multiply(new BigDecimal(100));
123 dimValue = dimValue.divide(new BigDecimal(255), 0, RoundingMode.HALF_UP);
126 logger.warn("Could not handle {} for {}", ((TellstickDevice) device).getStatus(), device);
131 public long getLastSend() {
135 public void setLastSend(long currentTimeMillis) {
136 lastSend = currentTimeMillis;
140 public void onRequest(TellstickSensorEvent newDevices) {
141 setLastSend(newDevices.getTimestamp());
145 public void onRequest(TellstickDeviceEvent newDevices) {
146 setLastSend(newDevices.getTimestamp());
149 private void sendEvent(Device device, int resendCount, boolean isdimmer, Command command)
150 throws TellstickException {
151 for (int i = 0; i < resendCount; i++) {
152 checkLastAndWait(resendInterval);
153 logger.debug("Send {} to {} times={}", command, device, i);
154 if (device instanceof DimmableDevice) {
155 if (command == OnOffType.ON) {
157 } else if (command == OnOffType.OFF) {
159 } else if (command instanceof PercentType percentCommand) {
160 dim(device, percentCommand);
161 } else if (command instanceof IncreaseDecreaseType increaseDecreaseCommand) {
162 increaseDecrease(device, increaseDecreaseCommand);
164 } else if (device instanceof SwitchableDevice) {
165 if (command == OnOffType.ON) {
167 logger.debug("Turn off first in case it is allready on");
169 checkLastAndWait(resendInterval);
172 } else if (command == OnOffType.OFF) {
176 logger.warn("Cannot send to {}", device);
181 private void increaseDecrease(Device dev, IncreaseDecreaseType increaseDecreaseType) throws TellstickException {
182 String strValue = ((TellstickDevice) dev).getData();
184 if (strValue != null) {
185 value = Double.valueOf(strValue);
187 int percent = (int) Math.round((value / 255) * 100);
188 if (IncreaseDecreaseType.INCREASE == increaseDecreaseType) {
189 percent = Math.min(percent + 10, 100);
190 } else if (IncreaseDecreaseType.DECREASE == increaseDecreaseType) {
191 percent = Math.max(percent - 10, 0);
194 dim(dev, new PercentType(percent));
197 private void dim(Device dev, PercentType command) throws TellstickException {
198 double value = command.doubleValue();
200 // 0 means OFF and 100 means ON
201 if (value == 0 && dev instanceof SwitchableDevice device) {
203 } else if (value == 100 && dev instanceof SwitchableDevice device) {
205 } else if (dev instanceof DimmableDevice device) {
206 long tdVal = Math.round((value / 100) * 255);
207 device.dim((int) tdVal);
209 throw new TelldusBindingException("Cannot send DIM to " + dev);
213 private void turnOff(Device dev) throws TellstickException {
214 if (dev instanceof SwitchableDevice device) {
217 throw new TelldusBindingException("Cannot send OFF to " + dev);
221 private void turnOn(Device dev) throws TellstickException {
222 if (dev instanceof SwitchableDevice device) {
225 throw new TelldusBindingException("Cannot send ON to " + dev);
229 private void checkLastAndWait(long resendInterval) {
230 while ((System.currentTimeMillis() - lastSend) < resendInterval) {
231 logger.debug("Wait for {} millisec", resendInterval);
233 Thread.sleep(resendInterval);
234 } catch (InterruptedException e) {
235 logger.error("Failed to sleep", e);
238 lastSend = System.currentTimeMillis();
242 * This class is a worker which execute the commands sent to the TelldusCoreDeviceController.
243 * This enables separation between Telldus Core and openHAB for preventing latency on the bus.
244 * The Tellstick have a send pace of 4 Hz which is far slower then the bus itself.
246 * @author Elias Gabrielsson
249 private class TelldusCoreWorker implements Runnable {
250 private SortedMap<Device, TelldusCoreSendEvent> messageQue;
252 public TelldusCoreWorker(SortedMap<Device, TelldusCoreSendEvent> messageQue) {
253 this.messageQue = messageQue;
258 while (!Thread.currentThread().isInterrupted()) {
260 TelldusCoreSendEvent sendEvent;
262 synchronized (messageQue) {
263 while (messageQue.isEmpty()) {
266 sendEvent = messageQue.remove(messageQue.firstKey());
270 sendEvent(sendEvent.getDevice(), sendEvent.getResendCount(), sendEvent.getDimmer(),
271 sendEvent.getCommand());
272 } catch (TellstickException e) {
273 logger.error("Failed to send msg:{} to {}", sendEvent.getCommand(), sendEvent.getDevice(), e);
276 } catch (InterruptedException ie) {
284 * This is a wrapper class to enable queuing of send events between the controller and the working thread.
286 * @author Elias Gabrielsson
289 private class TelldusCoreSendEvent implements Comparable<TelldusCoreSendEvent> {
290 private Device device;
291 private int resendCount;
292 private boolean isDimmer;
293 private Command command;
294 private Long eventTime;
296 public TelldusCoreSendEvent(Device device, int resendCount, boolean isDimmer, Command command, Long eventTime) {
297 this.device = device;
298 this.resendCount = resendCount;
299 this.isDimmer = isDimmer;
300 this.command = command;
301 this.eventTime = eventTime;
304 public Device getDevice() {
308 public int getResendCount() {
312 public boolean getDimmer() {
316 public Command getCommand() {
320 public Long getEventTime() {
325 public int compareTo(TelldusCoreSendEvent o) {
326 return eventTime.compareTo(o.getEventTime());