2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.hue.internal.handler;
15 import javax.measure.quantity.Temperature;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.hue.internal.api.dto.clip1.ColorTemperature;
20 import org.openhab.binding.hue.internal.api.dto.clip1.State;
21 import org.openhab.binding.hue.internal.api.dto.clip1.State.AlertMode;
22 import org.openhab.binding.hue.internal.api.dto.clip1.State.ColorMode;
23 import org.openhab.binding.hue.internal.api.dto.clip1.State.Effect;
24 import org.openhab.binding.hue.internal.api.dto.clip1.StateUpdate;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.HSBType;
27 import org.openhab.core.library.types.IncreaseDecreaseType;
28 import org.openhab.core.library.types.OnOffType;
29 import org.openhab.core.library.types.PercentType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.types.StringType;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.util.ColorUtil;
36 * The {@link LightStateConverter} is responsible for mapping to/from jue types.
38 * @author Dennis Nobel - Initial contribution
39 * @author Oliver Libutzki - Adjustments
40 * @author Kai Kreuzer - made code static
41 * @author Andre Fuechsel - added method for brightness
42 * @author Yordan Zhelev - added method for alert
43 * @author Denis Dudnik - switched to internally integrated source of Jue library, minor code cleanup
44 * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
47 public class LightStateConverter {
49 private static final double HUE_FACTOR = 65535 / 360.0;
50 private static final double SATURATION_FACTOR = 2.54;
51 private static final double BRIGHTNESS_FACTOR = 2.54;
54 * {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
56 static final String ALERT_MODE_NONE = "NONE";
58 * {@value #ALERT_MODE_SELECT}. The light is performing one breathe cycle.
60 static final String ALERT_MODE_SELECT = "SELECT";
62 * {@value #ALERT_MODE_LONG_SELECT}. The light is performing breathe cycles
63 * for 15 seconds or until an "alert": "none" command is received.
65 static final String ALERT_MODE_LONG_SELECT = "LSELECT";
67 private static final int DIM_STEPSIZE = 30;
70 * Transforms the given {@link HSBType} into a light state.
72 * @param hsbType HSB type
73 * @return light state representing the {@link HSBType}.
75 public static StateUpdate toColorLightState(HSBType hsbType, State lightState) {
76 // XY color is the implicit default: Use XY color mode if i) no color mode is set or ii) if the bulb is in
77 // CT mode or iii) already in XY mode. Only if the bulb is in HS mode, use this one.
78 StateUpdate stateUpdate = ColorMode.HS.equals(lightState.getColorMode()) ? toHSBColorLightState(hsbType)
79 : toXYColorLightState(hsbType);
81 int brightness = (int) Math.floor(hsbType.getBrightness().doubleValue() * BRIGHTNESS_FACTOR);
83 stateUpdate.setBrightness(brightness);
88 private static StateUpdate toHSBColorLightState(HSBType hsbType) {
89 int hue = (int) Math.round(hsbType.getHue().doubleValue() * HUE_FACTOR);
90 int saturation = (int) Math.floor(hsbType.getSaturation().doubleValue() * SATURATION_FACTOR);
92 return new StateUpdate().setHue(hue).setSat(saturation);
95 private static StateUpdate toXYColorLightState(HSBType hsbType) {
96 PercentType[] xy = hsbType.toXY();
97 float x = xy[0].floatValue() / 100.0f;
98 float y = xy[1].floatValue() / 100.0f;
100 return new StateUpdate().setXY(x, y);
104 * Transforms the given {@link OnOffType} into a light state containing the
107 * @param onOffType on or off state
108 * @return light state containing the 'on' value
110 public static StateUpdate toOnOffLightState(OnOffType onOffType) {
111 return new StateUpdate().setOn(OnOffType.ON.equals(onOffType));
115 * Transforms the given {@link PercentType} into a light state containing
116 * the brightness and the 'on' value represented by {@link PercentType}.
118 * @param percentType brightness represented as {@link PercentType}
119 * @return light state containing the brightness and the 'on' value
121 public static StateUpdate toBrightnessLightState(PercentType percentType) {
122 boolean on = !percentType.equals(PercentType.ZERO);
123 final StateUpdate stateUpdate = new StateUpdate().setOn(on);
125 int brightness = (int) Math.floor(percentType.doubleValue() * BRIGHTNESS_FACTOR);
126 if (brightness > 0) {
127 stateUpdate.setBrightness(brightness);
133 * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
134 * returns the updated value.
136 * @param command The {@link IncreaseDecreaseType} to be used
137 * @param currentBrightness The current brightness
138 * @return The adjusted brightness value
140 public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
142 if (command == IncreaseDecreaseType.DECREASE) {
143 newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
145 newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, (int) (BRIGHTNESS_FACTOR * 100));
147 return newBrightness;
151 * Transforms the given {@link PercentType} into a light state containing
152 * the color temperature represented by {@link PercentType}.
154 * @param percentType color temperature represented as {@link PercentType}
155 * @return light state containing the color temperature
157 public static StateUpdate toColorTemperatureLightStateFromPercentType(PercentType percentType,
158 ColorTemperature capabilities) {
159 int colorTemperature = capabilities.min
160 + Math.round(((capabilities.max - capabilities.min) * percentType.floatValue()) / 100);
161 return new StateUpdate().setColorTemperature(colorTemperature, capabilities);
164 public static int kelvinToMired(int kelvinValue) {
165 return (int) (1000000.0 / kelvinValue);
169 * Transforms the given color temperature in Kelvin into a Hue Light {@link State}.
171 * @param kelvinValue color temperature in Kelvin
172 * @param capabilities color temperature capabilities (e.g. min and max values)
173 * @return light state containing the color temperature
175 public static StateUpdate toColorTemperatureLightState(int kelvinValue, ColorTemperature capabilities) {
176 return new StateUpdate().setColorTemperature(kelvinToMired(kelvinValue), capabilities);
180 * Adjusts the given color temperature using the {@link IncreaseDecreaseType} and returns the updated value.
182 * @param type The {@link IncreaseDecreaseType} to be used
183 * @param currentColorTemp The current color temperature
184 * @return The adjusted color temperature value
186 public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp,
187 ColorTemperature capabilities) {
189 if (type == IncreaseDecreaseType.DECREASE) {
190 newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, capabilities.min);
192 newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, capabilities.max);
198 * Transforms Hue Light {@link State} into {@link PercentType} representing
199 * the color temperature.
201 * @param lightState light state
202 * @return percent type representing the color temperature
204 public static PercentType toColorTemperaturePercentType(State lightState, ColorTemperature capabilities) {
205 int percent = (int) Math.round(((lightState.getColorTemperature() - capabilities.min) * 100.0)
206 / (capabilities.max - capabilities.min));
207 return new PercentType(restrictToBounds(percent));
210 public static int miredToKelvin(int miredValue) {
211 return (int) (1000000.0 / miredValue);
215 * Transforms Hue Light {@link State} into {@link QuantityType} representing
216 * the color temperature in Kelvin.
218 * @param lightState light state
219 * @return quantity type representing the color temperature in Kelvin
221 public static QuantityType<Temperature> toColorTemperature(State lightState) {
222 return new QuantityType<>(miredToKelvin(lightState.getColorTemperature()), Units.KELVIN);
226 * Transforms Hue Light {@link State} into {@link PercentType} representing
229 * @param lightState light state
230 * @return percent type representing the brightness
232 public static PercentType toBrightnessPercentType(State lightState) {
233 int percent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
234 return new PercentType(restrictToBounds(percent));
238 * Transforms {@link State} into {@link StringType} representing the {@link AlertMode}.
240 * @param lightState light state.
241 * @return string type representing the alert mode.
243 public static StringType toAlertStringType(State lightState) {
244 AlertMode alertMode = lightState.getAlertMode();
245 if (alertMode == null) {
246 return new StringType("NULL");
248 return new StringType(alertMode.toString());
253 * Transforms Hue Light {@link State} into {@link HSBType} representing the
256 * @param lightState light state
257 * @return HSB type representing the color
259 public static HSBType toHSBType(State lightState) {
260 // even if color mode is reported to be XY, xy field of lightState might be null, while hsb is available
261 boolean isInXYMode = ColorMode.XY.equals(lightState.getColorMode()) && lightState.getXY() != null;
262 return isInXYMode ? fromXYtoHSBType(lightState) : fromHSBtoHSBType(lightState);
265 private static HSBType fromHSBtoHSBType(State lightState) {
266 int hue = (int) Math.round(lightState.getHue() / HUE_FACTOR) % 360;
268 int saturationInPercent = (int) Math.ceil(lightState.getSaturation() / SATURATION_FACTOR);
269 saturationInPercent = restrictToBounds(saturationInPercent);
271 int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
272 brightnessInPercent = restrictToBounds(brightnessInPercent);
274 return new HSBType(new DecimalType(hue), new PercentType(saturationInPercent),
275 new PercentType(brightnessInPercent));
278 private static HSBType fromXYtoHSBType(State lightState) {
279 float[] xy = lightState.getXY();
280 HSBType hsb = ColorUtil.xyToHsb(new double[] { xy[0], xy[1] });
282 int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
283 brightnessInPercent = restrictToBounds(brightnessInPercent);
285 return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(brightnessInPercent));
289 * Transforms the given {@link StringType} into a light state containing the {@link AlertMode} to be triggered.
291 * @param alertType {@link StringType} representing the required {@link AlertMode} . <br>
292 * Supported values are:
294 * <li>{@value #ALERT_MODE_NONE}.
295 * <li>{@value #ALERT_MODE_SELECT}.
296 * <li>{@value #ALERT_MODE_LONG_SELECT}.
298 * @return light state containing the {@link AlertMode} or <b><code>null </code></b> if the provided
299 * {@link StringType} represents unsupported mode.
301 public static @Nullable StateUpdate toAlertState(StringType alertType) {
304 switch (alertType.toString()) {
305 case ALERT_MODE_NONE:
306 alertMode = State.AlertMode.NONE;
308 case ALERT_MODE_SELECT:
309 alertMode = State.AlertMode.SELECT;
311 case ALERT_MODE_LONG_SELECT:
312 alertMode = State.AlertMode.LSELECT;
317 return new StateUpdate().setAlert(alertMode);
321 * Transforms the given {@link OnOffType} into a light state containing the {@link Effect} value.
322 * {@link OnOffType#ON} will result in {@link Effect#COLORLOOP}. {@link OnOffType#OFF} will result in
323 * {@link Effect#NONE}.
325 * @param onOffType on or off state
326 * @return light state containing the {@link Effect} value
328 public static StateUpdate toOnOffEffectState(OnOffType onOffType) {
329 StateUpdate stateUpdate;
331 if (OnOffType.ON.equals(onOffType)) {
332 stateUpdate = new StateUpdate().setEffect(Effect.COLORLOOP);
334 stateUpdate = new StateUpdate().setEffect(Effect.NONE);
340 private static int restrictToBounds(int percentValue) {
341 if (percentValue < 0) {
343 } else if (percentValue > 100) {