added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.wifiled</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,91 @@
# WiFi LED Binding
This binding is used to control LED strips connected by WiFi.
These devices are sold with different names, i.e. Magic Home LED, UFO LED, LED NET controller, etc.
## Supported Things
The following table shows a list of RGBW(W) LED devices supported by this binding.
Device table with supported channels:
| Device Type | power | color | white | white2 | program | programSpeed |
|-------------|:-----:|:-----:|:-----:|:------:|:-------:|:------------:|
| LD382 | ✓ | ✓ | ✓ | | ✓ | ✓ |
| LD382A | ✓ | ✓ | ✓ | | ✓ | ✓ |
| LD686 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Other LD*** devices might work but probably need some small adaptations.
## Discovery
The LED WiFi Controllers can be discovered by triggering a search in openHAB's inbox.
Your device needs to be connected to your local network (i.e. by using the WiFi PBC connection method or the native App shipped with the device).
Read the device manual for more information about how to connect your device to your network.
## Binding Configuration
No binding configuration required.
## Thing Configuration
The Thing can be configured through the Paper UI.
Use the configuration if you have devices of type LD382 or LD686, want to enable color fading,
or if the device discovery does not find your LED controller automatically.
### Drivers
You can choose between two drivers with different functionality:
| Driver | Supports Color Fading | Supports Programs | Polls LED State |
|---------|:---------------------:|:-----------------:|:---------------:|
| CLASSIC | | ✓ | ✓ |
| FADING | ✓ | | |
While the CLASSIC driver lets you choose and run device internal programs (e.g. alternating blue),
all normal operations (e.g. turn on or off, switch color, etc.) are performed immediately and
without any fading effect.
If you prefer to switch colors smoothly and to turn your light on and off by slightly increasing/decreasing the brightness, you should try the FADING driver.
If selected, you can also set the number of fading steps and the fading duration in the Thing configuration.
Each fading step will at least take 10 ms to be processed.
This limit comes from the speed of the LED controller and your network speed.
Thus a color fading with a configured fading duration of 0s might still take some time; count on more than 1 second for 100 steps.
If the FADING driver is chosen, the program and the programSpeed channels will not have any effect.
The polling period is a parameter only used by the CLASSIC driver and specifies the time in seconds after the LED state is refreshed in openHAB.
### Device Discovery
If the automatic discovery fails, you have to set the IP address and the port of your device manually. Make sure that the
device protocol matches your device type.
## Channels
| Channel Type ID | Item Type | Description | Access |
|-----------------|-----------|------------------------------------------------------------------------|--------|
| power | Switch | Power state of the LEDs (ON/OFF) | R/W |
| color | Color | Color of the RGB LEDs | R/W |
| white | Dimmer | Brightness of the first (warm) white LEDs (min=0, max=100) | R/W |
| white2 | Dimmer | Brightness of the second (warm) white LEDs (min=0, max=100) | R/W |
| program | String | Program to run by the controller (i.e. color cross fade, strobe, etc.) | R/W |
| programSpeed | Dimmer | Speed of the program | R/W |
## Example
Usually, there is no need to define your WiFi LED controllers via configuration files.
However, here is an example.
wifiled.things:
```
Thing wifiled:wifiled:F0FE6B19CB2A [ ip="192.168.178.91", port=5577, pollingPeriod=3000, protocol="LD686", driver="CLASSIC", fadeDurationInMs=1000, fadeSteps=100 ]
```
wifiled.items:
```
Switch MyWiFiLight_power "Power" (Light) {channel="wifiled:wifiled:F0FE6B19CB2A:power"}
Dimmer MyWiFiLight_white "White" (Light) {channel="wifiled:wifiled:F0FE6B19CB2A:white"}
Color MyWiFiLight_color "Color" (Light) {channel="wifiled:wifiled:F0FE6B19CB2A:color"}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.wifiled</artifactId>
<name>openHAB Add-ons :: Bundles :: WiFiLED Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.wifiled-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-wifiled" description="WiFi LED Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.wifiled/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link WiFiLEDBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Osman Basha - Initial contribution
*/
@NonNullByDefault
public class WiFiLEDBindingConstants {
public static final String BINDING_ID = "wifiled";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_WIFILED = new ThingTypeUID(BINDING_ID, "wifiled");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_WIFILED);
// List of all Channel IDs
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_COLOR = "color";
public static final String CHANNEL_WHITE = "white";
public static final String CHANNEL_WHITE2 = "white2";
public static final String CHANNEL_PROGRAM = "program";
public static final String CHANNEL_PROGRAM_SPEED = "programSpeed";
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal;
import static org.openhab.binding.wifiled.internal.WiFiLEDBindingConstants.THING_TYPE_WIFILED;
import java.util.Collections;
import java.util.Set;
import org.openhab.binding.wifiled.internal.handler.WiFiLEDHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link WiFiLEDHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Osman Basha - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.wifiled")
public class WiFiLEDHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_WIFILED);
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_WIFILED)) {
return new WiFiLEDHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.configuration;
/**
* The {@link WiFiLEDConfig} class holds the configuration properties of the thing.
*
* @author Osman Basha - Initial contribution
* @author Stefan Endrullis - Initial contribution
*/
public class WiFiLEDConfig {
private String ip;
private Integer port;
private Integer pollingPeriod;
private String protocol;
private String driver;
private Integer fadeDurationInMs;
private Integer fadeSteps;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public Integer getPollingPeriod() {
return pollingPeriod;
}
public void setPollingPeriod(Integer pollingPeriod) {
this.pollingPeriod = pollingPeriod;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public Integer getFadeDurationInMs() {
return fadeDurationInMs;
}
public void setFadeDurationInMs(Integer fadeDurationInMs) {
this.fadeDurationInMs = fadeDurationInMs;
}
public Integer getFadeSteps() {
return fadeSteps;
}
public void setFadeSteps(Integer fadeSteps) {
this.fadeSteps = fadeSteps;
}
}

View File

@@ -0,0 +1,142 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.discovery;
import static org.openhab.binding.wifiled.internal.WiFiLEDBindingConstants.*;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.wifiled.internal.handler.AbstractWiFiLEDDriver;
import org.openhab.binding.wifiled.internal.handler.ClassicWiFiLEDDriver;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WiFiLEDDiscoveryService} class implements a service
* for discovering supported WiFi LED Devices.
*
* @author Osman Basha - Initial contribution
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.wifiled")
public class WiFiLEDDiscoveryService extends AbstractDiscoveryService {
private static final int DEFAULT_BROADCAST_PORT = 48899;
private static final String DISCOVER_MESSAGE = "HF-A11ASSISTHREAD";
private Logger logger = LoggerFactory.getLogger(WiFiLEDDiscoveryService.class);
public WiFiLEDDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, 15, true);
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return SUPPORTED_THING_TYPES_UIDS;
}
@Override
protected void startBackgroundDiscovery() {
logger.debug("Start WiFi LED background discovery");
scheduler.schedule(() -> discover(), 0, TimeUnit.SECONDS);
}
@Override
public void startScan() {
logger.debug("Start WiFi LED scan");
discover();
}
private synchronized void discover() {
logger.debug("Try to discover all WiFi LED devices");
try (DatagramSocket socket = new DatagramSocket(DEFAULT_BROADCAST_PORT)) {
socket.setBroadcast(true);
socket.setSoTimeout(5000);
InetAddress inetAddress = InetAddress.getByName("255.255.255.255");
// send discover
byte[] discover = DISCOVER_MESSAGE.getBytes();
DatagramPacket packet = new DatagramPacket(discover, discover.length, inetAddress, DEFAULT_BROADCAST_PORT);
socket.send(packet);
logger.debug("Discover message sent: '{}'", DISCOVER_MESSAGE);
// wait for responses
while (true) {
byte[] rxbuf = new byte[256];
packet = new DatagramPacket(rxbuf, rxbuf.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException e) {
logger.trace("Timeout exceeded. Discovery process ended.");
break;
}
byte[] data = packet.getData();
String s = bytesToString(data);
logger.debug("Discovery response received: '{}' [{}] ", s, ClassicWiFiLEDDriver.bytesToHex(data));
// 192.168.178.25,ACCF23489C9A,HF-LPB100-ZJ200
// ^-IP..........,^-MAC.......,^-HOSTNAME.....
String[] ss = s.split(",");
if (ss.length < 3) {
logger.debug("Ignoring unparseable discovery response: '{}'", s);
continue;
}
String ip = ss[0];
String mac = ss[1];
String name = ss[2];
logger.debug("Adding a new WiFi LED with IP '{}' and MAC '{}' to inbox", ip, mac);
Map<String, Object> properties = new HashMap<>();
properties.put("ip", ip);
properties.put("protocol", AbstractWiFiLEDDriver.Protocol.LD382A);
ThingUID uid = new ThingUID(THING_TYPE_WIFILED, mac);
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(name)
.build();
thingDiscovered(result);
logger.debug("Thing discovered '{}'", result);
}
} catch (IOException e) {
logger.debug("Device discovery encountered an I/O Exception: {}", e.getMessage(), e);
}
}
private static String bytesToString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
if (aByte == 0) {
break;
}
sb.append((char) (aByte & 0xFF));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,216 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import static org.openhab.binding.wifiled.internal.handler.ClassicWiFiLEDDriver.bytesToHex;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract WiFi LED driver.
*
* @author Osman Basha - Initial contribution
* @author Stefan Endrullis
* @author Ries van Twisk
*/
public abstract class AbstractWiFiLEDDriver {
public enum Protocol {
LD382,
LD382A,
LD686
}
public enum Driver {
CLASSIC,
FADING
}
public static final Integer DEFAULT_PORT = 5577;
protected static final int DEFAULT_SOCKET_TIMEOUT = 5000;
protected Logger logger = LoggerFactory.getLogger(AbstractWiFiLEDDriver.class);
protected String host;
protected int port;
protected Protocol protocol;
public AbstractWiFiLEDDriver(String host, int port, Protocol protocol) {
this.host = host;
this.port = port;
this.protocol = protocol;
}
/**
* Allow to cleanup the driver
*/
public abstract void shutdown();
public abstract void setColor(HSBType color) throws IOException;
public abstract void setBrightness(PercentType brightness) throws IOException;
public abstract void incBrightness(int step) throws IOException;
public void decBrightness(int step) throws IOException {
incBrightness(-step);
}
public abstract void setWhite(PercentType white) throws IOException;
public abstract void incWhite(int step) throws IOException;
public void decWhite(int step) throws IOException {
incWhite(-step);
}
public abstract void setWhite2(PercentType white2) throws IOException;
public abstract void incWhite2(int step) throws IOException;
public void decWhite2(int step) throws IOException {
incWhite2(-step);
}
public abstract void setProgram(StringType program) throws IOException;
public abstract void setProgramSpeed(PercentType speed) throws IOException;
public abstract void incProgramSpeed(int step) throws IOException;
public void decProgramSpeed(int step) throws IOException {
incProgramSpeed(-step);
}
public abstract void setPower(OnOffType command) throws IOException;
public void init() throws IOException {
getLEDState();
}
public abstract LEDStateDTO getLEDStateDTO() throws IOException;
protected synchronized LEDState getLEDState() throws IOException {
try (Socket socket = new Socket(host, port);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
DataInputStream inputStream = new DataInputStream(socket.getInputStream())) {
logger.debug("Connected to '{}'", socket);
socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT);
byte[] data = { (byte) 0x81, (byte) 0x8A, (byte) 0x8B, (byte) 0x96 };
outputStream.write(data);
logger.debug("Data sent: '{}'", bytesToHex(data));
byte[] statusBytes = new byte[14];
inputStream.readFully(statusBytes);
logger.debug("Data read: '{}'", bytesToHex(statusBytes));
// Example response (14 Bytes):
// 0x81 0x04 0x23 0x26 0x21 0x10 0x45 0x00 0x00 0x00 0x03 0x00 0x00 0x47
// ..........^--- On/Off.........R....G....B....WW........CW
// ...............^-- PGM...^---SPEED...............
int state = statusBytes[2] & 0xFF; // On/Off
int program = statusBytes[3] & 0xFF;
int programSpeed = statusBytes[5] & 0xFF;
// On factory default the controller can be configured
// with a value of 255 but max should be 31.
if (programSpeed > 31) {
programSpeed = 31;
}
int red = statusBytes[6] & 0xFF;
int green = statusBytes[7] & 0xFF;
int blue = statusBytes[8] & 0xFF;
int white = statusBytes[9] & 0xFF;
int white2 = protocol == Protocol.LD686 ? statusBytes[11] & 0xFF : 0;
logger.debug("RGBW: {},{},{},{}, {}", red, green, blue, white, white2);
return new LEDState(state, program, programSpeed, red, green, blue, white, white2);
}
}
protected void sendRaw(byte[] data) throws IOException {
sendRaw(data, 100);
}
protected synchronized void sendRaw(byte[] data, int delay) throws IOException {
try (Socket socket = new Socket(host, port);
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) {
logger.debug("Connected to '{}'", socket);
socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT);
sendRaw(data, outputStream);
if (delay > 0) {
Thread.sleep(delay);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
protected void sendRaw(byte[] data, DataOutputStream outputStream) throws IOException {
byte[] dataWithCS;
// append 0x0F (if dev.type LD382A)
if (protocol == Protocol.LD382A || protocol == Protocol.LD686) {
dataWithCS = new byte[data.length + 2];
dataWithCS[dataWithCS.length - 2] = 0x0F;
} else {
dataWithCS = new byte[data.length + 1];
}
// append checksum
System.arraycopy(data, 0, dataWithCS, 0, data.length);
int cs = 0;
for (int i = 0; i < dataWithCS.length - 1; i++) {
cs += dataWithCS[i];
}
cs = cs & 0xFF;
dataWithCS[dataWithCS.length - 1] = (byte) cs;
outputStream.write(dataWithCS);
logger.debug("RAW data sent: '{}'", bytesToHex(dataWithCS));
}
protected byte[] getBytesForColor(byte r, byte g, byte b, byte w, byte w2) {
byte[] bytes;
if (protocol == Protocol.LD382 || protocol == Protocol.LD382A) {
bytes = new byte[] { 0x31, r, g, b, w, 0x00 };
} else if (protocol == Protocol.LD686) {
bytes = new byte[] { 0x31, r, g, b, w, w2, 0x00 };
} else {
throw new UnsupportedOperationException("Protocol " + protocol + " not yet implemented");
}
return bytes;
}
protected byte[] getBytesForPower(boolean on) {
return new byte[] { 0x71, on ? (byte) 0x23 : 0x24 };
}
}

View File

@@ -0,0 +1,206 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
/**
* The {@link ClassicWiFiLEDDriver} class is responsible for the communication with the WiFi LED controller.
* It's used for sending color or program settings and also extracting the data out of the received telegrams.
*
* @author Osman Basha - Initial contribution
* @author Stefan Endrullis
* @author Ries van Twisk - Prevent flashes during classic driver color + white updates
*/
public class ClassicWiFiLEDDriver extends AbstractWiFiLEDDriver {
private static final int WAIT_UPDATE_LED_FOR_MS = 25;
private final WiFiLEDHandler wifiLedHandler;
private final ExecutorService updateScheduler = Executors.newSingleThreadExecutor();
private Future<Boolean> ledUpdateFuture = CompletableFuture.completedFuture(null);
private LEDStateDTO cachedLedStatus = null;
public ClassicWiFiLEDDriver(WiFiLEDHandler wifiLedHandler, String host, int port, Protocol protocol) {
super(host, port, protocol);
this.wifiLedHandler = wifiLedHandler;
}
@Override
public void shutdown() {
}
@Override
public synchronized LEDStateDTO getLEDStateDTO() throws IOException {
if (ledUpdateFuture.isDone()) {
LEDState s = getLEDState();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new IOException(e);
}
return LEDStateDTO.valueOf(s.state, s.program, s.programSpeed, s.red, s.green, s.blue, s.white, s.white2);
} else {
return cachedLedStatus;
}
}
@Override
public synchronized void setColor(HSBType color) throws IOException {
logger.debug("Setting color to {}", color);
LEDStateDTO ledState = getLEDStateDTO().withColor(color).withoutProgram();
sendLEDData(ledState);
}
@Override
public synchronized void setBrightness(PercentType brightness) throws IOException {
logger.debug("Setting brightness to {}", brightness);
LEDStateDTO ledState = getLEDStateDTO().withBrightness(brightness).withoutProgram();
sendLEDData(ledState);
}
@Override
public synchronized void incBrightness(int step) throws IOException {
logger.debug("Changing brightness by {}", step);
LEDStateDTO ledState = getLEDStateDTO().withIncrementedBrightness(step).withoutProgram();
sendLEDData(ledState);
}
@Override
public synchronized void setWhite(PercentType white) throws IOException {
logger.debug("Setting (warm) white LED to {}", white);
LEDStateDTO ledState = getLEDStateDTO().withWhite(white).withoutProgram();
sendLEDData(ledState);
}
@Override
public synchronized void incWhite(int step) throws IOException {
logger.debug("Changing white by {}", step);
LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite(step).withoutProgram();
sendLEDData(ledState);
}
@Override
public void setWhite2(PercentType white2) throws IOException {
logger.debug("Setting (warm) white 2 LED to {}", white2);
LEDStateDTO ledState = getLEDStateDTO().withWhite2(white2).withoutProgram();
sendLEDData(ledState);
}
@Override
public void incWhite2(int step) throws IOException {
logger.debug("Changing white by {}", step);
LEDStateDTO ledState = getLEDStateDTO().withIncrementedWhite2(step).withoutProgram();
sendLEDData(ledState);
}
@Override
public void setPower(OnOffType command) throws IOException {
logger.debug("Power {}", command.name());
sendRaw(getBytesForPower(command == OnOffType.ON));
}
@Override
public synchronized void setProgram(StringType program) throws IOException {
logger.debug("Setting program '{}'", program);
LEDStateDTO ledState = getLEDStateDTO().withProgram(program);
sendLEDData(ledState);
}
@Override
public synchronized void setProgramSpeed(PercentType speed) throws IOException {
logger.debug("Setting program speed to {}", speed);
LEDStateDTO ledState = getLEDStateDTO().withProgramSpeed(speed);
if (speed.equals(PercentType.ZERO)) {
ledState = ledState.withoutProgram();
}
sendLEDData(ledState);
}
@Override
public synchronized void incProgramSpeed(int step) throws IOException {
logger.debug("Changing program speed by {}", step);
LEDStateDTO ledState = getLEDStateDTO().withIncrementedProgramSpeed(step);
sendLEDData(ledState);
}
private synchronized void sendLEDData(final LEDStateDTO ledState) {
cachedLedStatus = ledState;
if (!ledUpdateFuture.isDone()) {
ledUpdateFuture.cancel(true);
}
final byte[] bytes;
int program = Integer.valueOf(ledState.getProgram().toString());
if (program == 0x61) {
// "normal" program: set color etc.
byte r = (byte) (ledState.getRGB() >> 16 & 0xFF);
byte g = (byte) (ledState.getRGB() >> 8 & 0xFF);
byte b = (byte) (ledState.getRGB() & 0xFF);
byte w = (byte) (((int) (ledState.getWhite().doubleValue() * 255 / 100)) & 0xFF);
byte w2 = (byte) (((int) (ledState.getWhite2().doubleValue() * 255 / 100)) & 0xFF);
bytes = getBytesForColor(r, g, b, w, w2);
} else {
// program selected
byte p = (byte) (program & 0xFF);
byte s = (byte) (((100 - ledState.getProgramSpeed().intValue()) * 0x1F / 100) & 0xFF);
bytes = new byte[] { 0x61, p, s };
}
ledUpdateFuture = updateScheduler.submit(() -> {
try {
Thread.sleep(WAIT_UPDATE_LED_FOR_MS);
logger.debug("Setting LED State to {}", bytesToHex(bytes));
sendRaw(bytes);
return true;
} catch (IOException e) {
logger.debug("Exception occurred while sending command to LED", e);
wifiLedHandler.reportCommunicationError(e);
} catch (InterruptedException e) {
// Ignore, this is expected
}
return false;
});
}
public static String bytesToHex(byte[] bytes) {
StringBuilder builder = new StringBuilder();
for (byte aByte : bytes) {
builder.append(String.format("%02x ", aByte));
}
String string = builder.toString();
return string.substring(0, string.length() - 1);
}
}

View File

@@ -0,0 +1,338 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link FadingWiFiLEDDriver} class is responsible for the communication with the WiFi LED controller.
* It utilizes color fading when changing colors or turning the light on of off.
*
* @author Stefan Endrullis - Initial contribution
* @author Ries van Twisk
*/
public class FadingWiFiLEDDriver extends AbstractWiFiLEDDriver {
public static final int DEFAULT_FADE_DURATION_IN_MS = 1000;
public static final int DEFAULT_FADE_STEPS = 100;
public static final int KEEP_COMMUNICATION_OPEN_FOR_MS = 1000;
private static final InternalLedState BLACK_STATE = new InternalLedState();
private boolean power = false;
private InternalLedState currentState = new InternalLedState(); // Use to not update the controller with the same
// value
private InternalLedState currentFaderState = new InternalLedState();
private InternalLedState targetState = new InternalLedState();
private LEDStateDTO dtoState = LEDStateDTO.valueOf(0, 0, 0, 0, 0, 0, 0, 0);
private final ScheduledExecutorService waiterExecutor = Executors.newSingleThreadScheduledExecutor();
private final ScheduledExecutorService faderExecutor = Executors.newSingleThreadScheduledExecutor();
private LEDFaderRunner ledfaderThread = null;
private final Semaphore ledUpdateSyncSemaphore = new Semaphore(1, false);
private final int fadeDurationInMs;
private final int totalFadingSteps;
public FadingWiFiLEDDriver(String host, int port, AbstractWiFiLEDDriver.Protocol protocol, int fadeDurationInMs,
int totalFadingSteps) {
super(host, port, protocol);
this.fadeDurationInMs = fadeDurationInMs < 10 ? 10 : fadeDurationInMs;
this.totalFadingSteps = totalFadingSteps < 1 ? 1 : totalFadingSteps;
}
@Override
public void init() throws IOException {
try {
LEDState s = getLEDState();
dtoState = LEDStateDTO.valueOf(s.state, s.program, s.programSpeed, s.red, s.green, s.blue, s.white,
s.white2);
power = (s.state & 0x01) != 0;
currentState = InternalLedState.fromRGBW(s.red, s.green, s.blue, s.white, s.white2);
currentFaderState = currentState;
} catch (IOException e) {
logger.warn("IOException", e);
}
}
@Override
public void shutdown() {
waiterExecutor.shutdown();
faderExecutor.shutdown();
try {
if (!waiterExecutor.awaitTermination((fadeDurationInMs / totalFadingSteps) * 2, TimeUnit.MILLISECONDS)) {
waiterExecutor.shutdownNow();
}
if (!faderExecutor.awaitTermination((fadeDurationInMs / totalFadingSteps) * 2, TimeUnit.MILLISECONDS)) {
faderExecutor.shutdownNow();
}
} catch (InterruptedException e) {
// Ignored
}
}
@Override
public void setColor(HSBType color) throws IOException {
dtoState = dtoState.withColor(color);
changeState(targetState.withColor(color));
}
@Override
public void setBrightness(PercentType brightness) throws IOException {
dtoState = dtoState.withBrightness(brightness);
changeState(targetState.withBrightness(brightness.doubleValue() / 100));
}
@Override
public void incBrightness(int step) throws IOException {
dtoState = dtoState.withIncrementedBrightness(step);
changeState(targetState.withBrightness(targetState.getBrightness() + ((double) step / 100)));
}
@Override
public void decBrightness(int step) throws IOException {
dtoState = dtoState.withIncrementedBrightness(-step);
changeState(targetState.withBrightness(targetState.getBrightness() - ((double) step / 100)));
}
@Override
public void setWhite(PercentType white) throws IOException {
dtoState = dtoState.withWhite(white);
changeState(targetState.withWhite(white.doubleValue() / 100));
}
@Override
public void incWhite(int step) throws IOException {
dtoState = dtoState.withIncrementedWhite(step);
changeState(targetState.withWhite(targetState.getWhite() + ((double) step / 100)));
}
@Override
public void setWhite2(PercentType white2) throws IOException {
dtoState = dtoState.withWhite2(white2);
changeState(targetState.withWhite2(white2.doubleValue() / 100));
}
@Override
public void incWhite2(int step) throws IOException {
dtoState = dtoState.withIncrementedWhite2(step);
changeState(targetState.withWhite2(targetState.getWhite2() + ((double) step / 100)));
}
@Override
public void setProgram(StringType program) throws IOException {
}
@Override
public void setProgramSpeed(PercentType speed) throws IOException {
}
@Override
public void incProgramSpeed(int step) throws IOException {
}
@Override
public void setPower(OnOffType command) throws IOException {
dtoState = dtoState.withPower(command);
power = command == OnOffType.ON;
fadeToState(power ? targetState : BLACK_STATE);
}
@Override
public LEDStateDTO getLEDStateDTO() throws IOException {
return dtoState;
}
private void changeState(final InternalLedState newState) throws IOException {
targetState = newState;
if (power) {
fadeToState(targetState);
}
}
/**
* Runnable that takes care of fading of the LED's
*/
static final class LEDFaderRunner implements Runnable {
private final Logger logger = LoggerFactory.getLogger(LEDFaderRunner.class);
private String host;
private int port;
private InternalLedState fromState;
private InternalLedState toState;
private final int totalFadingSteps;
private final long keepCommOpenForMS;
private final Function<DataOutputStream, Boolean> powerOnFunc;
private final BiFunction<DataOutputStream, InternalLedState, Boolean> ledSender;
private long lastCommunicationTime = 0;
private int currentFadingStep = 1;
private final Lock lock = new ReentrantLock();
private InternalLedState currentFadeState;
private Socket socket;
private DataOutputStream outputStream;
public LEDFaderRunner(String host, int port, InternalLedState fromState, InternalLedState toState,
int totalFadingSteps, int keepCommOpenForMS, Function<DataOutputStream, Boolean> powerOnFunc,
BiFunction<DataOutputStream, InternalLedState, Boolean> ledSender) {
this.host = host;
this.port = port;
this.fromState = fromState;
this.toState = toState;
this.totalFadingSteps = totalFadingSteps;
this.keepCommOpenForMS = keepCommOpenForMS;
this.powerOnFunc = powerOnFunc;
this.ledSender = ledSender;
}
/**
* Call before starting a thre`ad, it will initialise a socket and power on the LEDs
*
* @throws IOException
*/
public void init() throws IOException {
socket = new Socket(host, port);
socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT);
outputStream = new DataOutputStream(socket.getOutputStream());
logger.debug("Connected to '{}'", socket);
powerOnFunc.apply(outputStream);
currentFadeState = fromState;
}
public void setToState(InternalLedState toState) {
lock.lock();
this.fromState = currentFadeState;
this.toState = toState;
this.currentFadingStep = 1;
lock.unlock();
}
@Override
public void run() {
lock.lock();
if (currentFadingStep <= totalFadingSteps) {
currentFadeState = fromState.fade(toState, (double) currentFadingStep / totalFadingSteps);
lock.unlock();
logger.debug("currentFadeState: {}", currentFadeState);
if (!ledSender.apply(outputStream, currentFadeState)) {
logger.warn("Failed sending at step {}", currentFadingStep);
throw new IllegalStateException("Failed sending at step " + currentFadingStep);
}
lastCommunicationTime = System.currentTimeMillis();
} else {
lock.unlock();
if (lastCommunicationTime < (System.currentTimeMillis() - keepCommOpenForMS)) {
throw new IllegalStateException("Reached end step");
}
}
currentFadingStep++;
}
public void shutdown() {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// Ignored
}
}
}
}
private synchronized void fadeToState(final InternalLedState newTargetState) throws IOException {
if (ledUpdateSyncSemaphore.tryAcquire(1)) {
// Create and Execute a new LED Fader
ledfaderThread = new LEDFaderRunner(host, port, currentFaderState, newTargetState, totalFadingSteps,
KEEP_COMMUNICATION_OPEN_FOR_MS, (outputStream) -> {
try {
sendRaw(getBytesForPower(true), outputStream);
return true;
} catch (IOException e) {
logger.warn("IOException", e);
return false;
}
}, (outputStream, fs) -> {
try {
sendLEDData(fs, outputStream);
currentFaderState = fs;
logger.trace("Current: {} {} {} {}", fs.getR(), fs.getG(), fs.getB(), fs.getWhite());
return true;
} catch (IOException e) {
logger.warn("IOException", e);
return false;
}
});
ledfaderThread.init();
final int period = fadeDurationInMs / totalFadingSteps;
final Future<?> future = faderExecutor.scheduleAtFixedRate(ledfaderThread, 0, period < 1 ? 1 : period,
TimeUnit.MILLISECONDS);
// Wait untill LED Thread has finished, when so shutdown fader
waiterExecutor.schedule(() -> {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
// Ignored
} catch (Exception e) {
logger.warn("Exception", e);
}
ledfaderThread.shutdown();
ledfaderThread = null;
ledUpdateSyncSemaphore.release(1);
}, 0, TimeUnit.MILLISECONDS);
} else {
ledfaderThread.setToState(newTargetState);
}
}
private void sendLEDData(InternalLedState ledState, DataOutputStream out) throws IOException {
logger.debug("Setting LED State to {}", ledState);
if (!ledState.equals(currentState)) {
// "normal" program: set color etc.
byte r = (byte) (ledState.getR() & 0xFF);
byte g = (byte) (ledState.getG() & 0xFF);
byte b = (byte) (ledState.getB() & 0xFF);
byte w = (byte) (ledState.getW() & 0xFF);
byte w2 = (byte) (ledState.getW2() & 0xFF);
logger.debug("RGBW: {}, {}, {}, {}, {}", r, g, b, w, w2);
byte[] bytes = getBytesForColor(r, g, b, w, w2);
sendRaw(bytes, out);
}
currentState = ledState;
}
}

