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