2 * Copyright (c) 2010-2020 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.*;
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.HSBType;
31 import org.openhab.core.library.types.IncreaseDecreaseType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.PercentType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.thing.Bridge;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.ThingUID;
40 import org.openhab.core.types.Command;
42 import com.google.gson.Gson;
43 import com.google.gson.JsonElement;
44 import com.google.gson.JsonParser;
47 * Tests for {@link HueLightHandler}.
49 * @author Oliver Libutzki - Initial contribution
50 * @author Michael Grammling - Initial contribution
51 * @author Markus Mazurczak - Added test for OSRAM Par16 50 TW bulbs
52 * @author Andre Fuechsel - modified tests after introducing the generic thing types
53 * @author Denis Dudnik - switched to internally integrated source of Jue library
54 * @author Simon Kaufmann - migrated to plain Java test
55 * @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
57 public class HueLightHandlerTest {
59 private static final int MIN_COLOR_TEMPERATURE = 153;
60 private static final int MAX_COLOR_TEMPERATURE = 500;
61 private static final int COLOR_TEMPERATURE_RANGE = MAX_COLOR_TEMPERATURE - MIN_COLOR_TEMPERATURE;
63 private static final String OSRAM_MODEL_TYPE = "PAR16 50 TW";
64 private static final String OSRAM_MODEL_TYPE_ID = "PAR16_50_TW";
74 public void assertCommandForOsramPar1650ForColorTemperatureChannelOn() {
75 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
76 assertSendCommandForColorTempForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
80 public void assertCommandForOsramPar1650ForColorTemperatureChannelOff() {
81 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
82 assertSendCommandForColorTempForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
86 public void assertCommandForOsramPar1650ForBrightnessChannelOn() {
87 String expectedReply = "{\"on\" : true, \"bri\" : 254}";
88 assertSendCommandForBrightnessForPar16(OnOffType.ON, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
92 public void assertCommandForOsramPar1650ForBrightnessChannelOff() {
93 String expectedReply = "{\"on\" : false, \"transitiontime\" : 0}";
94 assertSendCommandForBrightnessForPar16(OnOffType.OFF, new HueLightState(OSRAM_MODEL_TYPE), expectedReply);
98 public void assertCommandForColorChannelOn() {
99 String expectedReply = "{\"on\" : true}";
100 assertSendCommandForColor(OnOffType.ON, new HueLightState(), expectedReply);
104 public void assertCommandForColorTemperatureChannelOn() {
105 String expectedReply = "{\"on\" : true}";
106 assertSendCommandForColorTemp(OnOffType.ON, new HueLightState(), expectedReply);
110 public void assertCommandForColorChannelOff() {
111 String expectedReply = "{\"on\" : false}";
112 assertSendCommandForColor(OnOffType.OFF, new HueLightState(), expectedReply);
116 public void assertCommandForColorTemperatureChannelOff() {
117 String expectedReply = "{\"on\" : false}";
118 assertSendCommandForColorTemp(OnOffType.OFF, new HueLightState(), expectedReply);
122 public void assertCommandForColorTemperatureChannel0Percent() {
123 String expectedReply = "{\"ct\" : 153, \"transitiontime\" : 4}";
124 assertSendCommandForColorTemp(new PercentType(0), new HueLightState(), expectedReply);
128 public void assertCommandForColorTemperatureChannel50Percent() {
129 String expectedReply = "{\"ct\" : 327, \"transitiontime\" : 4}";
130 assertSendCommandForColorTemp(new PercentType(50), new HueLightState(), expectedReply);
134 public void assertCommandForColorTemperatureChannel1000Percent() {
135 String expectedReply = "{\"ct\" : 500, \"transitiontime\" : 4}";
136 assertSendCommandForColorTemp(new PercentType(100), new HueLightState(), expectedReply);
140 public void assertPercentageValueOfColorTemperatureWhenCt153() {
141 int expectedReply = 0;
142 asserttoColorTemperaturePercentType(153, expectedReply);
146 public void assertPercentageValueOfColorTemperatureWhenCt326() {
147 int expectedReply = 50;
148 asserttoColorTemperaturePercentType(326, expectedReply);
152 public void assertPercentageValueOfColorTemperatureWhenCt500() {
153 int expectedReply = 100;
154 asserttoColorTemperaturePercentType(500, expectedReply);
158 public void assertCommandForColorChannel0Percent() {
159 String expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
160 assertSendCommandForColor(new PercentType(0), new HueLightState(), expectedReply);
164 public void assertCommandForColorChannel50Percent() {
165 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
166 assertSendCommandForColor(new PercentType(50), new HueLightState(), expectedReply);
170 public void assertCommandForColorChannel100Percent() {
171 String expectedReply = "{\"bri\" : 254, \"on\" : true, \"transitiontime\" : 4}";
172 assertSendCommandForColor(new PercentType(100), new HueLightState(), expectedReply);
176 public void assertCommandForColorChannelBlack() {
177 String expectedReply = "{\"on\" : false}";
178 assertSendCommandForColor(HSBType.BLACK, new HueLightState(), expectedReply);
182 public void assertCommandForColorChannelRed() {
183 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 0, \"transitiontime\" : 4}";
184 assertSendCommandForColor(HSBType.RED, new HueLightState(), expectedReply);
188 public void assertCommandForColorChannelGreen() {
189 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 21845, \"transitiontime\" : 4}";
190 assertSendCommandForColor(HSBType.GREEN, new HueLightState(), expectedReply);
194 public void assertCommandForColorChannelBlue() {
195 String expectedReply = "{\"bri\" : 254, \"sat\" : 254, \"hue\" : 43690, \"transitiontime\" : 4}";
196 assertSendCommandForColor(HSBType.BLUE, new HueLightState(), expectedReply);
200 public void assertCommandForColorChannelWhite() {
201 String expectedReply = "{\"bri\" : 254, \"sat\" : 0, \"hue\" : 0, \"transitiontime\" : 4}";
202 assertSendCommandForColor(HSBType.WHITE, new HueLightState(), expectedReply);
206 public void assertXYCommandForColorChannelBlack() {
207 String expectedReply = "{\"on\" : false}";
208 assertSendCommandForColor(HSBType.BLACK, new HueLightState().colormode(ColorMode.XY), expectedReply);
212 public void assertXYCommandForColorChannelWhite() {
213 String expectedReply = "{\"xy\" : [ 0.31271592 , 0.32900152 ], \"bri\" : 254, \"transitiontime\" : 4}";
214 assertSendCommandForColor(HSBType.WHITE, new HueLightState().colormode(ColorMode.XY), expectedReply);
218 public void assertXYCommandForColorChannelColorful() {
219 String expectedReply = "{\"xy\" : [ 0.16969365 , 0.12379659 ], \"bri\" : 127, \"transitiontime\" : 4}";
220 assertSendCommandForColor(new HSBType("220,90,50"), new HueLightState().colormode(ColorMode.XY), expectedReply);
224 public void asserCommandForColorChannelIncrease() {
225 HueLightState currentState = new HueLightState().bri(1).on(false);
226 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
227 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
229 currentState.bri(200).on(true);
230 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
231 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
233 currentState.bri(230);
234 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
235 assertSendCommandForColor(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
239 public void asserCommandForColorChannelDecrease() {
240 HueLightState currentState = new HueLightState().bri(200);
241 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
242 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
244 currentState.bri(20);
245 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
246 assertSendCommandForColor(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
250 public void assertCommandForBrightnessChannel50Percent() {
251 HueLightState currentState = new HueLightState();
252 String expectedReply = "{\"bri\" : 127, \"on\" : true, \"transitiontime\" : 4}";
253 assertSendCommandForBrightness(new PercentType(50), currentState, expectedReply);
257 public void assertCommandForBrightnessChannelIncrease() {
258 HueLightState currentState = new HueLightState().bri(1).on(false);
259 String expectedReply = "{\"bri\" : 30, \"on\" : true, \"transitiontime\" : 4}";
260 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
262 currentState.bri(200).on(true);
263 expectedReply = "{\"bri\" : 230, \"transitiontime\" : 4}";
264 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
266 currentState.bri(230);
267 expectedReply = "{\"bri\" : 254, \"transitiontime\" : 4}";
268 assertSendCommandForBrightness(IncreaseDecreaseType.INCREASE, currentState, expectedReply);
272 public void assertCommandForBrightnessChannelDecrease() {
273 HueLightState currentState = new HueLightState().bri(200);
274 String expectedReply = "{\"bri\" : 170, \"transitiontime\" : 4}";
275 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
277 currentState.bri(20);
278 expectedReply = "{\"on\" : false, \"transitiontime\" : 4}";
279 assertSendCommandForBrightness(IncreaseDecreaseType.DECREASE, currentState, expectedReply);
283 public void assertCommandForBrightnessChannelOff() {
284 HueLightState currentState = new HueLightState();
285 String expectedReply = "{\"on\" : false}";
286 assertSendCommandForBrightness(OnOffType.OFF, currentState, expectedReply);
290 public void assertCommandForBrightnessChannelOn() {
291 HueLightState currentState = new HueLightState();
292 String expectedReply = "{\"on\" : true}";
293 assertSendCommandForBrightness(OnOffType.ON, currentState, expectedReply);
297 public void assertCommandForAlertChannel() {
298 HueLightState currentState = new HueLightState().alert("NONE");
299 String expectedReply = "{\"alert\" : \"none\"}";
300 assertSendCommandForAlert(new StringType("NONE"), currentState, expectedReply);
302 currentState.alert("NONE");
303 expectedReply = "{\"alert\" : \"select\"}";
304 assertSendCommandForAlert(new StringType("SELECT"), currentState, expectedReply);
306 currentState.alert("LSELECT");
307 expectedReply = "{\"alert\" : \"lselect\"}";
308 assertSendCommandForAlert(new StringType("LSELECT"), currentState, expectedReply);
312 public void assertCommandForEffectChannel() {
313 HueLightState currentState = new HueLightState().effect("ON");
314 String expectedReply = "{\"effect\" : \"colorloop\"}";
315 assertSendCommandForEffect(OnOffType.ON, currentState, expectedReply);
317 currentState.effect("OFF");
318 expectedReply = "{\"effect\" : \"none\"}";
319 assertSendCommandForEffect(OnOffType.OFF, currentState, expectedReply);
322 private void assertSendCommandForColorTempForPar16(Command command, HueLightState currentState,
323 String expectedReply) {
324 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, "OSRAM");
327 private void assertSendCommandForBrightnessForPar16(Command command, HueLightState currentState,
328 String expectedReply) {
329 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply, OSRAM_MODEL_TYPE_ID, "OSRAM");
332 private void assertSendCommandForColor(Command command, HueLightState currentState, String expectedReply) {
333 assertSendCommand(CHANNEL_COLOR, command, currentState, expectedReply);
336 private void assertSendCommandForColorTemp(Command command, HueLightState currentState, String expectedReply) {
337 assertSendCommand(CHANNEL_COLORTEMPERATURE, command, currentState, expectedReply);
340 private void asserttoColorTemperaturePercentType(int ctValue, int expectedPercent) {
341 int percent = (int) Math.round(((ctValue - MIN_COLOR_TEMPERATURE) * 100.0) / COLOR_TEMPERATURE_RANGE);
342 assertEquals(percent, expectedPercent);
345 private void assertSendCommandForBrightness(Command command, HueLightState currentState, String expectedReply) {
346 assertSendCommand(CHANNEL_BRIGHTNESS, command, currentState, expectedReply);
349 private void assertSendCommandForAlert(Command command, HueLightState currentState, String expectedReply) {
350 assertSendCommand(CHANNEL_ALERT, command, currentState, expectedReply);
353 private void assertSendCommandForEffect(Command command, HueLightState currentState, String expectedReply) {
354 assertSendCommand(CHANNEL_EFFECT, command, currentState, expectedReply);
357 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply) {
358 assertSendCommand(channel, command, currentState, expectedReply, "LCT001", "Philips");
361 private void assertSendCommand(String channel, Command command, HueLightState currentState, String expectedReply,
362 String expectedModel, String expectedVendor) {
363 FullLight light = gson.fromJson(currentState.toString(), FullConfig.class).getLights().get(0);
365 Bridge mockBridge = mock(Bridge.class);
366 when(mockBridge.getStatus()).thenReturn(ThingStatus.ONLINE);
368 Thing mockThing = mock(Thing.class);
369 when(mockThing.getConfiguration()).thenReturn(new Configuration(Collections.singletonMap(LIGHT_ID, "1")));
371 HueClient mockClient = mock(HueClient.class);
372 when(mockClient.getLightById(any())).thenReturn(light);
376 HueLightHandler hueLightHandler = new HueLightHandler(mockThing) {
378 protected synchronized HueClient getHueClient() {
383 protected Bridge getBridge() {
387 hueLightHandler.initialize();
389 verify(mockThing).setProperty(eq(Thing.PROPERTY_MODEL_ID), eq(expectedModel));
390 verify(mockThing).setProperty(eq(Thing.PROPERTY_VENDOR), eq(expectedVendor));
392 hueLightHandler.handleCommand(new ChannelUID(new ThingUID("hue::test"), channel), command);
394 ArgumentCaptor<StateUpdate> captorStateUpdate = ArgumentCaptor.forClass(StateUpdate.class);
395 verify(mockClient).updateLightState(any(LightStatusListener.class), any(FullLight.class),
396 captorStateUpdate.capture(), eq(fadeTime));
397 assertJson(expectedReply, captorStateUpdate.getValue().toJson());
400 private void assertJson(String expected, String actual) {
401 JsonParser parser = new JsonParser();
402 JsonElement jsonExpected = parser.parse(expected);
403 JsonElement jsonActual = parser.parse(actual);
404 assertEquals(jsonExpected, jsonActual);