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.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;
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
61 public class HueLightHandlerTest {
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;
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;
71 private final Gson gson = new Gson();
74 public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
75 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
76 assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
80 public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
81 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
82 assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
86 public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
87 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
88 assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE, OSRAM), expectedReply);
92 public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
93 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
94 assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE, OSRAM),
99 public void assertCommandForColorChannelOn() {
100 String expectedReply = "{\"on\" : true}";
101 assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
105 public void assertCommandForColorTemperatureChannelOn() {
106 String expectedReply = "{\"on\" : true}";
107 assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
111 public void assertCommandForColorChannelOff() {
112 String expectedReply = "{\"on\" : false}";
113 assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
117 public void assertCommandForColorTemperatureChannelOff() {
118 String expectedReply = "{\"on\" : false}";
119 assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
123 public void assertCommandForColorTemperatureChannel0Percent() {
124 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
125 assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
129 public void assertCommandForColorTemperatureChannel50Percent() {
130 String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
131 assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
135 public void assertCommandForColorTemperatureChannel1000Percent() {
136 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
137 assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
141 public void assertCommandForColorTemperatureAbsChannel6500Kelvin() {
142 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
143 assertSendCommandForColorTempAbs(new DecimalType(6500), new HueLightState(), expectedReply);
147 public void assertCommandForColorTemperatureAbsChannel4500Kelvin() {
148 String expectedReply = "{\"ct\" : 222, \"transitiontime\" : 4}";
149 assertSendCommandForColorTempAbs(new DecimalType(4500), new HueLightState(), expectedReply);
153 public void assertCommandForColorTemperatureAbsChannel2000Kelvin() {
154 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
155 assertSendCommandForColorTempAbs(new DecimalType(2000), new HueLightState(), expectedReply);
159 public void assertPercentageValueOfColorTemperatureWhenCt153() {
160 int expectedReply = 0;
161 asserttoColorTemperaturePercentType(153, expectedReply);
165 public void assertPercentageValueOfColorTemperatureWhenCt326() {
166 int expectedReply = 50;
167 asserttoColorTemperaturePercentType(326, expectedReply);
171 public void assertPercentageValueOfColorTemperatureWhenCt500() {
172 int expectedReply = 100;
173 asserttoColorTemperaturePercentType(500, expectedReply);
177 public void assertCommandForColorChannel0Percent() {
178 String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
179 assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
183 public void assertCommandForColorChannel50Percent() {
184 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
185 assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
189 public void assertCommandForColorChannel100Percent() {
190 String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
191 assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
195 public void assertCommandForColorChannelBlack() {
196 String expectedReply = "{\"on\" : false}";
197 assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
201 public void assertCommandForColorChannelRed() {
202 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
203 assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
207 public void assertCommandForColorChannelGreen() {
208 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
209 assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
213 public void assertCommandForColorChannelBlue() {
214 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
215 assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
219 public void assertCommandForColorChannelWhite() {
220 String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
221 assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
225 public void assertXYCommandForColorChannelBlack() {
226 String expectedReply = "{\"on\" : false}";
227 assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
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);
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);
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);
248 currentState.bri(200).on(true);
249 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
250 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
252 currentState.bri(230);
253 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
254 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
258 public void asserCommandForColorChannelDecrease() {
259 HueLightState currentState = new HueLightState().bri(200);
260 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
261 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
263 currentState.bri(20);
264 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
265 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
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);
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);
281 currentState.bri(200).on(true);
282 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
283 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
285 currentState.bri(230);
286 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
287 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
291 public void assertCommandForBrightnessChannelDecrease() {
292 HueLightState currentState = new HueLightState().bri(200);
293 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
294 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
296 currentState.bri(20);
297 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
298 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
302 public void assertCommandForBrightnessChannelOff() {
303 HueLightState currentState = new HueLightState();
304 String expectedReply = "{\"on\" : false}";
305 assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
309 public void assertCommandForBrightnessChannelOn() {
310 HueLightState currentState = new HueLightState();
311 String expectedReply = "{\"on\" : true}";
312 assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
316 public void assertCommandForAlertChannel() {
317 HueLightState currentState = new HueLightState().alert("NONE");
318 String expectedReply = "{\"alert\" : \"none\"}";
319 assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
321 currentState.alert("NONE");
322 expectedReply = "{\"alert\" : \"select\"}";
323 assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
325 currentState.alert("LSELECT");
326 expectedReply = "{\"alert\" : \"lselect\"}";
327 assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
331 public void assertCommandForEffectChannel() {
332 HueLightState currentState = new HueLightState().effect("ON");
333 String expectedReply = "{\"effect\" : \"colorloop\"}";
334 assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
336 currentState.effect("OFF");
337 expectedReply = "{\"effect\" : \"none\"}";
338 assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
341 private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
342 String expectedReply) {
343 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
346 private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
347 String expectedReply) {
348 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, OSRAM);
351 private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
352 assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
355 private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
356 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
359 private void assertSendCommandForColorTempAbs(Command command, HueLightState currentState, String expectedReply) {
360 assertSendCommand(CHANNEL_COLORTEMPERATURE_ABS, command, currentState, expectedReply);
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);
368 private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
369 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
372 private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
373 assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
376 private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
377 assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
380 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
381 assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
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);
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(Map.of(LIGHT_ID, "1")));
395 HueClient mockClient = mock(HueClient.class);
396 when(mockClient.getLightById(any())).thenReturn(light);
400 HueLightHandler hueLightHandler = new HueLightHandler(mockThing, mock(HueStateDescriptionProvider.class)) {
402 protected synchronized @Nullable HueClient getHueClient() {
407 protected @Nullable Bridge getBridge() {
411 hueLightHandler.setCallback(mock(ThingHandlerCallback.class));
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 JsonElement jsonExpected = JsonParser.parseString(expected);
427 JsonElement jsonActual = JsonParser.parseString(actual);
428 assertEquals(jsonExpected, jsonActual);