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.zway.internal.discovery;
15 import java.io.IOException;
16 import java.net.HttpURLConnection;
17 import java.net.InetSocketAddress;
18 import java.net.InterfaceAddress;
19 import java.net.NetworkInterface;
20 import java.net.Socket;
21 import java.net.SocketException;
23 import java.util.Enumeration;
24 import java.util.regex.Pattern;
26 import org.apache.commons.net.util.SubnetUtils;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.openhab.binding.zway.internal.ZWayBindingConstants;
29 import org.openhab.core.config.discovery.AbstractDiscoveryService;
30 import org.openhab.core.config.discovery.DiscoveryResult;
31 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
32 import org.openhab.core.config.discovery.DiscoveryService;
33 import org.openhab.core.thing.ThingUID;
34 import org.osgi.service.component.annotations.Component;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * The {@link ZWayBridgeDiscoveryService} is responsible for device discovery.
41 * @author Patrick Hecker - Initial contribution
44 @Component(service = DiscoveryService.class, configurationPid = "discovery.zway")
45 public class ZWayBridgeDiscoveryService extends AbstractDiscoveryService {
47 private final Logger logger = LoggerFactory.getLogger(getClass());
49 private static final int SEARCH_TIME = 240;
51 public ZWayBridgeDiscoveryService() {
52 super(ZWayBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS, SEARCH_TIME);
53 logger.debug("Initializing ZWayBridgeDiscoveryService");
57 logger.debug("Starting scan for Z-Way Server");
59 ValidateIPV4 validator = new ValidateIPV4();
62 Enumeration<NetworkInterface> enumNetworkInterface = NetworkInterface.getNetworkInterfaces();
63 while (enumNetworkInterface.hasMoreElements()) {
64 NetworkInterface networkInterface = enumNetworkInterface.nextElement();
65 if (networkInterface.isUp() && !networkInterface.isVirtual() && !networkInterface.isLoopback()) {
66 for (InterfaceAddress address : networkInterface.getInterfaceAddresses()) {
67 if (validator.isValidIPV4(address.getAddress().getHostAddress())) {
68 String ipAddress = address.getAddress().getHostAddress();
69 Short prefix = address.getNetworkPrefixLength();
71 logger.debug("Scan IP address for Z-Way Server: {}", ipAddress);
73 // Search on localhost first
74 scheduler.execute(new ZWayServerScan(ipAddress));
76 String subnet = ipAddress + "/" + prefix;
77 SubnetUtils utils = new SubnetUtils(subnet);
78 String[] addresses = utils.getInfo().getAllAddresses();
80 for (String addressInSubnet : addresses) {
81 scheduler.execute(new ZWayServerScan(addressInSubnet));
87 } catch (SocketException e) {
88 logger.warn("Error occurred while searching Z-Way servers ({})", e.getMessage());
92 public boolean pingHost(String host, int port, int timeout) {
93 try (Socket socket = new Socket()) {
94 socket.connect(new InetSocketAddress(host, port), timeout);
96 } catch (IOException e) {
97 return false; // Either timeout or unreachable or failed DNS lookup.
101 public class ZWayServerScan implements Runnable {
102 private String ipAddress;
104 public ZWayServerScan(String ipAddress) {
105 this.ipAddress = ipAddress;
110 if (!pingHost(ipAddress, 8083, 500)) {
111 return; // Error occurred while searching Z-Way servers (Unreachable)
115 URL url = new URL("http://" + ipAddress + ":8083/ZAutomation/api/v1/status");
116 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
118 if (connection.getResponseCode() == 401) {
119 ThingUID thingUID = new ThingUID(ZWayBindingConstants.THING_TYPE_BRIDGE,
120 ipAddress.replaceAll("\\.", "_"));
122 // Attention: if is already present as thing in the ThingRegistry
123 // the configuration for thing will be updated!
124 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
125 .withProperty(ZWayBindingConstants.BRIDGE_CONFIG_ZWAY_SERVER_IP_ADDRESS, ipAddress)
126 .withLabel("Z-Way Server " + ipAddress).build();
127 thingDiscovered(discoveryResult);
129 } catch (IOException e) {
130 logger.warn("Discovery resulted in an unexpected exception", e);
136 protected void startScan() {
141 protected synchronized void stopScan() {
143 removeOlderResults(getTimestampOfLastScan());
147 private final String ipV4Regex = "^((0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)\\.){3}(0|1\\d?\\d?|2[0-4]?\\d?|25[0-5]?|[3-9]\\d?)$";
148 private Pattern ipV4Pattern = Pattern.compile(ipV4Regex);
150 public boolean isValidIPV4(final String s) {
151 return ipV4Pattern.matcher(s).matches();