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
13 package org.openhab.binding.airvisualnode.internal.discovery;
15 import java.io.IOException;
16 import java.net.UnknownHostException;
17 import java.util.Collections;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants;
26 import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
27 import org.openhab.core.config.discovery.AbstractDiscoveryService;
28 import org.openhab.core.config.discovery.DiscoveryResult;
29 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
30 import org.openhab.core.config.discovery.DiscoveryService;
31 import org.openhab.core.thing.ThingUID;
32 import org.osgi.service.component.annotations.Component;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
36 import jcifs.netbios.NbtAddress;
37 import jcifs.smb.SmbFile;
40 * Autodiscovery for AirVisual Node by searching for a host advertised with the NetBIOS name 'AVISUAL-<SerialNumber>'.
42 * @author Victor Antonovich - Initial contribution
45 @Component(service = DiscoveryService.class)
46 public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
48 private final Logger logger = LoggerFactory.getLogger(AirVisualNodeDiscoveryService.class);
49 private static final int REFRESH_MINUTES = 5;
51 public static final String AVISUAL_WORKGROUP_NAME = "MSHOME";
53 private static final Pattern AVISUAL_NAME_PATTERN = Pattern.compile("^AVISUAL-([^/]+)$");
55 private @Nullable ScheduledFuture<?> backgroundDiscoveryFuture;
57 public AirVisualNodeDiscoveryService() {
58 super(Collections.singleton(AirVisualNodeBindingConstants.THING_TYPE_AVNODE), 600, true);
62 protected void startScan() {
63 logger.debug("Starting scan");
64 scheduler.execute(this::scan);
68 protected void startBackgroundDiscovery() {
69 logger.debug("Starting background discovery");
70 ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
71 if (localDiscoveryFuture == null || localDiscoveryFuture.isCancelled()) {
72 backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, REFRESH_MINUTES,
78 protected void stopBackgroundDiscovery() {
79 logger.debug("Stopping background discovery");
81 ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
82 if (localDiscoveryFuture != null) {
83 localDiscoveryFuture.cancel(true);
84 backgroundDiscoveryFuture = null;
89 // Get all workgroup members
90 SmbFile[] workgroupMembers;
92 String workgroupUrl = "smb://" + AVISUAL_WORKGROUP_NAME + "/";
93 workgroupMembers = new SmbFile(workgroupUrl).listFiles();
94 } catch (IOException e) {
95 logger.debug("IOException while trying to get workgroup member list", e);
99 // Check found workgroup members for the Node devices
100 for (SmbFile s : workgroupMembers) {
101 String serverName = s.getServer();
103 // Check workgroup member for the Node device name match
104 Matcher m = AVISUAL_NAME_PATTERN.matcher(serverName);
106 // Workgroup member server name doesn't match the Node device name pattern
110 // Extract the Node serial number from device name
111 String nodeSerialNumber = m.group(1);
113 if (nodeSerialNumber != null) {
114 logger.debug("Extracting the Node serial number failed");
117 // The Node Thing UID is serial number converted to lower case
118 ThingUID thingUID = new ThingUID(AirVisualNodeBindingConstants.THING_TYPE_AVNODE,
119 nodeSerialNumber.toLowerCase());
122 // Get the Node address by name
123 NbtAddress nodeNbtAddress = NbtAddress.getByName(serverName);
124 if (nodeNbtAddress == null) {
125 // The Node address not found by some reason, skip it
129 // Create discovery result
130 String nodeAddress = nodeNbtAddress.getInetAddress().getHostAddress();
131 if (nodeAddress != null) {
132 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
133 .withProperty(AirVisualNodeConfig.ADDRESS, nodeAddress)
134 .withRepresentationProperty(AirVisualNodeConfig.ADDRESS)
135 .withLabel("AirVisual Node (" + nodeSerialNumber + ")").build();
136 thingDiscovered(result);
138 logger.debug("Getting the node address from the host failed");
140 } catch (UnknownHostException e) {
141 logger.debug("The Node address resolving failed ", e);