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.*;
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 = "OSRAM";
66 private static final String OSRAM_MODEL_TYPE = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID;
67 private static final String OSRAM_MODEL_TYPE_ID = HueLightHandler.OSRAM_PAR16_50_TW_MODEL_ID;
77 public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
78 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
79 assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
83 public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
84 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
85 assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
89 public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
90 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
91 assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
95 public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
96 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
97 assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM),
102 public void assertCommandForColorChannelOn() {
103 String expectedReply = "{\"on\" : true}";
104 assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
108 public void assertCommandForColorTemperatureChannelOn() {
109 String expectedReply = "{\"on\" : true}";
110 assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
114 public void assertCommandForColorChannelOff() {
115 String expectedReply = "{\"on\" : false}";
116 assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
120 public void assertCommandForColorTemperatureChannelOff() {
121 String expectedReply = "{\"on\" : false}";
122 assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
126 public void assertCommandForColorTemperatureChannel0Percent() {
127 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
128 assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
132 public void assertCommandForColorTemperatureChannel50Percent() {
133 String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
134 assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
138 public void assertCommandForColorTemperatureChannel1000Percent() {
139 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
140 assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
144 public void assertCommandForColorTemperatureAbsChannel6500Kelvin() {
145 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
146 assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
150 public void assertCommandForColorTemperatureAbsChannel4500Kelvin() {
151 String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
152 assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
156 public void assertCommandForColorTemperatureAbsChannel2000Kelvin() {
157 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
158 assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
162 public void assertPercentageValueOfColorTemperatureWhenCt153() {
163 int expectedReply = 0;
164 asserttoColorTemperaturePercentType(153, expectedReply);
168 public void assertPercentageValueOfColorTemperatureWhenCt326() {
169 int expectedReply = 50;
170 asserttoColorTemperaturePercentType(326, expectedReply);
174 public void assertPercentageValueOfColorTemperatureWhenCt500() {
175 int expectedReply = 100;
176 asserttoColorTemperaturePercentType(500, expectedReply);
180 public void assertCommandForColorChannel0Percent() {
181 String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
182 assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
186 public void assertCommandForColorChannel50Percent() {
187 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
188 assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
192 public void assertCommandForColorChannel100Percent() {
193 String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
194 assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
198 public void assertCommandForColorChannelBlack() {
199 String expectedReply = "{\"on\" : false}";
200 assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
204 public void assertCommandForColorChannelRed() {
205 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
206 assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
210 public void assertCommandForColorChannelGreen() {
211 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
212 assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
216 public void assertCommandForColorChannelBlue() {
217 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
218 assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
222 public void assertCommandForColorChannelWhite() {
223 String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
224 assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
228 public void assertXYCommandForColorChannelBlack() {
229 String expectedReply = "{\"on\" : false}";
230 assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
234 public void assertXYCommandForColorChannelWhite() {
235 String expectedReply = "{\"xy\" : [ 0.31271592 , 0.32900152 ], \"bri\" : 254, \"transitiontime\" : 4}";
236 assertSendCommandForColor(HSBType.WHITE, new HueLightState().colormode(ColorMode.XY), expectedReply);
240 public void assertXYCommandForColorChannelColorful() {
241 String expectedReply = "{\"xy\" : [ 0.16969365 , 0.12379659 ], \"bri\" : 127, \"transitiontime\" : 4}";
242 assertSendCommandForColor(new HSBType("220,90,50"), new HueLightState().colormode(ColorMode.XY), expectedReply);
246 public void asserCommandForColorChannelIncrease() {
247 HueLightState currentState = new HueLightState().bri(1).on(false);
248 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
249 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
251 currentState.bri(200).on(true);
252 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
253 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
255 currentState.bri(230);
256 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
257 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
261 public void asserCommandForColorChannelDecrease() {
262 HueLightState currentState = new HueLightState().bri(200);
263 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
264 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
266 currentState.bri(20);
267 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
268 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
272 public void assertCommandForBrightnessChannel50Percent() {
273 HueLightState currentState = new HueLightState();
274 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
275 assertSendCommandForBrightness(new PercentType(50), currentState, expectedReply);
279 public void assertCommandForBrightnessChannelIncrease() {
280 HueLightState currentState = new HueLightState().bri(1).on(false);
281 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
282 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
284 currentState.bri(200).on(true);
285 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
286 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
288 currentState.bri(230);
289 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
290 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
294 public void assertCommandForBrightnessChannelDecrease() {
295 HueLightState currentState = new HueLightState().bri(200);
296 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
297 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
299 currentState.bri(20);
300 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
301 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
305 public void assertCommandForBrightnessChannelOff() {
306 HueLightState currentState = new HueLightState();
307 String expectedReply = "{\"on\" : false}";
308 assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
312 public void assertCommandForBrightnessChannelOn() {
313 HueLightState currentState = new HueLightState();
314 String expectedReply = "{\"on\" : true}";
315 assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
319 public void assertCommandForAlertChannel() {
320 HueLightState currentState = new HueLightState().alert("NONE");
321 String expectedReply = "{\"alert\" : \"none\"}";
322 assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
324 currentState.alert("NONE");
325 expectedReply = "{\"alert\" : \"select\"}";
326 assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
328 currentState.alert("LSELECT");
329 expectedReply = "{\"alert\" : \"lselect\"}";
330 assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
334 public void assertCommandForEffectChannel() {
335 HueLightState currentState = new HueLightState().effect("ON");
336 String expectedReply = "{\"effect\" : \"colorloop\"}";
337 assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
339 currentState.effect("OFF");
340 expectedReply = "{\"effect\" : \"none\"}";
341 assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
344 private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
345 String expectedReply) {
346 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
349 private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
350 String expectedReply) {
351 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
354 private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
355 assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
358 private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
359 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
362 private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
363 assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
366 private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
367 int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
368 assertEquals(percent, expectedPercent);
371 private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
372 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
375 private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
376 assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
379 private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
380 assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
383 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
384 assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
387 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply,
388 String expectedModel, String expectedVendor) {
389 FullLight light = gson.fromJson(currentState.toString(), FullConfig.class).getLights().get(0);
391 Bridge mockBridge = mock(Bridge.class);
392 when(mockBridge.getStatus()).thenReturn(ThingStatus.ONLINE);
394 Thing mockThing = mock(Thing.class);
395 when(mockThing.getConfiguration()).thenReturn(new Configuration(Map.of(LIGHT_ID, "1")));
397 HueClient mockClient = mock(HueClient.class);
398 when(mockClient.getLightById(any())).thenReturn(light);
402 HueLightHandler hueLightHandler = new HueLightHandler(mockThing,
403 new HueStateDescriptionOptionProvider(mock(ChannelTypeI18nLocalizationService.class))) {
405 protected synchronized HueClient getHueClient() {
410 protected Bridge getBridge() {
414 hueLightHandler.initialize();
416 verify(mockThing).setProperty(eq(Thing.PROPERTY_MODEL_ID), eq(expectedModel));
417 verify(mockThing).setProperty(eq(Thing.PROPERTY_VENDOR), eq(expectedVendor));
419 hueLightHandler.handleCommand(new ChannelUID(new ThingUID("hue::test"), channel), command);
421 ArgumentCaptor<StateUpdate> captorStateUpdate = ArgumentCaptor.forClass(StateUpdate.class);
422 verify(mockClient).updateLightState(any(LightStatusListener.class), any(FullLight.class),
423 captorStateUpdate.capture(), eq(fadeTime));
424 assertJson(expectedReply, captorStateUpdate.getValue().toJson());
427 private void assertJson(String expected, String actual) {
428 JsonElement jsonExpected = JsonParser.parseString(expected);
429 JsonElement jsonActual = JsonParser.parseString(actual);
430 assertEquals(jsonExpected, jsonActual);