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