]> git.basschouten.com Git - openhab-addons.git/blob
0237ab088963fd3af940d1f35d8b5bcafcd3cc29
[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.nikohomecontrol.internal.protocol;
14
15 import java.io.IOException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.InetAddress;
19 import java.net.SocketTimeoutException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.core.util.HexUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Class {@link NikoHomeControlDiscover} is used to get the Niko Home Control IP-interfaces IP address for bridge
34  * discovery.
35  * <p>
36  * The constructor broadcasts a UDP packet with content 0x44 on port 10000.
37  * The Niko Home Control IP-interfaces responds to this UDP packet.
38  * The IP-address from the Niko Home Control IP-interface is then extracted from the response packet.
39  * The data content of the response packet is used as a unique identifier for the bridge.
40  *
41  * @author Mark Herwege - Initial Contribution
42  */
43 /**
44  * @author Mark Herwege - Initial Contribution
45  *
46  */
47 @NonNullByDefault
48 public final class NikoHomeControlDiscover {
49
50     private final Logger logger = LoggerFactory.getLogger(NikoHomeControlDiscover.class);
51
52     private List<String> nhcBridgeIds = new ArrayList<>();
53     private Map<String, InetAddress> inetAdresses = new HashMap<>();
54     private Map<String, Boolean> isNhcII = new HashMap<>();
55
56     /**
57      * Discover the list of Niko Home Control IP interfaces by broadcasting UDP packet 0x44 to port 10000. The IP
58      * interface will reply. The address of the IP interface is than derived from that response.
59      *
60      * @param broadcast Broadcast address of the network
61      * @throws IOException
62      */
63     public NikoHomeControlDiscover(String broadcast) throws IOException {
64         final byte[] discoverBuffer = { (byte) 0x44 };
65         final InetAddress broadcastAddr = InetAddress.getByName(broadcast);
66         final int broadcastPort = 10000;
67
68         DatagramPacket discoveryPacket = new DatagramPacket(discoverBuffer, discoverBuffer.length, broadcastAddr,
69                 broadcastPort);
70         byte[] buffer = new byte[1024];
71         DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
72
73         try (DatagramSocket datagramSocket = new DatagramSocket(null)) {
74             datagramSocket.setBroadcast(true);
75             datagramSocket.setSoTimeout(500);
76             datagramSocket.send(discoveryPacket);
77             try {
78                 while (true) {
79                     datagramSocket.receive(packet);
80                     logger.trace("bridge discovery response {}",
81                             HexUtils.bytesToHex(Arrays.copyOf(packet.getData(), packet.getLength())));
82                     if (isNhcController(packet)) {
83                         String bridgeId = setNhcBridgeId(packet);
84                         setIsNhcII(bridgeId, packet);
85                         InetAddress addr = setAddr(bridgeId, packet);
86                         logger.debug("IP address is {}, unique ID is {}", addr, bridgeId);
87                     }
88                 }
89             } catch (SocketTimeoutException e) {
90                 // all received, continue
91             }
92         }
93     }
94
95     /**
96      * @return the discovered nhcBridgeIds
97      */
98     public List<String> getNhcBridgeIds() {
99         return nhcBridgeIds;
100     }
101
102     /**
103      * @param bridgeId discovered bridgeId
104      * @return the addr, null if not in the list of discovered bridgeId's
105      */
106     public @Nullable InetAddress getAddr(String bridgeId) {
107         return inetAdresses.get(bridgeId);
108     }
109
110     /**
111      * Check if the UDP packet comes from a Niko Home Control controller. The response should start with 0x44.
112      *
113      * @param packet
114      * @return true if packet is from a Niko Home Control controller
115      */
116     private boolean isNhcController(DatagramPacket packet) {
117         byte[] packetData = packet.getData();
118         boolean isNhc = (packet.getLength() > 2) && (packetData[0] == 0x44);
119         // filter response from Gen1 touchscreens
120         boolean isController = isNhc && (packetData[1] == 0x3b) || (packetData[1] == 0x0c) || (packetData[1] == 0x0e);
121         if (!isController) {
122             logger.trace("not a NHC controller");
123         }
124         return isController;
125     }
126
127     /**
128      * Retrieves a unique ID from the returned datagram packet received after sending the UDP discovery message.
129      *
130      * @param packet
131      */
132     private String setNhcBridgeId(DatagramPacket packet) {
133         byte[] packetData = packet.getData();
134         int packetLength = packet.getLength();
135         packetLength = packetLength > 6 ? 6 : packetLength;
136         StringBuilder sb = new StringBuilder(packetLength);
137         for (int i = 0; i < packetLength; i++) {
138             sb.append(String.format("%02x", packetData[i]));
139         }
140         String bridgeId = sb.toString();
141         nhcBridgeIds.add(bridgeId);
142         return bridgeId;
143     }
144
145     /**
146      * Checks if this is a NHC II Connected Controller
147      *
148      * @param bridgeId
149      * @param packet
150      */
151     private void setIsNhcII(String bridgeId, DatagramPacket packet) {
152         byte[] packetData = packet.getData();
153         int packetLength = packet.getLength();
154         // The 16th byte in the packet is 2 for a NHC II Connected Controller
155         if ((packetLength >= 16) && (packetData[15] >= 2)) {
156             isNhcII.put(bridgeId, true);
157         } else {
158             isNhcII.put(bridgeId, false);
159         }
160     }
161
162     /**
163      * Sets the IP address retrieved from the packet response and keep it with bridgeId
164      *
165      * @param bridgeId
166      * @param packet
167      * @return address from packet response
168      */
169     private InetAddress setAddr(String bridgeId, DatagramPacket packet) {
170         InetAddress address = packet.getAddress();
171         inetAdresses.put(bridgeId, address);
172         return address;
173     }
174
175     /**
176      * Test if the installation is a Niko Home Control II installation
177      *
178      * @param bridgeId
179      * @return true if this is a Niko Home Control II installation
180      */
181     public boolean isNhcII(String bridgeId) {
182         return isNhcII.getOrDefault(bridgeId, false);
183     }
184 }