]> git.basschouten.com Git - openhab-addons.git/blob
1212573e608feedcc85df167acb52d5d5fb3c28d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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 org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.eclipse.jdt.annotation.Nullable;
17 import org.openhab.binding.hue.internal.dto.ColorTemperature;
18 import org.openhab.binding.hue.internal.dto.State;
19 import org.openhab.binding.hue.internal.dto.State.AlertMode;
20 import org.openhab.binding.hue.internal.dto.State.ColorMode;
21 import org.openhab.binding.hue.internal.dto.State.Effect;
22 import org.openhab.binding.hue.internal.dto.StateUpdate;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.HSBType;
25 import org.openhab.core.library.types.IncreaseDecreaseType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.PercentType;
28 import org.openhab.core.library.types.StringType;
29
30 /**
31  * The {@link LightStateConverter} is responsible for mapping to/from jue types.
32  *
33  * @author Dennis Nobel - Initial contribution
34  * @author Oliver Libutzki - Adjustments
35  * @author Kai Kreuzer - made code static
36  * @author Andre Fuechsel - added method for brightness
37  * @author Yordan Zhelev - added method for alert
38  * @author Denis Dudnik - switched to internally integrated source of Jue library, minor code cleanup
39  * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
40  */
41 @NonNullByDefault
42 public class LightStateConverter {
43
44     private static final double HUE_FACTOR = 65535 / 360.0;
45     private static final double SATURATION_FACTOR = 2.54;
46     private static final double BRIGHTNESS_FACTOR = 2.54;
47
48     /**
49      * {@value #ALERT_MODE_NONE}. The light is not performing an alert effect.
50      */
51     static final String ALERT_MODE_NONE = "NONE";
52     /**
53      * {@value #ALERT_MODE_SELECT}. The light is performing one breathe cycle.
54      */
55     static final String ALERT_MODE_SELECT = "SELECT";
56     /**
57      * {@value #ALERT_MODE_LONG_SELECT}. The light is performing breathe cycles
58      * for 15 seconds or until an "alert": "none" command is received.
59      */
60     static final String ALERT_MODE_LONG_SELECT = "LSELECT";
61
62     private static final int DIM_STEPSIZE = 30;
63
64     /**
65      * Transforms the given {@link HSBType} into a light state.
66      *
67      * @param hsbType HSB type
68      * @return light state representing the {@link HSBType}.
69      */
70     public static StateUpdate toColorLightState(HSBType hsbType, State lightState) {
71         // XY color is the implicit default: Use XY color mode if i) no color mode is set or ii) if the bulb is in
72         // CT mode or iii) already in XY mode. Only if the bulb is in HS mode, use this one.
73         StateUpdate stateUpdate = ColorMode.HS.equals(lightState.getColorMode()) ? toHSBColorLightState(hsbType)
74                 : toXYColorLightState(hsbType);
75
76         int brightness = (int) Math.floor(hsbType.getBrightness().doubleValue() * BRIGHTNESS_FACTOR);
77         if (brightness > 0) {
78             stateUpdate.setBrightness(brightness);
79         }
80         return stateUpdate;
81     }
82
83     private static StateUpdate toHSBColorLightState(HSBType hsbType) {
84         int hue = (int) Math.round(hsbType.getHue().doubleValue() * HUE_FACTOR);
85         int saturation = (int) Math.floor(hsbType.getSaturation().doubleValue() * SATURATION_FACTOR);
86
87         return new StateUpdate().setHue(hue).setSat(saturation);
88     }
89
90     private static StateUpdate toXYColorLightState(HSBType hsbType) {
91         PercentType[] xy = hsbType.toXY();
92         float x = xy[0].floatValue() / 100.0f;
93         float y = xy[1].floatValue() / 100.0f;
94
95         return new StateUpdate().setXY(x, y);
96     }
97
98     /**
99      * Transforms the given {@link OnOffType} into a light state containing the
100      * 'on' value.
101      *
102      * @param onOffType on or off state
103      * @return light state containing the 'on' value
104      */
105     public static StateUpdate toOnOffLightState(OnOffType onOffType) {
106         return new StateUpdate().setOn(OnOffType.ON.equals(onOffType));
107     }
108
109     /**
110      * Transforms the given {@link PercentType} into a light state containing
111      * the brightness and the 'on' value represented by {@link PercentType}.
112      *
113      * @param percentType brightness represented as {@link PercentType}
114      * @return light state containing the brightness and the 'on' value
115      */
116     public static StateUpdate toBrightnessLightState(PercentType percentType) {
117         boolean on = !percentType.equals(PercentType.ZERO);
118         final StateUpdate stateUpdate = new StateUpdate().setOn(on);
119
120         int brightness = (int) Math.floor(percentType.doubleValue() * BRIGHTNESS_FACTOR);
121         if (brightness > 0) {
122             stateUpdate.setBrightness(brightness);
123         }
124         return stateUpdate;
125     }
126
127     /**
128      * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
129      * returns the updated value.
130      *
131      * @param command The {@link IncreaseDecreaseType} to be used
132      * @param currentBrightness The current brightness
133      * @return The adjusted brightness value
134      */
135     public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
136         int newBrightness;
137         if (command == IncreaseDecreaseType.DECREASE) {
138             newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
139         } else {
140             newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, (int) (BRIGHTNESS_FACTOR * 100));
141         }
142         return newBrightness;
143     }
144
145     /**
146      * Transforms the given {@link PercentType} into a light state containing
147      * the color temperature represented by {@link PercentType}.
148      *
149      * @param percentType color temperature represented as {@link PercentType}
150      * @return light state containing the color temperature
151      */
152     public static StateUpdate toColorTemperatureLightStateFromPercentType(PercentType percentType,
153             ColorTemperature capabilities) {
154         int colorTemperature = capabilities.min
155                 + Math.round(((capabilities.max - capabilities.min) * percentType.floatValue()) / 100);
156         return new StateUpdate().setColorTemperature(colorTemperature, capabilities);
157     }
158
159     public static int kelvinToMired(int kelvinValue) {
160         return (int) (1000000.0 / kelvinValue);
161     }
162
163     /**
164      * Transforms the given {@link DecimalType} into a light state containing
165      * the color temperature in Kelvin.
166      *
167      * @param decimalType color temperature in Kelvin
168      * @return light state containing the color temperature
169      */
170     public static StateUpdate toColorTemperatureLightState(DecimalType decimalType, ColorTemperature capabilities) {
171         return new StateUpdate().setColorTemperature(kelvinToMired(decimalType.intValue()), capabilities);
172     }
173
174     /**
175      * Adjusts the given color temperature using the {@link IncreaseDecreaseType} and returns the updated value.
176      *
177      * @param type The {@link IncreaseDecreaseType} to be used
178      * @param currentColorTemp The current color temperature
179      * @return The adjusted color temperature value
180      */
181     public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp,
182             ColorTemperature capabilities) {
183         int newColorTemp;
184         if (type == IncreaseDecreaseType.DECREASE) {
185             newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, capabilities.min);
186         } else {
187             newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, capabilities.max);
188         }
189         return newColorTemp;
190     }
191
192     /**
193      * Transforms Hue Light {@link State} into {@link PercentType} representing
194      * the color temperature.
195      *
196      * @param lightState light state
197      * @return percent type representing the color temperature
198      */
199     public static PercentType toColorTemperaturePercentType(State lightState, ColorTemperature capabilities) {
200         int percent = (int) Math.round(((lightState.getColorTemperature() - capabilities.min) * 100.0)
201                 / (capabilities.max - capabilities.min));
202         return new PercentType(restrictToBounds(percent));
203     }
204
205     public static int miredToKelvin(int miredValue) {
206         return (int) (1000000.0 / miredValue);
207     }
208
209     /**
210      * Transforms Hue Light {@link State} into {@link DecimalType} representing
211      * the color temperature in Kelvin.
212      *
213      * @param lightState light state
214      * @return percent type representing the color temperature in Kelvin
215      */
216     public static DecimalType toColorTemperature(State lightState) {
217         return new DecimalType(miredToKelvin(lightState.getColorTemperature()));
218     }
219
220     /**
221      * Transforms Hue Light {@link State} into {@link PercentType} representing
222      * the brightness.
223      *
224      * @param lightState light state
225      * @return percent type representing the brightness
226      */
227     public static PercentType toBrightnessPercentType(State lightState) {
228         int percent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
229         return new PercentType(restrictToBounds(percent));
230     }
231
232     /**
233      * Transforms {@link State} into {@link StringType} representing the {@link AlertMode}.
234      *
235      * @param lightState light state.
236      * @return string type representing the alert mode.
237      */
238     public static StringType toAlertStringType(State lightState) {
239         AlertMode alertMode = lightState.getAlertMode();
240         if (alertMode == null) {
241             return new StringType("NULL");
242         } else {
243             return new StringType(alertMode.toString());
244         }
245     }
246
247     /**
248      * Transforms Hue Light {@link State} into {@link HSBType} representing the
249      * color.
250      *
251      * @param lightState light state
252      * @return HSB type representing the color
253      */
254     public static HSBType toHSBType(State lightState) {
255         // even if color mode is reported to be XY, xy field of lightState might be null, while hsb is available
256         boolean isInXYMode = ColorMode.XY.equals(lightState.getColorMode()) && lightState.getXY() != null;
257         return isInXYMode ? fromXYtoHSBType(lightState) : fromHSBtoHSBType(lightState);
258     }
259
260     private static HSBType fromHSBtoHSBType(State lightState) {
261         int hue = (int) Math.round(lightState.getHue() / HUE_FACTOR) % 360;
262
263         int saturationInPercent = (int) Math.ceil(lightState.getSaturation() / SATURATION_FACTOR);
264         saturationInPercent = restrictToBounds(saturationInPercent);
265
266         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
267         brightnessInPercent = restrictToBounds(brightnessInPercent);
268
269         return new HSBType(new DecimalType(hue), new PercentType(saturationInPercent),
270                 new PercentType(brightnessInPercent));
271     }
272
273     private static HSBType fromXYtoHSBType(State lightState) {
274         float[] xy = lightState.getXY();
275         HSBType hsb = HSBType.fromXY(xy[0], xy[1]);
276
277         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
278         brightnessInPercent = restrictToBounds(brightnessInPercent);
279
280         return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(brightnessInPercent));
281     }
282
283     /**
284      * Transforms the given {@link StringType} into a light state containing the {@link AlertMode} to be triggered.
285      *
286      * @param alertType {@link StringType} representing the required {@link AlertMode} . <br>
287      *            Supported values are:
288      *            <ul>
289      *            <li>{@value #ALERT_MODE_NONE}.
290      *            <li>{@value #ALERT_MODE_SELECT}.
291      *            <li>{@value #ALERT_MODE_LONG_SELECT}.
292      *            <ul>
293      * @return light state containing the {@link AlertMode} or <b><code>null </code></b> if the provided
294      *         {@link StringType} represents unsupported mode.
295      */
296     public static @Nullable StateUpdate toAlertState(StringType alertType) {
297         AlertMode alertMode;
298
299         switch (alertType.toString()) {
300             case ALERT_MODE_NONE:
301                 alertMode = State.AlertMode.NONE;
302                 break;
303             case ALERT_MODE_SELECT:
304                 alertMode = State.AlertMode.SELECT;
305                 break;
306             case ALERT_MODE_LONG_SELECT:
307                 alertMode = State.AlertMode.LSELECT;
308                 break;
309             default:
310                 return null;
311         }
312         return new StateUpdate().setAlert(alertMode);
313     }
314
315     /**
316      * Transforms the given {@link OnOffType} into a light state containing the {@link Effect} value.
317      * {@link OnOffType#ON} will result in {@link Effect#COLORLOOP}. {@link OnOffType#OFF} will result in
318      * {@link Effect#NONE}.
319      *
320      * @param onOffType on or off state
321      * @return light state containing the {@link Effect} value
322      */
323     public static StateUpdate toOnOffEffectState(OnOffType onOffType) {
324         StateUpdate stateUpdate;
325
326         if (OnOffType.ON.equals(onOffType)) {
327             stateUpdate = new StateUpdate().setEffect(Effect.COLORLOOP);
328         } else {
329             stateUpdate = new StateUpdate().setEffect(Effect.NONE);
330         }
331
332         return stateUpdate;
333     }
334
335     private static int restrictToBounds(int percentValue) {
336         if (percentValue < 0) {
337             return 0;
338         } else if (percentValue > 100) {
339             return 100;
340         }
341         return percentValue;
342     }
343 }