Skip to content

Commit

Permalink
[miio] Improve Yeelight Color channel (openhab#7431)
Browse files Browse the repository at this point in the history
* [miio] Improve Yeelight Color channel

* fix yeelight delay channel
* allow better control for Color item
* Allow for google home  support without rules
* make yeelight dimmer more intuitive
https://community.openhab.org/t/xiaomi-robot-vacuum-binding/31317/1418

Signed-off-by: Marcel Verpaalen <marcel@verpaalen.com>
Signed-off-by: Daan Meijer <daan@studioseptember.nl>
  • Loading branch information
marcelrv authored and DaanMeijer committed Sep 1, 2020
1 parent 54f1350 commit 3441b75
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public enum MiIoCommand {
SET_MODE_BASIC("set_mode"),
SET_POWER("set_power"),
SET_BRIGHT("set_bright"),
SET_RGB("set_rgb"),
SET_WIFI_LET("set_wifi_led"),
SET_FAVORITE("set_level_favorite"),
ACTION("action"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.HSBType;
import org.eclipse.smarthome.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -40,35 +42,33 @@ public class ActionConditions {
* @return value in case firmware is matching, return null if not
*/
private static @Nullable JsonElement firmwareCheck(MiIoDeviceActionCondition condition,
Map<String, Object> deviceVariables, @Nullable JsonElement value) {
@Nullable Map<String, Object> deviceVariables, @Nullable JsonElement value) {
// TODO: placeholder for firmware version check to allow for firmware dependent actions
return value;
}

/**
* Check if the value is a valid brightness between 1-99.
* If <1 returns Off, if >99 returns On to activate the power On/Off switch
* Check if the value is a valid brightness for operating power On/Off switch.
* If brightness <1 returns Off, if >=1 returns On
*
* @param value
* @return
*/
private static @Nullable JsonElement brightness(@Nullable JsonElement value) {
if (value != null && value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
int intVal = value.getAsInt();
if (intVal > 99) {
return new JsonPrimitive("on");
}
if (intVal < 1) {
if (value.getAsInt() < 1) {
return new JsonPrimitive("off");
} else {
return new JsonPrimitive("on");
}
} else {
LOGGER.debug("Could not parse brightness. Value '{}' is not an int", value);
}
return null;
return value;
}

/**
* Check if the value is a valid brightness between 1-99 which can be send to brightness channel.
* Check if the value is a valid brightness between 1-100 which can be send to brightness channel.
* If not returns a null
*
* @param value
Expand All @@ -77,24 +77,45 @@ public class ActionConditions {
private static @Nullable JsonElement brightnessExists(@Nullable JsonElement value) {
if (value != null && value.isJsonPrimitive() && value.getAsJsonPrimitive().isNumber()) {
int intVal = value.getAsInt();
if (intVal > 0 && intVal < 99) {
if (intVal > 0 && intVal <= 100) {
return value;
} else if (intVal > 100) {
return new JsonPrimitive(100);
}
return null;
} else {
LOGGER.debug("Could not parse brightness. Value '{}' is not an int", value);
}
return value;
}

/**
* Check if the value is a color which can be send to Color channel.
* If not returns a null
*
* @param command
*
* @param value
* @return
*/
private static @Nullable JsonElement hsbOnly(@Nullable Command command, @Nullable JsonElement value) {
if (command != null && command instanceof HSBType) {
return value;
}
return null;
}

public static @Nullable JsonElement executeAction(MiIoDeviceActionCondition condition,
Map<String, Object> deviceVariables, @Nullable JsonElement value) {
@Nullable Map<String, Object> deviceVariables, @Nullable JsonElement value, @Nullable Command command) {
switch (condition.getName().toUpperCase()) {
case "FIRMWARE":
return firmwareCheck(condition, deviceVariables, value);
case "BRIGHTNESSEXISTING":
return brightnessExists(value);
case "BRIGHTNESSONOFF":
return brightness(value);
case "HSBONLY":
return hsbOnly(command, value);
default:
LOGGER.debug("Condition {} not found. Returning '{}'", condition,
value != null ? value.toString() : "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void handleCommand(ChannelUID channelUID, Command command) {
if (channelUID.getId().equals(CHANNEL_COMMAND)) {
cmds.put(sendCommand(command.toString()), command.toString());
}
logger.debug("Locating action for channel {}: {}", channelUID.getId(), command);
logger.debug("Locating action for channel '{}': '{}'", channelUID.getId(), command);
if (!actions.isEmpty()) {
if (actions.containsKey(channelUID)) {
int valuePos = 0;
Expand All @@ -156,8 +156,9 @@ public void handleCommand(ChannelUID channelUID, Command command) {
(color.getRed() << 16) + (color.getGreen() << 8) + color.getBlue());
} else if (command instanceof DecimalType) {
// actually brightness is being set instead of a color
cmd = "set_bright";
value = new JsonPrimitive(((DecimalType) command).toBigDecimal());
} else if (command instanceof OnOffType) {
value = new JsonPrimitive(command == OnOffType.ON ? 100 : 0);
} else {
logger.debug("Unsupported command for COLOR: {}", command);
}
Expand All @@ -184,14 +185,15 @@ public void handleCommand(ChannelUID channelUID, Command command) {
} else {
value = new JsonPrimitive(command.toString().toLowerCase());
}
final MiIoDeviceActionCondition miIoDeviceActionCondition = action.getCondition();
if (miIoDeviceActionCondition != null) {
value = ActionConditions.executeAction(miIoDeviceActionCondition, deviceVariables, value,
command);
}
// Check for miot channel
if (miIoBasicChannel.isMiOt()) {
value = miotTransform(miIoBasicChannel, value);
}
final MiIoDeviceActionCondition miIoDeviceActionCondition = action.getCondition();
if (miIoDeviceActionCondition != null) {
value = ActionConditions.executeAction(miIoDeviceActionCondition, deviceVariables, value);
}
if (paramType != CommandParameterType.NONE && value != null) {
if (parameters.size() > 0) {
parameters.set(valuePos, value);
Expand Down Expand Up @@ -357,7 +359,13 @@ private boolean buildChannelStructure(String deviceName) {
if (channelUID != null) {
actions.put(channelUID, miChannel);
channelsAdded++;
} else {
logger.debug("Channel for {} ({}) not loaded", miChannel.getChannel(),
miChannel.getFriendlyName());
}
} else {
logger.debug("Channel {} ({}), not loaded, missing type", miChannel.getChannel(),
miChannel.getFriendlyName());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"yeelink.light.bslamp1",
"yeelink.light.bslamp2"
],
"maxProperties": 7,
"channels": [
{
"property": "power",
Expand Down Expand Up @@ -53,7 +54,7 @@
"property": "delayoff",
"friendlyName": "Shutdown Timer",
"channel": "delayoff",
"type": "String",
"type": "Number",
"refresh": true,
"ChannelGroup": "actions",
"actions": [
Expand Down Expand Up @@ -120,13 +121,28 @@
{
"command": "set_rgb",
"parameterType": "COLOR",
"parameter1": "\"smooth\"",
"parameter2": "500",
"condition": {
"name": "HSBOnly"
},
"parameters": [
"$value$",
"smooth",
500
]
},
{
"command": "set_bright",
"parameterType": "NUMBER",
"condition": {
"name": "BrightnessExisting"
}
},
{
"command": "set_power",
"parameterType": "ONOFF",
"condition": {
"name": "BrightnessOnOff"
}
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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.miio.internal;

import static org.junit.Assert.*;

import java.util.Collections;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.miio.internal.basic.ActionConditions;
import org.openhab.binding.miio.internal.basic.MiIoDeviceActionCondition;

import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;

/**
* Test case for {@link ActionConditions}
*
* @author Marcel Verpaalen - Initial contribution
*
*/
@NonNullByDefault
public class ActionConditionTest {

@Test
public void assertBrightnessExisting() {
MiIoDeviceActionCondition condition = new MiIoDeviceActionCondition();
condition.setName("BrightnessExisting");
Map<String, Object> deviceVariables = Collections.emptyMap();
JsonElement value = new JsonPrimitive(1);
JsonElement resp = ActionConditions.executeAction(condition, deviceVariables, value, null);
// dimmed to 1
assertNotNull(resp);
assertEquals(new JsonPrimitive(1), resp);

// fully on
value = new JsonPrimitive(100);
resp = ActionConditions.executeAction(condition, deviceVariables, value, null);
assertNotNull(resp);
assertEquals(new JsonPrimitive(100), resp);

// >100
value = new JsonPrimitive(200);
resp = ActionConditions.executeAction(condition, deviceVariables, value, null);
assertNotNull(resp);
assertEquals(new JsonPrimitive(100), resp);

// switched off = invalid brightness
value = new JsonPrimitive(0);
resp = ActionConditions.executeAction(condition, deviceVariables, value, null);
assertNull(resp);
assertNotEquals(new JsonPrimitive(0), resp);

value = new JsonPrimitive(-1);
resp = ActionConditions.executeAction(condition, deviceVariables, value, null);
assertNull(resp);
assertNotEquals(new JsonPrimitive(-1), resp);
}
}

0 comments on commit 3441b75

Please sign in to comment.