added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.wifiled/.classpath
Normal file
32
bundles/org.openhab.binding.wifiled/.classpath
Normal 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>
|
||||
23
bundles/org.openhab.binding.wifiled/.project
Normal file
23
bundles/org.openhab.binding.wifiled/.project
Normal 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>
|
||||
13
bundles/org.openhab.binding.wifiled/NOTICE
Normal file
13
bundles/org.openhab.binding.wifiled/NOTICE
Normal 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
|
||||
91
bundles/org.openhab.binding.wifiled/README.md
Normal file
91
bundles/org.openhab.binding.wifiled/README.md
Normal 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"}
|
||||
```
|
||||
17
bundles/org.openhab.binding.wifiled/pom.xml
Normal file
17
bundles/org.openhab.binding.wifiled/pom.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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 <stefan@endrullis.de>
|
||||
*/
|
||||
public class WiFiLEDDiscoveryServiceTestApp {
|
||||
|
||||
public static void main(String[] args) {
|
||||
WiFiLEDDiscoveryService discoveryService = new WiFiLEDDiscoveryService();
|
||||
|
||||
discoveryService.startScan();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user