]> git.basschouten.com Git - openhab-addons.git/blob
fdea1c7bd772c19fe5f0ff3c4eeab4bca89450de
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.nobohub.internal.discovery;
14
15 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.NOBO_HUB_BROADCAST_ADDRESS;
16 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.NOBO_HUB_BROADCAST_PORT;
17 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.NOBO_HUB_MULTICAST_PORT;
18 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_HOSTNAME;
19 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_NAME;
20 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_VENDOR_NAME;
21 import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.THING_TYPE_HUB;
22 import static org.openhab.binding.nobohub.internal.NoboHubHandlerFactory.DISCOVERABLE_DEVICE_TYPES_UIDS;
23
24 import java.io.IOException;
25 import java.net.DatagramPacket;
26 import java.net.DatagramSocket;
27 import java.net.InetAddress;
28 import java.net.MulticastSocket;
29 import java.time.Duration;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import org.eclipse.jdt.annotation.NonNullByDefault;
35 import org.eclipse.jdt.annotation.Nullable;
36 import org.openhab.binding.nobohub.internal.NoboHubBridgeHandler;
37 import org.openhab.core.config.discovery.AbstractDiscoveryService;
38 import org.openhab.core.config.discovery.DiscoveryResult;
39 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
40 import org.openhab.core.config.discovery.DiscoveryService;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingUID;
43 import org.openhab.core.thing.binding.ThingHandler;
44 import org.openhab.core.thing.binding.ThingHandlerService;
45 import org.osgi.service.component.annotations.Component;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * This class identifies devices that are available on the Nobø hub and adds discovery results for them.
51  *
52  * @author Jørgen Austvik - Initial contribution
53  * @author Espen Fossen - Initial contribution
54  */
55 @NonNullByDefault
56 @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.nobohub")
57 public class NoboHubDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
58     private final Logger logger = LoggerFactory.getLogger(NoboHubDiscoveryService.class);
59
60     private @NonNullByDefault({}) NoboHubBridgeHandler hubBridgeHandler;
61
62     public NoboHubDiscoveryService() {
63         super(DISCOVERABLE_DEVICE_TYPES_UIDS, 10, true);
64     }
65
66     @Override
67     protected void startScan() {
68         scheduler.execute(scanner);
69     }
70
71     @Override
72     protected synchronized void stopScan() {
73         super.stopScan();
74         removeOlderResults(getTimestampOfLastScan());
75     }
76
77     @Override
78     public void deactivate() {
79         removeOlderResults(new Date().getTime());
80     }
81
82     @Override
83     public void setThingHandler(ThingHandler thingHandler) {
84         if (thingHandler instanceof NoboHubBridgeHandler bridgeHandler) {
85             this.hubBridgeHandler = bridgeHandler;
86         }
87     }
88
89     @Override
90     public @Nullable ThingHandler getThingHandler() {
91         return hubBridgeHandler;
92     }
93
94     private final Runnable scanner = new Runnable() {
95         @Override
96         public void run() {
97             boolean found = false;
98             logger.info("Detecting Glen Dimplex Nobø Hubs, trying Multicast");
99             try {
100                 MulticastSocket socket = new MulticastSocket(NOBO_HUB_MULTICAST_PORT);
101                 found = waitOnSocket(socket, "multicast");
102             } catch (IOException ioex) {
103                 logger.error("Failed detecting Nobø Hub via multicast", ioex);
104             }
105
106             if (!found) {
107                 logger.debug("Detecting Glen Dimplex Nobø Hubs, trying Broadcast");
108
109                 try {
110                     DatagramSocket socket = new DatagramSocket(NOBO_HUB_BROADCAST_PORT,
111                             InetAddress.getByName(NOBO_HUB_BROADCAST_ADDRESS));
112                     found = waitOnSocket(socket, "broadcast");
113                 } catch (IOException ioex) {
114                     logger.error("Failed detecting Nobø Hub via multicast, will try with Broadcast", ioex);
115                 }
116             }
117         }
118
119         private boolean waitOnSocket(DatagramSocket socket, String type) throws IOException {
120             try (socket) {
121                 socket.setBroadcast(true);
122
123                 byte[] buffer = new byte[1024];
124                 DatagramPacket data = new DatagramPacket(buffer, buffer.length);
125                 String received = "";
126                 while (!received.startsWith("__NOBOHUB__")) {
127                     socket.setSoTimeout((int) Duration.ofSeconds(4).toMillis());
128                     socket.receive(data);
129                     received = new String(buffer, 0, data.getLength());
130                 }
131
132                 logger.debug("Hub detection using {}: Received: {} from {}", type, received, data.getAddress());
133
134                 String[] parts = received.split("__", 3);
135                 if (3 != parts.length) {
136                     logger.debug("Data error, didn't contain three parts: '{}''", String.join("','", parts));
137                     return false;
138                 }
139
140                 String serialNumberStart = parts[parts.length - 1];
141                 addDevice(serialNumberStart, data.getAddress().getHostName());
142                 return true;
143             }
144         }
145
146         private void addDevice(String serialNumberStart, String hostName) {
147             ThingUID bridge = new ThingUID(THING_TYPE_HUB, serialNumberStart);
148             String label = "Nobø Hub " + serialNumberStart;
149
150             Map<String, Object> properties = new HashMap<>(4);
151             properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNumberStart);
152             properties.put(PROPERTY_NAME, label);
153             properties.put(Thing.PROPERTY_VENDOR, PROPERTY_VENDOR_NAME);
154             properties.put(PROPERTY_HOSTNAME, hostName);
155
156             logger.debug("Adding device {} to inbox: {} {} at {}", bridge, label, serialNumberStart, hostName);
157             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(bridge).withLabel(label)
158                     .withProperties(properties).withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER).build();
159             thingDiscovered(discoveryResult);
160         }
161     };
162 }