[hue] Added support for different color temperature capabilities and added Channel to set value in Kelvin (#9939)
* Added support for color temperature capabilities and set value in Kelvin * Use system default channel type Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
committed by
GitHub
parent
7ce7228a85
commit
7141a091ff
@@ -158,28 +158,29 @@ The group type also have an optional configuration value to specify the fade tim
|
|||||||
|
|
||||||
The devices support some of the following channels:
|
The devices support some of the following channels:
|
||||||
|
|
||||||
| Channel Type ID | Item Type | Description | Thing types supporting this channel |
|
| Channel Type ID | Item Type | Description | Thing types supporting this channel |
|
||||||
|-------------------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
|
|-----------------------|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
|
||||||
| switch | Switch | This channel supports switching the device on and off. | 0000, 0010, group |
|
| switch | Switch | This channel supports switching the device on and off. | 0000, 0010, group |
|
||||||
| color | Color | This channel supports full color control with hue, saturation and brightness values. | 0200, 0210, group |
|
| color | Color | This channel supports full color control with hue, saturation and brightness values. | 0200, 0210, group |
|
||||||
| brightness | Dimmer | This channel supports adjusting the brightness value. Note that this is not available, if the color channel is supported. | 0100, 0110, 0220, group |
|
| brightness | Dimmer | This channel supports adjusting the brightness value. Note that this is not available, if the color channel is supported. | 0100, 0110, 0220, group |
|
||||||
| color_temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | 0210, 0220, group |
|
| color_temperature | Dimmer | This channel supports adjusting the color temperature from cold (0%) to warm (100%). | 0210, 0220, group |
|
||||||
| alert | String | This channel supports displaying alerts by flashing the bulb either once or multiple times. Valid values are: NONE, SELECT and LSELECT. | 0000, 0100, 0200, 0210, 0220, group |
|
| color_temperature_abs | Number | This channel supports adjusting the color temperature in Kelvin. **Advanced** | 0210, 0220, group |
|
||||||
| effect | Switch | This channel supports color looping. | 0200, 0210, 0220 |
|
| alert | String | This channel supports displaying alerts by flashing the bulb either once or multiple times. Valid values are: NONE, SELECT and LSELECT. | 0000, 0100, 0200, 0210, 0220, group |
|
||||||
| dimmer_switch | Number | This channel shows which button was last pressed on the dimmer switch. | 0820 |
|
| effect | Switch | This channel supports color looping. | 0200, 0210, 0220 |
|
||||||
| illuminance | Number:Illuminance | This channel shows the current illuminance measured by the sensor. | 0106 |
|
| dimmer_switch | Number | This channel shows which button was last pressed on the dimmer switch. | 0820 |
|
||||||
| light_level | Number | This channel shows the current light level measured by the sensor. **Advanced** | 0106 |
|
| illuminance | Number:Illuminance | This channel shows the current illuminance measured by the sensor. | 0106 |
|
||||||
| dark | Switch | This channel indicates whether the light level is below the darkness threshold or not. | 0106 |
|
| light_level | Number | This channel shows the current light level measured by the sensor. **Advanced** | 0106 |
|
||||||
| daylight | Switch | This channel indicates whether the light level is below the daylight threshold or not. | 0106 |
|
| dark | Switch | This channel indicates whether the light level is below the darkness threshold or not. | 0106 |
|
||||||
| presence | Switch | This channel indicates whether a motion is detected by the sensor or not. | 0107 |
|
| daylight | Switch | This channel indicates whether the light level is below the daylight threshold or not. | 0106 |
|
||||||
| enabled | Switch | This channel activated or deactivates the sensor | 0107 |
|
| presence | Switch | This channel indicates whether a motion is detected by the sensor or not. | 0107 |
|
||||||
| temperature | Number:Temperature | This channel shows the current temperature measured by the sensor. | 0302 |
|
| enabled | Switch | This channel activated or deactivates the sensor | 0107 |
|
||||||
| flag | Switch | This channel save flag state for a CLIP sensor. | 0850 |
|
| temperature | Number:Temperature | This channel shows the current temperature measured by the sensor. | 0302 |
|
||||||
| status | Number | This channel save status state for a CLIP sensor. | 0840 |
|
| flag | Switch | This channel save flag state for a CLIP sensor. | 0850 |
|
||||||
| last_updated | DateTime | This channel the date and time when the sensor was last updated. | 0820, 0830, 0840, 0850, 0106, 0107, 0302 |
|
| status | Number | This channel save status state for a CLIP sensor. | 0840 |
|
||||||
| battery_level | Number | This channel shows the battery level. | 0820, 0106, 0107, 0302 |
|
| last_updated | DateTime | This channel the date and time when the sensor was last updated. | 0820, 0830, 0840, 0850, 0106, 0107, 0302 |
|
||||||
| battery_low | Switch | This channel indicates whether the battery is low or not. | 0820, 0106, 0107, 0302 |
|
| battery_level | Number | This channel shows the battery level. | 0820, 0106, 0107, 0302 |
|
||||||
| scene | String | This channel activates the scene with the given ID String. The ID String of each scene is assigned by the Hue bridge. | bridge, group |
|
| battery_low | Switch | This channel indicates whether the battery is low or not. | 0820, 0106, 0107, 0302 |
|
||||||
|
| scene | String | This channel activates the scene with the given ID String. The ID String of each scene is assigned by the Hue bridge. | bridge, group |
|
||||||
|
|
||||||
To load a hue scene inside a rule for example, the ID of the scene will be required.
|
To load a hue scene inside a rule for example, the ID of the scene will be required.
|
||||||
You can list all the scene IDs with the following console commands: `hue <bridgeUID> scenes` and `hue <groupThingUID> scenes`.
|
You can list all the scene IDs with the following console commands: `hue <bridgeUID> scenes` and `hue <groupThingUID> scenes`.
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import java.time.Duration;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.hue.internal.dto.Capabilities;
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ public class FullLight extends FullHueObject {
|
|||||||
public static final Type GSON_TYPE = new TypeToken<Map<String, FullLight>>() {
|
public static final Type GSON_TYPE = new TypeToken<Map<String, FullLight>>() {
|
||||||
}.getType();
|
}.getType();
|
||||||
|
|
||||||
|
public @Nullable Capabilities capabilities;
|
||||||
private @NonNullByDefault({}) State state;
|
private @NonNullByDefault({}) State state;
|
||||||
private final long fadetime = 400; // milliseconds
|
private final long fadetime = 400; // milliseconds
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,8 @@ public class HttpClient {
|
|||||||
synchronized (commandsQueue) {
|
synchronized (commandsQueue) {
|
||||||
if (commandsQueue.isEmpty()) {
|
if (commandsQueue.isEmpty()) {
|
||||||
commandsQueue.offer(asyncPutParameters);
|
commandsQueue.offer(asyncPutParameters);
|
||||||
if (job == null || job.isDone()) {
|
Future<?> localJob = job;
|
||||||
|
if (localJob == null || localJob.isDone()) {
|
||||||
job = scheduler.submit(this::executeCommands);
|
job = scheduler.submit(this::executeCommands);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class HueBindingConstants {
|
|||||||
|
|
||||||
// List all channels
|
// List all channels
|
||||||
public static final String CHANNEL_COLORTEMPERATURE = "color_temperature";
|
public static final String CHANNEL_COLORTEMPERATURE = "color_temperature";
|
||||||
|
public static final String CHANNEL_COLORTEMPERATURE_ABS = "color_temperature_abs";
|
||||||
public static final String CHANNEL_COLOR = "color";
|
public static final String CHANNEL_COLOR = "color";
|
||||||
public static final String CHANNEL_BRIGHTNESS = "brightness";
|
public static final String CHANNEL_BRIGHTNESS = "brightness";
|
||||||
public static final String CHANNEL_ALERT = "alert";
|
public static final String CHANNEL_ALERT = "alert";
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
|
|||||||
if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
return new HueBridgeHandler((Bridge) thing, stateOptionProvider);
|
return new HueBridgeHandler((Bridge) thing, stateOptionProvider);
|
||||||
} else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
} else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
return new HueLightHandler(thing);
|
return new HueLightHandler(thing, stateOptionProvider);
|
||||||
} else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
} else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
return new DimmerSwitchHandler(thing);
|
return new DimmerSwitchHandler(thing);
|
||||||
} else if (TapSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
} else if (TapSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class State {
|
|||||||
int hue;
|
int hue;
|
||||||
int sat;
|
int sat;
|
||||||
private float[] xy;
|
private float[] xy;
|
||||||
private int ct;
|
int ct;
|
||||||
private String alert;
|
private String alert;
|
||||||
private String effect;
|
private String effect;
|
||||||
String colormode;
|
String colormode;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ package org.openhab.binding.hue.internal;
|
|||||||
|
|
||||||
import org.openhab.binding.hue.internal.State.AlertMode;
|
import org.openhab.binding.hue.internal.State.AlertMode;
|
||||||
import org.openhab.binding.hue.internal.State.Effect;
|
import org.openhab.binding.hue.internal.State.Effect;
|
||||||
|
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of updates to the state of a light.
|
* Collection of updates to the state of a light.
|
||||||
@@ -139,12 +140,13 @@ public class StateUpdate extends ConfigUpdate {
|
|||||||
/**
|
/**
|
||||||
* Switch to CT color mode and set color temperature in mired.
|
* Switch to CT color mode and set color temperature in mired.
|
||||||
*
|
*
|
||||||
* @param colorTemperature color temperature [153..500]
|
* @param colorTemperature color temperature
|
||||||
* @return this object for chaining calls
|
* @return this object for chaining calls
|
||||||
*/
|
*/
|
||||||
public StateUpdate setColorTemperature(int colorTemperature) {
|
public StateUpdate setColorTemperature(int colorTemperature, ColorTemperature capabilities) {
|
||||||
if (colorTemperature < 153 || colorTemperature > 500) {
|
if (colorTemperature < capabilities.min || colorTemperature > capabilities.max) {
|
||||||
throw new IllegalArgumentException("Color temperature out of range");
|
throw new IllegalArgumentException(String.format("Color temperature %d is out of range [%d..%d]",
|
||||||
|
colorTemperature, capabilities.min, capabilities.max));
|
||||||
}
|
}
|
||||||
|
|
||||||
commands.add(new Command("ct", colorTemperature));
|
commands.add(new Command("ct", colorTemperature));
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of capabilities for lights.
|
||||||
|
*
|
||||||
|
* @author Christoph Weitkamp - Initial contribution
|
||||||
|
*/
|
||||||
|
public class Capabilities {
|
||||||
|
public Control control;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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.dto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of color temperature capabilities to control lights.
|
||||||
|
*
|
||||||
|
* @author Christoph Weitkamp - Initial contribution
|
||||||
|
*/
|
||||||
|
public class ColorTemperature {
|
||||||
|
public int max = 500;
|
||||||
|
public int min = 153;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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.dto;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of capabilities to control lights.
|
||||||
|
*
|
||||||
|
* @author Christoph Weitkamp - Initial contribution
|
||||||
|
*/
|
||||||
|
public class Control {
|
||||||
|
public @Nullable ColorTemperature ct;
|
||||||
|
}
|
||||||
@@ -15,8 +15,6 @@ package org.openhab.binding.hue.internal.handler;
|
|||||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -31,6 +29,8 @@ import org.openhab.binding.hue.internal.Scene;
|
|||||||
import org.openhab.binding.hue.internal.State;
|
import org.openhab.binding.hue.internal.State;
|
||||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||||
import org.openhab.binding.hue.internal.StateUpdate;
|
import org.openhab.binding.hue.internal.StateUpdate;
|
||||||
|
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
@@ -59,7 +59,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HueGroupHandler extends BaseThingHandler implements GroupStatusListener {
|
public class HueGroupHandler extends BaseThingHandler implements GroupStatusListener {
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_GROUP);
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_GROUP);
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(HueGroupHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(HueGroupHandler.class);
|
||||||
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider;
|
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider;
|
||||||
@@ -69,6 +69,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
private @Nullable Integer lastSentColorTemp;
|
private @Nullable Integer lastSentColorTemp;
|
||||||
private @Nullable Integer lastSentBrightness;
|
private @Nullable Integer lastSentBrightness;
|
||||||
|
|
||||||
|
private ColorTemperature colorTemperatureCapabilties = new ColorTemperature();
|
||||||
private long defaultFadeTime = 400;
|
private long defaultFadeTime = 400;
|
||||||
|
|
||||||
private @Nullable HueClient hueClient;
|
private @Nullable HueClient hueClient;
|
||||||
@@ -76,7 +77,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
private @Nullable ScheduledFuture<?> scheduledFuture;
|
private @Nullable ScheduledFuture<?> scheduledFuture;
|
||||||
private @Nullable FullGroup lastFullGroup;
|
private @Nullable FullGroup lastFullGroup;
|
||||||
|
|
||||||
private List<String> consoleScenesList = new ArrayList<>();
|
private List<String> consoleScenesList = List.of();
|
||||||
|
|
||||||
public HueGroupHandler(Thing thing, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) {
|
public HueGroupHandler(Thing thing, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) {
|
||||||
super(thing);
|
super(thing);
|
||||||
@@ -201,7 +202,8 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
break;
|
break;
|
||||||
case CHANNEL_COLORTEMPERATURE:
|
case CHANNEL_COLORTEMPERATURE:
|
||||||
if (command instanceof PercentType) {
|
if (command instanceof PercentType) {
|
||||||
newState = LightStateConverter.toColorTemperatureLightState((PercentType) command);
|
newState = LightStateConverter.toColorTemperatureLightStateFromPercentType((PercentType) command,
|
||||||
|
colorTemperatureCapabilties);
|
||||||
newState.setTransitionTime(fadeTime);
|
newState.setTransitionTime(fadeTime);
|
||||||
} else if (command instanceof OnOffType) {
|
} else if (command instanceof OnOffType) {
|
||||||
newState = LightStateConverter.toOnOffLightState((OnOffType) command);
|
newState = LightStateConverter.toOnOffLightState((OnOffType) command);
|
||||||
@@ -212,6 +214,13 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CHANNEL_COLORTEMPERATURE_ABS:
|
||||||
|
if (command instanceof DecimalType) {
|
||||||
|
newState = LightStateConverter.toColorTemperatureLightState((DecimalType) command,
|
||||||
|
colorTemperatureCapabilties);
|
||||||
|
newState.setTransitionTime(fadeTime);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CHANNEL_BRIGHTNESS:
|
case CHANNEL_BRIGHTNESS:
|
||||||
if (command instanceof PercentType) {
|
if (command instanceof PercentType) {
|
||||||
newState = LightStateConverter.toBrightnessLightState((PercentType) command);
|
newState = LightStateConverter.toBrightnessLightState((PercentType) command);
|
||||||
@@ -228,7 +237,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
if (newState != null && lastColorTemp != null) {
|
if (newState != null && lastColorTemp != null) {
|
||||||
// make sure that the light also has the latest color temp
|
// 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
|
// this might not have been yet set in the light, if it was off
|
||||||
newState.setColorTemperature(lastColorTemp);
|
newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
|
||||||
newState.setTransitionTime(fadeTime);
|
newState.setTransitionTime(fadeTime);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -240,7 +249,7 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
if (newState != null && lastColorTemp != null) {
|
if (newState != null && lastColorTemp != null) {
|
||||||
// make sure that the light also has the latest color temp
|
// 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
|
// this might not have been yet set in the light, if it was off
|
||||||
newState.setColorTemperature(lastColorTemp);
|
newState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
|
||||||
newState.setTransitionTime(fadeTime);
|
newState.setTransitionTime(fadeTime);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -296,8 +305,9 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
StateUpdate stateUpdate = null;
|
StateUpdate stateUpdate = null;
|
||||||
Integer currentColorTemp = getCurrentColorTemp(group.getState());
|
Integer currentColorTemp = getCurrentColorTemp(group.getState());
|
||||||
if (currentColorTemp != null) {
|
if (currentColorTemp != null) {
|
||||||
int newColorTemp = LightStateConverter.toAdjustedColorTemp(command, currentColorTemp);
|
int newColorTemp = LightStateConverter.toAdjustedColorTemp(command, currentColorTemp,
|
||||||
stateUpdate = new StateUpdate().setColorTemperature(newColorTemp);
|
colorTemperatureCapabilties);
|
||||||
|
stateUpdate = new StateUpdate().setColorTemperature(newColorTemp, colorTemperatureCapabilties);
|
||||||
}
|
}
|
||||||
return stateUpdate;
|
return stateUpdate;
|
||||||
}
|
}
|
||||||
@@ -380,25 +390,27 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
|
|
||||||
HSBType hsbType = LightStateConverter.toHSBType(state);
|
HSBType hsbType = LightStateConverter.toHSBType(state);
|
||||||
if (!state.isOn()) {
|
if (!state.isOn()) {
|
||||||
hsbType = new HSBType(hsbType.getHue(), hsbType.getSaturation(), new PercentType(0));
|
hsbType = new HSBType(hsbType.getHue(), hsbType.getSaturation(), PercentType.ZERO);
|
||||||
}
|
}
|
||||||
updateState(CHANNEL_COLOR, hsbType);
|
updateState(CHANNEL_COLOR, hsbType);
|
||||||
|
|
||||||
ColorMode colorMode = state.getColorMode();
|
ColorMode colorMode = state.getColorMode();
|
||||||
if (ColorMode.CT.equals(colorMode)) {
|
if (ColorMode.CT.equals(colorMode)) {
|
||||||
PercentType colorTempPercentType = LightStateConverter.toColorTemperaturePercentType(state);
|
updateState(CHANNEL_COLORTEMPERATURE,
|
||||||
updateState(CHANNEL_COLORTEMPERATURE, colorTempPercentType);
|
LightStateConverter.toColorTemperaturePercentType(state, colorTemperatureCapabilties));
|
||||||
|
updateState(CHANNEL_COLORTEMPERATURE_ABS, LightStateConverter.toColorTemperature(state));
|
||||||
} else {
|
} else {
|
||||||
updateState(CHANNEL_COLORTEMPERATURE, UnDefType.NULL);
|
updateState(CHANNEL_COLORTEMPERATURE, UnDefType.UNDEF);
|
||||||
|
updateState(CHANNEL_COLORTEMPERATURE_ABS, UnDefType.UNDEF);
|
||||||
}
|
}
|
||||||
|
|
||||||
PercentType brightnessPercentType = LightStateConverter.toBrightnessPercentType(state);
|
PercentType brightnessPercentType = LightStateConverter.toBrightnessPercentType(state);
|
||||||
if (!state.isOn()) {
|
if (!state.isOn()) {
|
||||||
brightnessPercentType = new PercentType(0);
|
brightnessPercentType = PercentType.ZERO;
|
||||||
}
|
}
|
||||||
updateState(CHANNEL_BRIGHTNESS, brightnessPercentType);
|
updateState(CHANNEL_BRIGHTNESS, brightnessPercentType);
|
||||||
|
|
||||||
updateState(CHANNEL_SWITCH, state.isOn() ? OnOffType.ON : OnOffType.OFF);
|
updateState(CHANNEL_SWITCH, OnOffType.from(state.isOn()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -423,8 +435,8 @@ public class HueGroupHandler extends BaseThingHandler implements GroupStatusList
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onScenesUpdated(List<Scene> updatedScenes) {
|
public void onScenesUpdated(List<Scene> updatedScenes) {
|
||||||
List<StateOption> stateOptions = Collections.emptyList();
|
List<StateOption> stateOptions = List.of();
|
||||||
consoleScenesList = new ArrayList<>();
|
consoleScenesList = List.of();
|
||||||
HueClient handler = getHueClient();
|
HueClient handler = getHueClient();
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
FullGroup group = handler.getGroupById(groupId);
|
FullGroup group = handler.getGroupById(groupId);
|
||||||
|
|||||||
@@ -16,18 +16,13 @@ import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
|||||||
import static org.openhab.core.thing.Thing.*;
|
import static org.openhab.core.thing.Thing.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
@@ -36,6 +31,9 @@ import org.openhab.binding.hue.internal.State;
|
|||||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||||
import org.openhab.binding.hue.internal.StateUpdate;
|
import org.openhab.binding.hue.internal.StateUpdate;
|
||||||
import org.openhab.binding.hue.internal.action.LightActions;
|
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;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
@@ -52,6 +50,8 @@ import org.openhab.core.thing.binding.BaseThingHandler;
|
|||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
|
import org.openhab.core.types.StateDescriptionFragmentBuilder;
|
||||||
import org.openhab.core.types.UnDefType;
|
import org.openhab.core.types.UnDefType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -77,24 +77,20 @@ import org.slf4j.LoggerFactory;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HueLightHandler extends BaseThingHandler implements LightStatusListener {
|
public class HueLightHandler extends BaseThingHandler implements LightStatusListener {
|
||||||
|
|
||||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream.of(THING_TYPE_COLOR_LIGHT,
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_COLOR_LIGHT,
|
||||||
THING_TYPE_COLOR_TEMPERATURE_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT,
|
THING_TYPE_COLOR_TEMPERATURE_LIGHT, THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_EXTENDED_COLOR_LIGHT,
|
||||||
THING_TYPE_ON_OFF_LIGHT, THING_TYPE_ON_OFF_PLUG, THING_TYPE_DIMMABLE_PLUG).collect(Collectors.toSet());
|
THING_TYPE_ON_OFF_LIGHT, THING_TYPE_ON_OFF_PLUG, THING_TYPE_DIMMABLE_PLUG);
|
||||||
|
|
||||||
// @formatter:off
|
private static final Map<String, List<String>> VENDOR_MODEL_MAP = Map.of( //
|
||||||
private static final Map<String, List<String>> VENDOR_MODEL_MAP = Stream.of(
|
"Philips", List.of("LCT001", "LCT002", "LCT003", "LCT007", "LLC001", "LLC006", "LLC007", "LLC010", //
|
||||||
new SimpleEntry<>("Philips",
|
"LLC011", "LLC012", "LLC013", "LLC020", "LST001", "LST002", "LWB004", "LWB006", "LWB007", //
|
||||||
Arrays.asList("LCT001", "LCT002", "LCT003", "LCT007", "LLC001", "LLC006", "LLC007", "LLC010",
|
"LWL001"),
|
||||||
"LLC011", "LLC012", "LLC013", "LLC020", "LST001", "LST002", "LWB004", "LWB006", "LWB007",
|
"OSRAM", List.of("Classic_A60_RGBW", "PAR16_50_TW", "Surface_Light_TW", "Plug_01"));
|
||||||
"LWL001")),
|
|
||||||
new SimpleEntry<>("OSRAM",
|
|
||||||
Arrays.asList("Classic_A60_RGBW", "PAR16_50_TW", "Surface_Light_TW", "Plug_01")))
|
|
||||||
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));
|
|
||||||
// @formatter:on
|
|
||||||
|
|
||||||
private static final String OSRAM_PAR16_50_TW_MODEL_ID = "PAR16_50_TW";
|
private static final String OSRAM_PAR16_50_TW_MODEL_ID = "PAR16_50_TW";
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(HueLightHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(HueLightHandler.class);
|
||||||
|
private final HueStateDescriptionOptionProvider stateDescriptionOptionProvider;
|
||||||
|
|
||||||
private @NonNullByDefault({}) String lightId;
|
private @NonNullByDefault({}) String lightId;
|
||||||
|
|
||||||
@@ -108,14 +104,17 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
private boolean isOsramPar16 = false;
|
private boolean isOsramPar16 = false;
|
||||||
|
|
||||||
private boolean propertiesInitializedSuccessfully = false;
|
private boolean propertiesInitializedSuccessfully = false;
|
||||||
|
private boolean capabilitiesInitializedSuccessfully = false;
|
||||||
|
private ColorTemperature colorTemperatureCapabilties = new ColorTemperature();
|
||||||
private long defaultFadeTime = 400;
|
private long defaultFadeTime = 400;
|
||||||
|
|
||||||
private @Nullable HueClient hueClient;
|
private @Nullable HueClient hueClient;
|
||||||
|
|
||||||
private @Nullable ScheduledFuture<?> scheduledFuture;
|
private @Nullable ScheduledFuture<?> scheduledFuture;
|
||||||
|
|
||||||
public HueLightHandler(Thing hueLight) {
|
public HueLightHandler(Thing hueLight, HueStateDescriptionOptionProvider stateDescriptionOptionProvider) {
|
||||||
super(hueLight);
|
super(hueLight);
|
||||||
|
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -145,7 +144,9 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
HueClient bridgeHandler = getHueClient();
|
HueClient bridgeHandler = getHueClient();
|
||||||
if (bridgeHandler != null) {
|
if (bridgeHandler != null) {
|
||||||
if (bridgeStatus == ThingStatus.ONLINE) {
|
if (bridgeStatus == ThingStatus.ONLINE) {
|
||||||
initializeProperties(bridgeHandler.getLightById(lightId));
|
FullLight fullLight = bridgeHandler.getLightById(lightId);
|
||||||
|
initializeProperties(fullLight);
|
||||||
|
initializeCapabilities(fullLight);
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
} else {
|
} else {
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
@@ -187,6 +188,33 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeCapabilities(@Nullable FullLight fullLight) {
|
||||||
|
if (!capabilitiesInitializedSuccessfully && fullLight != null) {
|
||||||
|
Capabilities capabilities = fullLight.capabilities;
|
||||||
|
if (capabilities != null) {
|
||||||
|
ColorTemperature ct = capabilities.control.ct;
|
||||||
|
if (ct != null) {
|
||||||
|
colorTemperatureCapabilties = ct;
|
||||||
|
|
||||||
|
// minimum and maximum are inverted due to mired/Kelvin conversion!
|
||||||
|
StateDescription stateDescription = StateDescriptionFragmentBuilder.create()
|
||||||
|
.withMinimum(new BigDecimal(LightStateConverter.miredToKelvin(ct.max))) //
|
||||||
|
.withMaximum(new BigDecimal(LightStateConverter.miredToKelvin(ct.min))) //
|
||||||
|
.withStep(new BigDecimal(100)) //
|
||||||
|
.withPattern("%.0f K") //
|
||||||
|
.build().toStateDescription();
|
||||||
|
if (stateDescription != null) {
|
||||||
|
stateDescriptionOptionProvider.setDescription(
|
||||||
|
new ChannelUID(thing.getUID(), CHANNEL_COLORTEMPERATURE_ABS), stateDescription);
|
||||||
|
} else {
|
||||||
|
logger.warn("Failed to create state description in thing {}", thing.getUID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capabilitiesInitializedSuccessfully = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private @Nullable String getVendor(String modelId) {
|
private @Nullable String getVendor(String modelId) {
|
||||||
for (String vendor : VENDOR_MODEL_MAP.keySet()) {
|
for (String vendor : VENDOR_MODEL_MAP.keySet()) {
|
||||||
if (VENDOR_MODEL_MAP.get(vendor).contains(modelId)) {
|
if (VENDOR_MODEL_MAP.get(vendor).contains(modelId)) {
|
||||||
@@ -235,7 +263,8 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
switch (channel) {
|
switch (channel) {
|
||||||
case CHANNEL_COLORTEMPERATURE:
|
case CHANNEL_COLORTEMPERATURE:
|
||||||
if (command instanceof PercentType) {
|
if (command instanceof PercentType) {
|
||||||
lightState = LightStateConverter.toColorTemperatureLightState((PercentType) command);
|
lightState = LightStateConverter.toColorTemperatureLightStateFromPercentType((PercentType) command,
|
||||||
|
colorTemperatureCapabilties);
|
||||||
lightState.setTransitionTime(fadeTime);
|
lightState.setTransitionTime(fadeTime);
|
||||||
} else if (command instanceof OnOffType) {
|
} else if (command instanceof OnOffType) {
|
||||||
lightState = LightStateConverter.toOnOffLightState((OnOffType) command);
|
lightState = LightStateConverter.toOnOffLightState((OnOffType) command);
|
||||||
@@ -248,7 +277,13 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
lightState.setTransitionTime(fadeTime);
|
lightState.setTransitionTime(fadeTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case CHANNEL_COLORTEMPERATURE_ABS:
|
||||||
|
if (command instanceof DecimalType) {
|
||||||
|
lightState = LightStateConverter.toColorTemperatureLightState((DecimalType) command,
|
||||||
|
colorTemperatureCapabilties);
|
||||||
|
lightState.setTransitionTime(fadeTime);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CHANNEL_BRIGHTNESS:
|
case CHANNEL_BRIGHTNESS:
|
||||||
if (command instanceof PercentType) {
|
if (command instanceof PercentType) {
|
||||||
@@ -269,7 +304,7 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
if (lightState != null && lastColorTemp != null) {
|
if (lightState != null && lastColorTemp != null) {
|
||||||
// make sure that the light also has the latest color temp
|
// 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
|
// this might not have been yet set in the light, if it was off
|
||||||
lightState.setColorTemperature(lastColorTemp);
|
lightState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
|
||||||
lightState.setTransitionTime(fadeTime);
|
lightState.setTransitionTime(fadeTime);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -285,7 +320,7 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
if (lightState != null && lastColorTemp != null) {
|
if (lightState != null && lastColorTemp != null) {
|
||||||
// make sure that the light also has the latest color temp
|
// 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
|
// this might not have been yet set in the light, if it was off
|
||||||
lightState.setColorTemperature(lastColorTemp);
|
lightState.setColorTemperature(lastColorTemp, colorTemperatureCapabilties);
|
||||||
lightState.setTransitionTime(fadeTime);
|
lightState.setTransitionTime(fadeTime);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -365,8 +400,9 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
StateUpdate stateUpdate = null;
|
StateUpdate stateUpdate = null;
|
||||||
Integer currentColorTemp = getCurrentColorTemp(light.getState());
|
Integer currentColorTemp = getCurrentColorTemp(light.getState());
|
||||||
if (currentColorTemp != null) {
|
if (currentColorTemp != null) {
|
||||||
int newColorTemp = LightStateConverter.toAdjustedColorTemp(command, currentColorTemp);
|
int newColorTemp = LightStateConverter.toAdjustedColorTemp(command, currentColorTemp,
|
||||||
stateUpdate = new StateUpdate().setColorTemperature(newColorTemp);
|
colorTemperatureCapabilties);
|
||||||
|
stateUpdate = new StateUpdate().setColorTemperature(newColorTemp, colorTemperatureCapabilties);
|
||||||
}
|
}
|
||||||
return stateUpdate;
|
return stateUpdate;
|
||||||
}
|
}
|
||||||
@@ -482,29 +518,27 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
|
|
||||||
HSBType hsbType = LightStateConverter.toHSBType(state);
|
HSBType hsbType = LightStateConverter.toHSBType(state);
|
||||||
if (!state.isOn()) {
|
if (!state.isOn()) {
|
||||||
hsbType = new HSBType(hsbType.getHue(), hsbType.getSaturation(), new PercentType(0));
|
hsbType = new HSBType(hsbType.getHue(), hsbType.getSaturation(), PercentType.ZERO);
|
||||||
}
|
}
|
||||||
updateState(CHANNEL_COLOR, hsbType);
|
updateState(CHANNEL_COLOR, hsbType);
|
||||||
|
|
||||||
ColorMode colorMode = state.getColorMode();
|
ColorMode colorMode = state.getColorMode();
|
||||||
if (ColorMode.CT.equals(colorMode)) {
|
if (ColorMode.CT.equals(colorMode)) {
|
||||||
PercentType colorTempPercentType = LightStateConverter.toColorTemperaturePercentType(state);
|
updateState(CHANNEL_COLORTEMPERATURE,
|
||||||
updateState(CHANNEL_COLORTEMPERATURE, colorTempPercentType);
|
LightStateConverter.toColorTemperaturePercentType(state, colorTemperatureCapabilties));
|
||||||
|
updateState(CHANNEL_COLORTEMPERATURE_ABS, LightStateConverter.toColorTemperature(state));
|
||||||
} else {
|
} else {
|
||||||
updateState(CHANNEL_COLORTEMPERATURE, UnDefType.NULL);
|
updateState(CHANNEL_COLORTEMPERATURE, UnDefType.UNDEF);
|
||||||
|
updateState(CHANNEL_COLORTEMPERATURE_ABS, UnDefType.UNDEF);
|
||||||
}
|
}
|
||||||
|
|
||||||
PercentType brightnessPercentType = LightStateConverter.toBrightnessPercentType(state);
|
PercentType brightnessPercentType = LightStateConverter.toBrightnessPercentType(state);
|
||||||
if (!state.isOn()) {
|
if (!state.isOn()) {
|
||||||
brightnessPercentType = new PercentType(0);
|
brightnessPercentType = PercentType.ZERO;
|
||||||
}
|
}
|
||||||
updateState(CHANNEL_BRIGHTNESS, brightnessPercentType);
|
updateState(CHANNEL_BRIGHTNESS, brightnessPercentType);
|
||||||
|
|
||||||
if (state.isOn()) {
|
updateState(CHANNEL_SWITCH, OnOffType.from(state.isOn()));
|
||||||
updateState(CHANNEL_SWITCH, OnOffType.ON);
|
|
||||||
} else {
|
|
||||||
updateState(CHANNEL_SWITCH, OnOffType.OFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringType stringType = LightStateConverter.toAlertStringType(state);
|
StringType stringType = LightStateConverter.toAlertStringType(state);
|
||||||
if (!"NULL".equals(stringType.toString())) {
|
if (!"NULL".equals(stringType.toString())) {
|
||||||
@@ -609,7 +643,7 @@ public class HueLightHandler extends BaseThingHandler implements LightStatusList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
return Collections.singletonList(LightActions.class);
|
return List.of(LightActions.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,10 +12,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.hue.internal.handler;
|
package org.openhab.binding.hue.internal.handler;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.Channel;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||||
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||||
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
@@ -29,14 +38,23 @@ import org.osgi.service.component.annotations.Reference;
|
|||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class HueStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider {
|
public class HueStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider {
|
||||||
|
|
||||||
@Reference
|
private final Map<ChannelUID, StateDescription> descriptions = new ConcurrentHashMap<>();
|
||||||
protected void setChannelTypeI18nLocalizationService(
|
|
||||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
@Activate
|
||||||
|
public HueStateDescriptionOptionProvider(
|
||||||
|
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
||||||
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void unsetChannelTypeI18nLocalizationService(
|
public void setDescription(ChannelUID channelUID, StateDescription description) {
|
||||||
final ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
|
descriptions.put(channelUID, description);
|
||||||
this.channelTypeI18nLocalizationService = null;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable StateDescription getStateDescription(Channel channel,
|
||||||
|
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
|
||||||
|
StateDescription stateDescription = descriptions.get(channel.getUID());
|
||||||
|
return stateDescription != null ? stateDescription
|
||||||
|
: super.getStateDescription(channel, originalStateDescription, locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.openhab.binding.hue.internal.State.AlertMode;
|
|||||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||||
import org.openhab.binding.hue.internal.State.Effect;
|
import org.openhab.binding.hue.internal.State.Effect;
|
||||||
import org.openhab.binding.hue.internal.StateUpdate;
|
import org.openhab.binding.hue.internal.StateUpdate;
|
||||||
|
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||||
@@ -44,10 +45,6 @@ public class LightStateConverter {
|
|||||||
private static final double SATURATION_FACTOR = 2.54;
|
private static final double SATURATION_FACTOR = 2.54;
|
||||||
private static final double BRIGHTNESS_FACTOR = 2.54;
|
private static final double BRIGHTNESS_FACTOR = 2.54;
|
||||||
|
|
||||||
private static final int MIN_COLOR_TEMPERATURE = 153;
|
|
||||||
private static final int MAX_COLOR_TEMPERATURE = 500;
|
|
||||||
private static final int COLOR_TEMPERATURE_RANGE = MAX_COLOR_TEMPERATURE - MIN_COLOR_TEMPERATURE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
|
* {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
|
||||||
*/
|
*/
|
||||||
@@ -150,10 +147,26 @@ public class LightStateConverter {
|
|||||||
* @param percentType color temperature represented as {@link PercentType}
|
* @param percentType color temperature represented as {@link PercentType}
|
||||||
* @return light state containing the color temperature
|
* @return light state containing the color temperature
|
||||||
*/
|
*/
|
||||||
public static StateUpdate toColorTemperatureLightState(PercentType percentType) {
|
public static StateUpdate toColorTemperatureLightStateFromPercentType(PercentType percentType,
|
||||||
int colorTemperature = MIN_COLOR_TEMPERATURE
|
ColorTemperature capabilities) {
|
||||||
+ Math.round((COLOR_TEMPERATURE_RANGE * percentType.floatValue()) / 100);
|
int colorTemperature = capabilities.min
|
||||||
return new StateUpdate().setColorTemperature(colorTemperature);
|
+ Math.round(((capabilities.max - capabilities.min) * percentType.floatValue()) / 100);
|
||||||
|
return new StateUpdate().setColorTemperature(colorTemperature, capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int kelvinToMired(int kelvinValue) {
|
||||||
|
return (int) (1000000.0 / kelvinValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the given {@link DecimalType} into a light state containing
|
||||||
|
* the color temperature in Kelvin.
|
||||||
|
*
|
||||||
|
* @param decimalType color temperature in Kelvin
|
||||||
|
* @return light state containing the color temperature
|
||||||
|
*/
|
||||||
|
public static StateUpdate toColorTemperatureLightState(DecimalType decimalType, ColorTemperature capabilities) {
|
||||||
|
return new StateUpdate().setColorTemperature(kelvinToMired(decimalType.intValue()), capabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,12 +176,13 @@ public class LightStateConverter {
|
|||||||
* @param currentColorTemp The current color temperature
|
* @param currentColorTemp The current color temperature
|
||||||
* @return The adjusted color temperature value
|
* @return The adjusted color temperature value
|
||||||
*/
|
*/
|
||||||
public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp) {
|
public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp,
|
||||||
|
ColorTemperature capabilities) {
|
||||||
int newColorTemp;
|
int newColorTemp;
|
||||||
if (type == IncreaseDecreaseType.DECREASE) {
|
if (type == IncreaseDecreaseType.DECREASE) {
|
||||||
newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, MIN_COLOR_TEMPERATURE);
|
newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, capabilities.min);
|
||||||
} else {
|
} else {
|
||||||
newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, MAX_COLOR_TEMPERATURE);
|
newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, capabilities.max);
|
||||||
}
|
}
|
||||||
return newColorTemp;
|
return newColorTemp;
|
||||||
}
|
}
|
||||||
@@ -180,12 +194,27 @@ public class LightStateConverter {
|
|||||||
* @param lightState light state
|
* @param lightState light state
|
||||||
* @return percent type representing the color temperature
|
* @return percent type representing the color temperature
|
||||||
*/
|
*/
|
||||||
public static PercentType toColorTemperaturePercentType(State lightState) {
|
public static PercentType toColorTemperaturePercentType(State lightState, ColorTemperature capabilities) {
|
||||||
int percent = (int) Math
|
int percent = (int) Math.round(((lightState.getColorTemperature() - capabilities.min) * 100.0)
|
||||||
.round(((lightState.getColorTemperature() - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
|
/ (capabilities.max - capabilities.min));
|
||||||
return new PercentType(restrictToBounds(percent));
|
return new PercentType(restrictToBounds(percent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int miredToKelvin(int miredValue) {
|
||||||
|
return (int) (1000000.0 / miredValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms Hue Light {@link State} into {@link DecimalType} representing
|
||||||
|
* the color temperature in Kelvin.
|
||||||
|
*
|
||||||
|
* @param lightState light state
|
||||||
|
* @return percent type representing the color temperature in Kelvin
|
||||||
|
*/
|
||||||
|
public static DecimalType toColorTemperature(State lightState) {
|
||||||
|
return new DecimalType(miredToKelvin(lightState.getColorTemperature()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms Hue Light {@link State} into {@link PercentType} representing
|
* Transforms Hue Light {@link State} into {@link PercentType} representing
|
||||||
* the brightness.
|
* the brightness.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
<channels>
|
<channels>
|
||||||
<channel id="color_temperature" typeId="system.color-temperature"/>
|
<channel id="color_temperature" typeId="system.color-temperature"/>
|
||||||
|
<channel id="color_temperature_abs" typeId="system.color-temperature-abs"/>
|
||||||
<channel id="brightness" typeId="system.brightness"/>
|
<channel id="brightness" typeId="system.brightness"/>
|
||||||
<channel id="alert" typeId="alert"/>
|
<channel id="alert" typeId="alert"/>
|
||||||
<channel id="effect" typeId="effect"/>
|
<channel id="effect" typeId="effect"/>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<channels>
|
<channels>
|
||||||
<channel id="color" typeId="system.color"/>
|
<channel id="color" typeId="system.color"/>
|
||||||
<channel id="color_temperature" typeId="system.color-temperature"/>
|
<channel id="color_temperature" typeId="system.color-temperature"/>
|
||||||
|
<channel id="color_temperature_abs" typeId="system.color-temperature-abs"/>
|
||||||
<channel id="alert" typeId="alert"/>
|
<channel id="alert" typeId="alert"/>
|
||||||
<channel id="effect" typeId="effect"/>
|
<channel id="effect" typeId="effect"/>
|
||||||
</channels>
|
</channels>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<channels>
|
<channels>
|
||||||
<channel id="switch" typeId="system.power"/>
|
<channel id="switch" typeId="system.power"/>
|
||||||
<channel id="color_temperature" typeId="system.color-temperature"/>
|
<channel id="color_temperature" typeId="system.color-temperature"/>
|
||||||
|
<channel id="color_temperature_abs" typeId="system.color-temperature-abs"/>
|
||||||
<channel id="brightness" typeId="system.brightness"/>
|
<channel id="brightness" typeId="system.brightness"/>
|
||||||
<channel id="color" typeId="system.color"/>
|
<channel id="color" typeId="system.color"/>
|
||||||
<channel id="alert" typeId="alert"/>
|
<channel id="alert" typeId="alert"/>
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ package org.openhab.binding.hue.internal;
|
|||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||||
|
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||||
import org.openhab.binding.hue.internal.handler.LightStateConverter;
|
import org.openhab.binding.hue.internal.handler.LightStateConverter;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
@@ -31,6 +33,38 @@ import org.openhab.core.library.types.PercentType;
|
|||||||
*/
|
*/
|
||||||
public class LightStateConverterTest {
|
public class LightStateConverterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void colorTemperatureLightStateConverterConversionIsBijectiveDefaultColorTemperatureCapabilities() {
|
||||||
|
final State lightState = new State();
|
||||||
|
final ColorTemperature colorTemperature = new ColorTemperature();
|
||||||
|
for (int percent = 1; percent <= 100; ++percent) {
|
||||||
|
StateUpdate stateUpdate = LightStateConverter
|
||||||
|
.toColorTemperatureLightStateFromPercentType(new PercentType(percent), colorTemperature);
|
||||||
|
assertThat(stateUpdate.commands, hasSize(1));
|
||||||
|
assertThat(stateUpdate.commands.get(0).key, is("ct"));
|
||||||
|
lightState.ct = Integer.parseInt(stateUpdate.commands.get(0).value.toString());
|
||||||
|
assertThat(LightStateConverter.toColorTemperaturePercentType(lightState, colorTemperature).intValue(),
|
||||||
|
is(percent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void colorTemperatureLightStateConverterConversionIsBijectiveIndividualColorTemperatureCapabilities() {
|
||||||
|
final State lightState = new State();
|
||||||
|
final ColorTemperature colorTemperature = new ColorTemperature();
|
||||||
|
colorTemperature.min = 250;
|
||||||
|
colorTemperature.max = 454;
|
||||||
|
for (int percent = 1; percent <= 100; ++percent) {
|
||||||
|
StateUpdate stateUpdate = LightStateConverter
|
||||||
|
.toColorTemperatureLightStateFromPercentType(new PercentType(percent), colorTemperature);
|
||||||
|
assertThat(stateUpdate.commands, hasSize(1));
|
||||||
|
assertThat(stateUpdate.commands.get(0).key, is("ct"));
|
||||||
|
lightState.ct = Integer.parseInt(stateUpdate.commands.get(0).value.toString());
|
||||||
|
assertThat(LightStateConverter.toColorTemperaturePercentType(lightState, colorTemperature).intValue(),
|
||||||
|
is(percent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void brightnessOfZeroIsZero() {
|
public void brightnessOfZeroIsZero() {
|
||||||
final State lightState = new State();
|
final State lightState = new State();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.hue.internal.handler;
|
package org.openhab.binding.hue.internal.handler;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||||
@@ -27,6 +27,7 @@ import org.openhab.binding.hue.internal.FullLight;
|
|||||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||||
import org.openhab.binding.hue.internal.StateUpdate;
|
import org.openhab.binding.hue.internal.StateUpdate;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
import org.openhab.core.library.types.OnOffType;
|
||||||
@@ -37,6 +38,7 @@ import org.openhab.core.thing.ChannelUID;
|
|||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -136,6 +138,24 @@ public class HueLightHandlerTest {
|
|||||||
assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
|
assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assertCommandForColorTemperatureAbsChannel6500Kelvin() {
|
||||||
|
String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
|
||||||
|
assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assertCommandForColorTemperatureAbsChannel4500Kelvin() {
|
||||||
|
String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
|
||||||
|
assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void assertCommandForColorTemperatureAbsChannel2000Kelvin() {
|
||||||
|
String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
|
||||||
|
assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void assertPercentageValueOfColorTemperatureWhenCt153() {
|
public void assertPercentageValueOfColorTemperatureWhenCt153() {
|
||||||
int expectedReply = 0;
|
int expectedReply = 0;
|
||||||
@@ -337,6 +357,10 @@ public class HueLightHandlerTest {
|
|||||||
assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
|
assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
|
||||||
|
assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
|
||||||
|
}
|
||||||
|
|
||||||
private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
|
private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
|
||||||
int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
|
int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
|
||||||
assertEquals(percent, expectedPercent);
|
assertEquals(percent, expectedPercent);
|
||||||
@@ -373,7 +397,8 @@ public class HueLightHandlerTest {
|
|||||||
|
|
||||||
long fadeTime = 400;
|
long fadeTime = 400;
|
||||||
|
|
||||||
HueLightHandler hueLightHandler = new HueLightHandler(mockThing) {
|
HueLightHandler hueLightHandler = new HueLightHandler(mockThing,
|
||||||
|
new HueStateDescriptionOptionProvider(mock(ChannelTypeI18nLocalizationService.class))) {
|
||||||
@Override
|
@Override
|
||||||
protected synchronized HueClient getHueClient() {
|
protected synchronized HueClient getHueClient() {
|
||||||
return mockClient;
|
return mockClient;
|
||||||
|
|||||||
Reference in New Issue
Block a user