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.binding.ThingHandlerCallback;
42 import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
43 import org.openhab.core.types.Command;
45 import com.google.gson.Gson;
46 import com.google.gson.JsonElement;
47 import com.google.gson.JsonParser;
50 * Tests for {@link HueLightHandler}.
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
60 public class HueLightHandlerTest {
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;
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;
78 public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
79 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
80 assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
84 public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
85 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
86 assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
90 public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
91 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
92 assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
96 public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
97 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
98 assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM),
103 public void assertCommandForColorChannelOn() {
104 String expectedReply = "{\"on\" : true}";
105 assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
109 public void assertCommandForColorTemperatureChannelOn() {
110 String expectedReply = "{\"on\" : true}";
111 assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
115 public void assertCommandForColorChannelOff() {
116 String expectedReply = "{\"on\" : false}";
117 assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
121 public void assertCommandForColorTemperatureChannelOff() {
122 String expectedReply = "{\"on\" : false}";
123 assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
127 public void assertCommandForColorTemperatureChannel0Percent() {
128 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
129 assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
133 public void assertCommandForColorTemperatureChannel50Percent() {
134 String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
135 assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
139 public void assertCommandForColorTemperatureChannel1000Percent() {
140 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
141 assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
145 public void assertCommandForColorTemperatureAbsChannel6500Kelvin() {
146 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
147 assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
151 public void assertCommandForColorTemperatureAbsChannel4500Kelvin() {
152 String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
153 assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
157 public void assertCommandForColorTemperatureAbsChannel2000Kelvin() {
158 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
159 assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
163 public void assertPercentageValueOfColorTemperatureWhenCt153() {
164 int expectedReply = 0;
165 asserttoColorTemperaturePercentType(153, expectedReply);
169 public void assertPercentageValueOfColorTemperatureWhenCt326() {
170 int expectedReply = 50;
171 asserttoColorTemperaturePercentType(326, expectedReply);
175 public void assertPercentageValueOfColorTemperatureWhenCt500() {
176 int expectedReply = 100;
177 asserttoColorTemperaturePercentType(500, expectedReply);
181 public void assertCommandForColorChannel0Percent() {
182 String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
183 assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
187 public void assertCommandForColorChannel50Percent() {
188 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
189 assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
193 public void assertCommandForColorChannel100Percent() {
194 String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
195 assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
199 public void assertCommandForColorChannelBlack() {
200 String expectedReply = "{\"on\" : false}";
201 assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
205 public void assertCommandForColorChannelRed() {
206 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
207 assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
211 public void assertCommandForColorChannelGreen() {
212 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
213 assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
217 public void assertCommandForColorChannelBlue() {
218 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
219 assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
223 public void assertCommandForColorChannelWhite() {
224 String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
225 assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
229 public void assertXYCommandForColorChannelBlack() {
230 String expectedReply = "{\"on\" : false}";
231 assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
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);
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);
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);
252 currentState.bri(200).on(true);
253 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
254 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
256 currentState.bri(230);
257 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
258 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
262 public void asserCommandForColorChannelDecrease() {
263 HueLightState currentState = new HueLightState().bri(200);
264 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
265 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
267 currentState.bri(20);
268 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
269 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
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);
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);
285 currentState.bri(200).on(true);
286 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
287 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
289 currentState.bri(230);
290 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
291 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
295 public void assertCommandForBrightnessChannelDecrease() {
296 HueLightState currentState = new HueLightState().bri(200);
297 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
298 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
300 currentState.bri(20);
301 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
302 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
306 public void assertCommandForBrightnessChannelOff() {
307 HueLightState currentState = new HueLightState();
308 String expectedReply = "{\"on\" : false}";
309 assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
313 public void assertCommandForBrightnessChannelOn() {
314 HueLightState currentState = new HueLightState();
315 String expectedReply = "{\"on\" : true}";
316 assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
320 public void assertCommandForAlertChannel() {
321 HueLightState currentState = new HueLightState().alert("NONE");
322 String expectedReply = "{\"alert\" : \"none\"}";
323 assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
325 currentState.alert("NONE");
326 expectedReply = "{\"alert\" : \"select\"}";
327 assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
329 currentState.alert("LSELECT");
330 expectedReply = "{\"alert\" : \"lselect\"}";
331 assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
335 public void assertCommandForEffectChannel() {
336 HueLightState currentState = new HueLightState().effect("ON");
337 String expectedReply = "{\"effect\" : \"colorloop\"}";
338 assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
340 currentState.effect("OFF");
341 expectedReply = "{\"effect\" : \"none\"}";
342 assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
345 private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
346 String expectedReply) {
347 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
350 private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
351 String expectedReply) {
352 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
355 private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
356 assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
359 private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
360 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
363 private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
364 assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
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);
372 private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
373 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
376 private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
377 assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
380 private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
381 assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
384 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
385 assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
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);
392 Bridge mockBridge = mock(Bridge.class);
393 when(mockBridge.getStatus()).thenReturn(ThingStatus.ONLINE);
395 Thing mockThing = mock(Thing.class);
396 when(mockThing.getConfiguration()).thenReturn(new Configuration(Map.of(LIGHT_ID, "1")));
398 HueClient mockClient = mock(HueClient.class);
399 when(mockClient.getLightById(any())).thenReturn(light);
403 HueLightHandler hueLightHandler = new HueLightHandler(mockThing,
404 new HueStateDescriptionOptionProvider(mock(ChannelTypeI18nLocalizationService.class))) {
406 protected synchronized HueClient getHueClient() {
411 protected Bridge getBridge() {
415 hueLightHandler.setCallback(mock(ThingHandlerCallback.class));
416 hueLightHandler.initialize();
418 verify(mockThing).setProperty(eq(Thing.PROPERTY_MODEL_ID), eq(expectedModel));
419 verify(mockThing).setProperty(eq(Thing.PROPERTY_VENDOR), eq(expectedVendor));
421 hueLightHandler.handleCommand(new ChannelUID(new ThingUID("hue::test"), channel), command);
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());
429 private void assertJson(String expected, String actual) {
430 JsonElement jsonExpected = JsonParser.parseString(expected);
431 JsonElement jsonActual = JsonParser.parseString(actual);
432 assertEquals(jsonExpected, jsonActual);