]> git.basschouten.com Git - openhab-addons.git/blob
36689d82854f61ced60b85cdd8a72ffd4ff1488a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.hue.internal.handler;
14
15 import javax.measure.quantity.Temperature;
16
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.hue.internal.dto.ColorTemperature;
20 import org.openhab.binding.hue.internal.dto.State;
21 import org.openhab.binding.hue.internal.dto.State.AlertMode;
22 import org.openhab.binding.hue.internal.dto.State.ColorMode;
23 import org.openhab.binding.hue.internal.dto.State.Effect;
24 import org.openhab.binding.hue.internal.dto.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;
34
35 /**
36  * The {@link LightStateConverter} is responsible for mapping to/from jue types.
37  *
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
45  */
46 @NonNullByDefault
47 public class LightStateConverter {
48
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;
52
53     /**
54      * {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
55      */
56     static final String ALERT_MODE_NONE = "NONE";
57     /**
58      * {@value #ALERT_MODE_SELECT}. The light is performing one breathe cycle.
59      */
60     static final String ALERT_MODE_SELECT = "SELECT";
61     /**
62      * {@value #ALERT_MODE_LONG_SELECT}. The light is performing breathe cycles
63      * for 15 seconds or until an "alert": "none" command is received.
64      */
65     static final String ALERT_MODE_LONG_SELECT = "LSELECT";
66
67     private static final int DIM_STEPSIZE = 30;
68
69     /**
70      * Transforms the given {@link HSBType} into a light state.
71      *
72      * @param hsbType HSB type
73      * @return light state representing the {@link HSBType}.
74      */
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);
80
81         int brightness = (int) Math.floor(hsbType.getBrightness().doubleValue() * BRIGHTNESS_FACTOR);
82         if (brightness > 0) {
83             stateUpdate.setBrightness(brightness);
84         }
85         return stateUpdate;
86     }
87
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);
91
92         return new StateUpdate().setHue(hue).setSat(saturation);
93     }
94
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;
99
100         return new StateUpdate().setXY(x, y);
101     }
102
103     /**
104      * Transforms the given {@link OnOffType} into a light state containing the
105      * 'on' value.
106      *
107      * @param onOffType on or off state
108      * @return light state containing the 'on' value
109      */
110     public static StateUpdate toOnOffLightState(OnOffType onOffType) {
111         return new StateUpdate().setOn(OnOffType.ON.equals(onOffType));
112     }
113
114     /**
115      * Transforms the given {@link PercentType} into a light state containing
116      * the brightness and the 'on' value represented by {@link PercentType}.
117      *
118      * @param percentType brightness represented as {@link PercentType}
119      * @return light state containing the brightness and the 'on' value
120      */
121     public static StateUpdate toBrightnessLightState(PercentType percentType) {
122         boolean on = !percentType.equals(PercentType.ZERO);
123         final StateUpdate stateUpdate = new StateUpdate().setOn(on);
124
125         int brightness = (int) Math.floor(percentType.doubleValue() * BRIGHTNESS_FACTOR);
126         if (brightness > 0) {
127             stateUpdate.setBrightness(brightness);
128         }
129         return stateUpdate;
130     }
131
132     /**
133      * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
134      * returns the updated value.
135      *
136      * @param command The {@link IncreaseDecreaseType} to be used
137      * @param currentBrightness The current brightness
138      * @return The adjusted brightness value
139      */
140     public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
141         int newBrightness;
142         if (command == IncreaseDecreaseType.DECREASE) {
143             newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
144         } else {
145             newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, (int) (BRIGHTNESS_FACTOR * 100));
146         }
147         return newBrightness;
148     }
149
150     /**
151      * Transforms the given {@link PercentType} into a light state containing
152      * the color temperature represented by {@link PercentType}.
153      *
154      * @param percentType color temperature represented as {@link PercentType}
155      * @return light state containing the color temperature
156      */
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);
162     }
163
164     public static int kelvinToMired(int kelvinValue) {
165         return (int) (1000000.0 / kelvinValue);
166     }
167
168     /**
169      * Transforms the given color temperature in Kelvin into a Hue Light {@link State}.
170      *
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
174      */
175     public static StateUpdate toColorTemperatureLightState(int kelvinValue, ColorTemperature capabilities) {
176         return new StateUpdate().setColorTemperature(kelvinToMired(kelvinValue), capabilities);
177     }
178
179     /**
180      * Adjusts the given color temperature using the {@link IncreaseDecreaseType} and returns the updated value.
181      *
182      * @param type The {@link IncreaseDecreaseType} to be used
183      * @param currentColorTemp The current color temperature
184      * @return The adjusted color temperature value
185      */
186     public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp,
187             ColorTemperature capabilities) {
188         int newColorTemp;
189         if (type == IncreaseDecreaseType.DECREASE) {
190             newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, capabilities.min);
191         } else {
192             newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, capabilities.max);
193         }
194         return newColorTemp;
195     }
196
197     /**
198      * Transforms Hue Light {@link State} into {@link PercentType} representing
199      * the color temperature.
200      *
201      * @param lightState light state
202      * @return percent type representing the color temperature
203      */
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));
208     }
209
210     public static int miredToKelvin(int miredValue) {
211         return (int) (1000000.0 / miredValue);
212     }
213
214     /**
215      * Transforms Hue Light {@link State} into {@link QuantityType} representing
216      * the color temperature in Kelvin.
217      *
218      * @param lightState light state
219      * @return quantity type representing the color temperature in Kelvin
220      */
221     public static QuantityType<Temperature> toColorTemperature(State lightState) {
222         return new QuantityType<>(miredToKelvin(lightState.getColorTemperature()), Units.KELVIN);
223     }
224
225     /**
226      * Transforms Hue Light {@link State} into {@link PercentType} representing
227      * the brightness.
228      *
229      * @param lightState light state
230      * @return percent type representing the brightness
231      */
232     public static PercentType toBrightnessPercentType(State lightState) {
233         int percent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
234         return new PercentType(restrictToBounds(percent));
235     }
236
237     /**
238      * Transforms {@link State} into {@link StringType} representing the {@link AlertMode}.
239      *
240      * @param lightState light state.
241      * @return string type representing the alert mode.
242      */
243     public static StringType toAlertStringType(State lightState) {
244         AlertMode alertMode = lightState.getAlertMode();
245         if (alertMode == null) {
246             return new StringType("NULL");
247         } else {
248             return new StringType(alertMode.toString());
249         }
250     }
251
252     /**
253      * Transforms Hue Light {@link State} into {@link HSBType} representing the
254      * color.
255      *
256      * @param lightState light state
257      * @return HSB type representing the color
258      */
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);
263     }
264
265     private static HSBType fromHSBtoHSBType(State lightState) {
266         int hue = (int) Math.round(lightState.getHue() / HUE_FACTOR) % 360;
267
268         int saturationInPercent = (int) Math.ceil(lightState.getSaturation() / SATURATION_FACTOR);
269         saturationInPercent = restrictToBounds(saturationInPercent);
270
271         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
272         brightnessInPercent = restrictToBounds(brightnessInPercent);
273
274         return new HSBType(new DecimalType(hue), new PercentType(saturationInPercent),
275                 new PercentType(brightnessInPercent));
276     }
277
278     private static HSBType fromXYtoHSBType(State lightState) {
279         float[] xy = lightState.getXY();
280         HSBType hsb = ColorUtil.xyToHsb(new double[] { xy[0], xy[1] });
281
282         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
283         brightnessInPercent = restrictToBounds(brightnessInPercent);
284
285         return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(brightnessInPercent));
286     }
287
288     /**
289      * Transforms the given {@link StringType} into a light state containing the {@link AlertMode} to be triggered.
290      *
291      * @param alertType {@link StringType} representing the required {@link AlertMode} . <br>
292      *            Supported values are:
293      *            <ul>
294      *            <li>{@value #ALERT_MODE_NONE}.
295      *            <li>{@value #ALERT_MODE_SELECT}.
296      *            <li>{@value #ALERT_MODE_LONG_SELECT}.
297      *            <ul>
298      * @return light state containing the {@link AlertMode} or <b><code>null </code></b> if the provided
299      *         {@link StringType} represents unsupported mode.
300      */
301     public static @Nullable StateUpdate toAlertState(StringType alertType) {
302         AlertMode alertMode;
303
304         switch (alertType.toString()) {
305             case ALERT_MODE_NONE:
306                 alertMode = State.AlertMode.NONE;
307                 break;
308             case ALERT_MODE_SELECT:
309                 alertMode = State.AlertMode.SELECT;
310                 break;
311             case ALERT_MODE_LONG_SELECT:
312                 alertMode = State.AlertMode.LSELECT;
313                 break;
314             default:
315                 return null;
316         }
317         return new StateUpdate().setAlert(alertMode);
318     }
319
320     /**
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}.
324      *
325      * @param onOffType on or off state
326      * @return light state containing the {@link Effect} value
327      */
328     public static StateUpdate toOnOffEffectState(OnOffType onOffType) {
329         StateUpdate stateUpdate;
330
331         if (OnOffType.ON.equals(onOffType)) {
332             stateUpdate = new StateUpdate().setEffect(Effect.COLORLOOP);
333         } else {
334             stateUpdate = new StateUpdate().setEffect(Effect.NONE);
335         }
336
337         return stateUpdate;
338     }
339
340     private static int restrictToBounds(int percentValue) {
341         if (percentValue < 0) {
342             return 0;
343         } else if (percentValue > 100) {
344             return 100;
345         }
346         return percentValue;
347     }
348 }