Skip to content

Commit

Permalink
[yeelight] Add support for yeelight 650 with ambient light (Closes op…
Browse files Browse the repository at this point in the history
…enhab#6… (openhab#6749)

* [yeelight] Add support for yeelight 650 with ambient light (Closes openhab#6227)

Signed-off-by: Viktor Koop <viktor.koop@googlemail.com>
Signed-off-by: Hans-Reiner Hoffmann <hans-reiner.hoffmann@gmx.de>
  • Loading branch information
vkoop authored and Hans-Reiner committed Apr 11, 2020
1 parent 70b3c85 commit 7ff4781
Show file tree
Hide file tree
Showing 30 changed files with 889 additions and 197 deletions.
3 changes: 3 additions & 0 deletions bundles/org.openhab.binding.yeelight/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ All devices support some of the following channels:
|`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 `ceiling4`.|


## Full Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class YeelightBindingConstants {
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");
Expand All @@ -44,6 +45,8 @@ public class YeelightBindingConstants {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@
import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.eclipse.smarthome.core.thing.binding.ThingHandlerFactory;
import org.openhab.binding.yeelight.internal.handler.YeelightCeilingHandler;
import org.openhab.binding.yeelight.internal.handler.YeelightColorHandler;
import org.openhab.binding.yeelight.internal.handler.YeelightStripeHandler;
import org.openhab.binding.yeelight.internal.handler.YeelightWhiteHandler;
import org.openhab.binding.yeelight.internal.handler.*;
import org.osgi.service.component.annotations.Component;

/**
Expand All @@ -36,10 +33,12 @@
@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);
Expand All @@ -65,6 +64,8 @@ protected ThingHandler createHandler(Thing thing) {
} else if (thingTypeUID.equals(THING_TYPE_CEILING) || thingTypeUID.equals(THING_TYPE_CEILING1)
|| thingTypeUID.equals(THING_TYPE_CEILING3) || thingTypeUID.equals(THING_TYPE_DESKLAMP)) {
return new YeelightCeilingHandler(thing);
} else if (thingTypeUID.equals(THING_TYPE_CEILING4)) {
return new YeelightCeilingWithAmbientHandler(thing);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ public void onDeviceLost(DeviceBase device) {
private ThingUID getThingUID(DeviceBase device) {
switch (device.getDeviceType()) {
case ceiling:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING, device.getDeviceId());
case ceiling1:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING, device.getDeviceId());
case ceiling3:
return new ThingUID(YeelightBindingConstants.THING_TYPE_CEILING, 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:
Expand All @@ -113,6 +113,8 @@ private ThingTypeUID getThingTypeUID(DeviceBase device) {
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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.HSBType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.types.Command;
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;

/**
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,8 @@

import java.util.concurrent.TimeUnit;

import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.HSBType;
import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.library.types.*;
import org.eclipse.smarthome.core.thing.*;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
Expand Down Expand Up @@ -84,6 +75,8 @@ private DeviceType getDeviceModel(ThingTypeUID typeUID) {
return DeviceType.ceiling;
} 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)) {
Expand Down Expand Up @@ -143,19 +136,26 @@ public void handleCommandHelper(ChannelUID channelUID, Command command, String l
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:
HSBType hsb = new HSBType();
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;
}
Expand Down Expand Up @@ -194,6 +194,25 @@ public void handleCommandHelper(ChannelUID channelUID, Command command, String l
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(";");
Expand All @@ -206,6 +225,7 @@ public void handleCommandHelper(ChannelUID channelUID, Command command, String l
handleCustomCommand(methodAction, methodParams);
updateState(channelUID, new StringType(""));
}
break;
default:
break;
}
Expand Down Expand Up @@ -258,6 +278,30 @@ void handleHSBCommand(HSBType color) {
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);
Expand All @@ -270,7 +314,7 @@ void handleCustomCommand(String action, String params) {
}

@Override
public void onStatusChanged(String prop, DeviceStatus status) {
public void onStatusChanged(DeviceStatus status) {
logger.debug("UpdateState->{}", status);
updateUI(status);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@ public CeilingDevice(String id) {
public void onNotify(String msg) {
JsonObject result = new JsonParser().parse(msg).getAsJsonObject();
try {
String id = "-1";
if (result.has("id")) {
id = result.get("id").getAsString();
String id = result.get("id").getAsString();
// for cmd transaction.

if (mQueryList.contains(id)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* 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));
}
}
}
Loading

0 comments on commit 7ff4781

Please sign in to comment.