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