2 * Copyright (c) 2010-2022 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.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants;
24 import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
25 import org.openhab.core.config.discovery.AbstractDiscoveryService;
26 import org.openhab.core.config.discovery.DiscoveryResult;
27 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
28 import org.openhab.core.config.discovery.DiscoveryService;
29 import org.openhab.core.thing.ThingUID;
30 import org.osgi.service.component.annotations.Component;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import jcifs.netbios.NbtAddress;
35 import jcifs.smb.SmbFile;
38 * Autodiscovery for AirVisual Node by searching for a host advertised with the NetBIOS name 'AVISUAL-<SerialNumber>'.
40 * @author Victor Antonovich - Initial contribution
42 @Component(service = DiscoveryService.class)
43 public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
45 private final Logger logger = LoggerFactory.getLogger(AirVisualNodeDiscoveryService.class);
47 public static final String AVISUAL_WORKGROUP_NAME = "MSHOME";
49 private static final Pattern AVISUAL_NAME_PATTERN = Pattern.compile("^AVISUAL-([^/]+)$");
51 private ScheduledFuture<?> backgroundDiscoveryFuture;
53 public AirVisualNodeDiscoveryService() {
54 super(Collections.singleton(AirVisualNodeBindingConstants.THING_TYPE_AVNODE), 600, true);
58 protected void startScan() {
59 logger.debug("Starting scan");
60 scheduler.execute(this::scan);
64 protected void startBackgroundDiscovery() {
65 logger.debug("Starting background discovery");
66 backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, 5, TimeUnit.MINUTES);
70 protected void stopBackgroundDiscovery() {
71 logger.debug("Stopping background discovery");
72 cancelBackgroundDiscoveryFuture();
73 super.stopBackgroundDiscovery();
76 private void cancelBackgroundDiscoveryFuture() {
77 if (backgroundDiscoveryFuture != null && !backgroundDiscoveryFuture.isDone()) {
78 backgroundDiscoveryFuture.cancel(true);
79 backgroundDiscoveryFuture = null;
84 // Get all workgroup members
85 SmbFile[] workgroupMembers;
87 String workgroupUrl = "smb://" + AVISUAL_WORKGROUP_NAME + "/";
88 workgroupMembers = new SmbFile(workgroupUrl).listFiles();
89 } catch (IOException e) {
90 // Can't get workgroup member list
94 // Check found workgroup members for the Node devices
95 for (SmbFile s : workgroupMembers) {
96 String serverName = s.getServer();
98 // Check workgroup member for the Node device name match
99 Matcher m = AVISUAL_NAME_PATTERN.matcher(serverName);
101 // Workgroup member server name doesn't match the Node device name pattern
105 // Extract the Node serial number from device name
106 String nodeSerialNumber = m.group(1);
108 // The Node Thing UID is serial number converted to lower case
109 ThingUID thingUID = new ThingUID(AirVisualNodeBindingConstants.THING_TYPE_AVNODE,
110 nodeSerialNumber.toLowerCase());
113 // Get the Node address by name
114 NbtAddress nodeNbtAddress = NbtAddress.getByName(serverName);
115 if (nodeNbtAddress == null) {
116 // The Node address not found by some reason, skip it
120 // Create discovery result
121 String nodeAddress = nodeNbtAddress.getInetAddress().getHostAddress();
122 DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
123 .withProperty(AirVisualNodeConfig.ADDRESS, nodeAddress)
124 .withRepresentationProperty(AirVisualNodeConfig.ADDRESS)
125 .withLabel("AirVisual Node (" + nodeSerialNumber + ")").build();
126 thingDiscovered(result);
127 } catch (UnknownHostException e) {
128 logger.debug("The Node address resolving failed ", e);