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.yeelight</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,75 @@
# Yeelight Binding
This binding integrates the [Yeelight Lighting Product](https://www.yeelight.com/).
## Supported Things
- [Yeelight LED White Bulb](https://www.yeelight.com/zh_CN/product/wifi-led-w) (Thing type `dolphin`)
- [Yeelight LED Color Bulb](https://www.yeelight.com/zh_CN/product/wifi-led-c) (Thing type `wonder`)
- [Yeelight LED Color Stripe](https://www.yeelight.com/zh_CN/product/pitaya) (Thing type `stripe`)
- [Yeelight LED Ceiling Light](https://www.yeelight.com/en_US/product/luna) (Thing type `ceiling`)
## Preconditions
To control Yeelight devices with this binding, you need to connect the device to your local network at first with the Yeelight app.
This app is available in the iOS AppStore and on Google Play.
Then you need to activate LAN control mode by enable Developer Mode in device settings, after which it will become discoverable by openHAB.
## Discovery
Yeelight smart LED devices announce themselves on the network through UPnP, so that they can be discovered.
## Thing Configuration
All Yeelight things require the `deviceId` from the device as a configuration parameter. This table shows all available parameters:
| Parameter | Values | Mandatory |
|---------------------|------------------------------------------------|-----------|
| deviceId | ID of the Yeelight device | Yes |
| duration | Duration for changing between different states | No |
## Channels
All devices support some of the following channels:
| Channel | Item Type | Description |
|--------|------|------|
|`brightness` | `Dimmer` | This channel supports adjusting the brightness value, it is available on `dolphin` and `ceiling`.|
|`color` | `Color` | This channel supports color control, it is available on `wonder` and `stripe`.|
|`colorTemperature` | `Dimmer` | This channel supports adjusting the color temperature, it is available on `wonder` and `stripe` and `ceiling`.|
|`command` | `String` | This channel sends a command directly to the device, it is available on all Yeelight Things.|
|`backgroundColor` | `Color` or `Dimmer` | This channel supports color control for the ambient light, it is available on `ceiling4`.|
|`nightlight` | `Switch` | This supports switching to nightlight mode, it is available on `ceiling1` or `ceiling4`.|
## Full Example
Things:
```
Thing yeelight:stripe:1 [ deviceId="0x000000000321a1bc", duration=1000 ]
```
Items:
```
Color YeelightLEDColor { channel="yeelight:stripe:1:color" }
Switch YeelightLEDSwitch { channel="yeelight:stripe:1:color" }
String YeelightLEDCommand { channel="yeelight:stripe:1:command" }
```
Rules:
```
rule "Yeelight Custom Command"
when
Time is noon
then
YeelightLEDCommand.sendCommand("set_power;\"on\",\"smooth\",2000")
end
```
Note that `set_power` is the command, then a separator `;` must be used. `\"on\",\"smooth\",2000` are the parameters.
Full list of commands can be found [here](https://www.yeelight.com/download/Yeelight_Inter-Operation_Spec.pdf).

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 coasterli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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.yeelight</artifactId>
<name>openHAB Add-ons :: Bundles :: Yeelight Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.yeelight-${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-yeelight" description="Yeelight Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.yeelight/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link YeelightBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Coaster Li - Initial contribution
* @author Joe Ho - Added Duration / Added command channel
*/
public class YeelightBindingConstants {
public static final String BINDING_ID = "yeelight";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_CEILING = new ThingTypeUID(BINDING_ID, "ceiling");
public static final ThingTypeUID THING_TYPE_CEILING1 = new ThingTypeUID(BINDING_ID, "ceiling1");
public static final ThingTypeUID THING_TYPE_CEILING3 = new ThingTypeUID(BINDING_ID, "ceiling3");
public static final ThingTypeUID THING_TYPE_CEILING4 = new ThingTypeUID(BINDING_ID, "ceiling4");
public static final ThingTypeUID THING_TYPE_DOLPHIN = new ThingTypeUID(BINDING_ID, "dolphin");
public static final ThingTypeUID THING_TYPE_CTBULB = new ThingTypeUID(BINDING_ID, "ct_bulb");
public static final ThingTypeUID THING_TYPE_WONDER = new ThingTypeUID(BINDING_ID, "wonder");
public static final ThingTypeUID THING_TYPE_STRIPE = new ThingTypeUID(BINDING_ID, "stripe");
public static final ThingTypeUID THING_TYPE_DESKLAMP = new ThingTypeUID(BINDING_ID, "desklamp");
// List of thing Parameters names
public static final String PARAMETER_DEVICE_ID = "deviceId";
public static final String PARAMETER_DURATION = "duration";
// List of all Channel ids
public static final String CHANNEL_BRIGHTNESS = "brightness";
public static final String CHANNEL_COLOR = "color";
public static final String CHANNEL_COLOR_TEMPERATURE = "colorTemperature";
public static final String CHANNEL_COMMAND = "command";
public static final String CHANNEL_BACKGROUND_COLOR = "backgroundColor";
public static final String CHANNEL_NIGHTLIGHT = "nightlight";
// Constants used
public static final int COLOR_TEMPERATURE_MINIMUM = 1700;
public static final int COLOR_TEMPERATURE_STEP = 48;
}

View File

@@ -0,0 +1,76 @@
/**
* 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.yeelight.internal;
import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.*;
import java.util.HashSet;
import java.util.Set;
import org.openhab.binding.yeelight.internal.handler.*;
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 YeelightHandlerFactory} is responsible for returning supported things and handlers for the devices.
*
* @author Coaster Li - Initial contribution
* @author Nikita Pogudalov - Added YeelightCeilingWithNightHandler for Ceiling 1
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.yeelight")
public class YeelightHandlerFactory extends BaseThingHandlerFactory {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
static {
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_CEILING);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_CEILING1);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_CEILING3);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_CEILING4);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_DOLPHIN);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_CTBULB);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_WONDER);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_STRIPE);
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_DESKLAMP);
}
@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_DOLPHIN) || thingTypeUID.equals(THING_TYPE_CTBULB)) {
return new YeelightWhiteHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_WONDER)) {
return new YeelightColorHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_STRIPE)) {
return new YeelightStripeHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_CEILING) || thingTypeUID.equals(THING_TYPE_CEILING3)
|| thingTypeUID.equals(THING_TYPE_DESKLAMP)) {
return new YeelightCeilingHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_CEILING1)) {
return new YeelightCeilingWithNightHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_CEILING4)) {
return new YeelightCeilingWithAmbientHandler(thing);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,133 @@
/**
* 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.yeelight.internal.discovery;
import org.openhab.binding.yeelight.internal.YeelightBindingConstants;
import org.openhab.binding.yeelight.internal.YeelightHandlerFactory;
import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceListener;
import org.openhab.binding.yeelight.internal.lib.services.DeviceManager;
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 YeelightDiscoveryService} is responsible for search and discovery of new devices.
*
* @author Coaster Li - Initial contribution
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.yeelight")
public class YeelightDiscoveryService extends AbstractDiscoveryService implements DeviceListener {
private final Logger logger = LoggerFactory.getLogger(YeelightDiscoveryService.class.getSimpleName());
public YeelightDiscoveryService() {
super(YeelightHandlerFactory.SUPPORTED_THING_TYPES_UIDS, 2, false);
}
@Override
protected void startScan() {
logger.debug("Starting Scan");
DeviceManager.getInstance().registerDeviceListener(this);
DeviceManager.getInstance().startDiscovery();
}
@Override
protected synchronized void stopScan() {
logger.debug(": stopScan");
DeviceManager.getInstance().stopDiscovery();
DeviceManager.getInstance().unregisterDeviceListener(this);
}
@Override
public void onDeviceFound(DeviceBase device) {
logger.info("onDeviceFound, id: {}", device.getDeviceId());
ThingUID thingUID = getThingUID(device);
if (thingUID == null) {
// We don't know about this thing type
logger.info("Skipping device {}, unknown type.", device.getDeviceId());
return;
}
ThingTypeUID thingTypeUID = getThingTypeUID(device);
String deviceName = device.getDeviceName().isEmpty() ? DeviceManager.getDefaultName(device)
: device.getDeviceName();
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
.withLabel(deviceName).withProperties(device.getBulbInfo())
.withProperty(YeelightBindingConstants.PARAMETER_DEVICE_ID, device.getDeviceId()).build();
thingDiscovered(discoveryResult);
}
@Override
public void onDeviceLost(DeviceBase device) {
logger.debug("onDeviceLost, id: {}", device.getDeviceId());
}
private ThingUID getThingUID(DeviceBase device) {
switch (device.getDeviceType()) {
case ceiling:
case ceiling3:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING, device.getDeviceId());
case ceiling1:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING1, device.getDeviceId());
case ceiling4:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING4, device.getDeviceId());
case color:
return new ThingUID(YeelightBindingConstants.THING_TYPE_WONDER, device.getDeviceId());
case mono:
return new ThingUID(YeelightBindingConstants.THING_TYPE_DOLPHIN, device.getDeviceId());
case ct_bulb:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CTBULB, device.getDeviceId());
case stripe:
return new ThingUID(YeelightBindingConstants.THING_TYPE_STRIPE, device.getDeviceId());
case desklamp:
return new ThingUID(YeelightBindingConstants.THING_TYPE_DESKLAMP, device.getDeviceId());
default:
return null;
}
}
private ThingTypeUID getThingTypeUID(DeviceBase device) {
switch (device.getDeviceType()) {
case ceiling:
return YeelightBindingConstants.THING_TYPE_CEILING;
case ceiling1:
return YeelightBindingConstants.THING_TYPE_CEILING1;
case ceiling3:
return YeelightBindingConstants.THING_TYPE_CEILING3;
case ceiling4:
return YeelightBindingConstants.THING_TYPE_CEILING4;
case color:
return YeelightBindingConstants.THING_TYPE_WONDER;
case mono:
return YeelightBindingConstants.THING_TYPE_DOLPHIN;
case ct_bulb:
return YeelightBindingConstants.THING_TYPE_CTBULB;
case stripe:
return YeelightBindingConstants.THING_TYPE_STRIPE;
case desklamp:
return YeelightBindingConstants.THING_TYPE_DESKLAMP;
default:
return null;
}
}
}

View File

@@ -0,0 +1,52 @@
/**
* 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.yeelight.internal.handler;
import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.*;
import org.openhab.binding.yeelight.internal.YeelightBindingConstants;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link YeelightCeilingHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Coaster Li - Initial contribution
*/
public class YeelightCeilingHandler extends YeelightHandlerBase {
public YeelightCeilingHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
handleCommandHelper(channelUID, command, "Handle Ceiling Command");
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
if (status.isPowerOff()) {
updateState(YeelightBindingConstants.CHANNEL_BRIGHTNESS, PercentType.ZERO);
} else {
updateState(YeelightBindingConstants.CHANNEL_BRIGHTNESS, new PercentType(status.getBrightness()));
updateState(YeelightBindingConstants.CHANNEL_COLOR_TEMPERATURE,
new PercentType((status.getCt() - COLOR_TEMPERATURE_MINIMUM) / COLOR_TEMPERATURE_STEP));
}
}
}

View File

@@ -0,0 +1,59 @@
/**
* 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.yeelight.internal.handler;
import org.openhab.binding.yeelight.internal.YeelightBindingConstants;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
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.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link YeelightCeilingWithAmbientHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Viktor Koop - Initial contribution
*/
public class YeelightCeilingWithAmbientHandler extends YeelightCeilingHandler {
public YeelightCeilingWithAmbientHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
handleCommandHelper(channelUID, command, "Handle ceiling ambient light command");
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
if (status.isBackgroundIsPowerOff()) {
updateState(YeelightBindingConstants.CHANNEL_BACKGROUND_COLOR, PercentType.ZERO);
} else {
final HSBType hsbType = new HSBType(new DecimalType(status.getBackgroundHue()),
new PercentType(status.getBackgroundSat()), new PercentType(status.getBackgroundBrightness()));
updateState(YeelightBindingConstants.CHANNEL_BACKGROUND_COLOR, hsbType);
}
updateState(YeelightBindingConstants.CHANNEL_NIGHTLIGHT,
(status.getActiveMode() == ActiveMode.MOONLIGHT_MODE) ? OnOffType.ON : OnOffType.OFF);
}
}

View File

@@ -0,0 +1,40 @@
/**
* 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.yeelight.internal.handler;
import org.openhab.binding.yeelight.internal.YeelightBindingConstants;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Thing;
/**
* The {@link YeelightCeilingWithNightHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Nikita Pogudalov - Initial contribution
*/
public class YeelightCeilingWithNightHandler extends YeelightCeilingHandler {
public YeelightCeilingWithNightHandler(Thing thing) {
super(thing);
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
updateState(YeelightBindingConstants.CHANNEL_NIGHTLIGHT,
(status.getActiveMode() == ActiveMode.MOONLIGHT_MODE) ? OnOffType.ON : OnOffType.OFF);
}
}

View File

@@ -0,0 +1,42 @@
/**
* 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.yeelight.internal.handler;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link YeelightColorHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Coaster Li - Initial contribution
*/
public class YeelightColorHandler extends YeelightHandlerBase {
public YeelightColorHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
handleCommandHelper(channelUID, command, "Handle Color Command");
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
updateBrightnessAndColorUI(status);
}
}

View File

@@ -0,0 +1,346 @@
/**
* 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.yeelight.internal.handler;
import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.*;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.yeelight.internal.lib.device.ConnectState;
import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
import org.openhab.binding.yeelight.internal.lib.device.DeviceFactory;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceAction;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceConnectionStateListener;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceStatusChangeListener;
import org.openhab.binding.yeelight.internal.lib.services.DeviceManager;
import org.openhab.core.library.types.*;
import org.openhab.core.thing.*;
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 YeelightHandlerBase} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Coaster Li - Initial contribution
* @author Joe Ho - Added Duration Thing parameter
* @author Nikita Pogudalov - Added DeviceType for ceiling 1
*/
public abstract class YeelightHandlerBase extends BaseThingHandler
implements DeviceConnectionStateListener, DeviceStatusChangeListener {
private final Logger logger = LoggerFactory.getLogger(YeelightHandlerBase.class);
protected DeviceBase mDevice;
// Reading the deviceId from the properties map.
private String deviceId = getThing().getConfiguration().get(PARAMETER_DEVICE_ID).toString();
public YeelightHandlerBase(Thing thing) {
super(thing);
}
protected void updateUI(DeviceStatus status) {
}
@Override
public void initialize() {
logger.debug("Initializing, Device ID: {}", deviceId);
mDevice = DeviceFactory.build(getDeviceModel(getThing().getThingTypeUID()).name(), deviceId);
mDevice.setDeviceName(getThing().getLabel());
mDevice.setAutoConnect(true);
DeviceManager.getInstance().addDevice(mDevice);
mDevice.registerConnectStateListener(this);
mDevice.registerStatusChangedListener(this);
updateStatusHelper(mDevice.getConnectionState());
DeviceManager.getInstance().startDiscovery(15 * 1000);
}
private DeviceType getDeviceModel(ThingTypeUID typeUID) {
if (typeUID.equals(THING_TYPE_CEILING)) {
return DeviceType.ceiling;
} else if (typeUID.equals(THING_TYPE_CEILING1)) {
return DeviceType.ceiling1;
} else if (typeUID.equals(THING_TYPE_CEILING3)) {
return DeviceType.ceiling3;
} else if (typeUID.equals(THING_TYPE_CEILING4)) {
return DeviceType.ceiling4;
} else if (typeUID.equals(THING_TYPE_WONDER)) {
return DeviceType.color;
} else if (typeUID.equals(THING_TYPE_DOLPHIN)) {
return DeviceType.mono;
} else if (typeUID.equals(THING_TYPE_CTBULB)) {
return DeviceType.ct_bulb;
} else if (typeUID.equals(THING_TYPE_STRIPE)) {
return DeviceType.stripe;
} else if (typeUID.equals(THING_TYPE_DESKLAMP)) {
return DeviceType.desklamp;
} else {
return null;
}
}
@Override
public void onConnectionStateChanged(ConnectState connectState) {
logger.debug("onConnectionStateChanged -> {}", connectState.name());
updateStatusHelper(connectState);
}
public void updateStatusHelper(ConnectState connectState) {
switch (connectState) {
case DISCONNECTED:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "Device is offline!");
if (mDevice.isAutoConnect()) {
DeviceManager.sInstance.startDiscovery(5 * 1000);
logger.debug("Thing OFFLINE. Initiated discovery");
}
break;
case CONNECTED:
updateStatus(ThingStatus.ONLINE);
mDevice.queryStatus();
break;
default:
updateStatus(ThingStatus.UNKNOWN);
break;
}
}
@Override
public void channelLinked(ChannelUID channelUID) {
logger.debug("ChannelLinked -> {}", channelUID.getId());
super.channelLinked(channelUID);
Runnable task = () -> {
mDevice.queryStatus();
};
scheduler.schedule(task, 500, TimeUnit.MILLISECONDS);
}
public void handleCommandHelper(ChannelUID channelUID, Command command, String logInfo) {
logger.debug("{}: {}", logInfo, command);
// If device is disconnected, start discovery to reconnect.
if (mDevice.isAutoConnect() && mDevice.getConnectionState() != ConnectState.CONNECTED) {
DeviceManager.getInstance().startDiscovery(5 * 1000);
}
if (command instanceof RefreshType) {
logger.debug("Refresh channel: {} Command: {}", channelUID, command);
DeviceManager.getInstance().startDiscovery(5 * 1000);
DeviceStatus s = mDevice.getDeviceStatus();
switch (channelUID.getId()) {
case CHANNEL_BRIGHTNESS:
updateState(channelUID, new PercentType(s.getBrightness()));
break;
case CHANNEL_COLOR:
updateState(channelUID, HSBType.fromRGB(s.getR(), s.getG(), s.getB()));
break;
case CHANNEL_COLOR_TEMPERATURE:
updateState(channelUID, new PercentType(s.getCt()));
break;
case CHANNEL_BACKGROUND_COLOR:
final HSBType hsbType = new HSBType(new DecimalType(s.getHue()), new PercentType(s.getSat()),
new PercentType(s.getBackgroundBrightness()));
updateState(channelUID, hsbType);
break;
default:
break;
}
return;
}
switch (channelUID.getId()) {
case CHANNEL_BRIGHTNESS:
if (command instanceof PercentType) {
handlePercentMessage((PercentType) command);
} else if (command instanceof OnOffType) {
handleOnOffCommand((OnOffType) command);
} else if (command instanceof IncreaseDecreaseType) {
handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
}
break;
case CHANNEL_COLOR:
if (command instanceof HSBType) {
HSBType hsbCommand = (HSBType) command;
if (hsbCommand.getBrightness().intValue() == 0) {
handleOnOffCommand(OnOffType.OFF);
} else {
handleHSBCommand(hsbCommand);
}
} else if (command instanceof PercentType) {
handlePercentMessage((PercentType) command);
} else if (command instanceof OnOffType) {
handleOnOffCommand((OnOffType) command);
} else if (command instanceof IncreaseDecreaseType) {
handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
}
break;
case CHANNEL_COLOR_TEMPERATURE:
if (command instanceof PercentType) {
handleColorTemperatureCommand((PercentType) command);
} else if (command instanceof IncreaseDecreaseType) {
handleIncreaseDecreaseBrightnessCommand((IncreaseDecreaseType) command);
}
break;
case CHANNEL_BACKGROUND_COLOR:
if (command instanceof HSBType) {
HSBType hsbCommand = (HSBType) command;
handleBackgroundHSBCommand(hsbCommand);
} else if (command instanceof PercentType) {
handleBackgroundBrightnessPercentMessage((PercentType) command);
} else if (command instanceof OnOffType) {
handleBackgroundOnOffCommand((OnOffType) command);
}
break;
case CHANNEL_NIGHTLIGHT:
if (command instanceof OnOffType) {
DeviceAction pAction = command == OnOffType.ON ? DeviceAction.nightlight_on
: DeviceAction.nightlight_off;
pAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, pAction);
}
break;
case CHANNEL_COMMAND:
if (!command.toString().isEmpty()) {
String[] tokens = command.toString().split(";");
String methodAction = tokens[0];
String methodParams = "";
if (tokens.length > 1) {
methodParams = tokens[1];
}
logger.debug("{}: {} {}", logInfo, methodAction, methodParams);
handleCustomCommand(methodAction, methodParams);
updateState(channelUID, new StringType(""));
}
break;
default:
break;
}
}
void handlePercentMessage(PercentType brightness) {
DeviceAction pAction;
if (brightness.intValue() == 0) {
pAction = DeviceAction.close;
pAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, pAction);
} else {
if (mDevice.getDeviceStatus().isPowerOff()) {
pAction = DeviceAction.open;
// hard coded to fast open, the duration should apply to brightness increase only
pAction.putDuration(0);
DeviceManager.getInstance().doAction(deviceId, pAction);
}
pAction = DeviceAction.brightness;
pAction.putValue(brightness.intValue());
pAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, pAction);
}
}
void handleIncreaseDecreaseBrightnessCommand(IncreaseDecreaseType increaseDecrease) {
DeviceAction idbAcation = increaseDecrease == IncreaseDecreaseType.INCREASE ? DeviceAction.increase_bright
: DeviceAction.decrease_bright;
idbAcation.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, idbAcation);
}
void handleIncreaseDecreaseColorTemperatureCommand(IncreaseDecreaseType increaseDecrease) {
DeviceAction idctAcation = increaseDecrease == IncreaseDecreaseType.INCREASE ? DeviceAction.increase_ct
: DeviceAction.decrease_ct;
idctAcation.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, idctAcation);
}
void handleOnOffCommand(OnOffType onoff) {
DeviceAction ofAction = onoff == OnOffType.ON ? DeviceAction.open : DeviceAction.close;
ofAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, ofAction);
}
void handleHSBCommand(HSBType color) {
DeviceAction cAction = DeviceAction.color;
cAction.putValue(color.getRGB() & 0xFFFFFF);
cAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, cAction);
}
void handleBackgroundHSBCommand(HSBType color) {
DeviceAction cAction = DeviceAction.background_color;
// TODO: actions seem to be an insufficiant abstraction.
cAction.putValue(color.getHue() + "," + color.getSaturation());
cAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, cAction);
}
void handleBackgroundBrightnessPercentMessage(PercentType brightness) {
DeviceAction pAction;
pAction = DeviceAction.background_brightness;
pAction.putValue(brightness.intValue());
pAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, pAction);
}
private void handleBackgroundOnOffCommand(OnOffType command) {
DeviceAction pAction = command == OnOffType.ON ? DeviceAction.background_on : DeviceAction.background_off;
pAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, pAction);
}
void handleColorTemperatureCommand(PercentType ct) {
DeviceAction ctAction = DeviceAction.colortemperature;
ctAction.putValue(COLOR_TEMPERATURE_STEP * ct.intValue() + COLOR_TEMPERATURE_MINIMUM);
ctAction.putDuration(getDuration());
DeviceManager.getInstance().doAction(deviceId, ctAction);
}
void handleCustomCommand(String action, String params) {
DeviceManager.getInstance().doCustomAction(deviceId, action, params);
}
@Override
public void onStatusChanged(DeviceStatus status) {
logger.debug("UpdateState->{}", status);
updateUI(status);
}
void updateBrightnessAndColorUI(DeviceStatus status) {
PercentType brightness = status.isPowerOff() ? PercentType.ZERO : new PercentType(status.getBrightness());
HSBType tempHsbType = HSBType.fromRGB(status.getR(), status.getG(), status.getB());
HSBType hsbType = status.getMode() == DeviceMode.MODE_HSV
? new HSBType(new DecimalType(status.getHue()), new PercentType(status.getSat()), brightness)
: new HSBType(tempHsbType.getHue(), tempHsbType.getSaturation(), brightness);
logger.debug("Update Color->{}", hsbType);
updateState(CHANNEL_COLOR, hsbType);
logger.debug("Update CT->{}", status.getCt());
updateState(CHANNEL_COLOR_TEMPERATURE,
new PercentType((status.getCt() - COLOR_TEMPERATURE_MINIMUM) / COLOR_TEMPERATURE_STEP));
}
int getDuration() {
// Duration should not be null, but just in case do a null check.
return getThing().getConfiguration().get(PARAMETER_DURATION) == null ? 500
: ((Number) getThing().getConfiguration().get(PARAMETER_DURATION)).intValue();
}
}

