added migrated 2.x add-ons

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

View File

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

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.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>

View File

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

View File

@@ -0,0 +1,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" }
```

View File

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

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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.
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,216 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.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())));
}
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>