2 * Copyright (c) 2010-2020 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 org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.openhab.binding.hue.internal.State;
18 import org.openhab.binding.hue.internal.State.AlertMode;
19 import org.openhab.binding.hue.internal.State.ColorMode;
20 import org.openhab.binding.hue.internal.State.Effect;
21 import org.openhab.binding.hue.internal.StateUpdate;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.HSBType;
24 import org.openhab.core.library.types.IncreaseDecreaseType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.PercentType;
27 import org.openhab.core.library.types.StringType;
30 * The {@link LightStateConverter} is responsible for mapping to/from jue types.
32 * @author Dennis Nobel - Initial contribution
33 * @author Oliver Libutzki - Adjustments
34 * @author Kai Kreuzer - made code static
35 * @author Andre Fuechsel - added method for brightness
36 * @author Yordan Zhelev - added method for alert
37 * @author Denis Dudnik - switched to internally integrated source of Jue library, minor code cleanup
38 * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
41 public class LightStateConverter {
43 private static final double HUE_FACTOR = 65535 / 360.0;
44 private static final double SATURATION_FACTOR = 2.54;
45 private static final double BRIGHTNESS_FACTOR = 2.54;
47 private static final int MIN_COLOR_TEMPERATURE = 153;
48 private static final int MAX_COLOR_TEMPERATURE = 500;
49 private static final int COLOR_TEMPERATURE_RANGE = MAX_COLOR_TEMPERATURE - MIN_COLOR_TEMPERATURE;
52 * {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
54 static final String ALERT_MODE_NONE = "NONE";
56 * {@value #ALERT_MODE_SELECT}. The light is performing one breathe cycle.
58 static final String ALERT_MODE_SELECT = "SELECT";
60 * {@value #ALERT_MODE_LONG_SELECT}. The light is performing breathe cycles
61 * for 15 seconds or until an "alert": "none" command is received.
63 static final String ALERT_MODE_LONG_SELECT = "LSELECT";
65 private static final int DIM_STEPSIZE = 30;
68 * Transforms the given {@link HSBType} into a light state.
70 * @param hsbType HSB type
71 * @return light state representing the {@link HSBType}.
73 public static StateUpdate toColorLightState(HSBType hsbType, State lightState) {
74 StateUpdate stateUpdate = ColorMode.XY.equals(lightState.getColorMode()) ? toXYColorLightState(hsbType)
75 : toHSBColorLightState(hsbType);
77 int brightness = (int) Math.floor(hsbType.getBrightness().doubleValue() * BRIGHTNESS_FACTOR);
79 stateUpdate.setBrightness(brightness);
84 private static StateUpdate toHSBColorLightState(HSBType hsbType) {
85 int hue = (int) Math.round(hsbType.getHue().doubleValue() * HUE_FACTOR);
86 int saturation = (int) Math.floor(hsbType.getSaturation().doubleValue() * SATURATION_FACTOR);
88 return new StateUpdate().setHue(hue).setSat(saturation);
91 private static StateUpdate toXYColorLightState(HSBType hsbType) {
92 PercentType[] xy = hsbType.toXY();
93 float x = xy[0].floatValue() / 100.0f;
94 float y = xy[1].floatValue() / 100.0f;
96 return new StateUpdate().setXY(x, y);
100 * Transforms the given {@link OnOffType} into a light state containing the
103 * @param onOffType on or off state
104 * @return light state containing the 'on' value
106 public static StateUpdate toOnOffLightState(OnOffType onOffType) {
107 return new StateUpdate().setOn(OnOffType.ON.equals(onOffType));
111 * Transforms the given {@link PercentType} into a light state containing
112 * the brightness and the 'on' value represented by {@link PercentType}.
114 * @param percentType brightness represented as {@link PercentType}
115 * @return light state containing the brightness and the 'on' value
117 public static StateUpdate toBrightnessLightState(PercentType percentType) {
118 boolean on = !percentType.equals(PercentType.ZERO);
119 final StateUpdate stateUpdate = new StateUpdate().setOn(on);
121 int brightness = (int) Math.floor(percentType.doubleValue() * BRIGHTNESS_FACTOR);
122 if (brightness > 0) {
123 stateUpdate.setBrightness(brightness);
129 * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
130 * returns the updated value.
132 * @param command The {@link IncreaseDecreaseType} to be used
133 * @param currentBrightness The current brightness
134 * @return The adjusted brightness value
136 public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
138 if (command == IncreaseDecreaseType.DECREASE) {
139 newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
141 newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, (int) (BRIGHTNESS_FACTOR * 100));
143 return newBrightness;
147 * Transforms the given {@link PercentType} into a light state containing
148 * the color temperature represented by {@link PercentType}.
150 * @param percentType color temperature represented as {@link PercentType}
151 * @return light state containing the color temperature
153 public static StateUpdate toColorTemperatureLightState(PercentType percentType) {
154 int colorTemperature = MIN_COLOR_TEMPERATURE
155 + Math.round((COLOR_TEMPERATURE_RANGE * percentType.floatValue()) / 100);
156 return new StateUpdate().setColorTemperature(colorTemperature);
160 * Adjusts the given color temperature using the {@link IncreaseDecreaseType} and returns the updated value.
162 * @param type The {@link IncreaseDecreaseType} to be used
163 * @param currentColorTemp The current color temperature
164 * @return The adjusted color temperature value
166 public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp) {
168 if (type == IncreaseDecreaseType.DECREASE) {
169 newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, MIN_COLOR_TEMPERATURE);
171 newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, MAX_COLOR_TEMPERATURE);
177 * Transforms Hue Light {@link State} into {@link PercentType} representing
178 * the color temperature.
180 * @param lightState light state
181 * @return percent type representing the color temperature
183 public static PercentType toColorTemperaturePercentType(State lightState) {
184 int percent = (int) Math
185 .round(((lightState.getColorTemperature() - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
186 return new PercentType(restrictToBounds(percent));
190 * Transforms Hue Light {@link State} into {@link PercentType} representing
193 * @param lightState light state
194 * @return percent type representing the brightness
196 public static PercentType toBrightnessPercentType(State lightState) {
197 int percent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
198 return new PercentType(restrictToBounds(percent));
202 * Transforms {@link State} into {@link StringType} representing the {@link AlertMode}.
204 * @param lightState light state.
205 * @return string type representing the alert mode.
207 public static StringType toAlertStringType(State lightState) {
208 AlertMode alertMode = lightState.getAlertMode();
209 if (alertMode == null) {
210 return new StringType("NULL");
212 return new StringType(alertMode.toString());
217 * Transforms Hue Light {@link State} into {@link HSBType} representing the
220 * @param lightState light state
221 * @return HSB type representing the color
223 public static HSBType toHSBType(State lightState) {
224 // even if color mode is reported to be XY, xy field of lightState might be null, while hsb is available
225 boolean isInXYMode = ColorMode.XY.equals(lightState.getColorMode()) && lightState.getXY() != null;
226 return isInXYMode ? fromXYtoHSBType(lightState) : fromHSBtoHSBType(lightState);
229 private static HSBType fromHSBtoHSBType(State lightState) {
230 int hue = (int) Math.round(lightState.getHue() / HUE_FACTOR) % 360;
232 int saturationInPercent = (int) Math.ceil(lightState.getSaturation() / SATURATION_FACTOR);
233 saturationInPercent = restrictToBounds(saturationInPercent);
235 int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
236 brightnessInPercent = restrictToBounds(brightnessInPercent);
238 return new HSBType(new DecimalType(hue), new PercentType(saturationInPercent),
239 new PercentType(brightnessInPercent));
242 private static HSBType fromXYtoHSBType(State lightState) {
243 float[] xy = lightState.getXY();
244 HSBType hsb = HSBType.fromXY(xy[0], xy[1]);
246 int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
247 brightnessInPercent = restrictToBounds(brightnessInPercent);
249 return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(brightnessInPercent));
253 * Transforms the given {@link StringType} into a light state containing the {@link AlertMode} to be triggered.
255 * @param alertType {@link StringType} representing the required {@link AlertMode} . <br>
256 * Supported values are:
258 * <li>{@value #ALERT_MODE_NONE}.
259 * <li>{@value #ALERT_MODE_SELECT}.
260 * <li>{@value #ALERT_MODE_LONG_SELECT}.
262 * @return light state containing the {@link AlertMode} or <b><code>null </code></b> if the provided
263 * {@link StringType} represents unsupported mode.
265 public static @Nullable StateUpdate toAlertState(StringType alertType) {
268 switch (alertType.toString()) {
269 case ALERT_MODE_NONE:
270 alertMode = State.AlertMode.NONE;
272 case ALERT_MODE_SELECT:
273 alertMode = State.AlertMode.SELECT;
275 case ALERT_MODE_LONG_SELECT:
276 alertMode = State.AlertMode.LSELECT;
281 return new StateUpdate().setAlert(alertMode);
285 * Transforms the given {@link OnOffType} into a light state containing the {@link Effect} value.
286 * {@link OnOffType#ON} will result in {@link Effect#COLORLOOP}. {@link OnOffType#OFF} will result in
287 * {@link Effect#NONE}.
289 * @param onOffType on or off state
290 * @return light state containing the {@link Effect} value
292 public static StateUpdate toOnOffEffectState(OnOffType onOffType) {
293 StateUpdate stateUpdate;
295 if (OnOffType.ON.equals(onOffType)) {
296 stateUpdate = new StateUpdate().setEffect(Effect.COLORLOOP);
298 stateUpdate = new StateUpdate().setEffect(Effect.NONE);
304 private static int restrictToBounds(int percentValue) {
305 if (percentValue < 0) {
307 } else if (percentValue > 100) {