added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.feican/.classpath
Normal file
32
bundles/org.openhab.binding.feican/.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.feican/.project
Normal file
23
bundles/org.openhab.binding.feican/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.feican</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.feican/NOTICE
Normal file
13
bundles/org.openhab.binding.feican/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
|
||||
99
bundles/org.openhab.binding.feican/README.md
Normal file
99
bundles/org.openhab.binding.feican/README.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Feican Binding
|
||||
|
||||
This binding adds support for the Feican Wi-Fi version of the smart light LED Bulb, the WiFi RGBW Bulb.
|
||||
|
||||
With this binding the light bulb can be switched on or off, set the color or set color based on color temperature.
|
||||
Set the brightness. And it contains a set of preset programs, where for some the program speed can be set.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports the Feican smart smart light LED Bulb, WiFi RGBW Bulb.
|
||||
This bulb supports color, color temperature, brightness.
|
||||
It also has a number of preset programs, with static color, jumping color(s), gradient color(s) and flashing color(s).
|
||||
With the program_speed the speed of some of the programs can be set.
|
||||
|
||||
Although it has not been tested, the Feican LED strips may also be supported as it seems they can be controlled using the same app.
|
||||
|
||||
### Limitations
|
||||
|
||||
It is not possible to get the state of the bulb from the bulb itself.
|
||||
Therefore the state visible to the user only reflects what was set in openHAB and may not correspond with the actual state.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using the Feican bulb with openHAB the devices must be connected to the Wi-Fi network.
|
||||
This can be done using the Feican Android or iPhone DreamColor app.
|
||||
|
||||
## Discovery
|
||||
|
||||
Devices can be auto discovered within the local network.
|
||||
It is possible to connect to devices in a different network, but these must be added manually.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The thing has a one configuration parameter:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|------------------------------------------------------------------------- |
|
||||
| ipAddress | IP Address of the device. Mandatory. |
|
||||
|
||||
## Channels
|
||||
|
||||
The following channels are available:
|
||||
|
||||
| Channel Type ID | Item Type | Description |
|
||||
|-------------------|-----------|--------------------------------------------------------------------------------------------|
|
||||
| color | Color | This channel supports switching, brightness and adjusting the color of a light. |
|
||||
| color_temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). |
|
||||
| program | String | This channel supports setting the bulb to a static, jumping, gradient or flashing light. |
|
||||
| program_speed | Dimmer | This channel supports adjusting speed of jump, gradient or flash programs |
|
||||
|
||||
The program channel supports the following values:
|
||||
|
||||
| Value | Description |
|
||||
|-------|---------------------|
|
||||
| 1 | Static red |
|
||||
| 2 | Static blue |
|
||||
| 3 | Static green |
|
||||
| 4 | Static cyan |
|
||||
| 5 | Static yellow |
|
||||
| 6 | Static purple |
|
||||
| 7 | Static white |
|
||||
| 8 | Tricolor jump |
|
||||
| 9 | 7-color jump |
|
||||
| 10 | Tricolor gradient |
|
||||
| 11 | 7-color gradient |
|
||||
| 12 | Red gradient |
|
||||
| 13 | Green gradient |
|
||||
| 14 | Blue gradient |
|
||||
| 15 | Yellow gradient |
|
||||
| 16 | Cyan gradient |
|
||||
| 17 | Purple gradient |
|
||||
| 18 | White gradient |
|
||||
| 19 | Red-Green gradient |
|
||||
| 20 | Red-Blue gradient |
|
||||
| 21 | Green-Blue gradient |
|
||||
| 22 | 7-color flash |
|
||||
| 23 | Red flash |
|
||||
| 24 | Green flash |
|
||||
| 25 | Blue flash |
|
||||
| 26 | Yellow flash |
|
||||
| 27 | Cyan flash |
|
||||
| 28 | Purple flash |
|
||||
| 29 | White flash |
|
||||
|
||||
## Full Example
|
||||
|
||||
**feican.things:**
|
||||
|
||||
```java
|
||||
feican:bulb:home "Living Room" [ ipAddress="192.168.0.13" ]
|
||||
```
|
||||
|
||||
**feican.items:**
|
||||
|
||||
```java
|
||||
Switch FC_1_Switch "Switch" { channel="feican:bulb:home:color" }
|
||||
Color FC_1_Color "Color" <slider> { channel="feican:bulb:home:color" }
|
||||
Dimmer FC_1_Dimmer "Brightness [%d]" <slider> { channel="feican:bulb:home:color" }
|
||||
```
|
||||
17
bundles/org.openhab.binding.feican/pom.xml
Normal file
17
bundles/org.openhab.binding.feican/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.feican</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Feican Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.feican-${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-feican" description="Feican Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.feican/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
|
||||
/**
|
||||
* Creates commands to send to Feican devices.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Commands {
|
||||
private static final byte[] DISCOVER_COMMAND = { 126, 7, 9, -128, -128, -128, -128, -128, -17 };
|
||||
private static final byte[] ON_COMMAND = { 126, 4, 4, 1, 0, -128, -128, 0, -17 };
|
||||
private static final byte[] OFF_COMMAND = { 126, 4, 4, 0, 0, -128, -128, 0, -17 };
|
||||
private static final byte[] RGB_COMMAND = { 126, 7, 5, 3, 0, 0, 0, 0, -17 };
|
||||
private static final byte[] COLOR_TEMPERATURE_COMMAND = { 126, 6, 5, 2, 0, 0, -128, 8, -17 };
|
||||
private static final byte[] BRIGHTNESS_COMMAND = { 126, 4, 1, 0, -128, -128, -128, 0, -17 };
|
||||
private static final byte[] PROGRAM_COMMAND = { 126, 5, 3, 0, 3, -128, -128, 0, -17 };
|
||||
private static final byte[] PROGRAM_SPEED_COMMAND = { 126, 4, 2, 0, -128, -128, -128, 0, -17 };
|
||||
|
||||
/**
|
||||
* Returns the command to discover devices.
|
||||
*
|
||||
* @return discover command
|
||||
*/
|
||||
public static byte[] discover() {
|
||||
return DISCOVER_COMMAND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to switch a device on or off depending on the given parameter.
|
||||
*
|
||||
* @param onOff command to be on or off command
|
||||
* @return the on/off command
|
||||
*/
|
||||
public byte[] switchOnOff(OnOffType onOff) {
|
||||
if (onOff == OnOffType.ON) {
|
||||
return ON_COMMAND;
|
||||
} else {
|
||||
return OFF_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to set the color.
|
||||
*
|
||||
* @param color the color to set
|
||||
* @return the color command
|
||||
*/
|
||||
public byte[] color(HSBType color) {
|
||||
byte[] command = RGB_COMMAND.clone();
|
||||
PercentType[] rgb = color.toRGB();
|
||||
command[4] = convertColorPercentToByte(rgb[0]);
|
||||
command[5] = convertColorPercentToByte(rgb[1]);
|
||||
command[6] = convertColorPercentToByte(rgb[2]);
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a percentage (0-100) to a color range value (0-255). This is needed because {@link HSBType} returns
|
||||
* color values in the 100-range while a 255 range is needed.
|
||||
*
|
||||
* @param percent value to be converted.
|
||||
* @return converted value as a byte value
|
||||
*/
|
||||
private byte convertColorPercentToByte(PercentType percent) {
|
||||
return percent.toBigDecimal().multiply(BigDecimal.valueOf(255))
|
||||
.divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).byteValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to set the color temperature to a value between 0 and 100.
|
||||
*
|
||||
* @param percentage the color temperature to set
|
||||
* @return the color temperature command
|
||||
*/
|
||||
public byte[] colorTemperature(PercentType percentage) {
|
||||
byte[] command = COLOR_TEMPERATURE_COMMAND.clone();
|
||||
command[4] = percentage.byteValue();
|
||||
command[5] = (byte) (100 - percentage.intValue());
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to set the brightness on a bulb running in color, color temperature or program.
|
||||
*
|
||||
* @param percentage the brightness to set
|
||||
* @return the brightness command
|
||||
*/
|
||||
public byte[] brightness(PercentType percentage) {
|
||||
byte[] command = BRIGHTNESS_COMMAND.clone();
|
||||
command[3] = percentage.byteValue();
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to set a preset program. Program codes on the device start with 0x80 and up. Binding maps
|
||||
* this with values starting with 1.
|
||||
*
|
||||
* @param program this binding value representing the preset program
|
||||
* @return the command to set the program
|
||||
*/
|
||||
public byte[] program(int program) {
|
||||
byte[] command = PROGRAM_COMMAND.clone();
|
||||
command[3] = (byte) (-129 + program);
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command to set program speed to a value between 0 and 100.
|
||||
*
|
||||
* @param percentage the program speed to set
|
||||
* @return the program speed command
|
||||
*/
|
||||
public byte[] programSpeed(PercentType percentage) {
|
||||
byte[] command = PROGRAM_SPEED_COMMAND.clone();
|
||||
command[3] = percentage.byteValue();
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Manages the connection to a Feican bulb.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Connection implements Closeable {
|
||||
|
||||
/**
|
||||
* UDP port to send command.
|
||||
*/
|
||||
public static final int FEICAN_SEND_PORT = 5000;
|
||||
/**
|
||||
* UDP port devices send discover replies back.
|
||||
*/
|
||||
public static final int FEICAN_RECEIVE_PORT = 6000;
|
||||
|
||||
private final InetAddress iNetAddress;
|
||||
private final DatagramSocket socket;
|
||||
|
||||
/**
|
||||
* Initializes a connection to the given IP address.
|
||||
*
|
||||
* @param ipAddress IP address of the connection
|
||||
* @throws UnknownHostException if ipAddress could not be resolved.
|
||||
* @throws SocketException if no Datagram socket connection could be made.
|
||||
*/
|
||||
public Connection(String ipAddress) throws SocketException, UnknownHostException {
|
||||
iNetAddress = InetAddress.getByName(ipAddress);
|
||||
socket = new DatagramSocket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the 9 bytes command to the Feican device.
|
||||
*
|
||||
* @param command the 9 bytes command
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
public void sendCommand(byte[] command) throws IOException {
|
||||
DatagramPacket sendPkt = new DatagramPacket(command, command.length, iNetAddress, FEICAN_SEND_PORT);
|
||||
socket.send(sendPkt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link FeicanBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class FeicanBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "feican";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_BULB = new ThingTypeUID(BINDING_ID, "bulb");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_BULB);
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_COLOR = "color";
|
||||
public static final String CHANNEL_COLOR_TEMPERATURE = "color_temperature";
|
||||
public static final String CHANNEL_PROGRAM = "program";
|
||||
public static final String CHANNEL_PROGRAM_SPEED = "program_speed";
|
||||
|
||||
// List of al property ids
|
||||
public static final String CONFIG_IP = "ipAddress";
|
||||
public static final String PROPERTY_MAC = "mac";
|
||||
|
||||
private FeicanBindingConstants() {
|
||||
// Constants class.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
/**
|
||||
* Feican configuration class.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
public class FeicanConfiguration {
|
||||
/**
|
||||
* IP Address of the device.
|
||||
*/
|
||||
public String ipAddress;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
import static org.openhab.binding.feican.internal.FeicanBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Discovery service for Feican Bulbs. When sending a discovery UDP broadcast command on port 5000 to a Feican bulb. The
|
||||
* bulp will respond with its mac address send via UDP broadcast over port 6000.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.feican")
|
||||
public class FeicanDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private static final int DISCOVERY_TIMEOUT_SECONDS = 5;
|
||||
private static final int RECEIVE_JOB_TIMEOUT = 20000;
|
||||
private static final int UDP_PACKET_TIMEOUT = RECEIVE_JOB_TIMEOUT - 50;
|
||||
private static final String FEICAN_NAME_PREFIX = "FC_";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FeicanDiscoveryService.class);
|
||||
|
||||
///// Network
|
||||
private final byte[] buffer = new byte[32];
|
||||
@Nullable
|
||||
private DatagramSocket discoverSocket;
|
||||
|
||||
public FeicanDiscoveryService() {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Start scan for Feican devices.");
|
||||
discoverThings();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
logger.debug("Stop scan for Feican devices.");
|
||||
closeDiscoverSocket();
|
||||
super.stopScan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual discovery of Feican devices (things).
|
||||
*/
|
||||
private void discoverThings() {
|
||||
try {
|
||||
final DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
|
||||
// No need to call close first, because the caller of this method already has done it.
|
||||
startDiscoverSocket();
|
||||
// Runs until the socket call gets a time out and throws an exception. When a time out is triggered it means
|
||||
// no data was present and nothing new to discover.
|
||||
while (true) {
|
||||
// Set packet length in case a previous call reduced the size.
|
||||
receivePacket.setLength(buffer.length);
|
||||
if (discoverSocket == null) {
|
||||
break;
|
||||
} else {
|
||||
discoverSocket.receive(receivePacket);
|
||||
}
|
||||
logger.debug("Feican device discovery returned package with length {}", receivePacket.getLength());
|
||||
if (receivePacket.getLength() > 0) {
|
||||
thingDiscovered(receivePacket);
|
||||
}
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
logger.debug("Discovering poller timeout...");
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error during discovery: {}", e.getMessage());
|
||||
} finally {
|
||||
closeDiscoverSocket();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a {@link DatagramSocket} and sends a packet for discovery of Feican devices.
|
||||
*
|
||||
* @throws SocketException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void startDiscoverSocket() throws SocketException, IOException {
|
||||
discoverSocket = new DatagramSocket(new InetSocketAddress(Connection.FEICAN_RECEIVE_PORT));
|
||||
discoverSocket.setBroadcast(true);
|
||||
discoverSocket.setSoTimeout(UDP_PACKET_TIMEOUT);
|
||||
final InetAddress broadcast = InetAddress.getByName("255.255.255.255");
|
||||
final DatagramPacket discoverPacket = new DatagramPacket(Commands.discover(), Commands.discover().length,
|
||||
broadcast, Connection.FEICAN_SEND_PORT);
|
||||
discoverSocket.send(discoverPacket);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Discovery package sent: {}", new String(discoverPacket.getData(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the discovery socket and cleans the value. No need for synchronization as this method is called from a
|
||||
* synchronized context.
|
||||
*/
|
||||
private void closeDiscoverSocket() {
|
||||
if (discoverSocket != null) {
|
||||
discoverSocket.close();
|
||||
discoverSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a device (thing) with the discovered properties.
|
||||
*
|
||||
* @param packet containing data of detected device
|
||||
*/
|
||||
private void thingDiscovered(DatagramPacket packet) {
|
||||
final String ipAddress = packet.getAddress().getHostAddress();
|
||||
if (packet.getData().length < 12) {
|
||||
logger.debug(
|
||||
"Feican device was detected, but the retrieved data is incomplete: '{}'. Device not registered",
|
||||
new String(packet.getData(), 0, packet.getLength() - 1, StandardCharsets.UTF_8));
|
||||
} else {
|
||||
String thingName = createThingName(packet.getData());
|
||||
ThingUID thingUID = new ThingUID(THING_TYPE_BULB, thingName.toLowerCase());
|
||||
thingDiscovered(DiscoveryResultBuilder.create(thingUID).withLabel(thingName)
|
||||
.withProperties(collectProperties(ipAddress, stringToMac(packet.getData(), packet.getLength())))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a name for the Feican device. The name is derived from the mac address (last 4 bytes) and prefixed with
|
||||
* FC_. This matches the wifi host it starts when not yet configured.
|
||||
*
|
||||
* @param byteMac mac address in bytes
|
||||
* @return the name for the device
|
||||
*/
|
||||
private String createThingName(final byte[] byteMac) {
|
||||
return FEICAN_NAME_PREFIX + new String(byteMac, 8, 4, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte representation of a mac address to a real mac address.
|
||||
*
|
||||
* @param stringMac byte representation of a mac address
|
||||
* @return real mac address
|
||||
*/
|
||||
private String stringToMac(byte[] data, int length) {
|
||||
return new String(data, 0, length - 1, StandardCharsets.UTF_8).replaceAll("(..)(?!$)", "$1:");
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects properties into a map.
|
||||
*
|
||||
* @param ipAddress IP address of the thing
|
||||
* @param mac mac address of the thing
|
||||
* @return map with properties
|
||||
*/
|
||||
private Map<String, Object> collectProperties(String ipAddress, String mac) {
|
||||
final Map<String, Object> properties = new TreeMap<>();
|
||||
properties.put(CONFIG_IP, ipAddress);
|
||||
properties.put(PROPERTY_MAC, mac);
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.feican.internal;
|
||||
|
||||
import static org.openhab.binding.feican.internal.FeicanBindingConstants.*;
|
||||
|
||||
import org.openhab.binding.feican.internal.handler.FeicanHandler;
|
||||
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 FeicanHandlerFactory} is responsible for creating things and thing handlers.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.feican")
|
||||
public class FeicanHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
@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_BULB)) {
|
||||
return new FeicanHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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.feican.internal.handler;
|
||||
|
||||
import static org.openhab.binding.feican.internal.FeicanBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.feican.internal.Commands;
|
||||
import org.openhab.binding.feican.internal.Connection;
|
||||
import org.openhab.binding.feican.internal.FeicanConfiguration;
|
||||
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;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link FeicanHandler} is responsible for handling commands, which are sent to one of the channels.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FeicanHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FeicanHandler.class);
|
||||
private final Commands commands = new Commands();
|
||||
|
||||
private @NonNullByDefault({}) Connection connection;
|
||||
|
||||
/**
|
||||
* Creates a new handler for the Feican thing.
|
||||
*
|
||||
* @param thing The thing to create the handler for
|
||||
*/
|
||||
public FeicanHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
try {
|
||||
if (command instanceof OnOffType) {
|
||||
handleOnOff((OnOffType) command);
|
||||
} else if (command instanceof HSBType) {
|
||||
handleColor(channelUID, (HSBType) command);
|
||||
} else if (command instanceof PercentType) {
|
||||
handlePercentage(channelUID, (PercentType) command);
|
||||
} else if (command instanceof StringType) {
|
||||
handleString(channelUID, (StringType) command);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
final FeicanConfiguration config = getConfigAs(FeicanConfiguration.class);
|
||||
|
||||
logger.debug("Initializing Feican Wifi RGWB Bulb on IP address {}", config.ipAddress);
|
||||
try {
|
||||
connection = new Connection(config.ipAddress);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (UnknownHostException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||
} catch (SocketException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle for {@link OnOffType} commands.
|
||||
*
|
||||
* @param onOff value to set: on or off
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleOnOff(OnOffType onOff) throws IOException {
|
||||
connection.sendCommand(commands.switchOnOff(onOff));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle for {@link DecimalType} as an {@link OnOffType} command.
|
||||
*
|
||||
* @param value value to derive on or off state from
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleOnOff(DecimalType value) throws IOException {
|
||||
handleOnOff(DecimalType.ZERO.equals(value) ? OnOffType.OFF : OnOffType.ON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle setting the color.
|
||||
*
|
||||
* The Feican bulb has a separate command for the brightness. This brightness value is applied to any color value
|
||||
* send. Because in the color command the brightness is also calculated this would mean the brightness will be
|
||||
* applied twice; first when getting the RGB values and second in the bulb itself when passing a color value a
|
||||
* earlier set brightness value is applied. To work around this, the brightness value is first send to the bulb and
|
||||
* the color is send with a 100% brightness. So the brightness is controlled by the bulb. This is also needed for 2
|
||||
* reasons. First color temperature also works with brightness and thus to set it with color temperature the
|
||||
* brightness must be set on the bulb. Secondly when setting brightness in the color widget only the brightness
|
||||
* value is passed and no color value is available, therefore this binding then sets the brightness on the bulb.
|
||||
*
|
||||
* @param channelUID Channel the command is for
|
||||
* @param command color to set
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleColor(ChannelUID channelUID, HSBType command) throws IOException {
|
||||
if (CHANNEL_COLOR.equals(channelUID.getId())) {
|
||||
handleBrightness(command.getBrightness());
|
||||
connection.sendCommand(
|
||||
commands.color(new HSBType(command.getHue(), command.getSaturation(), PercentType.HUNDRED)));
|
||||
handleOnOff(command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle percentType commands. Action depends on what channel send the command. For brightness related channels
|
||||
* after the brightness command an extra onOff command is send to update the onOff state conform the brightness
|
||||
* state.
|
||||
*
|
||||
* @param channelUID Channel the command is for
|
||||
* @param command The percentType command
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handlePercentage(ChannelUID channelUID, PercentType command) throws IOException {
|
||||
String id = channelUID.getId();
|
||||
|
||||
switch (id) {
|
||||
case CHANNEL_COLOR:
|
||||
handleBrightness(command);
|
||||
handleOnOff(command);
|
||||
break;
|
||||
case CHANNEL_COLOR_TEMPERATURE:
|
||||
handleColorTemperature(command);
|
||||
handleOnOff(OnOffType.ON);
|
||||
break;
|
||||
case CHANNEL_PROGRAM_SPEED:
|
||||
handleProgramSpeed(command);
|
||||
handleOnOff(OnOffType.ON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the brightness command.
|
||||
*
|
||||
* @param command percentage of brightness to set
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleBrightness(PercentType command) throws IOException {
|
||||
connection.sendCommand(commands.brightness(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the color temperature command.
|
||||
*
|
||||
* @param command color temperature as set a value between 0 and 100
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleColorTemperature(PercentType command) throws IOException {
|
||||
connection.sendCommand(commands.colorTemperature(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the speed of a preset program.
|
||||
*
|
||||
* @param command the speed as set as a percentage value
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleProgramSpeed(PercentType command) throws IOException {
|
||||
connection.sendCommand(commands.programSpeed(command));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles setting a preset program.
|
||||
*
|
||||
* @param channelUID works for the program channel
|
||||
* @param command String value as id of the program to set
|
||||
* @throws IOException Connection to the bulb failed
|
||||
*/
|
||||
private void handleString(ChannelUID channelUID, StringType command) throws NumberFormatException, IOException {
|
||||
if (CHANNEL_PROGRAM.equals(channelUID.getId())) {
|
||||
connection.sendCommand(commands.program(Integer.valueOf(command.toFullString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="feican" 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>Feican Binding</name>
|
||||
<description>This is the binding for Feican light bulbs.</description>
|
||||
<author>Hilbrand Bouwkamp</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="feican"
|
||||
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">
|
||||
|
||||
<channel-type id="color">
|
||||
<item-type>Color</item-type>
|
||||
<label>Color</label>
|
||||
<description>This channel supports adjusting the color of a light.
|
||||
</description>
|
||||
<category>ColorLight</category>
|
||||
<tags>
|
||||
<tag>Lighting</tag>
|
||||
</tags>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="color_temperature">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Color Temperature</label>
|
||||
<description>This channel supports adjusting the color temperature from cold (0%) to warm (100%).</description>
|
||||
<category>ColorLight</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="program" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Program</label>
|
||||
<description>This channel supports setting the bulb to a static, jumping, gradient or flashing light.</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="1">Static red</option>
|
||||
<option value="2">Static blue</option>
|
||||
<option value="3">Static green</option>
|
||||
<option value="4">Static cyan</option>
|
||||
<option value="5">Static yellow</option>
|
||||
<option value="6">Static purple</option>
|
||||
<option value="7">Static white</option>
|
||||
<option value="8">Tricolor jump</option>
|
||||
<option value="9">7-color jump</option>
|
||||
<option value="10">Tricolor gradient</option>
|
||||
<option value="11">7-color gradient</option>
|
||||
<option value="12">Red gradient</option>
|
||||
<option value="13">Green gradient</option>
|
||||
<option value="14">Blue gradient</option>
|
||||
<option value="15">Yellow gradient</option>
|
||||
<option value="16">Cyan gradient</option>
|
||||
<option value="17">Purple gradient</option>
|
||||
<option value="18">White gradient</option>
|
||||
<option value="19">Red-Green gradient</option>
|
||||
<option value="20">Red-Blue gradient</option>
|
||||
<option value="21">Green-Blue gradient</option>
|
||||
<option value="22">7-color flash</option>
|
||||
<option value="23">Red flash</option>
|
||||
<option value="24">Green flash</option>
|
||||
<option value="25">Blue flash</option>
|
||||
<option value="26">Yellow flash</option>
|
||||
<option value="27">Cyan flash</option>
|
||||
<option value="28">Purple flash</option>
|
||||
<option value="29">White flash</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="program_speed" advanced="true">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Program Speed</label>
|
||||
<description>Speed of flash and gradient programs from 0 (slow) to 100 (fast).</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="feican"
|
||||
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="bulb">
|
||||
<label>Wifi RGBW Bulb</label>
|
||||
<description>A dimmable light with changeable colors.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="color" typeId="color"/>
|
||||
<channel id="color_temperature" typeId="color_temperature"/>
|
||||
<channel id="program" typeId="program"/>
|
||||
<channel id="program_speed" typeId="program_speed"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>IP Address</label>
|
||||
<description>IP Address of the device.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user