2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.hue.internal.handler;
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.*;
20 import java.util.Collections;
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.i18n.ChannelTypeI18nLocalizationService;
42 import org.openhab.core.types.Command;
44 import com.google.gson.Gson;
45 import com.google.gson.JsonElement;
46 import com.google.gson.JsonParser;
49 * Tests for {@link HueLightHandler}.
51 * @author Oliver Libutzki - Initial contribution
52 * @author Michael Grammling - Initial contribution
53 * @author Markus Mazurczak - Added test for OSRAM Par16 50 TW bulbs
54 * @author Andre Fuechsel - modified tests after introducing the generic thing types
55 * @author Denis Dudnik - switched to internally integrated source of Jue library
56 * @author Simon Kaufmann - migrated to plain Java test
57 * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
59 public class HueLightHandlerTest {
61 private static final int MIN_COLOR_TEMPERATURE = 153;
62 private static final int MAX_COLOR_TEMPERATURE = 500;
63 private static final int COLOR_TEMPERATURE_RANGE = MAX_COLOR_TEMPERATURE - MIN_COLOR_TEMPERATURE;
65 private static final String OSRAM_MODEL_TYPE = "PAR16 50 TW";
66 private static final String OSRAM_MODEL_TYPE_ID = "PAR16_50_TW";
76 public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
77 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
78 assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
82 public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
83 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
84 assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
88 public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
89 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
90 assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
94 public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
95 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
96 assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
100 public void assertCommandForColorChannelOn() {
101 String expectedReply = "{\"on\" : true}";
102 assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
106 public void assertCommandForColorTemperatureChannelOn() {
107 String expectedReply = "{\"on\" : true}";
108 assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
112 public void assertCommandForColorChannelOff() {
113 String expectedReply = "{\"on\" : false}";
114 assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
118 public void assertCommandForColorTemperatureChannelOff() {
119 String expectedReply = "{\"on\" : false}";
120 assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
124 public void assertCommandForColorTemperatureChannel0Percent() {
125 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
126 assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
130 public void assertCommandForColorTemperatureChannel50Percent() {
131 String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
132 assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
136 public void assertCommandForColorTemperatureChannel1000Percent() {
137 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
138 assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
142 public void assertCommandForColorTemperatureAbsChannel6500Kelvin() {
143 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
144 assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
148 public void assertCommandForColorTemperatureAbsChannel4500Kelvin() {
149 String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
150 assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
154 public void assertCommandForColorTemperatureAbsChannel2000Kelvin() {
155 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
156 assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
160 public void assertPercentageValueOfColorTemperatureWhenCt153() {
161 int expectedReply = 0;
162 asserttoColorTemperaturePercentType(153, expectedReply);
166 public void assertPercentageValueOfColorTemperatureWhenCt326() {
167 int expectedReply = 50;
168 asserttoColorTemperaturePercentType(326, expectedReply);
172 public void assertPercentageValueOfColorTemperatureWhenCt500() {
173 int expectedReply = 100;
174 asserttoColorTemperaturePercentType(500, expectedReply);
178 public void assertCommandForColorChannel0Percent() {
179 String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
180 assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
184 public void assertCommandForColorChannel50Percent() {
185 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
186 assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
190 public void assertCommandForColorChannel100Percent() {
191 String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
192 assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
196 public void assertCommandForColorChannelBlack() {
197 String expectedReply = "{\"on\" : false}";
198 assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
202 public void assertCommandForColorChannelRed() {
203 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
204 assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
208 public void assertCommandForColorChannelGreen() {
209 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
210 assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
214 public void assertCommandForColorChannelBlue() {
215 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
216 assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
220 public void assertCommandForColorChannelWhite() {
221 String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
222 assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
226 public void assertXYCommandForColorChannelBlack() {
227 String expectedReply = "{\"on\" : false}";
228 assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
232 public void assertXYCommandForColorChannelWhite() {
233 String expectedReply = "{\"xy\" : [ 0.31271592 , 0.32900152 ], \"bri\" : 254, \"transitiontime\" : 4}";
234 assertSendCommandForColor(HSBType.WHITE, new HueLightState().colormode(ColorMode.XY), expectedReply);
238 public void assertXYCommandForColorChannelColorful() {
239 String expectedReply = "{\"xy\" : [ 0.16969365 , 0.12379659 ], \"bri\" : 127, \"transitiontime\" : 4}";
240 assertSendCommandForColor(new HSBType("220,90,50"), new HueLightState().colormode(ColorMode.XY), expectedReply);
244 public void asserCommandForColorChannelIncrease() {
245 HueLightState currentState = new HueLightState().bri(1).on(false);
246 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
247 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
249 currentState.bri(200).on(true);
250 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
251 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
253 currentState.bri(230);
254 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
255 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
259 public void asserCommandForColorChannelDecrease() {
260 HueLightState currentState = new HueLightState().bri(200);
261 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
262 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
264 currentState.bri(20);
265 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
266 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
270 public void assertCommandForBrightnessChannel50Percent() {
271 HueLightState currentState = new HueLightState();
272 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
273 assertSendCommandForBrightness(new PercentType(50), currentState, expectedReply);
277 public void assertCommandForBrightnessChannelIncrease() {
278 HueLightState currentState = new HueLightState().bri(1).on(false);
279 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
280 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
282 currentState.bri(200).on(true);
283 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
284 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
286 currentState.bri(230);
287 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
288 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
292 public void assertCommandForBrightnessChannelDecrease() {
293 HueLightState currentState = new HueLightState().bri(200);
294 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
295 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
297 currentState.bri(20);
298 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
299 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
303 public void assertCommandForBrightnessChannelOff() {
304 HueLightState currentState = new HueLightState();
305 String expectedReply = "{\"on\" : false}";
306 assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
310 public void assertCommandForBrightnessChannelOn() {
311 HueLightState currentState = new HueLightState();
312 String expectedReply = "{\"on\" : true}";
313 assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
317 public void assertCommandForAlertChannel() {
318 HueLightState currentState = new HueLightState().alert("NONE");
319 String expectedReply = "{\"alert\" : \"none\"}";
320 assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
322 currentState.alert("NONE");
323 expectedReply = "{\"alert\" : \"select\"}";
324 assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
326 currentState.alert("LSELECT");
327 expectedReply = "{\"alert\" : \"lselect\"}";
328 assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
332 public void assertCommandForEffectChannel() {
333 HueLightState currentState = new HueLightState().effect("ON");
334 String expectedReply = "{\"effect\" : \"colorloop\"}";
335 assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
337 currentState.effect("OFF");
338 expectedReply = "{\"effect\" : \"none\"}";
339 assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
342 private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
343 String expectedReply) {
344 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, "OSRAM");
347 private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
348 String expectedReply) {
349 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, "OSRAM");
352 private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
353 assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
356 private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
357 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
360 private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
361 assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
364 private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
365 int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
366 assertEquals(percent, expectedPercent);
369 private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
370 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
373 private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
374 assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
377 private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
378 assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
381 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
382 assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
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);
389 Bridge mockBridge = mock(Bridge.class);
390 when(mockBridge.getStatus()).thenReturn(ThingStatus.ONLINE);
392 Thing mockThing = mock(Thing.class);
393 when(mockThing.getConfiguration()).thenReturn(new Configuration(Collections.singletonMap(LIGHT_ID, "1")));
395 HueClient mockClient = mock(HueClient.class);
396 when(mockClient.getLightById(any())).thenReturn(light);
400 HueLightHandler hueLightHandler = new HueLightHandler(mockThing,
401 new HueStateDescriptionOptionProvider(mock(ChannelTypeI18nLocalizationService.class))) {
403 protected synchronized HueClient getHueClient() {
408 protected Bridge getBridge() {
412 hueLightHandler.initialize();
414 verify(mockThing).setProperty(eq(Thing.PROPERTY_MODEL_ID), eq(expectedModel));
415 verify(mockThing).setProperty(eq(Thing.PROPERTY_VENDOR), eq(expectedVendor));
417 hueLightHandler.handleCommand(new ChannelUID(new ThingUID("hue::test"), channel), command);
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());
425 private void assertJson(String expected, String actual) {
426 JsonParser parser = new JsonParser();
427 JsonElement jsonExpected = parser.parse(expected);
428 JsonElement jsonActual = parser.parse(actual);
429 assertEquals(jsonExpected, jsonActual);