diff --git a/bundles/org.openhab.binding.hue/README.md b/bundles/org.openhab.binding.hue/README.md index d6649590eb781..fee9426e00874 100644 --- a/bundles/org.openhab.binding.hue/README.md +++ b/bundles/org.openhab.binding.hue/README.md @@ -227,7 +227,7 @@ The `tap_switch_event` can trigger one of the following events: ## 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 +There is a separate instance for each light or light group, which can be retrieved e.g. through ```php val hueActions = getActions("hue","hue:0210:00178810d0dc:1") @@ -244,7 +244,7 @@ hueActions.fadingLightCommand("color", new PercentType(100), new DecimalType(100 |-----------|--------------------------------------------------------------------------------------------------| | 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") | +| fadeTime | Fade time in milliseconds to a new light value (min="0", step="100") | ## Full Example diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java index 8c94cb0f7337e..14ab8e3a11916 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/action/LightActions.java @@ -14,7 +14,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.openhab.binding.hue.internal.handler.HueLightHandler; +import org.openhab.binding.hue.internal.handler.HueLightActionsHandler; import org.openhab.core.automation.annotation.ActionInput; import org.openhab.core.automation.annotation.RuleAction; import org.openhab.core.library.types.DecimalType; @@ -26,20 +26,19 @@ import org.slf4j.LoggerFactory; /** - * The {@link LightActions} defines the thing actions for the hue binding. + * The {@link LightActions} defines {@link ThingActions} for the hue lights. * * @author Jochen Leopold - Initial contribution - * @author Laurent Garnier - new method invokeMethodOf + interface ILightActions */ @ThingActionsScope(name = "hue") @NonNullByDefault public class LightActions implements ThingActions { private final Logger logger = LoggerFactory.getLogger(LightActions.class); - private @Nullable HueLightHandler handler; + private @Nullable HueLightActionsHandler handler; @Override public void setThingHandler(@Nullable ThingHandler handler) { - this.handler = (HueLightHandler) handler; + this.handler = (HueLightActionsHandler) handler; } @Override @@ -52,8 +51,8 @@ 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) { + HueLightActionsHandler lightActionsHandler = handler; + if (lightActionsHandler == null) { logger.warn("Hue Action service ThingHandler is null!"); return; } @@ -62,7 +61,6 @@ public void fadingLightCommand( 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; @@ -72,8 +70,8 @@ public void fadingLightCommand( return; } - lightHandler.handleCommand(channel, command, fadeTime.longValue()); - logger.debug("send LightAction to {} with {}ms of fadeTime", channel, fadeTime); + lightActionsHandler.handleCommand(channel, command, fadeTime.longValue()); + logger.debug("send fadingLightCommand to channel '{}' with fadeTime of {}ms.", channel, fadeTime); } public static void fadingLightCommand(ThingActions actions, @Nullable String channel, @Nullable Command command, diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueGroupHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueGroupHandler.java index ac887ded8f57e..0832866458978 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueGroupHandler.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueGroupHandler.java @@ -57,7 +57,8 @@ * @author Laurent Garnier - Initial contribution */ @NonNullByDefault -public class HueGroupHandler extends BaseThingHandler implements GroupStatusListener { +public class HueGroupHandler extends BaseThingHandler implements HueLightActionsHandler, GroupStatusListener { + public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_GROUP); public static final String PROPERTY_MEMBERS = "members"; @@ -168,6 +169,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { handleCommand(channelUID.getId(), command, defaultFadeTime); } + @Override public void handleCommand(String channel, Command command, long fadeTime) { HueClient bridgeHandler = getHueClient(); if (bridgeHandler == null) { diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightActionsHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightActionsHandler.java new file mode 100644 index 0000000000000..2db7988355a99 --- /dev/null +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightActionsHandler.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2010-2021 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.handler; + +import java.util.Collection; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.hue.internal.action.LightActions; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; + +/** + * The {@link HueLightActionsHandler} defines interface handlers to handle {@link LightActions}. + * + * @author Christoph Weitkamp - Initial contribution + */ +@NonNullByDefault +public interface HueLightActionsHandler extends ThingHandler { + + @Override + default Collection> getServices() { + return Set.of(LightActions.class); + } + + /** + * Handles a command for a given channel. + * + * @param channel the id of the channel to which the command was sent + * @param command the {@link Command} + * @param fadeTime duration for execution of the command + */ + void handleCommand(String channel, Command command, long fadeTime); +} diff --git a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java index 33c90e7946708..a4c7868beadf3 100644 --- a/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java +++ b/bundles/org.openhab.binding.hue/src/main/java/org/openhab/binding/hue/internal/handler/HueLightHandler.java @@ -16,8 +16,6 @@ import static org.openhab.core.thing.Thing.*; import java.math.BigDecimal; -import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -29,7 +27,6 @@ import org.openhab.binding.hue.internal.FullLight; import org.openhab.binding.hue.internal.State; import org.openhab.binding.hue.internal.StateUpdate; -import org.openhab.binding.hue.internal.action.LightActions; import org.openhab.binding.hue.internal.dto.Capabilities; import org.openhab.binding.hue.internal.dto.ColorTemperature; import org.openhab.core.library.types.DecimalType; @@ -47,7 +44,6 @@ import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.binding.BaseThingHandler; import org.openhab.core.thing.binding.ThingHandler; -import org.openhab.core.thing.binding.ThingHandlerService; import org.openhab.core.types.Command; import org.openhab.core.types.StateDescriptionFragment; import org.openhab.core.types.StateDescriptionFragmentBuilder; @@ -73,7 +69,7 @@ * @author Jochen Leopold - Added support for custom fade times */ @NonNullByDefault -public class HueLightHandler extends BaseThingHandler implements LightStatusListener { +public class HueLightHandler extends BaseThingHandler implements HueLightActionsHandler, LightStatusListener { public static final Set SUPPORTED_THING_TYPES = Set.of(THING_TYPE_COLOR_LIGHT, THING_TYPE_COLOR_TEMPERATURE_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT, @@ -218,6 +214,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { handleCommand(channelUID.getId(), command, defaultFadeTime); } + @Override public void handleCommand(String channel, Command command, long fadeTime) { HueClient bridgeHandler = getHueClient(); if (bridgeHandler == null) { @@ -234,95 +231,95 @@ public void handleCommand(String channel, Command command, long fadeTime) { } Integer lastColorTemp; - StateUpdate lightState = null; + StateUpdate newState = null; switch (channel) { case CHANNEL_COLORTEMPERATURE: if (command instanceof PercentType) { - lightState = LightStateConverter.toColorTemperatureLightStateFromPercentType((PercentType) command, + newState = LightStateConverter.toColorTemperatureLightStateFromPercentType((PercentType) command, colorTemperatureCapabilties); - lightState.setTransitionTime(fadeTime); + newState.setTransitionTime(fadeTime); } else if (command instanceof OnOffType) { - lightState = LightStateConverter.toOnOffLightState((OnOffType) command); + newState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { - lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + newState = addOsramSpecificCommands(newState, (OnOffType) command); } } else if (command instanceof IncreaseDecreaseType) { - lightState = convertColorTempChangeToStateUpdate((IncreaseDecreaseType) command, light); - if (lightState != null) { - lightState.setTransitionTime(fadeTime); + newState = convertColorTempChangeToStateUpdate((IncreaseDecreaseType) command, light); + if (newState != null) { + newState.setTransitionTime(fadeTime); } } break; case CHANNEL_COLORTEMPERATURE_ABS: if (command instanceof DecimalType) { - lightState = LightStateConverter.toColorTemperatureLightState((DecimalType) command, + newState = LightStateConverter.toColorTemperatureLightState((DecimalType) command, colorTemperatureCapabilties); - lightState.setTransitionTime(fadeTime); + newState.setTransitionTime(fadeTime); } break; case CHANNEL_BRIGHTNESS: if (command instanceof PercentType) { - lightState = LightStateConverter.toBrightnessLightState((PercentType) command); - lightState.setTransitionTime(fadeTime); + newState = LightStateConverter.toBrightnessLightState((PercentType) command); + newState.setTransitionTime(fadeTime); } else if (command instanceof OnOffType) { - lightState = LightStateConverter.toOnOffLightState((OnOffType) command); + newState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { - lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + newState = addOsramSpecificCommands(newState, (OnOffType) command); } } else if (command instanceof IncreaseDecreaseType) { - lightState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); - if (lightState != null) { - lightState.setTransitionTime(fadeTime); + newState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); + if (newState != null) { + newState.setTransitionTime(fadeTime); } } lastColorTemp = lastSentColorTemp; - if (lightState != null && lastColorTemp != null) { + if (newState != null && lastColorTemp != 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(lastColorTemp, colorTemperatureCapabilties); - lightState.setTransitionTime(fadeTime); + newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties); + newState.setTransitionTime(fadeTime); } break; case CHANNEL_SWITCH: if (command instanceof OnOffType) { - lightState = LightStateConverter.toOnOffLightState((OnOffType) command); + newState = LightStateConverter.toOnOffLightState((OnOffType) command); if (isOsramPar16) { - lightState = addOsramSpecificCommands(lightState, (OnOffType) command); + newState = addOsramSpecificCommands(newState, (OnOffType) command); } } lastColorTemp = lastSentColorTemp; - if (lightState != null && lastColorTemp != null) { + if (newState != null && lastColorTemp != 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(lastColorTemp, colorTemperatureCapabilties); - lightState.setTransitionTime(fadeTime); + newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties); + newState.setTransitionTime(fadeTime); } break; case CHANNEL_COLOR: if (command instanceof HSBType) { HSBType hsbCommand = (HSBType) command; if (hsbCommand.getBrightness().intValue() == 0) { - lightState = LightStateConverter.toOnOffLightState(OnOffType.OFF); + newState = LightStateConverter.toOnOffLightState(OnOffType.OFF); } else { - lightState = LightStateConverter.toColorLightState(hsbCommand, light.getState()); - lightState.setTransitionTime(fadeTime); + newState = LightStateConverter.toColorLightState(hsbCommand, light.getState()); + newState.setTransitionTime(fadeTime); } } else if (command instanceof PercentType) { - lightState = LightStateConverter.toBrightnessLightState((PercentType) command); - lightState.setTransitionTime(fadeTime); + newState = LightStateConverter.toBrightnessLightState((PercentType) command); + newState.setTransitionTime(fadeTime); } else if (command instanceof OnOffType) { - lightState = LightStateConverter.toOnOffLightState((OnOffType) command); + newState = LightStateConverter.toOnOffLightState((OnOffType) command); } else if (command instanceof IncreaseDecreaseType) { - lightState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); - if (lightState != null) { - lightState.setTransitionTime(fadeTime); + newState = convertBrightnessChangeToStateUpdate((IncreaseDecreaseType) command, light); + if (newState != null) { + newState.setTransitionTime(fadeTime); } } break; case CHANNEL_ALERT: if (command instanceof StringType) { - lightState = LightStateConverter.toAlertState((StringType) command); - if (lightState == null) { + newState = LightStateConverter.toAlertState((StringType) command); + if (newState == null) { // Unsupported StringType is passed. Log a warning // message and return. logger.warn("Unsupported String command: {}. Supported commands are: {}, {}, {} ", command, @@ -336,21 +333,21 @@ public void handleCommand(String channel, Command command, long fadeTime) { break; case CHANNEL_EFFECT: if (command instanceof OnOffType) { - lightState = LightStateConverter.toOnOffEffectState((OnOffType) command); + newState = LightStateConverter.toOnOffEffectState((OnOffType) command); } break; } - if (lightState != null) { + if (newState != null) { // Cache values which we have sent - Integer tmpBrightness = lightState.getBrightness(); + Integer tmpBrightness = newState.getBrightness(); if (tmpBrightness != null) { lastSentBrightness = tmpBrightness; } - Integer tmpColorTemp = lightState.getColorTemperature(); + Integer tmpColorTemp = newState.getColorTemperature(); if (tmpColorTemp != null) { lastSentColorTemp = tmpColorTemp; } - bridgeHandler.updateLightState(this, light, lightState, fadeTime); + bridgeHandler.updateLightState(this, light, newState, fadeTime); } else { logger.warn("Command sent to an unknown channel id: {}:{}", getThing().getUID(), channel); } @@ -601,11 +598,6 @@ private int getAlertDuration(Command command) { return delay; } - @Override - public Collection> getServices() { - return List.of(LightActions.class); - } - @Override public String getLightId() { return lightId; diff --git a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java index bfdd8cc1c250e..5d9c2500be9f3 100644 --- a/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java +++ b/bundles/org.openhab.binding.hue/src/test/java/org/openhab/binding/hue/internal/handler/HueLightHandlerTest.java @@ -19,7 +19,8 @@ import java.util.Map; -import org.junit.jupiter.api.BeforeEach; +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.openhab.binding.hue.internal.FullConfig; @@ -56,6 +57,7 @@ * @author Simon Kaufmann - migrated to plain Java test * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only */ +@NonNullByDefault public class HueLightHandlerTest { private static final int MIN_COLOR_TEMPERATURE = 153; @@ -66,12 +68,7 @@ public class HueLightHandlerTest { private static final String OSRAM_MODEL_TYPE = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID; private static final String OSRAM_MODEL_TYPE_ID = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID; - private Gson gson; - - @BeforeEach - public void setUp() { - gson = new Gson(); - } + private final Gson gson = new Gson(); @Test public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() { @@ -402,12 +399,12 @@ private void assertSendCommand(String channel, Command command, HueLightState cu HueLightHandler hueLightHandler = new HueLightHandler(mockThing, mock(HueStateDescriptionProvider.class)) { @Override - protected synchronized HueClient getHueClient() { + protected synchronized @Nullable HueClient getHueClient() { return mockClient; } @Override - protected Bridge getBridge() { + protected @Nullable Bridge getBridge() { return mockBridge; } };