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.tapocontrol.internal.api;
15 import static org.openhab.binding.tapocontrol.internal.helpers.TapoUtils.*;
17 import java.net.DatagramPacket;
18 import java.net.DatagramSocket;
19 import java.net.InetAddress;
20 import java.net.SocketTimeoutException;
21 import java.security.KeyPair;
22 import java.security.KeyPairGenerator;
23 import java.security.SecureRandom;
24 import java.security.interfaces.RSAPrivateKey;
25 import java.security.interfaces.RSAPublicKey;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.openhab.binding.tapocontrol.internal.helpers.TapoCredentials;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
32 import com.google.gson.JsonArray;
33 import com.google.gson.JsonObject;
36 * Handler class for TAPO Smart Home device UDP-connections.
39 * @author Christian Wild - Initial contribution
42 public class TapoUDP {
43 private final Logger logger = LoggerFactory.getLogger(TapoUDP.class);
44 private static final Integer BROADCAST_TIMEOUT_MS = 5000;
45 private static final Integer BROADCAST_DISCOVERY_PORT = 20002; // int
46 private static final String BROADCAST_IP = "255.255.255.255";
47 private static final String DISCOVERY_MESSAGE_KEY = "rsa_key";
48 private static final String DISCOVERY_MESSAGE_START_BYTES = "0200000101e5110001cb8c577dd7deb8";
49 private static final Integer BUFFER_SIZE = 501;
50 private TapoCredentials credentials;
52 public TapoUDP(TapoCredentials credentials) {
53 this.credentials = credentials; // new TapoCredentials();
56 public JsonArray udpScan() {
58 DatagramSocket udpSocket = new DatagramSocket();
59 udpSocket.setSoTimeout(BROADCAST_TIMEOUT_MS);
60 udpSocket.setBroadcast(true);
62 /* create payload for handshake */
63 String publicKey = credentials.getPublicKey();
64 publicKey = generateOwnRSAKey(); // credentials.getPublicKey();
65 JsonObject parameters = new JsonObject();
66 JsonObject messageObject = new JsonObject();
67 parameters.addProperty(DISCOVERY_MESSAGE_KEY, publicKey);
68 messageObject.add("params", parameters);
70 String discoveryMessage = messageObject.toString();
72 byte[] startByte = hexStringToByteArray(DISCOVERY_MESSAGE_START_BYTES);
73 byte[] message = discoveryMessage.getBytes("UTF-8");
74 byte[] sendData = new byte[startByte.length + message.length];
75 System.arraycopy(startByte, 0, sendData, 0, startByte.length);
76 System.arraycopy(message, 0, sendData, startByte.length, message.length);
78 DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length,
79 InetAddress.getByName(BROADCAST_IP), BROADCAST_DISCOVERY_PORT);
81 udpSocket.send(sendPacket);
84 // Wait for a response
85 byte[] recvBuf = new byte[BUFFER_SIZE];
86 DatagramPacket receivePacket;
88 receivePacket = new DatagramPacket(recvBuf, recvBuf.length);
89 udpSocket.receive(receivePacket);
90 } catch (SocketTimeoutException e) {
92 return new JsonArray();
93 } catch (Exception e) {
95 return new JsonArray();
98 // Check if the message is correct
99 String responseMessage = new String(receivePacket.getData(), "UTF-8").trim();
101 if (responseMessage.length() == 0) {
104 String addressBC = receivePacket.getAddress().getHostAddress();
105 gotDeviceAdress(addressBC);
107 } catch (Exception e) {
110 return new JsonArray();
113 private void gotDeviceAdress(String ipAddress) {
117 private String generateOwnRSAKey() {
119 logger.trace("generating new keypair");
120 KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
121 instance.initialize(1536, new SecureRandom());
122 KeyPair generateKeyPair = instance.generateKeyPair();
124 String publicKey = new String(java.util.Base64.getMimeEncoder()
125 .encode(((RSAPublicKey) generateKeyPair.getPublic()).getEncoded()));
126 String privateKey = new String(java.util.Base64.getMimeEncoder()
127 .encode(((RSAPrivateKey) generateKeyPair.getPrivate()).getEncoded()));
128 logger.trace("new privateKey: '{}'", privateKey);
129 logger.trace("new ublicKey: '{}'", publicKey);
131 return String.format("-----BEGIN PUBLIC KEY-----%n%s%n-----END PUBLIC KEY-----%n", publicKey);
133 } catch (Exception e) {
134 // couldn't generate own rsa key