View File

@@ -0,0 +1,216 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import static java.lang.Math.max;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.PercentType;
/**
* Internal LED state.
*
* @author Stefan Endrullis - Initial contribution
*/
public class InternalLedState {
/** Values for the colors red, green, blue from 0 to 1. */
double r, g, b;
/** White values from 0 to 1. */
double w, w2;
public InternalLedState() {
this(0, 0, 0, 0, 0);
}
public InternalLedState(double r, double g, double b, double w, double w2) {
this.r = r;
this.g = g;
this.b = b;
this.w = w;
this.w2 = w2;
}
public static InternalLedState fromRGBW(int r, int g, int b, int w, int w2) {
return new InternalLedState(conv(r), conv(g), conv(b), conv(w), conv(w2));
}
public InternalLedState withColor(HSBType color) {
//@formatter:off
return new InternalLedState(
color.getRed().doubleValue() / 100,
color.getGreen().doubleValue() / 100,
color.getBlue().doubleValue() / 100,
w,
w2
);
//@formatter:on
}
public InternalLedState withBrightness(double brightness) {
return new InternalLedState(r * brightness, g * brightness, b * brightness, w, w2);
}
public InternalLedState withWhite(double w) {
return new InternalLedState(r, g, b, w, w2);
}
public InternalLedState withWhite2(double w2) {
return new InternalLedState(r, g, b, w, w2);
}
public PercentType toHSBType() {
return HSBType.fromRGB(conv(r), conv(g), conv(b));
}
/**
* Fades from this color to the that color according to the given progress value from 0 (this color)
* to 1 (that color).
*
* @param that that color
* @param progress value between 0 (this color) and 1 (that color)
* @return faded color
*/
public InternalLedState fade(InternalLedState that, double progress) {
double invProgress = 1 - progress;
//@formatter:off
return new InternalLedState(
this.r * invProgress + that.r * progress,
this.g * invProgress + that.g * progress,
this.b * invProgress + that.b * progress,
this.w * invProgress + that.w * progress,
this.w2 * invProgress + that.w2 * progress
);
//@formatter:on
}
/**
* Returns the brightness or the RGB color.
*
* @return value between 0 and 1
*/
public double getBrightness() {
return max(r, max(g, b));
}
/**
* Returns the white value.
*
* @return value between 0 and 1
*/
public double getWhite() {
return w;
}
/**
* Returns the white2 value.
*
* @return value between 0 and 1
*/
public double getWhite2() {
return w2;
}
private static double conv(int v) {
return (double) v / 255;
}
private static int conv(double v) {
return (int) (v * 255 + 0.5);
}
/**
* Returns red value.
*
* @return value between 0 and 255
*/
public int getR() {
return conv(r);
}
/**
* Returns green value.
*
* @return value between 0 and 255
*/
public int getG() {
return conv(g);
}
/**
* Returns blue value.
*
* @return value between 0 and 255
*/
public int getB() {
return conv(b);
}
/**
* Returns white value.
*
* @return value between 0 and 255
*/
public int getW() {
return conv(w);
}
/**
* Returns white2 value.
*
* @return value between 0 and 255
*/
public int getW2() {
return conv(w2);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
InternalLedState that = (InternalLedState) o;
//@formatter:off
return Double.compare(that.r, r) == 0
&& Double.compare(that.g, g) == 0
&& Double.compare(that.b, b) == 0
&& Double.compare(that.w, w) == 0;
//@formatter:on
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(r);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(g);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(b);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(w);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "InternalLedState{" + "r=" + r + ", g=" + g + ", b=" + b + ", w=" + w + '}';
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
/**
* @author Stefan Endrullis - Initial contribution
*/
public class LEDState {
public final int state, program, programSpeed;
public final int red, green, blue, white, white2;
public LEDState(int state, int program, int programSpeed, int red, int green, int blue, int white, int white2) {
this.state = state;
this.program = program;
this.programSpeed = programSpeed;
this.red = red;
this.green = green;
this.blue = blue;
this.white = white;
this.white2 = white2;
}
}

View File

@@ -0,0 +1,183 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import static java.lang.Math.*;
import java.awt.Color;
import java.math.BigDecimal;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
/**
* The {@link LEDStateDTO} class holds the data and the settings for a LED device (i.e. the selected colors, the running
* program, etc.).
*
* @author Osman Basha - Initial contribution
* @author Stefan Endrullis - Initial contribution
* @author Ries van Twisk - Prevent flashes during classic driver color + white updates
*/
public class LEDStateDTO {
private HSBType hsbType;
private OnOffType power;
private PercentType white;
private PercentType white2;
private StringType program;
private PercentType programSpeed;
public LEDStateDTO(OnOffType power, DecimalType hue, PercentType saturation, PercentType brightness,
PercentType white, PercentType white2, StringType program, PercentType programSpeed) {
this.hsbType = new HSBType(hue, saturation, brightness);
this.power = power;
this.white = white;
this.white2 = white2;
this.program = program;
this.programSpeed = programSpeed;
}
public LEDStateDTO(OnOffType power, HSBType hsb, PercentType white, PercentType white2, StringType program,
PercentType programSpeed) {
this.hsbType = hsb;
this.power = power;
this.white = white;
this.white2 = white2;
this.program = program;
this.programSpeed = programSpeed;
}
public PercentType getWhite() {
return white;
}
public PercentType getWhite2() {
return white2;
}
public StringType getProgram() {
return program;
}
public PercentType getProgramSpeed() {
return programSpeed;
}
@Override
public String toString() {
return power + "," + hsbType.getHue() + "," + hsbType.getSaturation() + "," + hsbType.getBrightness() + ","
+ getWhite() + "," + getWhite2() + " [" + getProgram() + "," + getProgramSpeed() + "]";
}
public static LEDStateDTO valueOf(int state, int program, int programSpeed, int red, int green, int blue, int white,
int white2) {
OnOffType power = (state & 0x01) != 0 ? OnOffType.ON : OnOffType.OFF;
float[] hsv = new float[3];
Color.RGBtoHSB(red, green, blue, hsv);
DecimalType h = new DecimalType(new BigDecimal(hsv[0]).multiply(new BigDecimal(360.0)));
PercentType s = new PercentType(new BigDecimal(hsv[1]).multiply(new BigDecimal(100.0)));
PercentType b = new PercentType(new BigDecimal(hsv[2]).multiply(new BigDecimal(100.0)));
HSBType hsbType = new HSBType(h, s, b);
PercentType w = new PercentType(new BigDecimal(white).divide(new BigDecimal(255.0), 3, BigDecimal.ROUND_HALF_UP)
.multiply(new BigDecimal(100.0)));
PercentType w2 = new PercentType(new BigDecimal(white2)
.divide(new BigDecimal(255.0), 3, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100.0)));
// Range: 0x00 .. 0x1F. Speed is inversed
BigDecimal ps = new BigDecimal(programSpeed).divide(new BigDecimal(0x1f), 2, BigDecimal.ROUND_HALF_UP)
.multiply(new BigDecimal(100.0));
PercentType e = new PercentType(new BigDecimal(100.0).subtract(ps));
StringType p = new StringType(Integer.toString(program));
return new LEDStateDTO(power, hsbType, w, w2, p, e);
}
public LEDStateDTO withColor(HSBType color) {
return new LEDStateDTO(power, color, this.getWhite(), this.getWhite2(), this.getProgram(),
this.getProgramSpeed());
}
public LEDStateDTO withBrightness(PercentType brightness) {
return new LEDStateDTO(power, hsbType.getHue(), hsbType.getSaturation(), brightness, this.getWhite(),
this.getWhite2(), this.getProgram(), this.getProgramSpeed());
}
public LEDStateDTO withIncrementedBrightness(int step) {
int brightness = hsbType.getBrightness().intValue();
brightness = max(min(brightness + step, 0), 100);
return withBrightness(new PercentType(brightness));
}
public LEDStateDTO withWhite(PercentType white) {
return new LEDStateDTO(power, hsbType, white, this.getWhite2(), this.getProgram(), this.getProgramSpeed());
}
public LEDStateDTO withIncrementedWhite(int step) {
int white = this.getWhite().intValue();
white = max(min(white + step, 0), 100);
return withWhite(new PercentType(white));
}
public LEDStateDTO withWhite2(PercentType white2) {
return new LEDStateDTO(power, hsbType, this.getWhite(), white2, this.getProgram(), this.getProgramSpeed());
}
public LEDStateDTO withIncrementedWhite2(int step) {
int white = this.getWhite().intValue();
white = max(min(white + step, 0), 100);
return withWhite(new PercentType(white));
}
public LEDStateDTO withProgram(StringType program) {
return new LEDStateDTO(power, hsbType, this.getWhite(), this.getWhite2(), program, this.getProgramSpeed());
}
public LEDStateDTO withoutProgram() {
return withProgram(new StringType(String.valueOf(0x61)));
}
public LEDStateDTO withProgramSpeed(PercentType programSpeed) {
return new LEDStateDTO(power, hsbType, this.getWhite(), this.getWhite2(), this.getProgram(), programSpeed);
}
public LEDStateDTO withIncrementedProgramSpeed(int step) {
int programSpeed = this.getProgramSpeed().intValue();
programSpeed = max(min(programSpeed + step, 0), 100);
return withProgramSpeed(new PercentType(programSpeed));
}
public LEDStateDTO withPower(OnOffType power) {
return new LEDStateDTO(power, hsbType, this.getWhite(), this.getWhite2(), this.getProgram(), getProgramSpeed());
}
public int getRGB() {
return hsbType.getRGB();
}
public HSBType getHSB() {
return hsbType;
}
public OnOffType getPower() {
return power;
}
}

View File

@@ -0,0 +1,234 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.wifiled.internal.WiFiLEDBindingConstants;
import org.openhab.binding.wifiled.internal.configuration.WiFiLEDConfig;
import org.openhab.binding.wifiled.internal.handler.AbstractWiFiLEDDriver.Driver;
import org.openhab.binding.wifiled.internal.handler.AbstractWiFiLEDDriver.Protocol;
import org.openhab.core.library.types.*;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link WiFiLEDHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Osman Basha - Initial contribution
* @author Ries van Twisk
*/
public class WiFiLEDHandler extends BaseThingHandler {
private static final int INC_DEC_STEP = 10;
private Logger logger = LoggerFactory.getLogger(WiFiLEDHandler.class);
private AbstractWiFiLEDDriver driver;
private ScheduledFuture<?> pollingJob;
public WiFiLEDHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
logger.debug("Initializing WiFiLED handler '{}'", getThing().getUID());
WiFiLEDConfig config = getConfigAs(WiFiLEDConfig.class);
int port = (config.getPort() == null) ? AbstractWiFiLEDDriver.DEFAULT_PORT : config.getPort();
Protocol protocol = config.getProtocol() == null ? Protocol.LD382A : Protocol.valueOf(config.getProtocol());
Driver driverName = config.getDriver() == null ? Driver.CLASSIC : Driver.valueOf(config.getDriver());
switch (driverName) {
case CLASSIC:
driver = new ClassicWiFiLEDDriver(this, config.getIp(), port, protocol);
break;
case FADING:
int fadeDurationInMs = config.getFadeDurationInMs() == null
? FadingWiFiLEDDriver.DEFAULT_FADE_DURATION_IN_MS
: config.getFadeDurationInMs();
int fadeSteps = config.getFadeSteps() == null ? FadingWiFiLEDDriver.DEFAULT_FADE_STEPS
: config.getFadeSteps();
driver = new FadingWiFiLEDDriver(config.getIp(), port, protocol, fadeDurationInMs, fadeSteps);
break;
}
try {
driver.init();
logger.debug("Found a WiFi LED device '{}'", getThing().getUID());
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
return;
}
updateStatus(ThingStatus.ONLINE);
int pollingPeriod = (config.getPollingPeriod() == null) ? 30 : config.getPollingPeriod();
if (pollingPeriod > 0) {
pollingJob = scheduler.scheduleWithFixedDelay(() -> update(), 0, pollingPeriod, TimeUnit.SECONDS);
logger.debug("Polling job scheduled to run every {} sec. for '{}'", pollingPeriod, getThing().getUID());
}
}
@Override
public void dispose() {
logger.debug("Disposing WiFiLED handler '{}'", getThing().getUID());
if (pollingJob != null) {
pollingJob.cancel(true);
pollingJob = null;
}
driver.shutdown();
driver = null;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Handle command '{}' for {}", command, channelUID);
try {
if (command == RefreshType.REFRESH) {
update();
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_POWER)) {
handleColorCommand(command);
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_COLOR)) {
handleColorCommand(command);
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_WHITE)) {
handleWhiteCommand(command);
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_WHITE2)) {
handleWhite2Command(command);
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_PROGRAM)
&& (command instanceof StringType)) {
driver.setProgram((StringType) command);
} else if (channelUID.getId().equals(WiFiLEDBindingConstants.CHANNEL_PROGRAM_SPEED)) {
handleProgramSpeedCommand(command);
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
private void handleColorCommand(Command command) throws IOException {
if (command instanceof HSBType) {
driver.setColor((HSBType) command);
} else if (command instanceof PercentType) {
driver.setBrightness((PercentType) command);
} else if (command instanceof OnOffType) {
driver.setPower((OnOffType) command);
} else if (command instanceof IncreaseDecreaseType) {
IncreaseDecreaseType increaseDecreaseType = (IncreaseDecreaseType) command;
if (increaseDecreaseType.equals(IncreaseDecreaseType.INCREASE)) {
driver.incBrightness(INC_DEC_STEP);
} else {
driver.decBrightness(INC_DEC_STEP);
}
}
}
private void handleWhiteCommand(Command command) throws IOException {
if (command instanceof PercentType) {
driver.setWhite((PercentType) command);
} else if (command instanceof OnOffType) {
OnOffType onOffCommand = (OnOffType) command;
if (onOffCommand.equals(OnOffType.ON)) {
driver.setWhite(PercentType.HUNDRED);
} else {
driver.setWhite(PercentType.ZERO);
}
} else if (command instanceof IncreaseDecreaseType) {
IncreaseDecreaseType increaseDecreaseType = (IncreaseDecreaseType) command;
if (increaseDecreaseType.equals(IncreaseDecreaseType.INCREASE)) {
driver.incWhite(INC_DEC_STEP);
} else {
driver.decWhite(INC_DEC_STEP);
}
}
}
private void handleWhite2Command(Command command) throws IOException {
if (command instanceof PercentType) {
driver.setWhite2((PercentType) command);
} else if (command instanceof OnOffType) {
OnOffType onOffCommand = (OnOffType) command;
if (onOffCommand.equals(OnOffType.ON)) {
driver.setWhite2(PercentType.HUNDRED);
} else {
driver.setWhite2(PercentType.ZERO);
}
} else if (command instanceof IncreaseDecreaseType) {
IncreaseDecreaseType increaseDecreaseType = (IncreaseDecreaseType) command;
if (increaseDecreaseType.equals(IncreaseDecreaseType.INCREASE)) {
driver.incWhite2(INC_DEC_STEP);
} else {
driver.decWhite2(INC_DEC_STEP);
}
}
}
private void handleProgramSpeedCommand(Command command) throws IOException {
if (command instanceof PercentType) {
driver.setProgramSpeed((PercentType) command);
} else if (command instanceof OnOffType) {
OnOffType onOffCommand = (OnOffType) command;
if (onOffCommand.equals(OnOffType.ON)) {
driver.setProgramSpeed(PercentType.HUNDRED);
} else {
driver.setProgramSpeed(PercentType.ZERO);
}
} else if (command instanceof IncreaseDecreaseType) {
IncreaseDecreaseType increaseDecreaseType = (IncreaseDecreaseType) command;
if (increaseDecreaseType.equals(IncreaseDecreaseType.INCREASE)) {
driver.incProgramSpeed(INC_DEC_STEP);
} else {
driver.decProgramSpeed(INC_DEC_STEP);
}
}
}
private synchronized void update() {
logger.debug("Updating WiFiLED data '{}'", getThing().getUID());
try {
LEDStateDTO ledState = driver.getLEDStateDTO();
HSBType color = ledState.getHSB();
updateState(WiFiLEDBindingConstants.CHANNEL_POWER, ledState.getPower());
updateState(WiFiLEDBindingConstants.CHANNEL_COLOR, color);
updateState(WiFiLEDBindingConstants.CHANNEL_WHITE, ledState.getWhite());
updateState(WiFiLEDBindingConstants.CHANNEL_WHITE2, ledState.getWhite2());
updateState(WiFiLEDBindingConstants.CHANNEL_PROGRAM, ledState.getProgram());
updateState(WiFiLEDBindingConstants.CHANNEL_PROGRAM_SPEED, ledState.getProgramSpeed());
if (getThing().getStatus().equals(ThingStatus.OFFLINE)) {
updateStatus(ThingStatus.ONLINE);
}
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
}
}
public void reportCommunicationError(IOException e) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="wifiled" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>WiFi LED Binding</name>
<description>Binding for WiFi LED devices. These are known as Magic Home RGBW LED, UFO LED, LED NET controller etc.</description>
<author>Osman Basha, Stefan Endrullis,Ries van Twisk</author>
</binding:binding>

View File

@@ -0,0 +1,27 @@
# binding
binding.wifiled.name = WiFi LED Binding
binding.wifiled.description = Binding für WiFi LED Geräte. Diese werden u.a. als Magic Home RGBW LED, UFO LED, LED NET controller etc.vertrieben.
# thing types
thing-type.wifiled.wifiled.label = WiFi LED
thing-type.wifiled.wifiled.description = WiFi LED Gerät
thing-type.config.wifiled.wifiled.ip.label = IP
thing-type.config.wifiled.wifiled.ip.description = IP-Adresse oder Host-Name des Gerätes
thing-type.config.wifiled.wifiled.port.label = Port
thing-type.config.wifiled.wifiled.port.description = Genutzte Portnummer des Gerätes
thing-type.config.wifiled.wifiled.pollingPeriod.label = Abfrageintervall
thing-type.config.wifiled.wifiled.pollingPeriod.description = Daten-Abfrageintervall in Sek.
thing-type.config.wifiled.wifiled.protocol.label = Protokoll
thing-type.config.wifiled.wifiled.protocol.description = Das zur Kommunikation mit dem Gerät genutzte Protokoll
thing-type.config.wifiled.wifiled.driver.label = Treiber
thing-type.config.wifiled.wifiled.driver.description = Treiber zur Ansteuerung des LED-Controllers
thing-type.config.wifiled.wifiled.fadeDurationInMs.description = Dauer der Überblendung
thing-type.config.wifiled.wifiled.fadeSteps.description = Anzahl von Schritten für Überblendung
# channel types
channel-type.wifiled.power.label = Eingeschaltet
channel-type.wifiled.colorType.label = Farbe
channel-type.wifiled.white.label = Weiß
channel-type.wifiled.white2.label = Weiß 2
channel-type.wifiled.program.label = Programm
channel-type.wifiled.programSpeed.label = Programm-Geschwindigkeit

View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="wifiled"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="wifiled">
<label>WiFi LED</label>
<description>WiFi LED Device</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="color" typeId="color"/>
<channel id="white" typeId="white"/>
<channel id="white2" typeId="white2"/>
<channel id="program" typeId="program"/>
<channel id="programSpeed" typeId="programSpeed"/>
</channels>
<config-description>
<parameter name="ip" type="text" required="true">
<label>IP</label>
<description>IP address or host name of the WIFI LED Controller</description>
</parameter>
<parameter name="port" type="integer" required="false" min="1024" max="49151">
<label>Port</label>
<description>Used Port of the device</description>
<default>5577</default>
<advanced>true</advanced>
</parameter>
<parameter name="pollingPeriod" type="integer" required="false">
<label>Polling Period</label>
<description>Polling period for refreshing the data in s</description>
<default>30</default>
<advanced>true</advanced>
</parameter>
<parameter name="protocol" type="text" required="false">
<label>Device Protocol</label>
<description>The protocol used for communication with the device</description>
<default>LD382A</default>
<options>
<option value="LD382A">LD382A</option>
<option value="LD382">LD382</option>
<option value="LD686">LD686</option>
</options>
<advanced>true</advanced>
</parameter>
<parameter name="driver" type="text" required="false">
<label>Device Driver</label>
<description>The driver used to control the device</description>
<default>CLASSIC</default>
<options>
<option value="CLASSIC">CLASSIC</option>
<option value="FADING">FADING</option>
</options>
<advanced>true</advanced>
</parameter>
<parameter name="fadeDurationInMs" type="integer" required="false" min="0" max="10000">
<label>Fading Duration</label>
<description>The duration for the color fading in milliseconds</description>
<default>1000</default>
<advanced>true</advanced>
</parameter>
<parameter name="fadeSteps" type="integer" required="false" min="1" max="256">
<label>Fading Steps</label>
<description>The number of steps used to fade over to the new color</description>
<default>100</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<channel-type id="power">
<item-type>Switch</item-type>
<label>Power</label>
<description>Power state</description>
</channel-type>
<channel-type id="color">
<item-type>Color</item-type>
<label>Color</label>
<category>ColorLight</category>
</channel-type>
<channel-type id="white">
<item-type>Dimmer</item-type>
<label>White</label>
</channel-type>
<channel-type id="white2">
<item-type>Dimmer</item-type>
<label>White 2</label>
</channel-type>
<channel-type id="program" advanced="true">
<item-type>String</item-type>
<label>Program</label>
<state readOnly="false">
<options>
<option value="97">NONE</option>
<option value="37">Seven Colors Cross Fade</option>
<option value="38">Red Gradual Change</option>
<option value="39">Green Gradual Change</option>
<option value="40">Blue Gradual Change</option>
<option value="41">Yellow Gradual Change</option>
<option value="42">Cyan Gradual Change</option>
<option value="43">Purple Gradual Change</option>
<option value="44">White Gradual Change</option>
<option value="45">Red,Green Cross Fade</option>
<option value="46">Red, Blue Cross Fade</option>
<option value="47">Green, Blue Cross Fade</option>
<option value="48">Seven Colors Strobe Flash</option>
<option value="49">Red Strobe Flash</option>
<option value="50">Green Strobe Flash</option>
<option value="51">Blue Strobe Flash</option>
<option value="52">Yellow Strobe Flash</option>
<option value="53">Cyan Strobe Flash</option>
<option value="54">Purple Strobe Flash</option>
<option value="55">White Strobe Flash</option>
<option value="56">Seven Colors Jumping Change</option>
</options>
</state>
</channel-type>
<channel-type id="programSpeed" advanced="true">
<item-type>Dimmer</item-type>
<label>Program Speed</label>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.discovery;
/**
* Test app for discovering devices.
*
* @author Stefan Endrullis &lt;stefan@endrullis.de&gt;
*/
public class WiFiLEDDiscoveryServiceTestApp {
public static void main(String[] args) {
WiFiLEDDiscoveryService discoveryService = new WiFiLEDDiscoveryService();
discoveryService.startScan();
}
}

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.handler;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import java.io.IOException;
/**
* Test app for the fading driver.
*
* @author Stefan Endrullis
*/
public class WiFiLEDHandlerTestApp {
private static AbstractWiFiLEDDriver driver;
public static void main(String[] args) throws IOException, InterruptedException {
String ip = "192.168.178.91";
Integer port = AbstractWiFiLEDDriver.DEFAULT_PORT;
AbstractWiFiLEDDriver.Protocol protocol = AbstractWiFiLEDDriver.Protocol.LD686;
boolean fadingDriver = false;
System.out.println("start");
driver = fadingDriver ?
new FadingWiFiLEDDriver(ip, port, protocol, 0, 1) :
new ClassicWiFiLEDDriver(this, ip, port, protocol);
System.out.println("driver created");
driver.init();
System.out.println("driver initialized");
testStateChanges();
//testFrequentStateChanges();
System.exit(0);
}
private static void testStateChanges() throws IOException, InterruptedException {
driver.setPower(OnOffType.OFF);
System.out.println("off");
Thread.sleep(500);
driver.setPower(OnOffType.ON);
driver.setWhite(PercentType.HUNDRED);
assertState("ON,0,0,0,100");
Thread.sleep(4000);
assertState("ON,0,0,0,100");
driver.setColor(HSBType.BLUE);
assertState("ON,240,100,100,100");
Thread.sleep(4000);
assertState("ON,240,100,100,100");
driver.setWhite(PercentType.ZERO);
assertState("ON,240,100,100,0");
Thread.sleep(4000);
assertState("ON,240,100,100,0");
driver.setColor(HSBType.GREEN);
driver.setWhite(PercentType.ZERO);
System.out.println("g: " + driver.getLEDStateDTO());
Thread.sleep(4000);
System.out.println("g: " + driver.getLEDStateDTO());
driver.setColor(HSBType.RED);
driver.setWhite(PercentType.ZERO);
System.out.println("r: " + driver.getLEDStateDTO());
Thread.sleep(4000);
System.out.println("r: " + driver.getLEDStateDTO());
driver.setColor(HSBType.fromRGB(255, 32, 0));
driver.setWhite(new PercentType(14));
System.out.println("c: " + driver.getLEDStateDTO());
Thread.sleep(4000);
System.out.println("c: " + driver.getLEDStateDTO());
driver.setPower(OnOffType.OFF);
System.out.println("o: " + driver.getLEDStateDTO());
Thread.sleep(4000);
System.out.println("o: " + driver.getLEDStateDTO());
}
private static void testFrequentStateChanges() throws IOException, InterruptedException {
driver.setPower(OnOffType.ON);
driver.setWhite(PercentType.ZERO);
for (int i = 0; i < 100; i++) {
driver.setColor(HSBType.BLUE);
Thread.sleep(100);
driver.setColor(HSBType.RED);
Thread.sleep(100);
}
}
private static void assertState(String state) throws IOException {
if (!driver.getLEDStateDTO().toString().equals(state + " [0,100]")) {
//throw new RuntimeException("Expected: " + state + " [0,100]; actually: " + driver.getLEDStateDTO().toString());
}
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.wifiled.internal.handler;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
/**
* Test for LEDStateDTO
*
* @author Ries van Twisk - Prevent flashes during classic driver color + white updates
*/
public class LEDStateDTOTest {
@Test
public void RGBTest() {
for (int r = 0; r < 256 - 3; r = r + 3) {
for (int g = 0; g < 256 - 5; g = g + 5) {
for (int b = 0; b < 256 - 7; b = b + 7) {
LEDStateDTO ledState = LEDStateDTO.valueOf(0, 0, 0, r, g, b, 0, 0);
assertThat(ledState.getRGB() & 0xffffff, is(r << 16 | g << 8 | b));
}
}
}
}
@Test
public void RGBTest1() {
LEDStateDTO ledState = LEDStateDTO.valueOf(0, 0, 0, 0xff, 0xff, 0xff, 0, 0);
assertThat(ledState.getRGB() & 0xffffff, is(0xffffff));
}
@Test
public void programSpeedtest() {
for (int ps = 0; ps < 0x20; ps++) {
LEDStateDTO ledState = LEDStateDTO.valueOf(0, 0, ps, 0, 0, 0, 0, 0);
assertThat(ledState.getProgramSpeed().intValue(), is((int) Math.round(100.0 - (100.0 / 0x1f * ps))));
}
}
@Test
public void whiteTest() {
for (int wt = 0; wt < 256; wt++) {
LEDStateDTO ledState = LEDStateDTO.valueOf(0, 0, 0, 0, 0, 0, wt, wt);
assertThat((int) Math.round(ledState.getWhite().doubleValue() * 2.55), is(wt));
assertThat(255 - (int) Math.round(ledState.getWhite2().doubleValue() * 2.55), is(255 - wt));
}
}
@Test
public void programTest() {
for (int p = 0; p < 256; p++) {
LEDStateDTO ledState = LEDStateDTO.valueOf(0, p, 0, 0, 0, 0, 0, 0);
assertThat(ledState.getProgram().toString(), is(String.valueOf(p)));
}
}
}