]> git.basschouten.com Git - openhab-addons.git/blob
678f60e3651707b985ebb5123321b44877ceaccd
[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.liquidcheck.internal.discovery;
14
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;
17
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;
30
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;
48
49 import com.google.gson.Gson;
50 import com.google.gson.JsonSyntaxException;
51
52 /**
53  * The {@link LiquidCheckDiscoveryService} class defines discovery service for the LiquidCheckBinding
54  *
55  * @author Marcel Goerentz - Initial contribution
56  */
57 @NonNullByDefault
58 @Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.liquidcheck")
59 public class LiquidCheckDiscoveryService extends AbstractDiscoveryService {
60
61     private static final int DISCOVER_TIMEOUT_SECONDS = 300;
62     private static final int REQUEST_TIMEOUT_MS = 10_000;
63
64     private final Logger logger = LoggerFactory.getLogger(this.getClass());
65     private final HttpClient httpClient;
66
67     @Activate
68     public LiquidCheckDiscoveryService(@Reference HttpClientFactory httpClientFactory) {
69         super(SUPPORTED_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, false);
70         httpClient = httpClientFactory.getCommonHttpClient();
71     }
72
73     /**
74      * Method for starting the scan
75      */
76     @Override
77     protected void startScan() {
78         scheduler.execute(liquidCheckDiscoveryRunnable());
79     }
80
81     /**
82      * Method to stop the scan
83      */
84     @Override
85     protected synchronized void stopScan() {
86         super.stopScan();
87         removeOlderResults(getTimestampOfLastScan());
88     }
89
90     /**
91      * Method for creating a Runnable to start a scan
92      * 
93      * @return the Runnable
94      */
95     protected Runnable liquidCheckDiscoveryRunnable() {
96         return () -> {
97             try {
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);
104                     try {
105                         ContentResponse response = request.send();
106                         if (response.getStatus() == 200) {
107                             CommData json = null;
108                             try {
109                                 json = new Gson().fromJson(response.getContentAsString(), CommData.class);
110                             } catch (JsonSyntaxException e) {
111                                 logger.debug("Json Syntax Exception!");
112                             }
113                             if (null != json) {
114                                 buildDiscoveryResult(json,
115                                         InetAddress.getByName(json.payload.wifi.station.hostname).isReachable(50));
116                             } else {
117                                 logger.debug("Response Object is null!");
118                             }
119                         }
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();
126                     }
127
128                 }
129             } catch (IOException e) {
130                 logger.debug("Message: {}", e.getMessage());
131             }
132         };
133     }
134
135     /**
136      * This Method retrieves all IPv4 addresses of the server
137      * 
138      * @return A list of all available IPv4 addresses that are registered
139      * @throws SocketException
140      */
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);
153                     }
154                 }
155             }
156         }
157         return addresses;
158     }
159
160     /**
161      * This method will find any active host in the network and return a list of them
162      * 
163      * @param addresses
164      * @return List of hosts
165      * @throws UnknownHostException
166      * @throws IOException
167      */
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];
173             int timeout = 50;
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));
179                     }
180                 }
181             }
182         }
183         return hosts;
184     }
185
186     /**
187      * This method builds a thing based on the response from the device
188      * 
189      * @param response
190      */
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);
196     }
197 }