]> git.basschouten.com Git - openhab-addons.git/blob
e9391bc7d297556dfe3e0e890e92c616e7f9825d
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.airvisualnode.internal.discovery;
14
15 import java.io.IOException;
16 import java.net.UnknownHostException;
17 import java.util.Set;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
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;
35
36 import jcifs.netbios.NbtAddress;
37 import jcifs.smb.SmbFile;
38
39 /**
40  * Autodiscovery for AirVisual Node by searching for a host advertised with the NetBIOS name
41  * {@code 'AVISUAL-<SerialNumber>'}.
42  *
43  * @author Victor Antonovich - Initial contribution
44  */
45 @NonNullByDefault
46 @Component(service = DiscoveryService.class)
47 public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
48
49     private final Logger logger = LoggerFactory.getLogger(AirVisualNodeDiscoveryService.class);
50     private static final int REFRESH_MINUTES = 5;
51
52     public static final String AVISUAL_WORKGROUP_NAME = "MSHOME";
53
54     private static final Pattern AVISUAL_NAME_PATTERN = Pattern.compile("^AVISUAL-([^/]+)$");
55
56     private @Nullable ScheduledFuture<?> backgroundDiscoveryFuture;
57
58     public AirVisualNodeDiscoveryService() {
59         super(Set.of(AirVisualNodeBindingConstants.THING_TYPE_AVNODE), 600, true);
60     }
61
62     @Override
63     protected void startScan() {
64         logger.debug("Starting scan");
65         scheduler.execute(this::scan);
66     }
67
68     @Override
69     protected void startBackgroundDiscovery() {
70         logger.debug("Starting background discovery");
71         ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
72         if (localDiscoveryFuture == null || localDiscoveryFuture.isCancelled()) {
73             backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, REFRESH_MINUTES,
74                     TimeUnit.MINUTES);
75         }
76     }
77
78     @Override
79     protected void stopBackgroundDiscovery() {
80         logger.debug("Stopping background discovery");
81
82         ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
83         if (localDiscoveryFuture != null) {
84             localDiscoveryFuture.cancel(true);
85             backgroundDiscoveryFuture = null;
86         }
87     }
88
89     private void scan() {
90         // Get all workgroup members
91         SmbFile[] workgroupMembers;
92         try {
93             String workgroupUrl = "smb://" + AVISUAL_WORKGROUP_NAME + "/";
94             workgroupMembers = new SmbFile(workgroupUrl).listFiles();
95         } catch (IOException e) {
96             logger.debug("IOException while trying to get workgroup member list", e);
97             return;
98         }
99
100         // Check found workgroup members for the Node devices
101         for (SmbFile s : workgroupMembers) {
102             String serverName = s.getServer();
103
104             // Check workgroup member for the Node device name match
105             Matcher m = AVISUAL_NAME_PATTERN.matcher(serverName);
106             if (!m.find()) {
107                 // Workgroup member server name doesn't match the Node device name pattern
108                 continue;
109             }
110
111             // Extract the Node serial number from device name
112             String nodeSerialNumber = m.group(1);
113
114             if (nodeSerialNumber != null) {
115                 logger.debug("Extracting the Node serial number failed");
116                 return;
117             }
118             // The Node Thing UID is serial number converted to lower case
119             ThingUID thingUID = new ThingUID(AirVisualNodeBindingConstants.THING_TYPE_AVNODE,
120                     nodeSerialNumber.toLowerCase());
121
122             try {
123                 // Get the Node address by name
124                 NbtAddress nodeNbtAddress = NbtAddress.getByName(serverName);
125                 if (nodeNbtAddress == null) {
126                     // The Node address not found by some reason, skip it
127                     continue;
128                 }
129
130                 // Create discovery result
131                 String nodeAddress = nodeNbtAddress.getInetAddress().getHostAddress();
132                 if (nodeAddress != null) {
133                     DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
134                             .withProperty(AirVisualNodeConfig.ADDRESS, nodeAddress)
135                             .withRepresentationProperty(AirVisualNodeConfig.ADDRESS)
136                             .withLabel("AirVisual Node (" + nodeSerialNumber + ")").build();
137                     thingDiscovered(result);
138                 } else {
139                     logger.debug("Getting the node address from the host failed");
140                 }
141             } catch (UnknownHostException e) {
142                 logger.debug("The Node address resolving failed ", e);
143             }
144
145         }
146     }
147 }