]> git.basschouten.com Git - openhab-addons.git/blob
bb86628c87f80b7cba7002350ffcfb6d31504463
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.hue.internal.discovery;
14
15 import static org.openhab.core.config.discovery.inbox.InboxPredicates.forThingTypeUID;
16 import static org.hamcrest.CoreMatchers.*;
17 import static org.junit.Assert.*;
18 import static org.openhab.binding.hue.internal.HueBindingConstants.THING_TYPE_BRIDGE;
19
20 import java.io.IOException;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.stream.Collectors;
26
27 import org.openhab.core.config.discovery.DiscoveryListener;
28 import org.openhab.core.config.discovery.DiscoveryResult;
29 import org.openhab.core.config.discovery.DiscoveryService;
30 import org.openhab.core.config.discovery.inbox.Inbox;
31 import org.openhab.core.thing.ThingTypeUID;
32 import org.openhab.core.thing.ThingUID;
33 import org.openhab.core.test.java.JavaOSGiTest;
34 import org.openhab.core.test.storage.VolatileStorageService;
35 import org.junit.Before;
36 import org.junit.Test;
37
38 /**
39  *
40  * @author Christoph Knauf - Initial contribution
41  * @author Markus Rathgeb - migrated to plain Java test
42  */
43 public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
44
45     HueBridgeNupnpDiscovery sut;
46     VolatileStorageService volatileStorageService = new VolatileStorageService();
47     DiscoveryListener discoveryListener;
48     Inbox inbox;
49
50     final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
51     final String ip1 = "192.168.31.17";
52     final String ip2 = "192.168.30.28";
53     final String sn1 = "00178820057f";
54     final String sn2 = "001788141b41";
55     final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1);
56     final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2);
57     final String validBridgeDiscoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1
58             + "},{\"id\":\"001788fffe141b41\",\"internalipaddress\":" + ip2 + "}]";
59     String discoveryResult;
60     String expBridgeDescription = "" + //
61             "<?xml version=\"1.0\"?>" + //
62             "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" + //
63             "  <specVersion>" + //
64             "    <major>1</major>" + //
65             "    <minor>0</minor>" + //
66             "  </specVersion>" + //
67             "  <URLBase>http://$IP:80/</URLBase>" + //
68             "  <device>" + //
69             "    <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>" + //
70             "    <friendlyName>Philips hue ($IP)</friendlyName>" + //
71             "    <manufacturer>Royal Philips Electronics</manufacturer>" + //
72             "    <manufacturerURL>http://www.philips.com</manufacturerURL>" + //
73             "<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>" + //
74             "<modelName>Philips hue bridge 2012</modelName>" + //
75             "<modelNumber>1000000000000</modelNumber>" + //
76             "<modelURL>http://www.meethue.com</modelURL>" + //
77             "    <serialNumber>93eadbeef13</serialNumber>" + //
78             "    <UDN>uuid:01234567-89ab-cdef-0123-456789abcdef</UDN>" + //
79             "    <serviceList>" + //
80             "      <service>" + //
81             "        <serviceType>(null)</serviceType>" + //
82             "        <serviceId>(null)</serviceId>" + //
83             "        <controlURL>(null)</controlURL>" + //
84             "        <eventSubURL>(null)</eventSubURL>" + //
85             "        <SCPDURL>(null)</SCPDURL>" + //
86             "      </service>" + //
87             "    </serviceList>" + //
88             "    <presentationURL>index.html</presentationURL>" + //
89             "    <iconList>" + //
90             "      <icon>" + //
91             "        <mimetype>image/png</mimetype>" + //
92             "        <height>48</height>" + //
93             "        <width>48</width>" + //
94             "        <depth>24</depth>" + //
95             "        <url>hue_logo_0.png</url>" + //
96             "      </icon>" + //
97             "      <icon>" + //
98             "        <mimetype>image/png</mimetype>" + //
99             "        <height>120</height>" + //
100             "        <width>120</width>" + //
101             "        <depth>24</depth>" + //
102             "        <url>hue_logo_3.png</url>" + //
103             "      </icon>" + //
104             "    </iconList>" + //
105             "  </device>" + //
106             "</root>";
107
108     private void checkDiscoveryResult(DiscoveryResult result, String expIp, String expSn) {
109         assertThat(result.getBridgeUID(), nullValue());
110         assertThat(result.getLabel(), is(HueBridgeNupnpDiscovery.LABEL_PATTERN.replace("IP", expIp)));
111         assertThat(result.getProperties().get("ipAddress"), is(expIp));
112         assertThat(result.getProperties().get("serialNumber"), is(expSn));
113     }
114
115     private void registerDiscoveryListener(DiscoveryListener discoveryListener) {
116         unregisterCurrentDiscoveryListener();
117         this.discoveryListener = discoveryListener;
118         sut.addDiscoveryListener(this.discoveryListener);
119     }
120
121     private void unregisterCurrentDiscoveryListener() {
122         if (this.discoveryListener != null) {
123             sut.removeDiscoveryListener(this.discoveryListener);
124         }
125     }
126
127     // Mock class which only overrides the doGetRequest method in order to make the class testable
128     class ConfigurableBridgeNupnpDiscoveryMock extends HueBridgeNupnpDiscovery {
129         @Override
130         protected String doGetRequest(String url) throws IOException {
131             if (url.contains("meethue")) {
132                 return discoveryResult;
133             } else if (url.contains(ip1)) {
134                 return expBridgeDescription.replaceAll("$IP", ip1);
135             } else if (url.contains(ip2)) {
136                 return expBridgeDescription.replaceAll("$IP", ip2);
137             }
138             throw new IOException();
139         }
140     }
141
142     @Before
143     public void setUp() {
144         registerService(volatileStorageService);
145
146         sut = getService(DiscoveryService.class, HueBridgeNupnpDiscovery.class);
147         assertThat(sut, is(notNullValue()));
148
149         inbox = getService(Inbox.class);
150         assertThat(inbox, is(notNullValue()));
151
152         unregisterCurrentDiscoveryListener();
153     }
154
155     @Test
156     public void bridgeThingTypeIsSupported() {
157         assertThat(sut.getSupportedThingTypes().size(), is(1));
158         assertThat(sut.getSupportedThingTypes().iterator().next(), is(THING_TYPE_BRIDGE));
159     }
160
161     @Test
162     public void validBridgesAreDiscovered() {
163         List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
164                 .collect(Collectors.toList());
165         for (final DiscoveryResult oldResult : oldResults) {
166             inbox.remove(oldResult.getThingUID());
167         }
168
169         sut = new ConfigurableBridgeNupnpDiscoveryMock();
170         registerService(sut, DiscoveryService.class.getName());
171         discoveryResult = validBridgeDiscoveryResult;
172         final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
173         registerDiscoveryListener(new DiscoveryListener() {
174             @Override
175             public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
176                 results.put(result.getThingUID(), result);
177             }
178
179             @Override
180             public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
181             }
182
183             @Override
184             public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
185                     Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
186                 return null;
187             }
188         });
189
190         sut.startScan();
191
192         waitForAssert(() -> {
193             assertThat(results.size(), is(2));
194             assertThat(results.get(BRIDGE_THING_UID_1), is(notNullValue()));
195             checkDiscoveryResult(results.get(BRIDGE_THING_UID_1), ip1, sn1);
196             assertThat(results.get(BRIDGE_THING_UID_2), is(notNullValue()));
197             checkDiscoveryResult(results.get(BRIDGE_THING_UID_2), ip2, sn2);
198
199             final List<DiscoveryResult> inboxResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
200                     .collect(Collectors.toList());
201             assertTrue(inboxResults.size() >= 2);
202
203             assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_1))
204                     .findFirst().orElse(null), is(notNullValue()));
205             assertThat(inboxResults.stream().filter(result -> result.getThingUID().equals(BRIDGE_THING_UID_2))
206                     .findFirst().orElse(null), is(notNullValue()));
207         });
208     }
209
210     @Test
211     public void invalidBridgesAreNotDiscovered() {
212         List<DiscoveryResult> oldResults = inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID))
213                 .collect(Collectors.toList());
214         for (final DiscoveryResult oldResult : oldResults) {
215             inbox.remove(oldResult.getThingUID());
216         }
217
218         sut = new ConfigurableBridgeNupnpDiscoveryMock();
219         registerService(sut, DiscoveryService.class.getName());
220         final Map<ThingUID, DiscoveryResult> results = new HashMap<>();
221         registerDiscoveryListener(new DiscoveryListener() {
222             @Override
223             public void thingDiscovered(DiscoveryService source, DiscoveryResult result) {
224                 results.put(result.getThingUID(), result);
225             }
226
227             @Override
228             public void thingRemoved(DiscoveryService source, ThingUID thingUID) {
229             }
230
231             @Override
232             public Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
233                     Collection<ThingTypeUID> thingTypeUIDs, ThingUID bridgeUID) {
234                 return null;
235             }
236         });
237
238         // missing ip
239         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":}]";
240         sut.startScan();
241         waitForAssert(() -> {
242             assertThat(results.size(), is(0));
243         });
244
245         // missing id
246         discoveryResult = "[{\"id\":\"\",\"internalipaddress\":192.168.30.22}]";
247         sut.startScan();
248         waitForAssert(() -> {
249             assertThat(results.size(), is(0));
250         });
251
252         // id < 10
253         discoveryResult = "[{\"id\":\"012345678\",\"internalipaddress\":192.168.30.22}]";
254         sut.startScan();
255         waitForAssert(() -> {
256             assertThat(results.size(), is(0));
257         });
258
259         // bridge indicator not part of id
260         discoveryResult = "[{\"id\":\"0123456789\",\"internalipaddress\":192.168.30.22}]";
261         sut.startScan();
262         waitForAssert(() -> {
263             assertThat(results.size(), is(0));
264         });
265
266         // bridge indicator at wrong position (-1)
267         discoveryResult = "[{\"id\":\"01234" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
268                 + "7891\",\"internalipaddress\":192.168.30.22}]";
269         sut.startScan();
270         waitForAssert(() -> {
271             assertThat(results.size(), is(0));
272         });
273
274         // bridge indicator at wrong position (+1)
275         discoveryResult = "[{\"id\":\"0123456" + HueBridgeNupnpDiscovery.BRIDGE_INDICATOR
276                 + "7891\",\"internalipaddress\":192.168.30.22}]";
277         sut.startScan();
278         waitForAssert(() -> {
279             assertThat(results.size(), is(0));
280         });
281
282         // bridge not reachable
283         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":192.168.30.1}]";
284         sut.startScan();
285         waitForAssert(() -> {
286             assertThat(results.size(), is(0));
287         });
288
289         // invalid bridge description
290         expBridgeDescription = "";
291         discoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1 + "}]";
292         sut.startScan();
293         waitForAssert(() -> {
294             assertThat(results.size(), is(0));
295         });
296
297         waitForAssert(() -> {
298             assertThat(
299                     inbox.stream().filter(forThingTypeUID(BRIDGE_THING_TYPE_UID)).collect(Collectors.toList()).size(),
300                     is(0));
301         });
302     }
303 }