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;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
19 import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
21 import java.io.IOException;
22 import java.lang.reflect.Field;
23 import java.util.Collection;
24 import java.util.concurrent.atomic.AtomicBoolean;
25 import java.util.concurrent.atomic.AtomicReference;
27 import org.junit.jupiter.api.AfterEach;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
31 import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
32 import org.openhab.core.config.core.Configuration;
33 import org.openhab.core.config.discovery.DiscoveryListener;
34 import org.openhab.core.config.discovery.DiscoveryResult;
35 import org.openhab.core.config.discovery.DiscoveryResultFlag;
36 import org.openhab.core.config.discovery.DiscoveryService;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ThingRegistry;
39 import org.openhab.core.thing.ThingStatus;
40 import org.openhab.core.thing.ThingStatusDetail;
41 import org.openhab.core.thing.ThingStatusInfo;
42 import org.openhab.core.thing.ThingTypeUID;
43 import org.openhab.core.thing.ThingUID;
44 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
47 * Tests for {@link HueDeviceDiscoveryService}.
49 * @author Kai Kreuzer - Initial contribution
50 * @author Andre Fuechsel - added test 'assert start search is called()'
51 * - modified tests after introducing the generic thing types
52 * @author Denis Dudnik - switched to internally integrated source of Jue library
53 * @author Markus Rathgeb - migrated to plain Java test
55 public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent {
57 protected HueThingHandlerFactory hueThingHandlerFactory;
58 protected DiscoveryListener discoveryListener;
59 protected ThingRegistry thingRegistry;
60 protected Bridge hueBridge;
61 protected HueBridgeHandler hueBridgeHandler;
62 protected HueDeviceDiscoveryService discoveryService;
64 protected final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
65 protected final ThingUID BRIDGE_THING_UID = new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge");
69 registerVolatileStorageService();
71 thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
72 assertThat(thingRegistry, is(notNullValue()));
74 Configuration configuration = new Configuration();
75 configuration.put(HOST, "1.2.3.4");
76 configuration.put(USER_NAME, "testUserName");
77 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
79 hueBridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID, BRIDGE_THING_UID, null, "Bridge",
82 assertThat(hueBridge, is(notNullValue()));
83 thingRegistry.add(hueBridge);
85 hueBridgeHandler = getThingHandler(hueBridge, HueBridgeHandler.class);
86 assertThat(hueBridgeHandler, is(notNullValue()));
88 discoveryService = getService(DiscoveryService.class, HueDeviceDiscoveryService.class);
89 assertThat(discoveryService, is(notNullValue()));
93 public void cleanUp() {
94 thingRegistry.remove(BRIDGE_THING_UID);
96 assertNull(getService(DiscoveryService.class, HueDeviceDiscoveryService.class));
100 private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
101 unregisterCurrentDiscoveryListener();
102 this.discoveryListener = discoveryListener;
103 discoveryService.addDiscoveryListener(this.discoveryListener);
106 private void unregisterCurrentDiscoveryListener() {
107 if (this.discoveryListener != null) {
108 discoveryService.removeDiscoveryListener(this.discoveryListener);
113 public void hueLightRegistration() {
114 FullLight light = new FullLight();
116 light.setUniqueID("AA:BB:CC:DD:EE:FF:00:11-XX");
117 light.setModelID("LCT001");
118 light.setType("Extended color light");
120 AtomicReference<DiscoveryResult> resultWrapper = new AtomicReference<>();
122 registerDiscoveryListener(new DiscoveryListener() {
124 public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
125 resultWrapper.set(result);
129 public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
133 public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
134 Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
139 discoveryService.addLightDiscovery(light);
140 waitForAssert(() -> {
141 assertTrue(resultWrapper.get() != null);
144 final DiscoveryResult result = resultWrapper.get();
145 assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
146 assertThat(result.getThingUID().toString(), is("hue:0210:testBridge:" + light.getId()));
147 assertThat(result.getThingTypeUID(), is(THING_TYPE_EXTENDED_COLOR_LIGHT));
148 assertThat(result.getBridgeUID(), is(hueBridge.getUID()));
149 assertThat(result.getProperties().get(LIGHT_ID), is(light.getId()));
153 public void startSearchIsCalled() {
154 final AtomicBoolean searchHasBeenTriggered = new AtomicBoolean(false);
156 MockedHttpClient mockedHttpClient = new MockedHttpClient() {
159 public Result put(String address, String body) throws IOException {
160 return new Result("", 200);
164 public Result get(String address) throws IOException {
165 if (address.endsWith("testUserName")) {
166 String body = "{\"lights\":{}}";
167 return new Result(body, 200);
168 } else if (address.endsWith("lights") || address.endsWith("sensors") || address.endsWith("groups")) {
170 return new Result(body, 200);
171 } else if (address.endsWith("testUserName/config")) {
172 String body = "{ \"apiversion\": \"1.26.0\"}";
173 return new Result(body, 200);
175 return new Result("", 404);
180 public Result post(String address, String body) throws IOException {
181 if (address.endsWith("lights")) {
182 String bodyReturn = "{\"success\": {\"/lights\": \"Searching for new devices\"}}";
183 searchHasBeenTriggered.set(true);
184 return new Result(bodyReturn, 200);
186 return new Result("", 404);
191 installHttpClientMock(hueBridgeHandler, mockedHttpClient);
193 ThingStatusInfo online = ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build();
194 waitForAssert(() -> {
195 assertThat(hueBridge.getStatusInfo(), is(online));
198 discoveryService.startScan();
199 waitForAssert(() -> {
200 assertTrue(searchHasBeenTriggered.get());
204 private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttpClient mockedHttpClient) {
205 waitForAssert(() -> {
208 final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("hueBridge");
209 hueBridgeField.setAccessible(true);
210 final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler);
211 assertThat(hueBridgeValue, is(notNullValue()));
213 final Field httpClientField = HueBridge.class.getDeclaredField("http");
214 httpClientField.setAccessible(true);
215 httpClientField.set(hueBridgeValue, mockedHttpClient);
217 final Field usernameField = HueBridge.class.getDeclaredField("username");
218 usernameField.setAccessible(true);
219 usernameField.set(hueBridgeValue, hueBridgeHandler.getThing().getConfiguration().get(USER_NAME));
220 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ex) {
221 fail("Reflection usage error");
224 hueBridgeHandler.initialize();