From 87ce8a99cf7139caf0cc3637896ed23d9c7cbdc5 Mon Sep 17 00:00:00 2001 From: xLAva Date: Wed, 23 Jan 2019 23:00:11 +0100 Subject: [PATCH] [Hue] Implemented fade time Implemented fade time config for light things. Implemented ThingAction fadingLightCommand for Hue lights Updated README.md Bug: #1722 Signed-off-by: Jochen jochen.leopold@model-view.com --- .../internal/handler/HueLightHandlerTest.java | 62 ++++++------- .../ESH-INF/i18n/hue.properties | 11 +++ .../ESH-INF/thing/ColorLight.xml | 5 ++ .../ESH-INF/thing/ColorTemperatureLight.xml | 5 ++ .../ESH-INF/thing/DimmableLight.xml | 5 ++ .../ESH-INF/thing/DimmablePlug.xml | 5 ++ .../ESH-INF/thing/ExtendedColorLight.xml | 5 ++ .../META-INF/MANIFEST.MF | 1 + .../binding/org.openhab.binding.hue/README.md | 42 ++++++++- .../binding/hue/internal/FullLight.java | 6 ++ .../hue/internal/HueBindingConstants.java | 1 + .../binding/hue/internal/StateUpdate.java | 2 +- .../hue/internal/action/LightActions.java | 87 +++++++++++++++++++ .../hue/internal/handler/HueLightHandler.java | 50 +++++++++-- 14 files changed, 247 insertions(+), 40 deletions(-) create mode 100644 addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java diff --git a/addons/binding/org.openhab.binding.hue.test/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java b/addons/binding/org.openhab.binding.hue.test/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java index b51ed7e22d58d..f7e444b6b1e59 100644 --- a/addons/binding/org.openhab.binding.hue.test/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java +++ b/addons/binding/org.openhab.binding.hue.test/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java @@ -99,43 +99,43 @@ public void assertCommandForOsramPar16_50ForBrightnessChannelOff() { @Test public void assertCommandForColorChannelOn() { - String expectedReply = "{\"on\" : true}"; + String expectedReply = "{\"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply); } @Test public void assertCommandForColorTemperatureChannelOn() { - String expectedReply = "{\"on\" : true}"; + String expectedReply = "{\"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelOff() { - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply); } @Test public void assertCommandForColorTemperatureChannelOff() { - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply); } @Test public void assertCommandForColorTemperatureChannel0Percent() { - String expectedReply = "{\"ct\" : 153}"; + String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}"; assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply); } @Test public void assertCommandForColorTemperatureChannel50Percent() { - String expectedReply = "{\"ct\" : 327}"; + String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}"; assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply); } @Test public void assertCommandForColorTemperatureChannel1000Percent() { - String expectedReply = "{\"ct\" : 500}"; + String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}"; assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply); } @@ -159,140 +159,140 @@ public void assertPercentageValueOfColorTemperatureWhenCt500() { @Test public void assertCommandForColorChannel0Percent() { - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannel50Percent() { - String expectedReply = "{\"bri\" : 127, \"on\" : true}"; + String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannel100Percent() { - String expectedReply = "{\"bri\" : 254, \"on\" : true}"; + String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelBlack() { - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelRed() { - String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0}"; + String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelGreen() { - String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845}"; + String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelBlue() { - String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690}"; + String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply); } @Test public void assertCommandForColorChannelWhite() { - String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0}"; + String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply); } @Test public void assertXYCommandForColorChannelBlack() { - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply); } @Test public void assertXYCommandForColorChannelWhite() { - String expectedReply = "{\"xy\" : [ 0.31271592 , 0.32900152 ], \"bri\" : 254}"; + String expectedReply = "{\"xy\" : [ 0.31271592 , 0.32900152 ], \"bri\" : 254, \"transitiontime\" : 4}"; assertSendCommandForColor(HSBType.WHITE, new HueLightState().colormode(ColorMode.XY), expectedReply); } @Test public void assertXYCommandForColorChannelColorful() { - String expectedReply = "{\"xy\" : [ 0.16969365 , 0.12379659 ], \"bri\" : 127}"; + String expectedReply = "{\"xy\" : [ 0.16969365 , 0.12379659 ], \"bri\" : 127, \"transitiontime\" : 4}"; assertSendCommandForColor(new HSBType("220,90,50"), new HueLightState().colormode(ColorMode.XY), expectedReply); } @Test public void asserCommandForColorChannelIncrease() { HueLightState currentState = new HueLightState().bri(1).on(false); - String expectedReply = "{\"bri\" : 30, \"on\" : true}"; + String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply); currentState.bri(200).on(true); - expectedReply = "{\"bri\" : 230}"; + expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}"; assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply); currentState.bri(230); - expectedReply = "{\"bri\" : 254}"; + expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}"; assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply); } @Test public void asserCommandForColorChannelDecrease() { HueLightState currentState = new HueLightState().bri(200); - String expectedReply = "{\"bri\" : 170}"; + String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}"; assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply); currentState.bri(20); - expectedReply = "{\"on\" : false}"; + expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply); } @Test public void assertCommandForBrightnessChannel50Percent() { HueLightState currentState = new HueLightState(); - String expectedReply = "{\"bri\" : 127, \"on\" : true}"; + String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForBrightness(new PercentType(50), currentState, expectedReply); } @Test public void assertCommandForBrightnessChannelIncrease() { HueLightState currentState = new HueLightState().bri(1).on(false); - String expectedReply = "{\"bri\" : 30, \"on\" : true}"; + String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply); currentState.bri(200).on(true); - expectedReply = "{\"bri\" : 230}"; + expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}"; assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply); currentState.bri(230); - expectedReply = "{\"bri\" : 254}"; + expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}"; assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply); } @Test public void assertCommandForBrightnessChannelDecrease() { HueLightState currentState = new HueLightState().bri(200); - String expectedReply = "{\"bri\" : 170}"; + String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}"; assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply); currentState.bri(20); - expectedReply = "{\"on\" : false}"; + expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply); } @Test public void assertCommandForBrightnessChannelOff() { HueLightState currentState = new HueLightState(); - String expectedReply = "{\"on\" : false}"; + String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}"; assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply); } @Test public void assertCommandForBrightnessChannelOn() { HueLightState currentState = new HueLightState(); - String expectedReply = "{\"on\" : true}"; + String expectedReply = "{\"on\" : true, \"transitiontime\" : 4}"; assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply); } diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/i18n/hue.properties b/addons/binding/org.openhab.binding.hue/ESH-INF/i18n/hue.properties index 9fc5e0862ce30..fa88cecbf0c88 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/i18n/hue.properties +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/i18n/hue.properties @@ -14,3 +14,14 @@ offline.light-not-reachable = Hue bridge reports light as not reachable. offline.sensor-not-reachable = Hue bridge reports sensor as not reachable. offline.light-removed = Hue bridge reports light as removed. offline.sensor-removed = Hue bridge reports sensor as removed. + +#LightActions +actionLabel=send a light command with a custom fade time +actionDesc=Send a light command with a custom fade time. + +actionInputChannelLabel=Channel +actionInputChannelDesc=The channel to send the command to. +actionInputCommandLabel=Command +actionInputCommandDesc=The Command for the light. +actionInputFadeTimeLabel=FadeTime +actionInputFadeTimeDesc=The fade time to use for the light command in ms. \ No newline at end of file diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorLight.xml b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorLight.xml index 3d99823009449..5da430f588dac 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorLight.xml +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorLight.xml @@ -26,6 +26,11 @@ The light identifier identifies one certain hue light. true + + + Fade time in ms for changing values + 400 + diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml index d2447eb0ce546..fe76bf2442374 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ColorTemperatureLight.xml @@ -27,6 +27,11 @@ The light identifier identifies one certain hue light. true + + + Fade time in ms for changing values + 400 + diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmableLight.xml b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmableLight.xml index 45721902e37d8..96c789df40d0c 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmableLight.xml +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmableLight.xml @@ -25,6 +25,11 @@ The light identifier identifies one certain hue light. true + + + Fade time in ms for changing values + 400 + diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmablePlug.xml b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmablePlug.xml index 5d2bbec20dd69..0e1169c26d7e8 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmablePlug.xml +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/DimmablePlug.xml @@ -24,6 +24,11 @@ The identifier that is used within the hue bridge. true + + + Fade time in ms for changing values + 400 + diff --git a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ExtendedColorLight.xml b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ExtendedColorLight.xml index 8e2da5b0dcebb..c5c7215ecbb7f 100644 --- a/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ExtendedColorLight.xml +++ b/addons/binding/org.openhab.binding.hue/ESH-INF/thing/ExtendedColorLight.xml @@ -27,6 +27,11 @@ The light identifier identifies one certain hue light. true + + + Fade time in ms for changing values + 400 + diff --git a/addons/binding/org.openhab.binding.hue/META-INF/MANIFEST.MF b/addons/binding/org.openhab.binding.hue/META-INF/MANIFEST.MF index df8cdcdeb7deb..12b15c82d6e6e 100644 --- a/addons/binding/org.openhab.binding.hue/META-INF/MANIFEST.MF +++ b/addons/binding/org.openhab.binding.hue/META-INF/MANIFEST.MF @@ -13,6 +13,7 @@ Import-Package: com.google.gson.stream, javax.measure.quantity, org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.smarthome.automation.annotation;resolution:=optional, org.eclipse.smarthome.config.core, org.eclipse.smarthome.config.core.status, org.eclipse.smarthome.config.discovery, diff --git a/addons/binding/org.openhab.binding.hue/README.md b/addons/binding/org.openhab.binding.hue/README.md index 63419ac19ffb6..ada43bd27f746 100644 --- a/addons/binding/org.openhab.binding.hue/README.md +++ b/addons/binding/org.openhab.binding.hue/README.md @@ -38,8 +38,8 @@ The following matrix lists the capabilities (channels) for each type: | Thing type | On/Off | Brightness | Color | Color Temperature | |-------------|:------:|:----------:|:-----:|:-----------------:| -| 0000 | X | | | | -| 0010 | X | | | | +| 0000 | X | | | | +| 0010 | X | | | | | 0100 | X | X | | | | 0110 | X | X | | | | 0200 | X | | X | | @@ -116,6 +116,19 @@ or 0107 motion-sensor [ sensorId="4" ] ``` +The following device types also have an optional configuration value to specify the fade time in milliseconds for the transition to a new state: +* Dimmable Light +* Dimmable Plug-in Unit +* Colour Light +* Extended Colour Light +* Colour Temperature Light + +| Parameter | Description | +|-----------|-------------------------------------------------------------------------------| +| lightId | Number of the device provided by the Hue bridge. **Mandatory** | +| fadetime | Fade time in Milliseconds to a new state (min="0", step="100", default="400") | + + ## Channels The devices support some of the following channels: @@ -178,6 +191,29 @@ The `tap_switch_event` can trigger one of the following events: | Button 3 | Button 3 | 17 | | Button 4 | Button 4 | 18 | + +## Rule Actions + +This binding includes a rule action, which allows to change a light channel with a specific fading time from within rules. +There is a separate instance for each light, which can be retrieved e.g. through + +``` +val hueActions = getActions("hue","hue:0210:00178810d0dc:1") +``` + +where the first parameter always has to be `hue` and the second is the full Thing UID of the light that should be used. +Once this action instance is retrieved, you can invoke the `fadingLightCommand(String channel, Command command, DecimalType fadeTime)` method on it: + +``` +hueActions.fadingLightCommand("color", new PercentType(100), new DecimalType(1000)) +``` + +| Parameter | Description | +|-----------|--------------------------------------------------------------------------------------------------| +| channel | The following channels have fade time support: **brightness, color, color_temperature, switch** | +| command | All commands supported by the channel can be used | +| fadeTime | Fade time in Milliseconds to a new light value (min="0", step="100") | + ## Full Example In this example **bulb1** is a standard Philips Hue bulb (LCT001) which supports `color` and `color_temperature`. @@ -190,7 +226,7 @@ And there is one Hue Motion Sensor (represented by three devices) and a Hue Dimm ``` Bridge hue:bridge:1 [ ipAddress="192.168.0.64" ] { 0210 bulb1 [ lightId="1" ] - 0220 bulb2 [ lightId="2" ] + 0220 bulb2 [ lightId="2" fadetime=300 ] 0106 light-level-sensor [ sensorId="3" ] 0107 motion-sensor [ sensorId="4" ] 0302 temperature-sensor [ sensorId="5" ] diff --git a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/FullLight.java b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/FullLight.java index 7b2b901720533..b8ad612c38b2d 100644 --- a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/FullLight.java +++ b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/FullLight.java @@ -13,6 +13,7 @@ package org.openhab.binding.hue.internal; import java.lang.reflect.Type; +import java.time.Duration; import java.util.Map; import com.google.gson.reflect.TypeToken; @@ -30,6 +31,7 @@ public class FullLight extends FullHueObject { }.getType(); private State state; + private final long fadetime = 400; // milliseconds FullLight() { } @@ -42,4 +44,8 @@ public class FullLight extends FullHueObject { public State getState() { return state; } + + public Duration getFadeTime() { + return Duration.ofMillis(fadetime); + } } diff --git a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java index 815fbdd1a1f42..91da0cee02427 100644 --- a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java +++ b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/HueBindingConstants.java @@ -81,6 +81,7 @@ public class HueBindingConstants { public static final String SENSOR_ID = "sensorId"; public static final String PRODUCT_NAME = "productName"; public static final String UNIQUE_ID = "uniqueId"; + public static final String FADETIME = "fadetime"; public static final String NORMALIZE_ID_REGEX = "[^a-zA-Z0-9_]"; } diff --git a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/StateUpdate.java b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/StateUpdate.java index 67cc294c9e092..ec4b8f18ed2de 100644 --- a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/StateUpdate.java +++ b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/StateUpdate.java @@ -187,7 +187,7 @@ public StateUpdate setEffect(Effect effect) { * @param timeMillis time in milliseconds [0..6553600] * @return this object for chaining calls */ - public StateUpdate setTransitionTime(int timeMillis) { + public StateUpdate setTransitionTime(long timeMillis) { if (timeMillis < 0 || timeMillis > 6553600) { throw new IllegalArgumentException("Transition time out of range"); } diff --git a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java new file mode 100644 index 0000000000000..b990c3d8eb2c4 --- /dev/null +++ b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2010-2019 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.hue.internal.action; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.automation.annotation.ActionInput; +import org.eclipse.smarthome.automation.annotation.RuleAction; +import org.eclipse.smarthome.core.library.types.DecimalType; +import org.eclipse.smarthome.core.thing.binding.ThingActions; +import org.eclipse.smarthome.core.thing.binding.ThingActionsScope; +import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.types.Command; +import org.openhab.binding.hue.internal.handler.HueLightHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the automation engine action handler service for the + * fadingLightCommand action. + * + * @author Jochen Leopold - Initial contribution + */ +@ThingActionsScope(name = "hue") +@NonNullByDefault +public class LightActions implements ThingActions { + private final Logger logger = LoggerFactory.getLogger(LightActions.class); + private @Nullable HueLightHandler handler; + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + this.handler = (HueLightHandler) handler; + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return this.handler; + } + + @RuleAction(label = "@text/actionLabel", description = "@text/actionDesc") + public void fadingLightCommand( + @ActionInput(name = "channel", label = "@text/actionInputChannelLabel", description = "@text/actionInputChannelDesc") @Nullable String channel, + @ActionInput(name = "command", label = "@text/actionInputCommandLabel", description = "@text/actionInputCommandDesc") @Nullable Command command, + @ActionInput(name = "fadeTime", label = "@text/actionInputFadeTimeLabel", description = "@text/actionInputFadeTimeDesc") @Nullable DecimalType fadeTime) { + HueLightHandler lightHandler = handler; + if (lightHandler == null) { + logger.warn("Hue Action service ThingHandler is null!"); + return; + } + + if (channel == null) { + logger.debug("skipping Hue fadingLightCommand to channel '{}' due to null value.", channel); + return; + } + + if (command == null) { + logger.debug("skipping Hue fadingLightCommand to command '{}' due to null value.", command); + return; + } + if (fadeTime == null) { + logger.debug("skipping Hue fadingLightCommand to fadeTime '{}' due to null value.", fadeTime); + return; + } + + lightHandler.handleCommand(channel, command, fadeTime.longValue()); + logger.debug("send LightAction to {} with {}ms of fadeTime", channel, fadeTime); + } + + public static void fadingLightCommand(@Nullable ThingActions actions, @Nullable String channel, + @Nullable Command command, @Nullable DecimalType fadeTime) { + if (actions instanceof LightActions) { + ((LightActions) actions).fadingLightCommand(channel, command, fadeTime); + } else { + throw new IllegalArgumentException("Instance is not an LightActions class."); + } + } +} diff --git a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java index c90ea0c514279..6f2a8f4863d5a 100644 --- a/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java +++ b/addons/binding/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java @@ -12,11 +12,13 @@ */ package org.openhab.binding.hue.internal.handler; -import java.util.AbstractMap.SimpleEntry; - import static org.openhab.binding.hue.internal.HueBindingConstants.*; +import java.math.BigDecimal; +import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,14 +42,16 @@ import org.eclipse.smarthome.core.thing.ThingTypeUID; import org.eclipse.smarthome.core.thing.binding.BaseThingHandler; import org.eclipse.smarthome.core.thing.binding.ThingHandler; +import org.eclipse.smarthome.core.thing.binding.ThingHandlerService; import org.eclipse.smarthome.core.types.Command; import org.eclipse.smarthome.core.types.UnDefType; import org.openhab.binding.hue.internal.FullHueObject; import org.openhab.binding.hue.internal.FullLight; import org.openhab.binding.hue.internal.HueBridge; import org.openhab.binding.hue.internal.State; -import org.openhab.binding.hue.internal.StateUpdate; import org.openhab.binding.hue.internal.State.ColorMode; +import org.openhab.binding.hue.internal.StateUpdate; +import org.openhab.binding.hue.internal.action.LightActions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,6 +71,7 @@ * @author Yordan Zhelev - added alert and effect functions * @author Denis Dudnik - switched to internally integrated source of Jue library * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only + * @author Jochen Leopold - Added support for custom fade times */ @NonNullByDefault public class HueLightHandler extends BaseThingHandler implements LightStatusListener { @@ -100,6 +105,7 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList private boolean isOsramPar16 = false; private boolean propertiesInitializedSuccessfully = false; + private long defaultFadeTime = 400; private @Nullable HueClient hueClient; @@ -121,6 +127,11 @@ private void initializeThing(@Nullable ThingStatus bridgeStatus) { logger.debug("initializeThing thing {} bridge status {}", getThing().getUID(), bridgeStatus); final String configLightId = (String) getConfig().get(LIGHT_ID); if (configLightId != null) { + BigDecimal time = (BigDecimal) getConfig().get(FADETIME); + if (time != null) { + defaultFadeTime = time.longValueExact(); + } + lightId = configLightId; // note: this call implicitly registers our handler as a listener on // the bridge @@ -200,6 +211,10 @@ public void dispose() { @Override public void handleCommand(ChannelUID channelUID, Command command) { + handleCommand(channelUID.getId(), command, defaultFadeTime); + } + + public void handleCommand(String channel, Command command, long fadeTime) { HueClient hueBridge = getHueClient(); if (hueBridge == null) { logger.warn("hue bridge handler not found. Cannot handle command without bridge."); @@ -213,34 +228,48 @@ public void handleCommand(ChannelUID channelUID, Command command) { } StateUpdate lightState = null; - switch (channelUID.getId()) { + switch (channel) { case CHANNEL_COLORTEMPERATURE: if (command instanceof PercentType) { lightState = LightStateConverter.toColorTemperatureLightState((PercentType) command); + lightState.setTransitionTime(fadeTime); } else if (command instanceof OnOffType) { lightState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + } else { + lightState.setTransitionTime(fadeTime); } } else if (command instanceof IncreaseDecreaseType) { lightState = convertColorTempChangeToStateUpdate((IncreaseDecreaseType) command, light); + if (lightState != null) { + lightState.setTransitionTime(fadeTime); + } } + break; case CHANNEL_BRIGHTNESS: if (command instanceof PercentType) { lightState = LightStateConverter.toBrightnessLightState((PercentType) command); + lightState.setTransitionTime(fadeTime); } else if (command instanceof OnOffType) { lightState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + } else { + lightState.setTransitionTime(fadeTime); } } else if (command instanceof IncreaseDecreaseType) { lightState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); + if (lightState != null) { + lightState.setTransitionTime(fadeTime); + } } if (lightState != null && lastSentColorTemp != null) { // make sure that the light also has the latest color temp // this might not have been yet set in the light, if it was off lightState.setColorTemperature(lastSentColorTemp); + lightState.setTransitionTime(fadeTime); } break; case CHANNEL_SWITCH: @@ -249,12 +278,15 @@ public void handleCommand(ChannelUID channelUID, Command command) { lightState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + } else { + lightState.setTransitionTime(fadeTime); } } if (lightState != null && lastSentColorTemp != null) { // make sure that the light also has the latest color temp // this might not have been yet set in the light, if it was off lightState.setColorTemperature(lastSentColorTemp); + lightState.setTransitionTime(fadeTime); } break; case CHANNEL_COLOR: @@ -272,6 +304,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { } else if (command instanceof IncreaseDecreaseType) { lightState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); } + if (lightState != null) { + lightState.setTransitionTime(fadeTime); + } break; case CHANNEL_ALERT: if (command instanceof StringType) { @@ -306,7 +341,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } hueBridge.updateLightState(light, lightState); } else { - logger.warn("Command sent to an unknown channel id: {}", channelUID); + logger.warn("Command sent to an unknown channel id: {}:{}", getThing().getUID(), channel); } } @@ -538,4 +573,9 @@ private int getAlertDuration(Command command) { return delay; } + + @Override + public Collection> getServices() { + return Collections.singletonList(LightActions.class); + } }