]> git.basschouten.com Git - openhab-addons.git/blob
4ad11a5e3d8d1cf8ee6934f632b9ba9339e57721
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.core;
14
15 import java.math.BigDecimal;
16 import java.util.Collections;
17 import java.util.SortedMap;
18 import java.util.TreeMap;
19
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;
39
40 /**
41  * Device controller for telldus core (Basic and Duo).
42  * This communicates with the telldus DLL using the javatellstick
43  * library.
44  *
45  * @author Jarle Hjortland, Elias Gabrielsson - Initial contribution
46  */
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;
52
53     private TelldusCoreWorker telldusCoreWorker;
54     private Thread workerThread;
55     private SortedMap<Device, TelldusCoreSendEvent> messageQue;
56
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);
62     }
63
64     @Override
65     public void dispose() {
66         workerThread.interrupt();
67     }
68
69     @Override
70     public void handleSendEvent(Device device, int resendCount, boolean isDimmer, Command command)
71             throws TellstickException {
72         if (!workerThread.isAlive()) {
73             workerThread.start();
74         }
75
76         Long eventTime = System.currentTimeMillis();
77         synchronized (messageQue) {
78             messageQue.put(device, new TelldusCoreSendEvent(device, resendCount, isDimmer, command, eventTime));
79             messageQue.notify();
80         }
81     }
82
83     @Override
84     public State calcState(Device dev) {
85         TellstickDevice device = (TellstickDevice) dev;
86         State st = null;
87         switch (device.getStatus()) {
88             case JNA.CLibrary.TELLSTICK_TURNON:
89                 st = OnOffType.ON;
90                 break;
91             case JNA.CLibrary.TELLSTICK_TURNOFF:
92                 st = OnOffType.OFF;
93                 break;
94             case JNA.CLibrary.TELLSTICK_DIM:
95                 BigDecimal dimValue = new BigDecimal(device.getData());
96                 if (dimValue.intValue() == 0) {
97                     st = OnOffType.OFF;
98                 } else if (dimValue.intValue() >= 255) {
99                     st = OnOffType.ON;
100                 } else {
101                     st = OnOffType.ON;
102                 }
103                 break;
104             default:
105                 logger.warn("Could not handle {} for {}", device.getStatus(), device);
106         }
107         return st;
108     }
109
110     @Override
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);
116                 break;
117             case JNA.CLibrary.TELLSTICK_TURNOFF:
118                 break;
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);
123                 break;
124             default:
125                 logger.warn("Could not handle {} for {}", ((TellstickDevice) device).getStatus(), device);
126         }
127         return dimValue;
128     }
129
130     public long getLastSend() {
131         return lastSend;
132     }
133
134     public void setLastSend(long currentTimeMillis) {
135         lastSend = currentTimeMillis;
136     }
137
138     @Override
139     public void onRequest(TellstickSensorEvent newDevices) {
140         setLastSend(newDevices.getTimestamp());
141     }
142
143     @Override
144     public void onRequest(TellstickDeviceEvent newDevices) {
145         setLastSend(newDevices.getTimestamp());
146     }
147
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) {
155                     turnOn(device);
156                 } else if (command == OnOffType.OFF) {
157                     turnOff(device);
158                 } else if (command instanceof PercentType) {
159                     dim(device, (PercentType) command);
160                 } else if (command instanceof IncreaseDecreaseType) {
161                     increaseDecrease(device, ((IncreaseDecreaseType) command));
162                 }
163             } else if (device instanceof SwitchableDevice) {
164                 if (command == OnOffType.ON) {
165                     if (isdimmer) {
166                         logger.debug("Turn off first in case it is allready on");
167                         turnOff(device);
168                         checkLastAndWait(resendInterval);
169                     }
170                     turnOn(device);
171                 } else if (command == OnOffType.OFF) {
172                     turnOff(device);
173                 }
174             } else {
175                 logger.warn("Cannot send to {}", device);
176             }
177         }
178     }
179
180     private void increaseDecrease(Device dev, IncreaseDecreaseType increaseDecreaseType) throws TellstickException {
181         String strValue = ((TellstickDevice) dev).getData();
182         double value = 0;
183         if (strValue != null) {
184             value = Double.valueOf(strValue);
185         }
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);
191         }
192
193         dim(dev, new PercentType(percent));
194     }
195
196     private void dim(Device dev, PercentType command) throws TellstickException {
197         double value = command.doubleValue();
198
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);
207         } else {
208             throw new TelldusBindingException("Cannot send DIM to " + dev);
209         }
210     }
211
212     private void turnOff(Device dev) throws TellstickException {
213         if (dev instanceof SwitchableDevice) {
214             ((SwitchableDevice) dev).off();
215         } else {
216             throw new TelldusBindingException("Cannot send OFF to " + dev);
217         }
218     }
219
220     private void turnOn(Device dev) throws TellstickException {
221         if (dev instanceof SwitchableDevice) {
222             ((SwitchableDevice) dev).on();
223         } else {
224             throw new TelldusBindingException("Cannot send ON to " + dev);
225         }
226     }
227
228     private void checkLastAndWait(long resendInterval) {
229         while ((System.currentTimeMillis() - lastSend) < resendInterval) {
230             logger.debug("Wait for {} millisec", resendInterval);
231             try {
232                 Thread.sleep(resendInterval);
233             } catch (InterruptedException e) {
234                 logger.error("Failed to sleep", e);
235             }
236         }
237         lastSend = System.currentTimeMillis();
238     }
239
240     /**
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.
244      *
245      * @author Elias Gabrielsson
246      *
247      */
248     private class TelldusCoreWorker implements Runnable {
249         private SortedMap<Device, TelldusCoreSendEvent> messageQue;
250
251         public TelldusCoreWorker(SortedMap<Device, TelldusCoreSendEvent> messageQue) {
252             this.messageQue = messageQue;
253         }
254
255         @Override
256         public void run() {
257             while (!Thread.currentThread().isInterrupted()) {
258                 try {
259                     TelldusCoreSendEvent sendEvent;
260                     // Get event to send
261                     synchronized (messageQue) {
262                         while (messageQue.isEmpty()) {
263                             messageQue.wait();
264                         }
265                         sendEvent = messageQue.remove(messageQue.firstKey());
266                     }
267                     // Send event
268                     try {
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);
273                     }
274
275                 } catch (InterruptedException ie) {
276                     break; // Terminate
277                 }
278             }
279         }
280     }
281
282     /**
283      * This is a wrapper class to enable queuing of send events between the controller and the working thread.
284      *
285      * @author Elias Gabrielsson
286      *
287      */
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;
294
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;
301         }
302
303         public Device getDevice() {
304             return device;
305         }
306
307         public int getResendCount() {
308             return resendCount;
309         }
310
311         public boolean getDimmer() {
312             return isDimmer;
313         }
314
315         public Command getCommand() {
316             return command;
317         }
318
319         public Long getEventTime() {
320             return eventTime;
321         }
322
323         @Override
324         public int compareTo(TelldusCoreSendEvent o) {
325             return eventTime.compareTo(o.getEventTime());
326         }
327     }
328 }