2 * Copyright (c) 2010-2024 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.gree.internal.discovery;
15 import static org.openhab.binding.gree.internal.GreeBindingConstants.*;
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;
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;
40 import com.google.gson.Gson;
41 import com.google.gson.GsonBuilder;
42 import com.google.gson.JsonSyntaxException;
45 * The GreeDeviceFinder provides functionality for searching for GREE Airconditioners on the network and keeping a list
48 * @author John Cunha - Initial contribution
49 * @author Markus Michels - Refactoring, adapted to OH 2.5x
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();
57 protected final InetAddress ipAddress = InetAddress.getLoopbackAddress();
58 protected Map<String, GreeAirDevice> deviceTable = new HashMap<>();
61 public GreeDeviceFinder() {
64 public void scan(DatagramSocket clientSocket, String broadcastAddress, boolean scanNetwork) throws GreeException {
65 InetAddress ipAddress;
67 ipAddress = InetAddress.getByName(broadcastAddress);
68 } catch (UnknownHostException e) {
69 throw new GreeException("Unknown host or invalid IP address", e);
72 byte[] sendData = new byte[1024];
73 byte[] receiveData = new byte[1024];
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);
85 // Loop for respnses from devices until we get a timeout.
86 int retries = scanNetwork ? MAX_SCAN_CYCLES : 1;
87 while ((retries > 0)) {
89 DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
91 clientSocket.receive(receivePacket);
92 InetAddress remoteAddress = receivePacket.getAddress();
93 int remotePort = receivePacket.getPort();
96 GreeScanResponseDTO scanResponseGson = fromJson(receivePacket, GreeScanResponseDTO.class);
98 // If there was no pack, ignore the response
99 if (scanResponseGson.pack == null) {
100 logger.debug("Invalid packet format, ignore");
105 // Decrypt message - a GreeException is thrown when something went wrong
106 String decryptedMsg = scanResponseGson.decryptedPack = GreeCryptoUtil.decrypt(scanResponseGson);
108 logger.debug("Response received from address {}: {}", remoteAddress.getHostAddress(), decryptedMsg);
110 // Create the JSON to hold the response values
111 scanResponseGson.packJson = GSON.fromJson(decryptedMsg, GreeScanResponsePackDTO.class);
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);
120 logger.debug("Unit discovered, but brand is not GREE");
122 } catch (SocketTimeoutException e) {
124 } catch (IOException | JsonSyntaxException e) {
127 throw new GreeException("Exception on device scan", e);
131 } catch (IOException e) {
132 throw new GreeException("I/O exception during device scan", e);
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);
141 public void addDevice(GreeAirDevice newDevice) {
142 deviceTable.put(newDevice.getId(), newDevice);
145 public @Nullable GreeAirDevice getDevice(String id) {
146 return deviceTable.get(id);
149 public Map<String, GreeAirDevice> getDevices() {
153 public @Nullable GreeAirDevice getDeviceByIPAddress(String ipAddress) {
154 for (GreeAirDevice currDevice : deviceTable.values()) {
155 if (currDevice.getAddress().getHostAddress().equals(ipAddress)) {
162 public int getScannedDeviceCount() {
163 return deviceTable.size();