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