]> git.basschouten.com Git - openhab-addons.git/blob
bc277d7034e61748e8d89a69744af93aa4f96290
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.gree.internal.discovery;
14
15 import static org.openhab.binding.gree.internal.GreeBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.DatagramPacket;
19 import java.net.DatagramSocket;
20 import java.net.InetAddress;
21 import java.net.SocketTimeoutException;
22 import java.net.UnknownHostException;
23 import java.nio.charset.StandardCharsets;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.gree.internal.GreeCryptoUtil;
30 import org.openhab.binding.gree.internal.GreeException;
31 import org.openhab.binding.gree.internal.gson.GreeScanRequestDTO;
32 import org.openhab.binding.gree.internal.gson.GreeScanResponseDTO;
33 import org.openhab.binding.gree.internal.gson.GreeScanResponsePackDTO;
34 import org.openhab.binding.gree.internal.handler.GreeAirDevice;
35 import org.osgi.service.component.annotations.Activate;
36 import org.osgi.service.component.annotations.Component;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.gson.Gson;
41 import com.google.gson.GsonBuilder;
42 import com.google.gson.JsonSyntaxException;
43
44 /**
45  * The GreeDeviceFinder provides functionality for searching for GREE Airconditioners on the network and keeping a list
46  * of found devices.
47  *
48  * @author John Cunha - Initial contribution
49  * @author Markus Michels - Refactoring, adapted to OH 2.5x
50  */
51 @NonNullByDefault
52 @Component(service = GreeDeviceFinder.class, configurationPid = "devicefinder.gree")
53 public class GreeDeviceFinder {
54     private final Logger logger = LoggerFactory.getLogger(GreeDeviceFinder.class);
55     private static final Gson GSON = (new GsonBuilder()).create();
56
57     protected final InetAddress ipAddress = InetAddress.getLoopbackAddress();
58     protected Map<String, GreeAirDevice> deviceTable = new HashMap<>();
59
60     @Activate
61     public GreeDeviceFinder() {
62     }
63
64     public void scan(DatagramSocket clientSocket, String broadcastAddress, boolean scanNetwork) throws GreeException {
65         InetAddress ipAddress;
66         try {
67             ipAddress = InetAddress.getByName(broadcastAddress);
68         } catch (UnknownHostException e) {
69             throw new GreeException("Unknown host or invalid IP address", e);
70         }
71         try {
72             byte[] sendData = new byte[1024];
73             byte[] receiveData = new byte[1024];
74
75             // Send the Scan message
76             GreeScanRequestDTO scanGson = new GreeScanRequestDTO();
77             scanGson.t = GREE_CMDT_SCAN;
78             String scanReq = GSON.toJson(scanGson);
79             sendData = scanReq.getBytes(StandardCharsets.UTF_8);
80             logger.debug("Sending scan packet to {}", ipAddress.getHostAddress());
81             clientSocket.setSoTimeout(DISCOVERY_TIMEOUT_MS);
82             DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress, DISCOVERY_TIMEOUT_MS);
83             clientSocket.send(sendPacket);
84
85             // Loop for respnses from devices until we get a timeout.
86             int retries = scanNetwork ? MAX_SCAN_CYCLES : 1;
87             while ((retries > 0)) {
88                 // Receive a response
89                 DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
90                 try {
91                     clientSocket.receive(receivePacket);
92                     InetAddress remoteAddress = receivePacket.getAddress();
93                     int remotePort = receivePacket.getPort();
94
95                     // Read the response
96                     GreeScanResponseDTO scanResponseGson = fromJson(receivePacket, GreeScanResponseDTO.class);
97
98                     // If there was no pack, ignore the response
99                     if (scanResponseGson.pack == null) {
100                         logger.debug("Invalid packet format, ignore");
101                         retries--;
102                         continue;
103                     }
104
105                     // Decrypt message - a GreeException is thrown when something went wrong
106                     String decryptedMsg = scanResponseGson.decryptedPack = GreeCryptoUtil.decrypt(scanResponseGson);
107
108                     logger.debug("Response received from address {}: {}", remoteAddress.getHostAddress(), decryptedMsg);
109
110                     // Create the JSON to hold the response values
111                     scanResponseGson.packJson = GSON.fromJson(decryptedMsg, GreeScanResponsePackDTO.class);
112
113                     // Now make sure the device is reported as a Gree device
114                     if ("gree".equalsIgnoreCase(scanResponseGson.packJson.brand)) {
115                         // Create a new GreeDevice
116                         logger.debug("Discovered device at {}:{}", remoteAddress.getHostAddress(), remotePort);
117                         GreeAirDevice newDevice = new GreeAirDevice(remoteAddress, remotePort, scanResponseGson);
118                         addDevice(newDevice);
119                     } else {
120                         logger.debug("Unit discovered, but brand is not GREE");
121                     }
122                 } catch (SocketTimeoutException e) {
123                     return;
124                 } catch (IOException | JsonSyntaxException e) {
125                     retries--;
126                     if (retries == 0) {
127                         throw new GreeException("Exception on device scan", e);
128                     }
129                 }
130             }
131         } catch (IOException e) {
132             throw new GreeException("I/O exception during device scan", e);
133         }
134     }
135
136     private <T> T fromJson(DatagramPacket packet, Class<T> classOfT) {
137         String json = new String(packet.getData(), StandardCharsets.UTF_8).replace("\\u0000", "").trim();
138         return GSON.fromJson(json, classOfT);
139     }
140
141     public void addDevice(GreeAirDevice newDevice) {
142         deviceTable.put(newDevice.getId(), newDevice);
143     }
144
145     public @Nullable GreeAirDevice getDevice(String id) {
146         return deviceTable.get(id);
147     }
148
149     public Map<String, GreeAirDevice> getDevices() {
150         return deviceTable;
151     }
152
153     public @Nullable GreeAirDevice getDeviceByIPAddress(String ipAddress) {
154         for (GreeAirDevice currDevice : deviceTable.values()) {
155             if (currDevice.getAddress().getHostAddress().equals(ipAddress)) {
156                 return currDevice;
157             }
158         }
159         return null;
160     }
161
162     public int getScannedDeviceCount() {
163         return deviceTable.size();
164     }
165 }