View File

@@ -0,0 +1,42 @@
/**
* 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.yeelight.internal.handler;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
/**
* The {@link YeelightStripeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Coaster Li - Initial contribution
*/
public class YeelightStripeHandler extends YeelightHandlerBase {
public YeelightStripeHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
handleCommandHelper(channelUID, command, "Handle Stripe Command");
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
updateBrightnessAndColorUI(status);
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal.handler;
import org.openhab.binding.yeelight.internal.YeelightBindingConstants;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link YeelightWhiteHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Coaster Li - Initial contribution
*/
public class YeelightWhiteHandler extends YeelightHandlerBase {
private final Logger logger = LoggerFactory.getLogger(YeelightWhiteHandler.class);
public YeelightWhiteHandler(Thing thing) {
super(thing);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
handleCommandHelper(channelUID, command, "Handle White Command");
}
@Override
protected void updateUI(DeviceStatus status) {
super.updateUI(status);
if (status.isPowerOff()) {
logger.debug("Device is powered off!");
updateState(YeelightBindingConstants.CHANNEL_BRIGHTNESS, PercentType.ZERO);
} else {
logger.debug("Device is powered on!");
updateState(YeelightBindingConstants.CHANNEL_BRIGHTNESS, new PercentType(status.getBrightness()));
}
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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.yeelight.internal.lib;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The {@link CommonLogger} is responsible for logging.
*
* @author Coaster Li - Initial contribution
*/
public class CommonLogger {
private static final boolean DEBUG = false;
private static Logger sLogger = Logger.getLogger("YeelightLib");
public static void debug(String tag, String msg) {
if (DEBUG) {
sLogger.log(Level.INFO, tag + ":" + msg);
}
}
public static void debug(String msg) {
if (DEBUG) {
sLogger.log(Level.INFO, msg);
}
}
public static void warning(String msg) {
if (DEBUG) {
sLogger.warning(msg);
}
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link CeilingDevice} contains methods for handling the ceiling device.
*
* @author Coaster Li - Initial contribution
*/
public class CeilingDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(CeilingDevice.class);
public CeilingDevice(String id) {
super(id);
mDeviceType = DeviceType.ceiling;
mConnection = new WifiConnection(this);
mMinCt = 2700;
mMaxCt = 6500;
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright", "ct" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
// ct:
mDeviceStatus.setCt(status.get(3).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Problem setting values", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,104 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link CeilingDeviceWithAmbientDevice} contains methods for handling the ceiling device with ambient light.
*
* @author Viktor Koop - Initial contribution
*/
public class CeilingDeviceWithAmbientDevice extends CeilingDevice
implements DeviceWithAmbientLight, DeviceWithNightlight {
private final Logger logger = LoggerFactory.getLogger(CeilingDeviceWithAmbientDevice.class);
public CeilingDeviceWithAmbientDevice(String id) {
super(id);
mDeviceType = DeviceType.ceiling4;
}
@Override
public void onNotify(String msg) {
logger.debug("Got state: {}", msg);
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
JsonArray status = result.get("result").getAsJsonArray();
final String backgroundPowerState = status.get(4).toString();
if ("\"off\"".equals(backgroundPowerState)) {
mDeviceStatus.setBackgroundIsPowerOff(true);
} else if ("\"on\"".equals(backgroundPowerState)) {
mDeviceStatus.setBackgroundIsPowerOff(false);
}
final int backgroundBrightness = status.get(5).getAsInt();
mDeviceStatus.setBackgroundBrightness(backgroundBrightness);
final int backgroundHue = status.get(6).getAsInt();
mDeviceStatus.setBackgroundHue(backgroundHue);
final int backgroundSaturation = status.get(7).getAsInt();
mDeviceStatus.setBackgroundSat(backgroundSaturation);
final int activeMode = status.get(8).getAsInt();
mDeviceStatus.setActiveMode(ActiveMode.values()[activeMode]);
}
}
super.onNotify(msg);
}
@Override
public void setBackgroundColor(int hue, int saturation, int duration) {
mConnection
.invoke(MethodFactory.buildBackgroundHSVMethod(hue, saturation, DeviceMethod.EFFECT_SMOOTH, duration));
}
@Override
public void setBackgroundBrightness(int brightness, int duration) {
mConnection
.invoke(MethodFactory.buildBackgroundBrightnessMethd(brightness, DeviceMethod.EFFECT_SMOOTH, duration));
}
@Override
public void setBackgroundPower(boolean on, int duration) {
mConnection.invoke(new DeviceMethod(MethodAction.BG_SWITCH,
new Object[] { on ? "on" : "off", DeviceMethod.EFFECT_SMOOTH, duration }));
}
@Override
public void toggleNightlightMode(boolean turnOn) {
if (turnOn) {
mConnection.invoke(
new DeviceMethod(MethodAction.SCENE, new Object[] { "nightlight", mDeviceStatus.getBrightness() }));
} else {
mConnection.invoke(MethodFactory.buildCTMethod(mDeviceStatus.getCt(), DeviceMethod.EFFECT_SMOOTH, 500));
}
}
}

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link CeilingDeviceWithNightDevice} contains methods for handling the ceiling device with ambient light.
*
* @author Nikita Pogudalov - Initial contribution
*/
public class CeilingDeviceWithNightDevice extends CeilingDevice implements DeviceWithNightlight {
private final Logger logger = LoggerFactory.getLogger(CeilingDeviceWithNightDevice.class);
public CeilingDeviceWithNightDevice(String id) {
super(id);
mDeviceType = DeviceType.ceiling1;
}
@Override
public void onNotify(String msg) {
logger.debug("Got state: {}", msg);
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
JsonArray status = result.get("result").getAsJsonArray();
final int activeMode = status.get(8).getAsInt();
mDeviceStatus.setActiveMode(ActiveMode.values()[activeMode]);
}
}
super.onNotify(msg);
}
@Override
public void toggleNightlightMode(boolean turnOn) {
if (turnOn) {
mConnection.invoke(
new DeviceMethod(MethodAction.SCENE, new Object[] { "nightlight", mDeviceStatus.getBrightness() }));
} else {
mConnection.invoke(MethodFactory.buildCTMethod(mDeviceStatus.getCt(), DeviceMethod.EFFECT_SMOOTH, 500));
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.yeelight.internal.lib.device;
/**
* @author Coaster Li - Initial contribution
*/
public class ColorFlowItem {
public int duration;
public int mode;
public int value;
public int brightness;
@Override
public String toString() {
return "ColorFlowItem [duration=" + duration + ", mode=" + mode + ", value=" + value + ", brightness="
+ brightness + "]";
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal.lib.device;
/**
* The {@link ConnectState} lists different connection states.
*
* @author Coaster Li - Initial contribution
*/
public enum ConnectState {
DISCONNECTED,
CONNECTTING,
CONNECTED
}

View File

@@ -0,0 +1,76 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link CtBulbDevice} contains methods for handling the CT bulb device.
*
* @author Claudius Ellsel - Initial contribution
*/
public class CtBulbDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(CtBulbDevice.class);
public CtBulbDevice(String id) {
super(id);
mDeviceType = DeviceType.ct_bulb;
mConnection = new WifiConnection(this);
mMinCt = 2700;
mMaxCt = 6500;
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright", "ct" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
// ct:
mDeviceStatus.setCt(status.get(3).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Problem setting values", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link DesklampDevice} contains methods for handling the desklamp device.
*
* @author Sebastian Rakel - Initial contribution
*/
public class DesklampDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(DesklampDevice.class);
public DesklampDevice(String id) {
super(id);
mDeviceType = DeviceType.desklamp;
mConnection = new WifiConnection(this);
mMinCt = 2700;
mMaxCt = 6500;
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright", "ct" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
// ct:
mDeviceStatus.setCt(status.get(3).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Problem setting values", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,433 @@
/**
* 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.yeelight.internal.lib.device;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.openhab.binding.yeelight.internal.lib.device.connection.ConnectionBase;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceConnectionStateListener;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceStatusChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link DeviceBase} is a generic class for all devices.
*
* @author Coaster Li - Initial contribution
* @author Daniel Walters - Correct handling of brightness
* @author Joe Ho - Added duration to some commands
*/
public abstract class DeviceBase {
private final Logger logger = LoggerFactory.getLogger(DeviceBase.class);
private static final String TAG = DeviceBase.class.getSimpleName();
protected String mDeviceId;
protected String mDeviceName;
protected DeviceType mDeviceType;
protected String mAddress;
private String[] mSupportProps;
protected int mPort;
private int mFwVersion;
protected boolean bIsOnline;
protected boolean bIsAutoConnect;
protected ConnectionBase mConnection;
protected ConnectState mConnectState = ConnectState.DISCONNECTED;
protected DeviceStatus mDeviceStatus;
private Map<String, Object> mBulbInfo = null;
protected List<String> mQueryList = new ArrayList<>();
protected int mMinCt, mMaxCt;
List<DeviceConnectionStateListener> mConnectionListeners = new ArrayList<>();
List<DeviceStatusChangeListener> mStatusChangeListeners = new ArrayList<>();
public DeviceBase(String id) {
mDeviceId = id;
mDeviceStatus = new DeviceStatus();
}
public DeviceBase(String id, boolean isAutoConnect) {
mDeviceId = id;
this.bIsAutoConnect = isAutoConnect;
}
public void onNotify(String response) {
boolean needNotify = true;
JsonObject message = new JsonParser().parse(response).getAsJsonObject();
try {
if (message.has("method")) {
String method = message.get("method").toString().replace("\"", "");
if (method.equals("props")) {// Property notify
String params = message.get("params").toString();
JsonObject propsObject = new JsonParser().parse(params).getAsJsonObject();
for (Entry<String, JsonElement> prop : propsObject.entrySet()) {
final YeelightDeviceProperty property = YeelightDeviceProperty.fromString(prop.getKey());
if (null == property) {
logger.debug("Unhandled property: {}", prop.getKey());
continue;
}
switch (property) {
case POWER:
if (prop.getValue().toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (prop.getValue().toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
break;
case BRIGHT:
mDeviceStatus.setBrightness(prop.getValue().getAsInt());
break;
case CT:
mDeviceStatus.setCt(prop.getValue().getAsInt());
mDeviceStatus.setMode(DeviceMode.MODE_SUNHINE);
break;
case RGB: {
mDeviceStatus.setMode(DeviceMode.MODE_COLOR);
int color = prop.getValue().getAsInt();
mDeviceStatus.setColor(color);
mDeviceStatus.setR((color >> 16) & 0xFF);
mDeviceStatus.setG((color >> 8) & 0xFF);
mDeviceStatus.setB(color & 0xFF);
break;
}
case HUE:
mDeviceStatus.setMode(DeviceMode.MODE_HSV);
mDeviceStatus.setHue(prop.getValue().getAsInt());
break;
case SAT:
mDeviceStatus.setMode(DeviceMode.MODE_HSV);
mDeviceStatus.setSat(prop.getValue().getAsInt());
break;
case COLOR_MODE:
switch (prop.getValue().getAsInt()) {
case DeviceStatus.MODE_COLOR:
mDeviceStatus.setMode(DeviceMode.MODE_COLOR);
break;
case DeviceStatus.MODE_COLORTEMPERATURE:
mDeviceStatus.setMode(DeviceMode.MODE_SUNHINE);
break;
case DeviceStatus.MODE_HSV:
mDeviceStatus.setMode(DeviceMode.MODE_HSV);
break;
default:
break;
}
break;
case FLOWING:
mDeviceStatus.setIsFlowing(prop.getValue().getAsInt() == 1);
break;
case FLOW_PARAMS:
// {"method":"props","params":{"flow_params":"0,0,1000,1,15935488,31,1000,1,13366016,31,1000,1,62370,31,1000,1,7995635,31"}}
String[] flowStrs = prop.getValue().toString().replace("\"", "").split(",");
if (flowStrs.length > 2 && (flowStrs.length - 2) % 4 == 0) {
mDeviceStatus.setFlowCount(Integer.parseInt(flowStrs[0]));
mDeviceStatus.setFlowEndAction(Integer.parseInt(flowStrs[1]));
if (mDeviceStatus.getFlowItems() == null) {
mDeviceStatus.setFlowItems(new ArrayList<>());
}
mDeviceStatus.getFlowItems().clear();
for (int i = 0; i < ((flowStrs.length - 2) / 4); i++) {
ColorFlowItem item = new ColorFlowItem();
item.duration = Integer.valueOf(flowStrs[4 * i + 2]);
item.mode = Integer.valueOf(flowStrs[4 * i + 3]);
item.value = Integer.valueOf(flowStrs[4 * i + 4]);
item.brightness = Integer.valueOf(flowStrs[4 * i + 5]);
mDeviceStatus.getFlowItems().add(item);
}
}
break;
case DELAYOFF:
int delayOff = prop.getValue().getAsInt();
if (delayOff > 0 && delayOff <= 60) {
mDeviceStatus.setDelayOff(delayOff);
} else {
mDeviceStatus.setDelayOff(DeviceStatus.DEFAULT_NO_DELAY);
}
break;
case MUSIC_ON:
mDeviceStatus.setMusicOn(prop.getValue().getAsInt() == 1);
break;
case NAME:
mDeviceName = prop.getValue().toString();
break;
case BG_RGB: {
int color = prop.getValue().getAsInt();
mDeviceStatus.setBackgroundR((color >> 16) & 0xFF);
mDeviceStatus.setBackgroundG((color >> 8) & 0xFF);
mDeviceStatus.setBackgroundB(color & 0xFF);
break;
}
case BG_HUE:
mDeviceStatus.setBackgroundHue(prop.getValue().getAsInt());
break;
case BG_SAT:
mDeviceStatus.setBackgroundSat(prop.getValue().getAsInt());
break;
case BG_BRIGHT:
mDeviceStatus.setBackgroundBrightness(prop.getValue().getAsInt());
break;
case BG_POWER:
if ("\"off\"".equals(prop.getValue().toString())) {
mDeviceStatus.setBackgroundIsPowerOff(true);
} else if ("\"on\"".equals(prop.getValue().toString())) {
mDeviceStatus.setBackgroundIsPowerOff(false);
}
break;
case NL_BR:
// when the light is switched from nightlight-mode to sunlight-mode it will send nl_br:0
// therefore we have to ignore the case.
final int intValue = prop.getValue().getAsInt();
if (intValue > 0) {
mDeviceStatus.setBrightness(intValue);
}
break;
case ACTIVE_MODE:
int activeModeInt = prop.getValue().getAsInt();
final ActiveMode activeMode = ActiveMode.values()[activeModeInt];
mDeviceStatus.setActiveMode(activeMode);
break;
default:
logger.debug("Maybe unsupported property: {} - {}", property, prop.getKey());
break;
}
}
}
} else if (message.has("id") && message.has("result")) {
// no method, but result : ["ok"]
JsonArray result = message.get("result").getAsJsonArray();
if (result.get(0).toString().equals("\"ok\"")) {
logger.info("######### this is control command response, don't need to notify status change!");
needNotify = false;
}
}
if (needNotify) {
logger.info("status = {}", mDeviceStatus);
for (DeviceStatusChangeListener statusChangeListener : mStatusChangeListeners) {
statusChangeListener.onStatusChanged(mDeviceStatus);
}
}
} catch (Exception e) {
logger.debug("Exception", e);
}
}
public void open(int duration) {
mConnection.invoke(
new DeviceMethod(MethodAction.SWITCH, new Object[] { "on", DeviceMethod.EFFECT_SMOOTH, duration }));
}
public void close(int duration) {
mConnection.invoke(
new DeviceMethod(MethodAction.SWITCH, new Object[] { "off", DeviceMethod.EFFECT_SMOOTH, duration }));
}
public void decreaseBrightness(int duration) {
int bright = getDeviceStatus().getBrightness() - 10;
if (bright <= 0) {
close(duration);
} else {
setBrightness(bright, duration);
}
}
public void increaseBrightness(int duration) {
int bright = getDeviceStatus().getBrightness() + 10;
if (bright > 100) {
bright = 100;
}
setBrightness(bright, duration);
}
public void setBrightness(int brightness, int duration) {
mConnection.invoke(MethodFactory.buildBrightnessMethd(brightness, DeviceMethod.EFFECT_SMOOTH, duration));
}
public void setColor(int color, int duration) {
mConnection.invoke(MethodFactory.buildRgbMethod(color, DeviceMethod.EFFECT_SMOOTH, duration));
}
public void increaseCt(int duration) {
int ct = getDeviceStatus().getCt() - ((mMaxCt - mMinCt) / 10);
if (ct < mMinCt) {
ct = mMinCt;
}
setCT(ct, duration);
}
public void decreaseCt(int duration) {
int ct = getDeviceStatus().getCt() + ((mMaxCt - mMinCt) / 10);
if (ct > mMaxCt) {
ct = mMaxCt;
}
setCT(ct, duration);
}
public void setCT(int ct, int duration) {
mConnection.invoke(MethodFactory.buildCTMethod(ct, DeviceMethod.EFFECT_SMOOTH, duration));
}
public void connect() {
setConnectionState(ConnectState.CONNECTTING);
mConnection.connect();
}
public void setConnectionState(ConnectState connectState) {
logger.debug("{}: set connection state to: {}", TAG, connectState.name());
if (connectState == ConnectState.DISCONNECTED) {
setOnline(false);
}
if (mConnectState != connectState) {
mConnectState = connectState;
if (mConnectionListeners != null) {
for (DeviceConnectionStateListener listener : mConnectionListeners) {
listener.onConnectionStateChanged(mConnectState);
}
}
}
}
public void sendCustomCommand(String action, String params) {
mConnection.invokeCustom(new DeviceMethod(action, params));
}
// ===================== setter and getter=====================
public String getDeviceId() {
return mDeviceId;
}
public void setDeviceName(String name) {
mDeviceName = name;
}
public String getDeviceName() {
return mDeviceName;
}
public DeviceType getDeviceType() {
return mDeviceType;
}
public String getDeviceModel() {
return mDeviceType.name();
}
public String getAddress() {
return mAddress;
}
public void setAddress(String address) {
this.mAddress = address;
}
public int getPort() {
return mPort;
}
public void setPort(int port) {
this.mPort = port;
}
public boolean isAutoConnect() {
return bIsAutoConnect;
}
public int getFwVersion() {
return mFwVersion;
}
public void setFwVersion(int fwVersion) {
this.mFwVersion = fwVersion;
}
public Map<String, Object> getBulbInfo() {
return mBulbInfo;
}
public void setBulbInfo(Map<String, Object> bulbInfo) {
this.mBulbInfo = bulbInfo;
}
public String[] getSupportProps() {
return mSupportProps;
}
public void setSupportProps(String[] supportProps) {
this.mSupportProps = supportProps;
}
public void setAutoConnect(boolean isAutoConnect) {
if (bIsAutoConnect != isAutoConnect) {
this.bIsAutoConnect = isAutoConnect;
checkAutoConnect();
}
}
public DeviceStatus getDeviceStatus() {
return mDeviceStatus;
}
public void registerConnectStateListener(DeviceConnectionStateListener listener) {
mConnectionListeners.add(listener);
}
public void registerStatusChangedListener(DeviceStatusChangeListener listener) {
mStatusChangeListeners.add(listener);
}
public void setOnline(boolean isOnline) {
bIsOnline = isOnline;
checkAutoConnect();
}
public boolean isOnline() {
return bIsOnline;
}
public ConnectState getConnectionState() {
return mConnectState;
}
public void queryStatus() {
DeviceMethod cmd = MethodFactory.buildQuery(this);
mQueryList.add(cmd.getCmdId());
mConnection.invoke(cmd);
}
private void checkAutoConnect() {
logger.debug(
"{}: CheckAutoConnect: online: {}, autoConnect: {}, connection state: {}, device = {}, device id: {}",
TAG, bIsOnline, bIsAutoConnect, mConnectState.name(), this, this.getDeviceId());
if (bIsOnline && bIsAutoConnect && mConnectState == ConnectState.DISCONNECTED) {
connect();
}
}
}

View File

@@ -0,0 +1,75 @@
/**
* 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.yeelight.internal.lib.device;
import java.util.HashMap;
import java.util.Map;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceFactory} creates device handler classes.
*
* @author Coaster Li - Initial contribution
* @author Nikita Pogudalov - Added CeilingDeviceWithNightDevice for Ceiling 1
*/
public class DeviceFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceFactory.class);
private static final String TAG = DeviceFactory.class.getSimpleName();
public static DeviceBase build(String model, String id) {
DeviceType type = DeviceType.valueOf(model);
switch (type) {
case ceiling:
case ceiling3:
return new CeilingDevice(id);
case ceiling1:
return new CeilingDeviceWithNightDevice(id);
case ceiling4:
return new CeilingDeviceWithAmbientDevice(id);
case color:
return new WonderDevice(id);
case mono:
return new MonoDevice(id);
case ct_bulb:
return new CtBulbDevice(id);
case stripe:
return new PitayaDevice(id);
case desklamp:
return new DesklampDevice(id);
default:
return null;
}
}
public static DeviceBase build(Map<String, String> bulbInfo) {
DeviceBase device = build(bulbInfo.get("model"), bulbInfo.get("id"));
if (null == device) {
return null;
}
Map<String, Object> infos = new HashMap<>(bulbInfo);
device.setBulbInfo(infos);
LOGGER.debug("{}: DeviceFactory Device = {}", TAG, bulbInfo.get("Location"));
// TODO enhancement!!!
String[] addressInfo = bulbInfo.get("Location").split(":");
device.setAddress(addressInfo[1].substring(2));
device.setPort(Integer.parseInt(addressInfo[2]));
device.setOnline(true);
LOGGER.debug("{}: DeviceFactory Device info = {}, port = {}", TAG, device.getAddress(), device.getPort());
return device;
}
}

View File

@@ -0,0 +1,103 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
/**
* @author Coaster Li - Initial contribution
*/
public class DeviceMethod {
public static final String EFFECT_SMOOTH = "smooth";
public static final String EFFECT_SUDDEN = "sudden";
public static final int CF_END_STATE_RECOVER = 0;
public static final int CF_END_STATE_STAY = 1;
public static final int CF_END_STATE_TURNOFF = 2;
public static final int CF_ITEM_MODE_COLOR = 1;
public static final int CF_ITEM_MODE_CT = 2;
public static final int CF_ITEM_MODE_SLEEP = 7;
public static final String SCENE_TYPE_COLOR = "color";
public static final String SCENE_TYPE_HSV = "hsv";
public static final String SCENE_TYPE_CT = "ct";
public static final String SCENE_TYPE_CF = "cf";
public static final String SCENE_TYPE_DELAY = "auto_delay_off";
public static final String ADJUST_ACTION_INCREASE = "increase";
public static final String ADJUST_ACTION_DECREASE = "decrease";
public static final String ADJUST_ACTION_CIRCLE = "circle";
public static final String ADJUST_PROP_BRIGHT = "bright";
public static final String ADJUST_PROP_CT = "ct";
public static final String ADJUST_PROP_COLOR = "color";
public static final int MUSIC_ACTION_ON = 1;
public static final int MUSIC_ACTION_OFF = 0;
private static int sIndex = 0;
private String mMethodAction;
private Object[] mMethodParams;
private String mCustomMethodParams;
private int mIndex;
public DeviceMethod(MethodAction action, Object[] params) {
this.mMethodAction = action.action;
this.mMethodParams = params;
this.mCustomMethodParams = "";
mIndex = ++sIndex;
}
public DeviceMethod(String action, String params) {
this.mMethodAction = action;
this.mMethodParams = null;
this.mCustomMethodParams = params;
mIndex = ++sIndex;
}
public String getParamsStr() {
StringBuilder cmdBuilder = new StringBuilder();
cmdBuilder.append("{\"id\":").append(mIndex).append(",");
cmdBuilder.append("\"method\":\"").append(mMethodAction).append("\",");
cmdBuilder.append("\"params\":[");
if (mMethodParams != null && mMethodParams.length > 0) {
for (Object param : mMethodParams) {
if (param instanceof String) {
cmdBuilder.append("\"" + param.toString() + "\"");
} else {
cmdBuilder.append(Integer.parseInt(param.toString()));
}
cmdBuilder.append(",");
}
// delete last ","
cmdBuilder.deleteCharAt(cmdBuilder.length() - 1);
}
cmdBuilder.append("]}\r\n");
return cmdBuilder.toString();
}
public String getCustomParamsStr() {
StringBuilder cmdBuilder = new StringBuilder();
cmdBuilder.append("{\"id\":").append(mIndex).append(",");
cmdBuilder.append("\"method\":\"").append(mMethodAction).append("\",");
cmdBuilder.append("\"params\":[").append(mCustomMethodParams).append("]}\r\n");
return cmdBuilder.toString();
}
public String getCmdId() {
return String.valueOf(mIndex);
}
}

View File

@@ -0,0 +1,269 @@
/**
* 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.yeelight.internal.lib.device;
import java.util.List;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceMode;
/**
* @author Coaster Li - Initial contribution
*/
public class DeviceStatus {
public static final int MODE_COLOR = 1;
public static final int MODE_COLORTEMPERATURE = 2;
public static final int MODE_HSV = 3;
public static final int DEFAULT_NO_DELAY = -1;
private boolean isPowerOff;
private int r;
private int g;
private int b;
private int color;
private int brightness;
private int ct;
private int hue;
private int sat;
private boolean isFlowing;
private int delayOff = DEFAULT_NO_DELAY;
private List<ColorFlowItem> mFlowItems;
private DeviceMode mode;
private boolean isMusicOn;
private String name;
private int flowEndAction;
private int flowCount;
private int backgroundBrightness;
private boolean backgroundIsPowerOff;
private int backgroundR;
private int backgroundG;
private int backgroundB;
private int backgroundHue;
private int backgroundSat;
private ActiveMode activeMode;
public int getR() {
return r;
}
public void setR(int r) {
this.r = r;
}
public int getG() {
return g;
}
public void setG(int g) {
this.g = g;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int getBrightness() {
return brightness;
}
public void setBrightness(int brightness) {
this.brightness = brightness;
}
public int getCt() {
return ct;
}
public void setCt(int ct) {
this.ct = ct;
}
public int getHue() {
return hue;
}
public void setHue(int hue) {
this.hue = hue;
}
public int getSat() {
return sat;
}
public void setSat(int sat) {
this.sat = sat;
}
public DeviceMode getMode() {
return mode;
}
public void setMode(DeviceMode mode) {
this.mode = mode;
}
public boolean isPowerOff() {
return isPowerOff;
}
public void setPowerOff(boolean isPowerOff) {
this.isPowerOff = isPowerOff;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public boolean getIsFlowing() {
return isFlowing;
}
public void setIsFlowing(boolean isFlowing) {
this.isFlowing = isFlowing;
}
public List<ColorFlowItem> getFlowItems() {
return mFlowItems;
}
public void setFlowItems(List<ColorFlowItem> mFlowItems) {
this.mFlowItems = mFlowItems;
}
public boolean isMusicOn() {
return isMusicOn;
}
public void setMusicOn(boolean isMusicOn) {
this.isMusicOn = isMusicOn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDelayOff() {
return delayOff;
}
public void setDelayOff(int delayOff) {
this.delayOff = delayOff;
}
public boolean isBackgroundIsPowerOff() {
return backgroundIsPowerOff;
}
public void setBackgroundIsPowerOff(boolean backgroundIsPowerOff) {
this.backgroundIsPowerOff = backgroundIsPowerOff;
}
public int getBackgroundR() {
return backgroundR;
}
public void setBackgroundR(int backgroundR) {
this.backgroundR = backgroundR;
}
public int getBackgroundG() {
return backgroundG;
}
public void setBackgroundG(int backgroundG) {
this.backgroundG = backgroundG;
}
public int getBackgroundB() {
return backgroundB;
}
public void setBackgroundB(int backgroundB) {
this.backgroundB = backgroundB;
}
public int getBackgroundHue() {
return backgroundHue;
}
public void setBackgroundHue(int backgroundHue) {
this.backgroundHue = backgroundHue;
}
public int getBackgroundBrightness() {
return backgroundBrightness;
}
public void setBackgroundBrightness(int backgroundBrightness) {
this.backgroundBrightness = backgroundBrightness;
}
public int getBackgroundSat() {
return backgroundSat;
}
public void setBackgroundSat(int backgroundSat) {
this.backgroundSat = backgroundSat;
}
public ActiveMode getActiveMode() {
return activeMode;
}
public void setActiveMode(ActiveMode activeMode) {
this.activeMode = activeMode;
}
@Override
public String toString() {
return "DeviceStatus [isPowerOff=" + isPowerOff + ", r=" + r + ", g=" + g + ", b=" + b + ", color=" + color
+ ", brightness=" + brightness + ", ct=" + ct + ", hue=" + hue + ", sat=" + sat + ", isFlowing="
+ isFlowing + ", delayOff=" + delayOff + ", mFlowItems=" + mFlowItems + ", mode=" + mode
+ ", isMusicOn=" + isMusicOn + ", name=" + name + ", backgroundIsPowerOff=" + backgroundIsPowerOff
+ ", backgroundR=" + backgroundR + ", backgroundG=" + backgroundG + ", backgroundB=" + backgroundB
+ ", backgroundHue=" + backgroundHue + ", backgroundBrightness=" + backgroundBrightness
+ ", backgroundSat=" + backgroundSat + ", activeMode=" + activeMode + "]";
}
public int getFlowCount() {
return flowCount;
}
public void setFlowCount(int flowCount) {
this.flowCount = flowCount;
}
public int getFlowEndAction() {
return flowEndAction;
}
public void setFlowEndAction(int flowEndAction) {
this.flowEndAction = flowEndAction;
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.yeelight.internal.lib.device;
/**
* Interface for devices with background light.
*
* @author Viktor Koop - Initial contribution
*/
public interface DeviceWithAmbientLight {
void setBackgroundColor(int hue, int saturation, int duration);
void setBackgroundBrightness(int brightness, int duration);
void setBackgroundPower(boolean on, int intDuration);
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal.lib.device;
/**
* Implemented by devices with the possibility to enable nightlight mode.
*
* @author Viktor Koop - Initial contribution
*/
public interface DeviceWithNightlight {
/**
* Toggle the nightlight mode on or off.
*
* @param mode
*/
void toggleNightlightMode(boolean mode);
}

View File

@@ -0,0 +1,184 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.openhab.binding.yeelight.internal.lib.enums.MethodAction;
/**
* @author Coaster Li - Initial contribution
* @author Nikita Pogudalov - Added DeviceMethod for Ceiling 1
*/
public class MethodFactory {
public static DeviceMethod buildBrightnessMethd(int brightness, String effect, int duration) {
return new DeviceMethod(MethodAction.BRIGHTNESS, new Object[] { brightness, effect, duration });
}
public static DeviceMethod buildRgbMethod(int color, String effect, int duration) {
return new DeviceMethod(MethodAction.RGB, new Object[] { color, effect, duration });
}
public static DeviceMethod buildCTMethod(int colorTemperature, String effect, int duration) {
return new DeviceMethod(MethodAction.COLORTEMPERATURE, new Object[] { colorTemperature, effect, duration });
}
public static DeviceMethod buildHsvMethod(int hue, int sat, String effect, int duration) {
return new DeviceMethod(MethodAction.HSV, new Object[] { hue, sat, effect, duration });
}
public static DeviceMethod buildToggle() {
return new DeviceMethod(MethodAction.HSV, null);
}
public static DeviceMethod buildSetDefault() {
return new DeviceMethod(MethodAction.DEFAULT, null);
}
public static DeviceMethod buildStartCF(int count, int endAction, ColorFlowItem[] items) {
if (items == null || items.length < 1) {
return null;
}
String itemStr = "\"";
StringBuilder builder;
for (int i = 0; i < items.length; i++) {
builder = new StringBuilder();
ColorFlowItem item = items[i];
builder.append(item.duration).append(",").append(item.mode).append(item.value).append(item.brightness);
if (i < items.length - 1) {
builder.append(",");
}
}
itemStr += "\"";
return new DeviceMethod(MethodAction.STARTCF, new Object[] { count, endAction, itemStr });
}
public static DeviceMethod buildStopCf() {
return new DeviceMethod(MethodAction.STOPCF, null);
}
public static DeviceMethod buildScnene(String type, int value, int brightness) {
return buildScene(type, value, brightness, 0, 0, null);
}
/**
* @param type scene type {@link DeviceMethod#SCENE_TYPE_COLOR}
* {@link DeviceMethod#SCENE_TYPE_CT}
* {@link DeviceMethod#SCENE_TYPE_DELAY}
* {@link DeviceMethod#SCENE_TYPE_HSV}
* {@link DeviceMethod#SCENE_TYPE_CF}
*
*/
public static DeviceMethod buildScene(String type, int value, int brightness, int count, int endAction,
ColorFlowItem[] items) {
if (DeviceMethod.SCENE_TYPE_CF.equals(type) && items == null) {
throw new IllegalArgumentException("Type is colorFlow, but no flow tuples given");
}
Object[] params = null;
switch (type) {
case DeviceMethod.SCENE_TYPE_COLOR:
params = new Object[] { "color", value, brightness };
break;
case DeviceMethod.SCENE_TYPE_CT:
params = new Object[] { "ct", value, brightness };
break;
case DeviceMethod.SCENE_TYPE_DELAY:
params = new Object[] { "auto_delay_off", brightness, value };
break;
case DeviceMethod.SCENE_TYPE_HSV:
params = new Object[] { "hsv", value, brightness };
break;
case DeviceMethod.SCENE_TYPE_CF:
String itemStr = "\"";
StringBuilder builder;
for (int i = 0; i < items.length; i++) {
builder = new StringBuilder();
ColorFlowItem item = items[i];
builder.append(item.duration).append(",").append(item.mode).append(item.value)
.append(item.brightness);
if (i < items.length - 1) {
builder.append(",");
}
}
itemStr += "\"";
params = new Object[] { "cf", count, endAction, itemStr };
break;
default:
return null;
}
return new DeviceMethod(MethodAction.SCENE, params);
}
public static DeviceMethod buildCronAdd(int value) {
return new DeviceMethod(MethodAction.CRON_ADD, new Object[] { 0, value });
}
public static DeviceMethod buildCronGet() {
return new DeviceMethod(MethodAction.CRON_ADD, new Object[] { 0 });
}
public static DeviceMethod buildCronDel() {
return new DeviceMethod(MethodAction.CRON_DEL, new Object[] { 0 });
}
public static DeviceMethod buildAdjust(String action, String prop) {
if (DeviceMethod.ADJUST_PROP_COLOR.equals(prop) && !DeviceMethod.ADJUST_ACTION_CIRCLE.equals(action)) {
throw new IllegalArgumentException("When prop is COLOR, the action can only be CIRCLE!!!");
}
return new DeviceMethod(MethodAction.ADJUST, new Object[] { action, prop });
}
public static DeviceMethod buildMusic(int action, String ipAddress, int port) {
if (action == DeviceMethod.MUSIC_ACTION_ON) {
return new DeviceMethod(MethodAction.MUSIC, new Object[] { action, ipAddress, port });
} else {
return new DeviceMethod(MethodAction.MUSIC, new Object[] { action });
}
}
public static DeviceMethod buildName(String name) {
return new DeviceMethod(MethodAction.NAME, new Object[] { name });
}
public static DeviceMethod buildBackgroundHSVMethod(int hue, int sat, String effect, int duration) {
return new DeviceMethod(MethodAction.BG_HSV, new Object[] { hue, sat, effect, duration });
}
public static DeviceMethod buildBackgroundBrightnessMethd(int brightness, String effect, int duration) {
return new DeviceMethod(MethodAction.BG_BRIGHTNESS, new Object[] { brightness, effect, duration });
}
public static DeviceMethod buildQuery(DeviceBase device) {
DeviceType type = device.getDeviceType();
switch (type) {
case mono:
return new DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright" });
case color:
case stripe:
return new DeviceMethod(MethodAction.PROP,
new Object[] { "power", "name", "bright", "ct", "rgb", "hue", "sat" });
case ceiling:
case desklamp:
case ct_bulb:
return new DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright", "ct" });
case ceiling1:
case ceiling4:
return new DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright", "ct", "bg_power",
"bg_bright", "bg_hue", "bg_sat", "active_mode" });
default:
return null;
}
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link MonoDevice} contains methods for handling the mono color device.
*
* @author Coaster Li - Initial contribution
*/
public class MonoDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(MonoDevice.class);
public MonoDevice(String id) {
super(id);
mDeviceType = DeviceType.mono;
mConnection = new WifiConnection(this);
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP, new Object[] { "power", "name", "bright" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Exception", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link PitayaDevice} contains methods for handling the light strip device.
*
* @author Coaster Li - Initial contribution
*/
public class PitayaDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(PitayaDevice.class);
public PitayaDevice(String id) {
super(id);
mDeviceType = DeviceType.stripe;
mConnection = new WifiConnection(this);
mMinCt = 1700;
mMaxCt = 6500;
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP,
// new Object[] { "power", "name", "bright", "ct", "rgb", "hue", "sat" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
// ct:
mDeviceStatus.setCt(status.get(3).getAsInt());
// color:
int color = status.get(4).getAsInt();
mDeviceStatus.setColor(color);
mDeviceStatus.setR((color >> 16) & 0xFF);
mDeviceStatus.setG((color >> 8) & 0xFF);
mDeviceStatus.setB(color & 0xFF);
mDeviceStatus.setColor(color);
mDeviceStatus.setHue(status.get(5).getAsInt());
mDeviceStatus.setSat(status.get(6).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Exception", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.yeelight.internal.lib.device;
import org.openhab.binding.yeelight.internal.lib.device.connection.WifiConnection;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
* The {@link WonderDevice} contains methods for handling the color bulb device.
*
* @author Coaster Li - Initial contribution
*/
public class WonderDevice extends DeviceBase {
private final Logger logger = LoggerFactory.getLogger(WonderDevice.class);
public WonderDevice(String id) {
super(id);
mDeviceType = DeviceType.color;
mConnection = new WifiConnection(this);
mMinCt = 1700;
mMaxCt = 6500;
}
@Override
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
if (result.has("id")) {
String id = result.get("id").getAsString();
// for cmd transaction.
if (mQueryList.contains(id)) {
mQueryList.remove(id);
// DeviceMethod(MethodAction.PROP,
// new Object[] { "power", "name", "bright", "ct", "rgb", "hue", "sat" });
JsonArray status = result.get("result").getAsJsonArray();
// power:
if (status.get(0).toString().equals("\"off\"")) {
mDeviceStatus.setPowerOff(true);
} else if (status.get(0).toString().equals("\"on\"")) {
mDeviceStatus.setPowerOff(false);
}
// name:
mDeviceStatus.setName(status.get(1).getAsString());
// brightness:
mDeviceStatus.setBrightness(status.get(2).getAsInt());
// ct:
mDeviceStatus.setCt(status.get(3).getAsInt());
// color:
int color = status.get(4).getAsInt();
mDeviceStatus.setColor(color);
mDeviceStatus.setR((color >> 16) & 0xFF);
mDeviceStatus.setG((color >> 8) & 0xFF);
mDeviceStatus.setB(color & 0xFF);
mDeviceStatus.setColor(color);
mDeviceStatus.setHue(status.get(5).getAsInt());
mDeviceStatus.setSat(status.get(6).getAsInt());
}
}
} catch (Exception e) {
logger.debug("Exception", e);
}
super.onNotify(msg);
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.yeelight.internal.lib.device;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Properties of yeelight devices as described in the specification.
*
* @see <a href="https://www.yeelight.com/download/Yeelight_Inter-Operation_Spec.pdf"></a>
*
* @author Viktor Koop - Initial contribution
*/
public enum YeelightDeviceProperty {
POWER("power"),
BRIGHT("bright"),
CT("ct"),
RGB("rgb"),
HUE("hue"),
SAT("sat"),
COLOR_MODE("color_mode"),
FLOWING("flowing"),
DELAYOFF("delayoff"),
FLOW_PARAMS("flow_params"),
MUSIC_ON("music_on"),
NAME("name"),
BG_POWER("bg_power"),
BG_FLOWING("bg_flowing"),
BG_FLOW_PARAMS("bg_flow_params"),
BG_CT("bg_ct"),
BG_LMODE("bg_lmode"),
BG_BRIGHT("bg_bright"),
BG_RGB("bg_rgb"),
BG_HUE("bg_hue"),
BG_SAT("bg_sat"),
NL_BR("nl_br"),
ACTIVE_MODE("active_mode"),
MAIN_POWER("main_power");
private String value;
private static final Map<String, YeelightDeviceProperty> ENUM_MAP;
static {
final Map<String, YeelightDeviceProperty> tempMap = new HashMap<>();
for (YeelightDeviceProperty property : YeelightDeviceProperty.values()) {
tempMap.put(property.value, property);
}
ENUM_MAP = Collections.unmodifiableMap(tempMap);
}
YeelightDeviceProperty(String stringValue) {
this.value = stringValue;
}
public String getValue() {
return value;
}
public static YeelightDeviceProperty fromString(String value) {
return ENUM_MAP.get(value);
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.yeelight.internal.lib.device.connection;
import org.openhab.binding.yeelight.internal.lib.device.DeviceMethod;
/**
* Created by jiang on 16/10/21.
*
* @author Coaster Li - Initial contribution
*/
public interface ConnectionBase {
boolean invoke(DeviceMethod method);
boolean invokeCustom(DeviceMethod method);
boolean connect();
boolean disconnect();
}

View File

@@ -0,0 +1,133 @@
/**
* 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.yeelight.internal.lib.device.connection;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;
import org.openhab.binding.yeelight.internal.lib.device.ConnectState;
import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
import org.openhab.binding.yeelight.internal.lib.device.DeviceMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by jiang on 16/10/25.
*
* @author Coaster Li - Initial contribution
*/
public class WifiConnection implements ConnectionBase {
private final Logger logger = LoggerFactory.getLogger(WifiConnection.class);
private static final String TAG = WifiConnection.class.getSimpleName();
private Socket mSocket;
private BufferedReader mReader;
private BufferedOutputStream mWriter;
private Thread mConnectThread;
private DeviceBase mDevice;
private boolean mCmdRun = false;
public WifiConnection(DeviceBase device) {
mDevice = device;
}
@Override
public boolean invoke(DeviceMethod method) {
if (mWriter != null) {
try {
mWriter.write(method.getParamsStr().getBytes());
mWriter.flush();
logger.debug("{}: Write Success!", TAG);
} catch (Exception e) {
logger.debug("{}: write exception, set device to disconnected!", TAG);
logger.debug("Exception", e);
mDevice.setConnectionState(ConnectState.DISCONNECTED);
return false;
}
return true;
}
return false;
}
@Override
public boolean invokeCustom(DeviceMethod method) {
if (mWriter != null) {
try {
mWriter.write(method.getCustomParamsStr().getBytes());
mWriter.flush();
logger.debug("{}: Write Success!", TAG);
} catch (Exception e) {
logger.debug("{}: write exception, set device to disconnected!", TAG);
logger.debug("Exception", e);
mDevice.setConnectionState(ConnectState.DISCONNECTED);
return false;
}
return true;
}
return false;
}
@Override
public boolean connect() {
logger.debug("{}: connect() entering!", TAG);
if (mSocket != null && mSocket.isConnected()) {
logger.debug("{}: socket not null, return!", TAG);
return true;
}
mConnectThread = new Thread(() -> {
try {
mCmdRun = true;
logger.debug("{}: connect device! {}, {}", TAG, mDevice.getAddress(), mDevice.getPort());
mSocket = new Socket(mDevice.getAddress(), mDevice.getPort());
mSocket.setKeepAlive(true);
mWriter = new BufferedOutputStream(mSocket.getOutputStream());
mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
mDevice.setConnectionState(ConnectState.CONNECTED);
while (mCmdRun) {
try {
String value = mReader.readLine();
logger.debug("{}: get response: {}", TAG, value);
if (value == null) {
mCmdRun = false;
} else {
mDevice.onNotify(value);
}
} catch (Exception e) {
logger.debug("Exception", e);
mCmdRun = false;
}
}
mSocket.close();
} catch (Exception e) {
logger.debug("{}: connect device! ERROR! {}", TAG, e.getMessage());
logger.debug("Exception", e);
} finally {
mDevice.setConnectionState(ConnectState.DISCONNECTED);
mSocket = null;
}
});
mConnectThread.start();
return false;
}
@Override
public boolean disconnect() {
mDevice.setAutoConnect(false);
return false;
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.yeelight.internal.lib.enums;
/**
* Yeelight Ceiling lights have to different modes: daylight and moonlight.
*
* @author Viktor Koop - Initial contribution
*/
public enum ActiveMode {
DAYLIGHT_MODE,
MOONLIGHT_MODE
}

View File

@@ -0,0 +1,63 @@
/**
* 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.yeelight.internal.lib.enums;
/**
* @author Coaster Li - Initial contribution
* @author Joe Ho - Added duration
*/
public enum DeviceAction {
open,
close,
brightness,
color,
colortemperature,
increase_bright,
decrease_bright,
increase_ct,
decrease_ct,
background_color,
background_brightness,
background_on,
background_off,
nightlight_off,
nightlight_on;
private String mStrValue;
private int mIntValue;
private int mIntDuration;
public void putValue(String value) {
this.mStrValue = value;
}
public void putValue(int value) {
this.mIntValue = value;
}
public void putDuration(int duration) {
this.mIntDuration = duration;
}
public String strValue() {
return mStrValue;
}
public int intValue() {
return mIntValue;
}
public int intDuration() {
return mIntDuration;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.yeelight.internal.lib.enums;
/**
* @author Coaster Li - Initial contribution
*/
public enum DeviceMode {
/*
* color temperature mode
*/
MODE_SUNHINE,
/*
* color mode
*/
MODE_COLOR,
/*
* color mode
*/
MODE_HSV,
/*
* color flow mode
*/
MODE_COLORFLOW,
/*
* device is off.
*/
MODE_OFF
}

View File

@@ -0,0 +1,30 @@
/**
* 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.yeelight.internal.lib.enums;
/**
* The {@link DeviceType} lists the available device/model types.
*
* @author Coaster Li - Initial contribution
*/
public enum DeviceType {
mono,
ct_bulb,
color,
ceiling,
ceiling1,
ceiling3,
ceiling4,
stripe,
desklamp
}

View File

@@ -0,0 +1,47 @@
/**
* 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.yeelight.internal.lib.enums;
/**
* @author Coaster Li - Initial contribution
*/
public enum MethodAction {
PROP("get_prop"),
SWITCH("set_power"),
TOGGLE("toggle"),
BRIGHTNESS("set_bright"),
COLORTEMPERATURE("set_ct_abx"),
HSV("set_hsv"),
RGB("set_rgb"),
DEFAULT("set_default"),
STARTCF("start_cf"),
STOPCF("setop_cf"),
SCENE("set_scene"),
CRON_ADD("cron_add"),
CRON_GET("cron_get"),
CRON_DEL("cron_del"),
ADJUST("set_adjust"),
MUSIC("set_music"),
NAME("set_name"),
BG_HSV("bg_set_hsv"),
BG_RGB("bg_set_rgb"),
BG_BRIGHTNESS("bg_set_bright"),
BG_SWITCH("bg_set_power");
public String action;
MethodAction(String action) {
this.action = action;
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.yeelight.internal.lib.listeners;
import org.openhab.binding.yeelight.internal.lib.device.ConnectState;
/**
* @author Coaster Li - Initial contribution
*/
public interface DeviceConnectionStateListener {
void onConnectionStateChanged(ConnectState connectState);
}

View File

@@ -0,0 +1,24 @@
/**
* 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.yeelight.internal.lib.listeners;
import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
/**
* @author Coaster Li - Initial contribution
*/
public interface DeviceListener {
void onDeviceFound(DeviceBase device);
void onDeviceLost(DeviceBase device);
}

View File

@@ -0,0 +1,22 @@
/**
* 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.yeelight.internal.lib.listeners;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
/**
* @author Coaster Li - Initial contribution
*/
public interface DeviceStatusChangeListener {
void onStatusChanged(DeviceStatus status);
}

View File

@@ -0,0 +1,343 @@
/**
* 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.yeelight.internal.lib.services;
import java.awt.Color;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import org.openhab.binding.yeelight.internal.lib.device.DeviceBase;
import org.openhab.binding.yeelight.internal.lib.device.DeviceFactory;
import org.openhab.binding.yeelight.internal.lib.device.DeviceStatus;
import org.openhab.binding.yeelight.internal.lib.device.DeviceWithAmbientLight;
import org.openhab.binding.yeelight.internal.lib.device.DeviceWithNightlight;
import org.openhab.binding.yeelight.internal.lib.enums.DeviceAction;
import org.openhab.binding.yeelight.internal.lib.listeners.DeviceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DeviceManager} is a class for managing all devices.
*
* @author Coaster Li - Initial contribution
* @author Joe Ho - Added duration
* @author Nikita Pogudalov - Added name for Ceiling 1
*/
public class DeviceManager {
private final Logger logger = LoggerFactory.getLogger(DeviceManager.class);
private static final String TAG = DeviceManager.class.getSimpleName();
private static final String MULTI_CAST_HOST = "239.255.255.250";
private static final int MULTI_CAST_PORT = 1982;
private static final String DISCOVERY_MSG = "M-SEARCH * HTTP/1.1\r\n" + "HOST:" + MULTI_CAST_HOST + ":"
+ MULTI_CAST_PORT + "\r\n" + "MAN:\"ssdp:discover\"\r\n" + "ST:wifi_bulb\r\n";
private static final int TIMEOUT = 10000;
public static final DeviceManager sInstance = new DeviceManager();
public volatile boolean mSearching = false;
public Map<String, DeviceBase> mDeviceList = new HashMap<>();
public List<DeviceListener> mListeners = new ArrayList<>();
private ExecutorService executorService = Executors.newCachedThreadPool();
private DeviceManager() {
}
public static DeviceManager getInstance() {
return sInstance;
}
public void registerDeviceListener(DeviceListener listener) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
}
}
public void unregisterDeviceListener(DeviceListener listener) {
mListeners.remove(listener);
}
public void startDiscovery() {
startDiscovery(-1);
}
public void startDiscovery(long timeToStop) {
searchDevice();
if (timeToStop > 0) {
new Thread(() -> {
try {
Thread.sleep(timeToStop);
} catch (InterruptedException e) {
logger.debug("Interrupted while sleeping", e);
} finally {
stopDiscovery();
}
}).start();
}
}
public void stopDiscovery() {
mSearching = false;
}
private void searchDevice() {
if (mSearching) {
logger.debug("{}: Already in discovery, return!", TAG);
return;
}
logger.debug("Starting Discovery");
try {
final InetAddress multicastAddress = InetAddress.getByName(MULTI_CAST_HOST);
final List<NetworkInterface> networkInterfaces = getNetworkInterfaces();
mSearching = true;
for (final NetworkInterface networkInterface : networkInterfaces) {
logger.debug("Starting Discovery on: {}", networkInterface.getDisplayName());
executorService.execute(() -> {
try (MulticastSocket multiSocket = new MulticastSocket(MULTI_CAST_PORT)) {
multiSocket.setSoTimeout(TIMEOUT);
multiSocket.setNetworkInterface(networkInterface);
multiSocket.joinGroup(multicastAddress);
DatagramPacket dpSend = new DatagramPacket(DISCOVERY_MSG.getBytes(),
DISCOVERY_MSG.getBytes().length, multicastAddress, MULTI_CAST_PORT);
multiSocket.send(dpSend);
while (mSearching) {
byte[] buf = new byte[1024];
DatagramPacket dpRecv = new DatagramPacket(buf, buf.length);
try {
multiSocket.receive(dpRecv);
byte[] bytes = dpRecv.getData();
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < dpRecv.getLength(); i++) {
// parse /r
if (bytes[i] == Character.LINE_SEPARATOR) {
continue;
}
buffer.append((char) bytes[i]);
}
String receivedMessage = buffer.toString();
logger.debug("{}: got message: {}", TAG, receivedMessage);
// we can skip the request because we aren't interested in our own search broadcast
// message.
if (receivedMessage.startsWith("M-SEARCH")) {
continue;
}
String[] infos = receivedMessage.split("\n");
Map<String, String> bulbInfo = new HashMap<>();
for (String info : infos) {
int index = info.indexOf(':');
if (index == -1) {
continue;
}
String key = info.substring(0, index).trim();
String value = info.substring(index + 1).trim();
bulbInfo.put(key, value);
}
logger.debug("{}: got bulbInfo: {}", TAG, bulbInfo);
if (bulbInfo.containsKey("model") && bulbInfo.containsKey("id")) {
DeviceBase device = DeviceFactory.build(bulbInfo);
if (null == device) {
logger.warn("Found unsupported device.");
continue;
}
device.setDeviceName(bulbInfo.getOrDefault("name", ""));
if (mDeviceList.containsKey(device.getDeviceId())) {
updateDevice(mDeviceList.get(device.getDeviceId()), bulbInfo);
}
notifyDeviceFound(device);
}
} catch (SocketTimeoutException e) {
logger.debug("Error timeout: {}", e.getMessage(), e);
}
}
} catch (Exception e) {
if (!e.getMessage().contains("No IP addresses bound to interface")) {
logger.debug("Error getting ip addresses: {}", e.getMessage(), e);
}
}
});
}
} catch (IOException e) {
logger.debug("Error getting ip addresses: {}", e.getMessage(), e);
}
}
private List<NetworkInterface> getNetworkInterfaces() throws SocketException {
return Collections.list(NetworkInterface.getNetworkInterfaces()).stream().filter(device -> {
try {
return device.isUp() && !device.isLoopback();
} catch (SocketException e) {
logger.debug("Failed to get device information", e);
return false;
}
}).collect(Collectors.toList());
}
private void notifyDeviceFound(DeviceBase device) {
for (DeviceListener listener : mListeners) {
listener.onDeviceFound(device);
}
}
public void doAction(String deviceId, DeviceAction action) {
DeviceBase device = mDeviceList.get(deviceId);
if (device != null) {
switch (action) {
case open:
device.open(action.intDuration());
break;
case close:
device.close(action.intDuration());
break;
case brightness:
device.setBrightness(action.intValue(), action.intDuration());
break;
case color:
device.setColor(action.intValue(), action.intDuration());
break;
case colortemperature:
device.setCT(action.intValue(), action.intDuration());
break;
case increase_bright:
device.increaseBrightness(action.intDuration());
break;
case decrease_bright:
device.decreaseBrightness(action.intDuration());
break;
case increase_ct:
device.increaseCt(action.intDuration());
break;
case decrease_ct:
device.decreaseCt(action.intDuration());
break;
case background_color:
if (device instanceof DeviceWithAmbientLight) {
final String[] split = action.strValue().split(",");
((DeviceWithAmbientLight) device).setBackgroundColor(Integer.parseInt(split[0]),
Integer.parseInt(split[1]), action.intDuration());
}
break;
case background_brightness:
if (device instanceof DeviceWithAmbientLight) {
((DeviceWithAmbientLight) device).setBackgroundBrightness(action.intValue(),
action.intDuration());
}
break;
case background_on:
if (device instanceof DeviceWithAmbientLight) {
((DeviceWithAmbientLight) device).setBackgroundPower(true, action.intDuration());
}
break;
case background_off:
if (device instanceof DeviceWithAmbientLight) {
((DeviceWithAmbientLight) device).setBackgroundPower(false, action.intDuration());
}
break;
case nightlight_off:
if (device instanceof DeviceWithNightlight) {
((DeviceWithNightlight) device).toggleNightlightMode(false);
}
break;
case nightlight_on:
if (device instanceof DeviceWithNightlight) {
((DeviceWithNightlight) device).toggleNightlightMode(true);
}
break;
default:
break;
}
}
}
public void doCustomAction(String deviceId, String action, String params) {
DeviceBase device = mDeviceList.get(deviceId);
device.sendCustomCommand(action, params);
}
public void addDevice(DeviceBase device) {
mDeviceList.put(device.getDeviceId(), device);
}
public void updateDevice(DeviceBase device, Map<String, String> bulbInfo) {
String[] addressInfo = bulbInfo.get("Location").split(":");
device.setAddress(addressInfo[1].substring(2));
device.setPort(Integer.parseInt(addressInfo[2]));
device.setOnline(true);
Color color = new Color(Integer.parseInt(bulbInfo.get("rgb")));
DeviceStatus status = device.getDeviceStatus();
status.setR(color.getRed());
status.setG(color.getGreen());
status.setB(color.getBlue());
status.setCt(Integer.parseInt(bulbInfo.get("ct")));
status.setHue(Integer.parseInt(bulbInfo.get("hue")));
status.setSat(Integer.parseInt(bulbInfo.get("sat")));
}
public static String getDefaultName(DeviceBase device) {
if (device.getDeviceModel() != null && !device.getDeviceName().equals("")) {
return device.getDeviceName();
}
switch (device.getDeviceType()) {
case ceiling:
case ceiling3:
return "Yeelight LED Ceiling";
case ceiling1:
return "Yeelight LED Ceiling with night mode";
case ceiling4:
return "Yeelight LED Ceiling with ambient light";
case color:
return "Yeelight Color LED Bulb";
case mono:
return "Yeelight White LED Bulb";
case ct_bulb:
return "Yeelight White LED Bulb v2";
case stripe:
return "Yeelight Color LED Stripe";
case desklamp:
return "Yeelight Mi LED Desk Lamp";
default:
return "";
}
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="yeelight" 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>Yeelight Binding</name>
<description>This is the binding for Yeelight products.</description>
</binding:binding>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:device:base">
<parameter name="deviceId" type="text" required="true">
<label>Device ID</label>
<description>Id of the Yeelight device to connect with.</description>
</parameter>
<parameter name="duration" type="integer" unit="ms" min="50" max="10000">
<label>Duration</label>
<description>Duration of transition of events such as on/off, change of brightness and change of color, in
milliseconds.</description>
<unitLabel>milliseconds</unitLabel>
<default>500</default>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="ceiling">
<label>Yeelight LED Ceiling</label>
<description>Yeelight LED Ceiling lamp</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="ceiling1">
<label>Yeelight LED Ceiling (v1)</label>
<description>Yeelight LED Ceiling lamp with Night Mode</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="nightlight" typeId="nightlight"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="ceiling4">
<label>Yeelight LED Ceiling (v4)</label>
<description>Yeelight LED Ceiling with ambient light</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="backgroundColor" typeId="backgroundColor"/>
<channel id="nightlight" typeId="nightlight"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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">
<!-- Brightness Channel -->
<channel-type id="brightness">
<item-type>Dimmer</item-type>
<label>Brightness</label>
<description>The brightness channel allows to control the brightness of a light.
It is also possible to switch the
light on and off.
</description>
<category>DimmableLight</category>
<tags>
<tag>Lighting</tag>
</tags>
</channel-type>
<!-- Color Channel -->
<channel-type id="color">
<item-type>Color</item-type>
<label>Color</label>
<description>The color channel allows to control the color of a light.</description>
<category>ColorLight</category>
<tags>
<tag>Lighting</tag>
</tags>
</channel-type>
<!-- Color Temperature Channel -->
<channel-type id="colorTemperature">
<item-type>Dimmer</item-type>
<label>Color Temperature</label>
<description>The CT channel allows to control the CT of a light.</description>
<category>DimmableCT</category>
<tags>
<tag>ColorTemperature</tag>
</tags>
</channel-type>
<!-- Command Channel -->
<channel-type id="command">
<item-type>String</item-type>
<label>Command</label>
<description>Send a command directly to the device. For advanced users only.</description>
</channel-type>
<!-- Background Color Channel -->
<channel-type id="backgroundColor">
<item-type>Color</item-type>
<label>Background Color</label>
<description>The color channel allows to control the color of a light.</description>
<category>ColorLight</category>
<tags>
<tag>Lighting</tag>
</tags>
</channel-type>
<channel-type id="nightlight">
<item-type>Switch</item-type>
<label>nightlight</label>
<description>The nightlight channel allows to switch to nightlight mode.
</description>
<tags>
<tag>Lighting</tag>
</tags>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="ct_bulb">
<label>Yeelight White LED Bulb v2</label>
<description>Yeelight White LED Bulb v2</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="desklamp">
<label>Yeelight MI LED Desk Lamp</label>
<description>Yeelight MI LED Desk Lamp</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="dolphin">
<label>Yeelight White LED Bulb</label>
<description>Yeelight White LED Bulb</description>
<channels>
<channel id="brightness" typeId="brightness"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="stripe">
<label>Yeelight Color LED Stripe</label>
<description>Yeelight Color LED Stripe</description>
<channels>
<channel id="color" typeId="color"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="yeelight"
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="wonder">
<label>Yeelight Color LED Bulb</label>
<description>Yeelight Color LED Bulb</description>
<channels>
<channel id="color" typeId="color"/>
<channel id="colorTemperature" typeId="colorTemperature"/>
<channel id="command" typeId="command"/>
</channels>
<config-description-ref uri="thing-type:device:base"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,94 @@
/**
* 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.yeelight.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.openhab.binding.yeelight.internal.YeelightBindingConstants.PARAMETER_DEVICE_ID;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mock;
import org.openhab.binding.yeelight.internal.handler.*;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ThingHandler;
/**
* Unit tests for {@link YeelightHandlerFactory}
*
* @author Viktor Koop - Initial contribution
* @author Nikita Pogudalov - Added YeelightCeilingWithNightHandler for Ceiling 1
*/
@RunWith(value = Parameterized.class)
public class YeelightHandlerFactoryTest {
private static final List<Object[]> TESTS = Arrays.asList(
new Object[][] { { "dolphin", YeelightWhiteHandler.class }, { "ct_bulb", YeelightWhiteHandler.class },
{ "wonder", YeelightColorHandler.class }, { "stripe", YeelightStripeHandler.class },
{ "ceiling", YeelightCeilingHandler.class }, { "ceiling3", YeelightCeilingHandler.class },
{ "ceiling1", YeelightCeilingWithNightHandler.class }, { "desklamp", YeelightCeilingHandler.class },
{ "ceiling4", YeelightCeilingWithAmbientHandler.class }, { "unknown", null } });
private final YeelightHandlerFactory factory = new YeelightHandlerFactory();
@Mock
private Thing thing;
private final String name;
private final Class<?> clazz;
public YeelightHandlerFactoryTest(String name, Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
@Parameters(name = "{0} - {1}")
public static List<Object[]> data() {
return TESTS;
}
@Before
public void setUp() {
initMocks(this);
Configuration configuration = new Configuration();
configuration.put(PARAMETER_DEVICE_ID, "");
when(thing.getConfiguration()).thenReturn(configuration);
}
@Test
public void testCorrectClass() {
when(thing.getThingTypeUID()).thenReturn(new ThingTypeUID(YeelightBindingConstants.BINDING_ID, name));
ThingHandler handler = factory.createHandler(thing);
if (null == clazz) {
assertNull(name + " should not return any handler but null", handler);
} else {
assertNotNull(name + " should no return null handler", handler);
assertEquals(" should be correct matcher", clazz, handler.getClass());
assertEquals(thing, handler.getThing());
}
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.yeelight.internal.lib.device;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.yeelight.internal.lib.enums.ActiveMode;
/**
* Unit tests for {@link DeviceBase}
*
* @author Viktor Koop - Initial contribution
*/
public class DeviceBaseTest {
private DeviceBase deviceBase;
@Before
public void setUp() {
deviceBase = new DeviceBase("myid") {
};
}
@Test
public void testSetColorTemp() {
String json = "{\"method\":\"props\",\"params\":{\"ct\":4013}}";
deviceBase.onNotify(json);
final DeviceStatus deviceStatus = deviceBase.getDeviceStatus();
assertEquals(4013, deviceStatus.getCt());
}
@Test
public void testSwitchToNightlightMode() {
deviceBase.onNotify("{\"method\":\"props\",\"params\":{\"nl_br\":96,\"active_mode\":1,\"active_bright\":96}}");
final DeviceStatus deviceStatus = deviceBase.getDeviceStatus();
assertEquals(ActiveMode.MOONLIGHT_MODE, deviceStatus.getActiveMode());
}
@Test
public void testNightlightBrightnessUpdate() {
String json = "{\"method\":\"props\",\"params\":{\"nl_br\":61,\"active_bright\":61}}";
deviceBase.onNotify(json);
final DeviceStatus deviceStatus = deviceBase.getDeviceStatus();
assertEquals(61, deviceStatus.getBrightness());
}
}