]> git.basschouten.com Git - openhab-addons.git/blob
7fa02c612bc121fd76193c64d5a2f30c1b4b7329
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.wifiled.internal.handler;
14
15 import java.io.IOException;
16 import java.util.concurrent.CompletableFuture;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.Future;
20
21 import org.openhab.core.library.types.HSBType;
22 import org.openhab.core.library.types.OnOffType;
23 import org.openhab.core.library.types.PercentType;
24 import org.openhab.core.library.types.StringType;
25
26 /**
27  * The {@link ClassicWiFiLEDDriver} class is responsible for the communication with the WiFi LED controller.
28  * It's used for sending color or program settings and also extracting the data out of the received telegrams.
29  *
30  * @author Osman Basha - Initial contribution
31  * @author Stefan Endrullis
32  * @author Ries van Twisk - Prevent flashes during classic driver color + white updates
33  */
34 public class ClassicWiFiLEDDriver extends AbstractWiFiLEDDriver {
35
36     private static final int WAIT_UPDATE_LED_FOR_MS = 25;
37     private final WiFiLEDHandler wifiLedHandler;
38     private final ExecutorService updateScheduler = Executors.newSingleThreadExecutor();
39     private Future<Boolean> ledUpdateFuture = CompletableFuture.completedFuture(null);
40     private LEDStateDTO cachedLedStatus = null;
41
42     public ClassicWiFiLEDDriver(WiFiLEDHandler wifiLedHandler, String host, int port, Protocol protocol) {
43         super(host, port, protocol);
44         this.wifiLedHandler = wifiLedHandler;
45     }
46
47     @Override
48     public void shutdown() {
49     }
50
51     @Override
52     public synchronized LEDStateDTO getLEDStateDTO() throws IOException {
53         if (ledUpdateFuture.isDone()) {
54             LEDState s = getLEDState();
55
56             try {
57                 Thread.sleep(100);
58             } catch (InterruptedException e) {
59                 throw new IOException(e);
60             }
61
62             return LEDStateDTO.valueOf(s.state, s.program, s.programSpeed, s.red, s.green, s.blue, s.white, s.white2);
63         } else {
64             return cachedLedStatus;
65         }
66     }
67
68     @Override
69     public synchronized void setColor(HSBType color) throws IOException {
70         logger.debug("Setting color to {}", color);
71
72         LEDStateDTO ledState = getLEDStateDTO().withColor(color).withoutProgram();
73         sendLEDData(ledState);
74     }
75
76     @Override
77     public synchronized void setBrightness(PercentType brightness) throws IOException {
78         logger.debug("Setting brightness to {}", brightness);
79
80         LEDStateDTO ledState = getLEDStateDTO().withBrightness(brightness).withoutProgram();
81         sendLEDData(ledState);
82     }
83
84     @Override
85     public synchronized void incBrightness(int step) throws IOException {
86         logger.debug("Changing brightness by {}", step);
87
88         LEDStateDTO ledState = getLEDStateDTO().withIncrementedBrightness(step).withoutProgram();
89         sendLEDData(ledState);
90     }
91
92     @Override
93     public synchronized void setWhite(PercentType white) throws IOException {
94         logger.debug("Setting (warm) white LED to {}", white);
95
96         LEDStateDTO ledState = getLEDStateDTO().withWhite(white).withoutProgram();
97         sendLEDData(ledState);
98     }
99
100     @Override
101     public synchronized void incWhite(int step) throws IOException {
102         logger.debug("Changing white by {}", step);
103
104         LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite(step).withoutProgram();
105         sendLEDData(ledState);
106     }
107
108     @Override
109     public void setWhite2(PercentType white2) throws IOException {
110         logger.debug("Setting (warm) white 2 LED to {}", white2);
111
112         LEDStateDTO ledState = getLEDStateDTO().withWhite2(white2).withoutProgram();
113         sendLEDData(ledState);
114     }
115
116     @Override
117     public void incWhite2(int step) throws IOException {
118         logger.debug("Changing white by {}", step);
119
120         LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite2(step).withoutProgram();
121         sendLEDData(ledState);
122     }
123
124     @Override
125     public void setPower(OnOffType command) throws IOException {
126         logger.debug("Power {}", command.name());
127
128         sendRaw(getBytesForPower(command == OnOffType.ON));
129     }
130
131     @Override
132     public synchronized void setProgram(StringType program) throws IOException {
133         logger.debug("Setting program '{}'", program);
134
135         LEDStateDTO ledState = getLEDStateDTO().withProgram(program);
136         sendLEDData(ledState);
137     }
138
139     @Override
140     public synchronized void setProgramSpeed(PercentType speed) throws IOException {
141         logger.debug("Setting program speed to {}", speed);
142
143         LEDStateDTO ledState = getLEDStateDTO().withProgramSpeed(speed);
144         if (speed.equals(PercentType.ZERO)) {
145             ledState = ledState.withoutProgram();
146         }
147         sendLEDData(ledState);
148     }
149
150     @Override
151     public synchronized void incProgramSpeed(int step) throws IOException {
152         logger.debug("Changing program speed by {}", step);
153
154         LEDStateDTO ledState = getLEDStateDTO().withIncrementedProgramSpeed(step);
155         sendLEDData(ledState);
156     }
157
158     private synchronized void sendLEDData(final LEDStateDTO ledState) {
159         cachedLedStatus = ledState;
160         if (!ledUpdateFuture.isDone()) {
161             ledUpdateFuture.cancel(true);
162         }
163
164         final byte[] bytes;
165         int program = Integer.valueOf(ledState.getProgram().toString());
166         if (program == 0x61) {
167             // "normal" program: set color etc.
168             byte r = (byte) (ledState.getRGB() >> 16 & 0xFF);
169             byte g = (byte) (ledState.getRGB() >> 8 & 0xFF);
170             byte b = (byte) (ledState.getRGB() & 0xFF);
171             byte w = (byte) (((int) (ledState.getWhite().doubleValue() * 255 / 100)) & 0xFF);
172             byte w2 = (byte) (((int) (ledState.getWhite2().doubleValue() * 255 / 100)) & 0xFF);
173
174             bytes = getBytesForColor(r, g, b, w, w2);
175         } else {
176             // program selected
177             byte p = (byte) (program & 0xFF);
178             byte s = (byte) (((100 - ledState.getProgramSpeed().intValue()) * 0x1F / 100) & 0xFF);
179             bytes = new byte[] { 0x61, p, s };
180         }
181
182         ledUpdateFuture = updateScheduler.submit(() -> {
183             try {
184                 Thread.sleep(WAIT_UPDATE_LED_FOR_MS);
185                 logger.debug("Setting LED State to {}", bytesToHex(bytes));
186                 sendRaw(bytes);
187                 return true;
188             } catch (IOException e) {
189                 logger.debug("Exception occurred while sending command to LED", e);
190                 wifiLedHandler.reportCommunicationError(e);
191             } catch (InterruptedException e) {
192                 // Ignore, this is expected
193             }
194             return false;
195         });
196     }
197
198     public static String bytesToHex(byte[] bytes) {
199         StringBuilder builder = new StringBuilder();
200         for (byte aByte : bytes) {
201             builder.append(String.format("%02x ", aByte));
202         }
203         String string = builder.toString();
204         return string.substring(0, string.length() - 1);
205     }
206 }