]> git.basschouten.com Git - openhab-addons.git/blob
5f47d7c43cf52b06bf0c21b44d901a8e20dc3bec
[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 static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
19
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.junit.jupiter.api.Test;
25 import org.mockito.ArgumentCaptor;
26 import org.openhab.binding.hue.internal.dto.FullConfig;
27 import org.openhab.binding.hue.internal.dto.FullLight;
28 import org.openhab.binding.hue.internal.dto.State.ColorMode;
29 import org.openhab.binding.hue.internal.dto.StateUpdate;
30 import org.openhab.core.config.core.Configuration;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.HSBType;
33 import org.openhab.core.library.types.IncreaseDecreaseType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.PercentType;
36 import org.openhab.core.library.types.QuantityType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.library.unit.Units;
39 import org.openhab.core.thing.Bridge;
40 import org.openhab.core.thing.ChannelUID;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingUID;
44 import org.openhab.core.thing.binding.ThingHandlerCallback;
45 import org.openhab.core.types.Command;
46
47 import com.google.gson.Gson;
48 import com.google.gson.JsonElement;
49 import com.google.gson.JsonParser;
50
51 /**
52  * Tests for {@link HueLightHandler}.
53  *
54  * @author Oliver Libutzki - Initial contribution
55  * @author Michael Grammling - Initial contribution
56  * @author Markus Mazurczak - Added test for OSRAM Par16 50 TW bulbs
57  * @author Andre Fuechsel - modified tests after introducing the generic thing types
58  * @author Denis Dudnik - switched to internally integrated source of Jue library
59  * @author Simon Kaufmann - migrated to plain Java test
60  * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
61  * @author Jacob Laursen - Add workaround for LK Wiser products
62  */
63 @NonNullByDefault
64 public class HueLightHandlerTest {
65
66     private static final int MIN_COLOR_TEMPERATURE = 153;
67     private static final int MAX_COLOR_TEMPERATURE = 500;
68     private static final int COLOR_TEMPERATURE_RANGE = MAX_COLOR_TEMPERATURE - MIN_COLOR_TEMPERATURE;
69
70     private static final String OSRAM = "OSRAM";
71     private static final String OSRAM_MODEL_TYPE = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID;
72     private static final String OSRAM_MODEL_TYPE_ID = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID;
73
74     private final Gson gson = new Gson();
75
76     @Test
77     public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
78         String expectedReply = "{\"on\" : true, \"bri\" : 254}";
79         assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
80     }
81
82     @Test
83     public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
84         String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
85         assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
86     }
87
88     @Test
89     public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
90         String expectedReply = "{\"on\" : true, \"bri\" : 254}";
91         assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
92     }
93
94     @Test
95     public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
96         String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
97         assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM),
98                 expectedReply);
99     }
100
101     @Test
102     public void assertCommandForLkWiserForBrightnessChannelOff() {
103         final String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
104         final String vendor = "Schneider Electric";
105         assertSendCommand(CHANNEL_BRIGHTNESS, OnOffType.OFF,
106                 new HueLightState(HueLightHandler.LK_WISER_MODEL_ID, vendor), expectedReply,
107                 HueLightHandler.LK_WISER_MODEL_ID, vendor);
108     }
109
110     @Test
111     public void assertCommandForColorChannelOn() {
112         String expectedReply = "{\"on\" : true}";
113         assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
114     }
115
116     @Test
117     public void assertCommandForColorTemperatureChannelOn() {
118         String expectedReply = "{\"on\" : true}";
119         assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
120     }
121
122     @Test
123     public void assertCommandForColorChannelOff() {
124         String expectedReply = "{\"on\" : false}";
125         assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
126     }
127
128     @Test
129     public void assertCommandForColorTemperatureChannelOff() {
130         String expectedReply = "{\"on\" : false}";
131         assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
132     }
133
134     @Test
135     public void assertCommandForColorTemperatureChannel0Percent() {
136         String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
137         assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
138     }
139
140     @Test
141     public void assertCommandForColorTemperatureChannel50Percent() {
142         String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
143         assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
144     }
145
146     @Test
147     public void assertCommandForColorTemperatureChannel1000Percent() {
148         String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
149         assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
150     }
151
152     @Test
153     public void assertDecimalTypeCommandForColorTemperatureAbsChannel6500Kelvin() {
154         String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
155         assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
156     }
157
158     @Test
159     public void assertDecimalTypeCommandForColorTemperatureAbsChannel4500Kelvin() {
160         String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
161         assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
162     }
163
164     @Test
165     public void assertDecimalTypeCommandForColorTemperatureAbsChannel2000Kelvin() {
166         String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
167         assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
168     }
169
170     @Test
171     public void assertQuantityTypeCommandForColorTemperatureAbsChannel6500Kelvin() {
172         String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
173         assertSendCommandForColorTempAbs(new QuantityType<>(6500, Units.KELVIN), new HueLightState(), expectedReply);
174     }
175
176     @Test
177     public void assertQuantityTypeCommandForColorTemperatureAbsChannel4500Kelvin() {
178         String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
179         assertSendCommandForColorTempAbs(new QuantityType<>(4500, Units.KELVIN), new HueLightState(), expectedReply);
180     }
181
182     @Test
183     public void assertQuantityTypeCommandForColorTemperatureAbsChannel2000Kelvin() {
184         String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
185         assertSendCommandForColorTempAbs(new QuantityType<>(2000, Units.KELVIN), new HueLightState(), expectedReply);
186     }
187
188     @Test
189     public void assertQuantityTypeCommandForColorTemperatureAbsChannel500Mired() {
190         String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
191         assertSendCommandForColorTempAbs(new QuantityType<>(500, Units.MIRED), new HueLightState(), expectedReply);
192     }
193
194     @Test
195     public void assertPercentageValueOfColorTemperatureWhenCt153() {
196         int expectedReply = 0;
197         asserttoColorTemperaturePercentType(153, expectedReply);
198     }
199
200     @Test
201     public void assertPercentageValueOfColorTemperatureWhenCt326() {
202         int expectedReply = 50;
203         asserttoColorTemperaturePercentType(326, expectedReply);
204     }
205
206     @Test
207     public void assertPercentageValueOfColorTemperatureWhenCt500() {
208         int expectedReply = 100;
209         asserttoColorTemperaturePercentType(500, expectedReply);
210     }
211
212     @Test
213     public void assertCommandForColorChannel0Percent() {
214         String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
215         assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
216     }
217
218     @Test
219     public void assertCommandForColorChannel50Percent() {
220         String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
221         assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
222     }
223
224     @Test
225     public void assertCommandForColorChannel100Percent() {
226         String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
227         assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
228     }
229
230     @Test
231     public void assertCommandForColorChannelBlack() {
232         String expectedReply = "{\"on\" : false}";
233         assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
234     }
235
236     @Test
237     public void assertCommandForColorChannelRed() {
238         String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
239         assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
240     }
241
242     @Test
243     public void assertCommandForColorChannelGreen() {
244         String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
245         assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
246     }
247
248     @Test
249     public void assertCommandForColorChannelBlue() {
250         String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
251         assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
252     }
253
254     @Test
255     public void assertCommandForColorChannelWhite() {
256         String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
257         assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
258     }
259
260     @Test
261     public void assertXYCommandForColorChannelBlack() {
262         String expectedReply = "{\"on\" : false}";
263         assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
264     }
265
266     @Test
267     public void assertXYCommandForColorChannelWhite() {
268         String expectedReply = "{\"xy\" : [ 0.3227 , 0.32900003 ], \"bri\" : 254, \"transitiontime\" : 4}";
269         assertSendCommandForColor(HSBType.WHITE, new HueLightState().colormode(ColorMode.XY), expectedReply);
270     }
271
272     @Test
273     public void assertXYCommandForColorChannelColorful() {
274         String expectedReply = "{\"xy\" : [ 0.14649999 , 0.115600005 ], \"bri\" : 127, \"transitiontime\" : 4}";
275         assertSendCommandForColor(new HSBType("220,90,50"), new HueLightState().colormode(ColorMode.XY), expectedReply);
276     }
277
278     @Test
279     public void assertCommandForColorChannelIncrease() {
280         HueLightState currentState = new HueLightState().bri(1).on(false);
281         String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
282         assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
283
284         currentState.bri(200).on(true);
285         expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
286         assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
287
288         currentState.bri(230);
289         expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
290         assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
291     }
292
293     @Test
294     public void assertCommandForColorChannelDecrease() {
295         HueLightState currentState = new HueLightState().bri(200);
296         String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
297         assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
298
299         currentState.bri(20);
300         expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
301         assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
302     }
303
304     @Test
305     public void assertCommandForBrightnessChannel50Percent() {
306         HueLightState currentState = new HueLightState();
307         String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
308         assertSendCommandForBrightness(new PercentType(50), currentState, expectedReply);
309     }
310
311     @Test
312     public void assertCommandForBrightnessChannelIncrease() {
313         HueLightState currentState = new HueLightState().bri(1).on(false);
314         String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
315         assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
316
317         currentState.bri(200).on(true);
318         expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
319         assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
320
321         currentState.bri(230);
322         expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
323         assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
324     }
325
326     @Test
327     public void assertCommandForBrightnessChannelDecrease() {
328         HueLightState currentState = new HueLightState().bri(200);
329         String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
330         assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
331
332         currentState.bri(20);
333         expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
334         assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
335     }
336
337     @Test
338     public void assertCommandForBrightnessChannelOff() {
339         HueLightState currentState = new HueLightState();
340         String expectedReply = "{\"on\" : false}";
341         assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
342     }
343
344     @Test
345     public void assertCommandForBrightnessChannelOn() {
346         HueLightState currentState = new HueLightState();
347         String expectedReply = "{\"on\" : true}";
348         assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
349     }
350
351     @Test
352     public void assertCommandForAlertChannel() {
353         HueLightState currentState = new HueLightState().alert("NONE");
354         String expectedReply = "{\"alert\" : \"none\"}";
355         assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
356
357         currentState.alert("NONE");
358         expectedReply = "{\"alert\" : \"select\"}";
359         assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
360
361         currentState.alert("LSELECT");
362         expectedReply = "{\"alert\" : \"lselect\"}";
363         assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
364     }
365
366     @Test
367     public void assertCommandForEffectChannel() {
368         HueLightState currentState = new HueLightState().effect("ON");
369         String expectedReply = "{\"effect\" : \"colorloop\"}";
370         assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
371
372         currentState.effect("OFF");
373         expectedReply = "{\"effect\" : \"none\"}";
374         assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
375     }
376
377     private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
378             String expectedReply) {
379         assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
380     }
381
382     private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
383             String expectedReply) {
384         assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
385     }
386
387     private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
388         assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
389     }
390
391     private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
392         assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
393     }
394
395     private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
396         assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
397     }
398
399     private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
400         int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
401         assertEquals(percent, expectedPercent);
402     }
403
404     private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
405         assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
406     }
407
408     private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
409         assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
410     }
411
412     private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
413         assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
414     }
415
416     private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
417         assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
418     }
419
420     @SuppressWarnings("null")
421     private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply,
422             String expectedModel, String expectedVendor) {
423         FullLight light = gson.fromJson(currentState.toString(), FullConfig.class).getLights().get(0);
424
425         Bridge mockBridge = mock(Bridge.class);
426         when(mockBridge.getStatus()).thenReturn(ThingStatus.ONLINE);
427
428         Thing mockThing = mock(Thing.class);
429         when(mockThing.getConfiguration()).thenReturn(new Configuration(Map.of(LIGHT_ID, "1")));
430
431         HueClient mockClient = mock(HueClient.class);
432         when(mockClient.getLightById(any())).thenReturn(light);
433
434         long fadeTime = 400;
435
436         HueLightHandler hueLightHandler = new HueLightHandler(mockThing, mock(HueStateDescriptionProvider.class)) {
437             @Override
438             protected synchronized @Nullable HueClient getHueClient() {
439                 return mockClient;
440             }
441
442             @Override
443             protected @Nullable Bridge getBridge() {
444                 return mockBridge;
445             }
446         };
447         hueLightHandler.setCallback(mock(ThingHandlerCallback.class));
448         hueLightHandler.initialize();
449
450         verify(mockThing).setProperty(eq(Thing.PROPERTY_MODEL_ID), eq(expectedModel));
451         verify(mockThing).setProperty(eq(Thing.PROPERTY_VENDOR), eq(expectedVendor));
452
453         hueLightHandler.handleCommand(new ChannelUID(new ThingUID("hue::test"), channel), command);
454
455         ArgumentCaptor<StateUpdate> captorStateUpdate = ArgumentCaptor.forClass(StateUpdate.class);
456         verify(mockClient).updateLightState(any(LightStatusListener.class), any(FullLight.class),
457                 captorStateUpdate.capture(), eq(fadeTime));
458         assertJson(expectedReply, captorStateUpdate.getValue().toJson());
459     }
460
461     private void assertJson(String expected, String actual) {
462         JsonElement jsonExpected = JsonParser.parseString(expected);
463         JsonElement jsonActual = JsonParser.parseString(actual);
464         assertEquals(jsonExpected, jsonActual);
465     }
466 }