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.liquidcheck.internal.discovery;
15 import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.SUPPORTED_THING_TYPES_UIDS;
16 import static org.openhab.binding.liquidcheck.internal.LiquidCheckBindingConstants.THING_TYPE_LIQUID_CHECK;
18 import java.io.IOException;
19 import java.net.Inet4Address;
20 import java.net.InetAddress;
21 import java.net.NetworkInterface;
22 import java.net.SocketException;
23 import java.net.UnknownHostException;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.TimeUnit;
29 import java.util.concurrent.TimeoutException;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jetty.client.HttpClient;
33 import org.eclipse.jetty.client.api.ContentResponse;
34 import org.eclipse.jetty.client.api.Request;
35 import org.eclipse.jetty.http.HttpMethod;
36 import org.openhab.binding.liquidcheck.internal.json.CommData;
37 import org.openhab.core.config.discovery.AbstractDiscoveryService;
38 import org.openhab.core.config.discovery.DiscoveryResult;
39 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
40 import org.openhab.core.config.discovery.DiscoveryService;
41 import org.openhab.core.io.net.http.HttpClientFactory;
42 import org.openhab.core.thing.ThingUID;
43 import org.osgi.service.component.annotations.Activate;
44 import org.osgi.service.component.annotations.Component;
45 import org.osgi.service.component.annotations.Reference;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import com.google.gson.Gson;
50 import com.google.gson.JsonSyntaxException;
53 * The {@link LiquidCheckDiscoveryService} class defines discovery service for the LiquidCheckBinding
55 * @author Marcel Goerentz - Initial contribution
58 @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.liquidcheck")
59 public class LiquidCheckDiscoveryService extends AbstractDiscoveryService {
61 private static final int DISCOVER_TIMEOUT_SECONDS = 300;
62 private static final int REQUEST_TIMEOUT_MS = 10_000;
64 private final Logger logger = LoggerFactory.getLogger(this.getClass());
65 private final HttpClient httpClient;
68 public LiquidCheckDiscoveryService(@Reference HttpClientFactory httpClientFactory) {
69 super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
70 httpClient = httpClientFactory.getCommonHttpClient();
74 * Method for starting the scan
77 protected void startScan() {
78 scheduler.execute(liquidCheckDiscoveryRunnable());
82 * Method to stop the scan
85 protected synchronized void stopScan() {
87 removeOlderResults(getTimestampOfLastScan());
91 * Method for creating a Runnable to start a scan
93 * @return the Runnable
95 protected Runnable liquidCheckDiscoveryRunnable() {
98 List<InetAddress> addresses = getIPv4Addresses();
99 List<InetAddress> hosts = findActiveHosts(addresses);
100 for (InetAddress host : hosts) {
101 Request request = httpClient.newRequest("http://" + host.getHostAddress() + "/infos.json")
102 .timeout(REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS).method(HttpMethod.GET)
103 .followRedirects(false);
105 ContentResponse response = request.send();
106 if (response.getStatus() == 200) {
107 CommData json = null;
109 json = new Gson().fromJson(response.getContentAsString(), CommData.class);
110 } catch (JsonSyntaxException e) {
111 logger.debug("Json Syntax Exception!");
114 buildDiscoveryResult(json,
115 InetAddress.getByName(json.payload.wifi.station.hostname).isReachable(50));
117 logger.debug("Response Object is null!");
120 } catch (TimeoutException e) {
121 logger.debug("TimeOut: {}", e.getMessage());
122 } catch (ExecutionException e) {
123 logger.debug("ExecutionException: {}", e.getMessage());
124 } catch (InterruptedException e) {
125 Thread.currentThread().interrupt();
129 } catch (IOException e) {
130 logger.debug("Message: {}", e.getMessage());
136 * This Method retrieves all IPv4 addresses of the server
138 * @return A list of all available IPv4 addresses that are registered
139 * @throws SocketException
141 private List<InetAddress> getIPv4Addresses() throws SocketException {
142 Iterator<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces().asIterator();
143 List<InetAddress> addresses = new ArrayList<>();
144 // Get IPv4 addresses from all network interfaces
145 if (null != networkInterfaces) {
146 while (networkInterfaces.hasNext()) {
147 NetworkInterface currentNetworkInterface = networkInterfaces.next();
148 Iterator<InetAddress> inetAddresses = currentNetworkInterface.getInetAddresses().asIterator();
149 while (inetAddresses.hasNext()) {
150 InetAddress currentAddress = inetAddresses.next();
151 if (currentAddress instanceof Inet4Address && !currentAddress.isLoopbackAddress()) {
152 addresses.add(currentAddress);
161 * This method will find any active host in the network and return a list of them
164 * @return List of hosts
165 * @throws UnknownHostException
166 * @throws IOException
168 private List<InetAddress> findActiveHosts(List<InetAddress> addresses) throws UnknownHostException, IOException {
169 List<InetAddress> hosts = new ArrayList<>();
170 for (InetAddress inetAddress : addresses) {
171 String[] addressStrings = inetAddress.getHostAddress().split("[.]");
172 String subnet = addressStrings[0] + "." + addressStrings[1] + "." + addressStrings[2];
174 for (int i = 0; i < 255; i++) {
175 String host = subnet + "." + i;
176 if (!inetAddress.getHostAddress().equals(host)) {
177 if (InetAddress.getByName(host).isReachable(timeout)) {
178 hosts.add(InetAddress.getByName(host));
187 * This method builds a thing based on the response from the device
191 private void buildDiscoveryResult(CommData response, Boolean isHostname) {
192 ThingUID thingUID = new ThingUID(THING_TYPE_LIQUID_CHECK, response.payload.device.uuid);
193 DiscoveryResult dResult = DiscoveryResultBuilder.create(thingUID)
194 .withProperties(response.createPropertyMap(isHostname)).withLabel(response.payload.device.name).build();
195 thingDiscovered(dResult);