2 * Copyright (c) 2010-2024 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.wifiled.internal.handler;
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;
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;
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.
30 * @author Osman Basha - Initial contribution
31 * @author Stefan Endrullis
32 * @author Ries van Twisk - Prevent flashes during classic driver color + white updates
34 public class ClassicWiFiLEDDriver extends AbstractWiFiLEDDriver {
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;
42 public ClassicWiFiLEDDriver(WiFiLEDHandler wifiLedHandler, String host, int port, Protocol protocol) {
43 super(host, port, protocol);
44 this.wifiLedHandler = wifiLedHandler;
48 public void shutdown() {
52 public synchronized LEDStateDTO getLEDStateDTO() throws IOException {
53 if (ledUpdateFuture.isDone()) {
54 LEDState s = getLEDState();
58 } catch (InterruptedException e) {
59 throw new IOException(e);
62 return LEDStateDTO.valueOf(s.state, s.program, s.programSpeed, s.red, s.green, s.blue, s.white, s.white2);
64 return cachedLedStatus;
69 public synchronized void setColor(HSBType color) throws IOException {
70 logger.debug("Setting color to {}", color);
72 LEDStateDTO ledState = getLEDStateDTO().withColor(color).withoutProgram();
73 sendLEDData(ledState);
77 public synchronized void setBrightness(PercentType brightness) throws IOException {
78 logger.debug("Setting brightness to {}", brightness);
80 LEDStateDTO ledState = getLEDStateDTO().withBrightness(brightness).withoutProgram();
81 sendLEDData(ledState);
85 public synchronized void incBrightness(int step) throws IOException {
86 logger.debug("Changing brightness by {}", step);
88 LEDStateDTO ledState = getLEDStateDTO().withIncrementedBrightness(step).withoutProgram();
89 sendLEDData(ledState);
93 public synchronized void setWhite(PercentType white) throws IOException {
94 logger.debug("Setting (warm) white LED to {}", white);
96 LEDStateDTO ledState = getLEDStateDTO().withWhite(white).withoutProgram();
97 sendLEDData(ledState);
101 public synchronized void incWhite(int step) throws IOException {
102 logger.debug("Changing white by {}", step);
104 LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite(step).withoutProgram();
105 sendLEDData(ledState);
109 public void setWhite2(PercentType white2) throws IOException {
110 logger.debug("Setting (warm) white 2 LED to {}", white2);
112 LEDStateDTO ledState = getLEDStateDTO().withWhite2(white2).withoutProgram();
113 sendLEDData(ledState);
117 public void incWhite2(int step) throws IOException {
118 logger.debug("Changing white by {}", step);
120 LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite2(step).withoutProgram();
121 sendLEDData(ledState);
125 public void setPower(OnOffType command) throws IOException {
126 logger.debug("Power {}", command.name());
128 sendRaw(getBytesForPower(command == OnOffType.ON));
132 public synchronized void setProgram(StringType program) throws IOException {
133 logger.debug("Setting program '{}'", program);
135 LEDStateDTO ledState = getLEDStateDTO().withProgram(program);
136 sendLEDData(ledState);
140 public synchronized void setProgramSpeed(PercentType speed) throws IOException {
141 logger.debug("Setting program speed to {}", speed);
143 LEDStateDTO ledState = getLEDStateDTO().withProgramSpeed(speed);
144 if (speed.equals(PercentType.ZERO)) {
145 ledState = ledState.withoutProgram();
147 sendLEDData(ledState);
151 public synchronized void incProgramSpeed(int step) throws IOException {
152 logger.debug("Changing program speed by {}", step);
154 LEDStateDTO ledState = getLEDStateDTO().withIncrementedProgramSpeed(step);
155 sendLEDData(ledState);
158 private synchronized void sendLEDData(final LEDStateDTO ledState) {
159 cachedLedStatus = ledState;
160 if (!ledUpdateFuture.isDone()) {
161 ledUpdateFuture.cancel(true);
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);
174 bytes = getBytesForColor(r, g, b, w, w2);
177 byte p = (byte) (program & 0xFF);
178 byte s = (byte) (((100 - ledState.getProgramSpeed().intValue()) * 0x1F / 100) & 0xFF);
179 bytes = new byte[] { 0x61, p, s };
182 ledUpdateFuture = updateScheduler.submit(() -> {
184 Thread.sleep(WAIT_UPDATE_LED_FOR_MS);
185 logger.debug("Setting LED State to {}", bytesToHex(bytes));
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
198 public static String bytesToHex(byte[] bytes) {
199 StringBuilder builder = new StringBuilder();
200 for (byte aByte : bytes) {
201 builder.append(String.format("%02x ", aByte));
203 String string = builder.toString();
204 return string.substring(0, string.length() - 1);