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