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