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