]> git.basschouten.com Git - openhab-addons.git/blob
4057df83ae7bdcb8c22350dd06ad983bc1287e29
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.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.binding.hue.internal.dto.ColorTemperature;
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         StateUpdate stateUpdate = ColorMode.XY.equals(lightState.getColorMode()) ? toXYColorLightState(hsbType)
72                 : toHSBColorLightState(hsbType);
73
74         int brightness = (int) Math.floor(hsbType.getBrightness().doubleValue() * BRIGHTNESS_FACTOR);
75         if (brightness > 0) {
76             stateUpdate.setBrightness(brightness);
77         }
78         return stateUpdate;
79     }
80
81     private static StateUpdate toHSBColorLightState(HSBType hsbType) {
82         int hue = (int) Math.round(hsbType.getHue().doubleValue() * HUE_FACTOR);
83         int saturation = (int) Math.floor(hsbType.getSaturation().doubleValue() * SATURATION_FACTOR);
84
85         return new StateUpdate().setHue(hue).setSat(saturation);
86     }
87
88     private static StateUpdate toXYColorLightState(HSBType hsbType) {
89         PercentType[] xy = hsbType.toXY();
90         float x = xy[0].floatValue() / 100.0f;
91         float y = xy[1].floatValue() / 100.0f;
92
93         return new StateUpdate().setXY(x, y);
94     }
95
96     /**
97      * Transforms the given {@link OnOffType} into a light state containing the
98      * 'on' value.
99      *
100      * @param onOffType on or off state
101      * @return light state containing the 'on' value
102      */
103     public static StateUpdate toOnOffLightState(OnOffType onOffType) {
104         return new StateUpdate().setOn(OnOffType.ON.equals(onOffType));
105     }
106
107     /**
108      * Transforms the given {@link PercentType} into a light state containing
109      * the brightness and the 'on' value represented by {@link PercentType}.
110      *
111      * @param percentType brightness represented as {@link PercentType}
112      * @return light state containing the brightness and the 'on' value
113      */
114     public static StateUpdate toBrightnessLightState(PercentType percentType) {
115         boolean on = !percentType.equals(PercentType.ZERO);
116         final StateUpdate stateUpdate = new StateUpdate().setOn(on);
117
118         int brightness = (int) Math.floor(percentType.doubleValue() * BRIGHTNESS_FACTOR);
119         if (brightness > 0) {
120             stateUpdate.setBrightness(brightness);
121         }
122         return stateUpdate;
123     }
124
125     /**
126      * Adjusts the given brightness using the {@link IncreaseDecreaseType} and
127      * returns the updated value.
128      *
129      * @param command The {@link IncreaseDecreaseType} to be used
130      * @param currentBrightness The current brightness
131      * @return The adjusted brightness value
132      */
133     public static int toAdjustedBrightness(IncreaseDecreaseType command, int currentBrightness) {
134         int newBrightness;
135         if (command == IncreaseDecreaseType.DECREASE) {
136             newBrightness = Math.max(currentBrightness - DIM_STEPSIZE, 0);
137         } else {
138             newBrightness = Math.min(currentBrightness + DIM_STEPSIZE, (int) (BRIGHTNESS_FACTOR * 100));
139         }
140         return newBrightness;
141     }
142
143     /**
144      * Transforms the given {@link PercentType} into a light state containing
145      * the color temperature represented by {@link PercentType}.
146      *
147      * @param percentType color temperature represented as {@link PercentType}
148      * @return light state containing the color temperature
149      */
150     public static StateUpdate toColorTemperatureLightStateFromPercentType(PercentType percentType,
151             ColorTemperature capabilities) {
152         int colorTemperature = capabilities.min
153                 + Math.round(((capabilities.max - capabilities.min) * percentType.floatValue()) / 100);
154         return new StateUpdate().setColorTemperature(colorTemperature, capabilities);
155     }
156
157     public static int kelvinToMired(int kelvinValue) {
158         return (int) (1000000.0 / kelvinValue);
159     }
160
161     /**
162      * Transforms the given {@link DecimalType} into a light state containing
163      * the color temperature in Kelvin.
164      *
165      * @param decimalType color temperature in Kelvin
166      * @return light state containing the color temperature
167      */
168     public static StateUpdate toColorTemperatureLightState(DecimalType decimalType, ColorTemperature capabilities) {
169         return new StateUpdate().setColorTemperature(kelvinToMired(decimalType.intValue()), capabilities);
170     }
171
172     /**
173      * Adjusts the given color temperature using the {@link IncreaseDecreaseType} and returns the updated value.
174      *
175      * @param type The {@link IncreaseDecreaseType} to be used
176      * @param currentColorTemp The current color temperature
177      * @return The adjusted color temperature value
178      */
179     public static int toAdjustedColorTemp(IncreaseDecreaseType type, int currentColorTemp,
180             ColorTemperature capabilities) {
181         int newColorTemp;
182         if (type == IncreaseDecreaseType.DECREASE) {
183             newColorTemp = Math.max(currentColorTemp - DIM_STEPSIZE, capabilities.min);
184         } else {
185             newColorTemp = Math.min(currentColorTemp + DIM_STEPSIZE, capabilities.max);
186         }
187         return newColorTemp;
188     }
189
190     /**
191      * Transforms Hue Light {@link State} into {@link PercentType} representing
192      * the color temperature.
193      *
194      * @param lightState light state
195      * @return percent type representing the color temperature
196      */
197     public static PercentType toColorTemperaturePercentType(State lightState, ColorTemperature capabilities) {
198         int percent = (int) Math.round(((lightState.getColorTemperature() - capabilities.min) * 100.0)
199                 / (capabilities.max - capabilities.min));
200         return new PercentType(restrictToBounds(percent));
201     }
202
203     public static int miredToKelvin(int miredValue) {
204         return (int) (1000000.0 / miredValue);
205     }
206
207     /**
208      * Transforms Hue Light {@link State} into {@link DecimalType} representing
209      * the color temperature in Kelvin.
210      *
211      * @param lightState light state
212      * @return percent type representing the color temperature in Kelvin
213      */
214     public static DecimalType toColorTemperature(State lightState) {
215         return new DecimalType(miredToKelvin(lightState.getColorTemperature()));
216     }
217
218     /**
219      * Transforms Hue Light {@link State} into {@link PercentType} representing
220      * the brightness.
221      *
222      * @param lightState light state
223      * @return percent type representing the brightness
224      */
225     public static PercentType toBrightnessPercentType(State lightState) {
226         int percent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
227         return new PercentType(restrictToBounds(percent));
228     }
229
230     /**
231      * Transforms {@link State} into {@link StringType} representing the {@link AlertMode}.
232      *
233      * @param lightState light state.
234      * @return string type representing the alert mode.
235      */
236     public static StringType toAlertStringType(State lightState) {
237         AlertMode alertMode = lightState.getAlertMode();
238         if (alertMode == null) {
239             return new StringType("NULL");
240         } else {
241             return new StringType(alertMode.toString());
242         }
243     }
244
245     /**
246      * Transforms Hue Light {@link State} into {@link HSBType} representing the
247      * color.
248      *
249      * @param lightState light state
250      * @return HSB type representing the color
251      */
252     public static HSBType toHSBType(State lightState) {
253         // even if color mode is reported to be XY, xy field of lightState might be null, while hsb is available
254         boolean isInXYMode = ColorMode.XY.equals(lightState.getColorMode()) && lightState.getXY() != null;
255         return isInXYMode ? fromXYtoHSBType(lightState) : fromHSBtoHSBType(lightState);
256     }
257
258     private static HSBType fromHSBtoHSBType(State lightState) {
259         int hue = (int) Math.round(lightState.getHue() / HUE_FACTOR) % 360;
260
261         int saturationInPercent = (int) Math.ceil(lightState.getSaturation() / SATURATION_FACTOR);
262         saturationInPercent = restrictToBounds(saturationInPercent);
263
264         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
265         brightnessInPercent = restrictToBounds(brightnessInPercent);
266
267         return new HSBType(new DecimalType(hue), new PercentType(saturationInPercent),
268                 new PercentType(brightnessInPercent));
269     }
270
271     private static HSBType fromXYtoHSBType(State lightState) {
272         float[] xy = lightState.getXY();
273         HSBType hsb = HSBType.fromXY(xy[0], xy[1]);
274
275         int brightnessInPercent = (int) Math.ceil(lightState.getBrightness() / BRIGHTNESS_FACTOR);
276         brightnessInPercent = restrictToBounds(brightnessInPercent);
277
278         return new HSBType(hsb.getHue(), hsb.getSaturation(), new PercentType(brightnessInPercent));
279     }
280
281     /**
282      * Transforms the given {@link StringType} into a light state containing the {@link AlertMode} to be triggered.
283      *
284      * @param alertType {@link StringType} representing the required {@link AlertMode} . <br>
285      *            Supported values are:
286      *            <ul>
287      *            <li>{@value #ALERT_MODE_NONE}.
288      *            <li>{@value #ALERT_MODE_SELECT}.
289      *            <li>{@value #ALERT_MODE_LONG_SELECT}.
290      *            <ul>
291      * @return light state containing the {@link AlertMode} or <b><code>null </code></b> if the provided
292      *         {@link StringType} represents unsupported mode.
293      */
294     public static @Nullable StateUpdate toAlertState(StringType alertType) {
295         AlertMode alertMode;
296
297         switch (alertType.toString()) {
298             case ALERT_MODE_NONE:
299                 alertMode = State.AlertMode.NONE;
300                 break;
301             case ALERT_MODE_SELECT:
302                 alertMode = State.AlertMode.SELECT;
303                 break;
304             case ALERT_MODE_LONG_SELECT:
305                 alertMode = State.AlertMode.LSELECT;
306                 break;
307             default:
308                 return null;
309         }
310         return new StateUpdate().setAlert(alertMode);
311     }
312
313     /**
314      * Transforms the given {@link OnOffType} into a light state containing the {@link Effect} value.
315      * {@link OnOffType#ON} will result in {@link Effect#COLORLOOP}. {@link OnOffType#OFF} will result in
316      * {@link Effect#NONE}.
317      *
318      * @param onOffType on or off state
319      * @return light state containing the {@link Effect} value
320      */
321     public static StateUpdate toOnOffEffectState(OnOffType onOffType) {
322         StateUpdate stateUpdate;
323
324         if (OnOffType.ON.equals(onOffType)) {
325             stateUpdate = new StateUpdate().setEffect(Effect.COLORLOOP);
326         } else {
327             stateUpdate = new StateUpdate().setEffect(Effect.NONE);
328         }
329
330         return stateUpdate;
331     }
332
333     private static int restrictToBounds(int percentValue) {
334         if (percentValue < 0) {
335             return 0;
336         } else if (percentValue > 100) {
337             return 100;
338         }
339         return percentValue;
340     }
341 }