]> git.basschouten.com Git - openhab-addons.git/blob
fa3e17aa96ce04581659ee6e6b90bbf282d34f2f
[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.souliss.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.InetSocketAddress;
20 import java.net.InterfaceAddress;
21 import java.net.NetworkInterface;
22 import java.net.SocketException;
23 import java.net.UnknownHostException;
24 import java.nio.channels.DatagramChannel;
25 import java.util.ArrayList;
26 import java.util.Enumeration;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.souliss.internal.SoulissUDPConstants;
31 import org.openhab.binding.souliss.internal.config.GatewayConfig;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * This class provide to construct MaCaco and UDP frame
37  *
38  * @author Tonino Fazio - Initial contribution
39  * @author Luca Calcaterra - Refactor for OH3
40  * @author Alessandro Del Pex - Souliss App
41  */
42 @NonNullByDefault
43 public class CommonCommands {
44
45     private final Logger logger = LoggerFactory.getLogger(CommonCommands.class);
46
47     private static final String LITERAL_SEND_FRAME = "sendFORCEFrame - {}, soulissNodeIPAddressOnLAN: {}";
48
49     public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand) {
50         sendFORCEFrame(gwConfig, idNode, slot, shortCommand, null, null, null);
51     }
52
53     /*
54      * used for set dimmer value. It set command at first byte and dimmerVal to
55      * second byte
56      */
57     public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand, byte lDimmer) {
58         sendFORCEFrame(gwConfig, idNode, slot, shortCommand, lDimmer, null, null);
59     }
60
61     /*
62      * send force frame with command and RGB value
63      */
64     public final void sendFORCEFrame(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand,
65             @Nullable Byte byte1, @Nullable Byte byte2, @Nullable Byte byte3) {
66         ArrayList<Byte> macacoFrame = new ArrayList<>();
67         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE);
68
69         // PUTIN, STARTOFFEST, NUMBEROF
70         // PUTIN
71         macacoFrame.add((byte) 0x0);
72         // PUTIN
73         macacoFrame.add((byte) 0x0);
74
75         macacoFrame.add((byte) (idNode));// Start Offset
76
77         if (byte1 == null && byte2 == null && byte3 == null) {
78             // Number Of
79             macacoFrame.add((byte) ((byte) slot + 1));
80         } else if (byte2 == null && byte3 == null) {
81             // Number Of byte of payload= command + set byte
82             macacoFrame.add((byte) ((byte) slot + 2));
83         } else {
84             // Number Of byte of payload= OnOFF + Red + Green + Blu
85             macacoFrame.add((byte) ((byte) slot + 4));
86         }
87
88         for (var i = 0; i <= slot - 1; i++) {
89             // I set the bytes preceding the slot to be modified to zero
90             macacoFrame.add((byte) 00);
91         }
92         // PAYLOAD
93         macacoFrame.add(shortCommand);
94
95         if (byte1 != null && byte2 != null && byte3 != null) {
96             // PAYLOAD RED
97             macacoFrame.add(byte1);
98             // PAYLOAD GREEN
99             macacoFrame.add(byte2);
100             // PAYLOAD BLUE
101             macacoFrame.add(byte3);
102         } else if (byte1 != null) {
103             // PAYLOAD DIMMER
104             macacoFrame.add(byte1);
105         }
106
107         logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig);
108         queueToDispatcher(macacoFrame, gwConfig);
109     }
110
111     /*
112      * T61 send frame to push the setpoint value
113      */
114
115     public final void sendFORCEFrameT61SetPoint(GatewayConfig gwConfig, int idNode, int slot, Byte byte1, Byte byte2) {
116         ArrayList<Byte> macacoFrame = new ArrayList<>();
117         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE);
118
119         // PUTIN, STARTOFFEST, NUMBEROF
120         // PUTIN
121         macacoFrame.add((byte) 0x00);
122         // PUTIN
123         macacoFrame.add((byte) 0x00);
124         // Start Offset
125         macacoFrame.add((byte) (idNode));
126         // Number Of byte of payload= command + set byte
127         macacoFrame.add((byte) ((byte) slot + 2));
128
129         for (var i = 0; i <= slot - 1; i++) {
130             // I set the bytes preceding the slot to be modified to zero
131             macacoFrame.add((byte) 00);
132         }
133         // PAYLOAD
134         // first byte Setpoint Value
135         macacoFrame.add(byte1);
136         // second byte Setpoint Value
137         macacoFrame.add(byte2);
138
139         logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig);
140
141         queueToDispatcher(macacoFrame, gwConfig);
142     }
143
144     /*
145      * T31 send force frame with command and setpoint float
146      */
147     public final void sendFORCEFrameT31SetPoint(GatewayConfig gwConfig, int idNode, int slot, byte shortCommand,
148             Byte byte1, Byte byte2) {
149         ArrayList<Byte> macacoFrame = new ArrayList<>();
150         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE);
151
152         // PUTIN, STARTOFFEST, NUMBEROF
153         // PUTIN
154         macacoFrame.add((byte) 0x00);
155         // PUTIN
156         macacoFrame.add((byte) 0x00);
157
158         // Start Offset
159         macacoFrame.add((byte) (idNode));
160         // Number Of byte of payload= command + set byte
161         macacoFrame.add((byte) ((byte) slot + 5));
162
163         for (var i = 0; i <= slot - 1; i++) {
164             // prvious byte to zero
165             macacoFrame.add((byte) 00);
166             // slot to be changed
167         }
168         // PAYLOAD
169         macacoFrame.add(shortCommand);
170
171         // Empty - Temperature Measured Value
172         macacoFrame.add((byte) 0x0);
173         // Empty - Temperature Measured Value
174         macacoFrame.add((byte) 0x0);
175         // Temperature Setpoint Value
176         macacoFrame.add(byte1);
177         // Temperature Setpoint Value
178         macacoFrame.add(byte2);
179
180         logger.debug(LITERAL_SEND_FRAME, macacoToString(macacoFrame), gwConfig);
181         queueToDispatcher(macacoFrame, gwConfig);
182     }
183
184     public final void sendDBStructFrame(GatewayConfig gwConfig) {
185         ArrayList<Byte> macacoFrame = new ArrayList<>();
186         macacoFrame.add((byte) SoulissUDPConstants.SOULISS_UDP_FUNCTION_DBSTRUCT_REQ);
187         // PUTIN
188         macacoFrame.add((byte) 0x0);
189         // PUTIN
190         macacoFrame.add((byte) 0x0);
191         // Start Offset
192         macacoFrame.add((byte) 0x0);
193         // Number Of
194         macacoFrame.add((byte) 0x0);
195
196         logger.debug("sendDBStructFrame - {}, soulissNodeIPAddressOnLAN: {}", macacoToString(macacoFrame), gwConfig);
197         queueToDispatcher(macacoFrame, gwConfig);
198     }
199
200     /*
201      * Queue command to Dispatcher (for securesend retransmission)
202      */
203     private final void queueToDispatcher(ArrayList<Byte> macacoFrame, GatewayConfig gwConfig) {
204         ArrayList<Byte> buf = buildVNetFrame(macacoFrame, gwConfig.gatewayLanAddress, (byte) gwConfig.userIndex,
205                 (byte) gwConfig.nodeIndex);
206         byte[] merd = toByteArray(buf);
207
208         InetAddress serverAddr;
209         try {
210             serverAddr = gwConfig.gatewayWanAddress.isEmpty() ? InetAddress.getByName(gwConfig.gatewayLanAddress)
211                     : InetAddress.getByName(gwConfig.gatewayWanAddress);
212             var packet = new DatagramPacket(merd, merd.length, serverAddr,
213                     SoulissUDPConstants.SOULISS_GATEWAY_DEFAULT_PORT);
214             SendDispatcherRunnable.put(packet, logger);
215         } catch (IOException e) {
216             logger.warn("Error: {} ", e.getMessage());
217         }
218     }
219
220     /*
221      * send broadcast UDP frame - unused in this version
222      */
223     private final void sendBroadcastNow(ArrayList<Byte> macacoFrame) {
224         byte iUserIndex = (byte) 120;
225         byte iNodeIndex = (byte) 70;
226
227         // Broadcast the message over all the network interfaces
228         Enumeration<@Nullable NetworkInterface> interfaces;
229         DatagramSocket sender = null;
230         try {
231             interfaces = NetworkInterface.getNetworkInterfaces();
232
233             while (interfaces.hasMoreElements()) {
234                 var networkInterface = interfaces.nextElement();
235                 if (networkInterface != null) {
236                     if (networkInterface.isLoopback() || !networkInterface.isUp()) {
237                         continue;
238                     }
239                     for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
240                         var broadcast = new InetAddress[3];
241                         broadcast[0] = InetAddress.getByName("224.0.0.1");
242                         broadcast[1] = InetAddress.getByName("255.255.255.255");
243                         broadcast[2] = interfaceAddress.getBroadcast();
244                         for (InetAddress bc : broadcast) {
245                             // Send the broadcast package!
246                             if (bc != null) {
247                                 try {
248                                     ArrayList<Byte> buf = buildVNetFrame(macacoFrame, "255.255.255.255", iUserIndex,
249                                             iNodeIndex);
250                                     byte[] merd = toByteArray(buf);
251                                     var packet = new DatagramPacket(merd, merd.length, bc,
252                                             SoulissUDPConstants.SOULISS_GATEWAY_DEFAULT_PORT);
253                                     // Datagramsocket creation
254                                     var channel = DatagramChannel.open();
255                                     sender = channel.socket();
256                                     sender.setReuseAddress(true);
257                                     sender.setBroadcast(true);
258
259                                     var sa = new InetSocketAddress(230);
260                                     sender.bind(sa);
261
262                                     sender.send(packet);
263                                     logger.debug("Request packet sent to: {} Interface: {}", bc.getHostAddress(),
264                                             networkInterface.getDisplayName());
265
266                                 } catch (IOException e) {
267                                     logger.debug("IO error: {}", e.getMessage());
268                                 } catch (Exception e) {
269                                     logger.debug("{}", e.getMessage(), e);
270                                 } finally {
271                                     if ((sender != null) && (!sender.isClosed())) {
272                                         sender.close();
273                                     }
274                                 }
275                             }
276                         }
277                     }
278                 }
279             }
280         } catch (SocketException | UnknownHostException e) {
281             logger.warn("{}", e.getMessage());
282         }
283     }
284
285     /*
286      * Build VNet Frame
287      */
288     private final ArrayList<Byte> buildVNetFrame(ArrayList<Byte> macacoFrame2, @Nullable String gatewayLanAddress,
289             byte iUserIndex, byte iNodeIndex) {
290         if (gatewayLanAddress != null) {
291             ArrayList<Byte> frame = new ArrayList<>();
292             InetAddress ip;
293             try {
294                 ip = InetAddress.getByName(gatewayLanAddress);
295             } catch (UnknownHostException e) {
296                 logger.warn("{}", e.getMessage());
297                 return frame;
298             }
299             byte[] dude = ip.getAddress();
300
301             // Port
302             frame.add((byte) 23);
303             // es 192.168.1.XX BOARD
304             frame.add((byte) (dude[3] & 0xFF));
305
306             // n broadcast : communication by Ip
307             // 255.255.255.255 to associate vNet 0xFFFF address.
308             frame.add(gatewayLanAddress.compareTo(SoulissUDPConstants.BROADCASTADDR) == 0 ? dude[2] : 0);
309             // NODE INDEX - source vNet address User Interface
310             frame.add(iNodeIndex);
311             // USER INDEX - source vNet address User Interface
312             frame.add(iUserIndex);
313
314             // adds the calculation in the head
315             // Length
316             frame.add(0, (byte) (frame.size() + macacoFrame2.size() + 1));
317             // Length Check 2
318             frame.add(0, (byte) (frame.size() + macacoFrame2.size() + 1));
319
320             frame.addAll(macacoFrame2);
321             return frame;
322         } else {
323             throw new IllegalArgumentException("Cannot build VNet Frame . Null Souliss IP address");
324         }
325     }
326
327     /**
328      * Builds old-school byte array
329      *
330      * @param buf
331      * @return
332      */
333     private final byte[] toByteArray(ArrayList<Byte> buf) {
334         var merd = new byte[buf.size()];
335         for (var i = 0; i < buf.size(); i++) {
336             merd[i] = buf.get(i);
337         }
338         return merd;
339     }
340
341     /**
342      * Build MULTICAST FORCE Frame
343      */
344     public final void sendMULTICASTFORCEFrame(GatewayConfig gwConfig, byte typical, byte shortCommand) {
345         ArrayList<Byte> macacoFrame = new ArrayList<>();
346         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_FORCE_MASSIVE);
347
348         // PUTIN, STARTOFFEST, NUMBEROF
349         // PUTIN
350         macacoFrame.add((byte) 0x0);
351         // PUTIN
352         macacoFrame.add((byte) 0x0);
353         // Start Offset
354         macacoFrame.add(typical);
355         // Number Of
356         macacoFrame.add((byte) 1);
357         // PAYLOAD
358         macacoFrame.add(shortCommand);
359         logger.debug("sendMULTICASTFORCEFrame - {}, soulissNodeIPAddressOnLAN: {}", macacoToString(macacoFrame),
360                 gwConfig);
361         queueToDispatcher(macacoFrame, gwConfig);
362     }
363
364     /**
365      * Build PING Frame
366      */
367     public final void sendPing(@Nullable GatewayConfig gwConfig) {
368         if (gwConfig != null) {
369             ArrayList<Byte> macacoFrame = new ArrayList<>();
370             macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_PING_REQ);
371
372             // PUTIN, STARTOFFEST, NUMBEROF
373             // PUTIN
374             macacoFrame.add((byte) 0x00);
375             // PUTIN
376             macacoFrame.add((byte) 0x00);
377             // Start Offset
378             macacoFrame.add((byte) 0x00);
379             // Number Of
380             macacoFrame.add((byte) 0x00);
381             logger.debug("sendPing - {}, IP: {} ", macacoToString(macacoFrame), gwConfig);
382             queueToDispatcher(macacoFrame, gwConfig);
383         } else {
384             logger.warn("Cannot send Souliss Ping -  Ip null");
385         }
386     }
387
388     /**
389      * Build BROADCAST PING Frame
390      */
391     public final void sendBroadcastGatewayDiscover() {
392         ArrayList<Byte> macacoFrame = new ArrayList<>();
393         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_DISCOVER_GW_NODE_BCAST_REQ);
394
395         // PUTIN, STARTOFFEST, NUMBEROF
396         // PUTIN
397         macacoFrame.add((byte) 0x05);
398         // PUTIN
399         macacoFrame.add((byte) 0x00);
400         // Start Offset
401         macacoFrame.add((byte) 0x00);
402         // Number Of
403         macacoFrame.add((byte) 0x00);
404         logger.debug("sendBroadcastPing - {} ", macacoToString(macacoFrame));
405         sendBroadcastNow(macacoFrame);
406     }
407
408     /**
409      * Build SUBSCRIPTION Frame
410      */
411     public final void sendSUBSCRIPTIONframe(GatewayConfig gwConfig, int iNodes) {
412         ArrayList<Byte> macacoFrame = new ArrayList<>();
413         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_SUBSCRIBE_REQ);
414
415         // PUTIN, STARTOFFEST, NUMBEROF
416         // PUTIN
417         macacoFrame.add((byte) 0x00);
418         // PUTIN
419         macacoFrame.add((byte) 0x00);
420         macacoFrame.add((byte) 0x00);
421
422         macacoFrame.add((byte) iNodes);
423         logger.debug("sendSUBSCRIPTIONframe - {}, IP: {} ", macacoToString(macacoFrame), gwConfig);
424         queueToDispatcher(macacoFrame, gwConfig);
425     }
426
427     /**
428      * Build HEALTHY REQUEST Frame
429      */
430     public final void sendHealthyRequestFrame(GatewayConfig gwConfig, int iNodes) {
431         ArrayList<Byte> macacoFrame = new ArrayList<>();
432         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_HEALTHY_REQ);
433
434         // PUTIN, STARTOFFSET, NUMBEROF
435         // PUTIN
436         macacoFrame.add((byte) 0x00);
437         // PUTIN
438         macacoFrame.add((byte) 0x00);
439         macacoFrame.add((byte) 0x00);
440         macacoFrame.add((byte) iNodes);
441         logger.debug("sendHealthyRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig);
442         queueToDispatcher(macacoFrame, gwConfig);
443     }
444
445     /**
446      * Build TYPICAL REQUEST Frame
447      */
448     public final void sendTypicalRequestFrame(GatewayConfig gwConfig, int nodes) {
449         ArrayList<Byte> macacoFrame = new ArrayList<>();
450         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_TYP_REQ);
451         // PUTIN, STARTOFFEST, NUMBEROF
452         // PUTIN
453         macacoFrame.add((byte) 0x00);
454         // PUTIN
455         macacoFrame.add((byte) 0x00);
456         // startOffset
457         macacoFrame.add((byte) 0x00);
458         // iNodes
459         macacoFrame.add((byte) nodes);
460         logger.debug("sendTypicalRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig.gatewayLanAddress);
461         queueToDispatcher(macacoFrame, gwConfig);
462     }
463
464     /**
465      * Build TYPICAL REQUEST Frame with start offset
466      */
467     public final void sendTypicalRequestFrame(GatewayConfig gwConfig, int start, int nodes) {
468         ArrayList<Byte> macacoFrame = new ArrayList<>();
469         macacoFrame.add(SoulissUDPConstants.SOULISS_UDP_FUNCTION_TYP_REQ);
470         // PUTIN, STARTOFFEST, NUMBEROF
471         // PUTIN
472         macacoFrame.add((byte) 0x00);
473         // PUTIN
474         macacoFrame.add((byte) 0x00);
475         // startOffset
476         macacoFrame.add((byte) start);
477         // iNodes
478         macacoFrame.add((byte) nodes);
479         logger.debug("sendTypicalRequestFrame - {}, IP: {} ", macacoToString(macacoFrame), gwConfig.gatewayLanAddress);
480         queueToDispatcher(macacoFrame, gwConfig);
481     }
482
483     boolean flag = true;
484
485     private final String macacoToString(ArrayList<Byte> mACACOframe) {
486         // I copy arrays to avoid concurrent changes
487         ArrayList<Byte> mACACOframe2 = new ArrayList<>();
488         mACACOframe2.addAll(mACACOframe);
489         flag = false;
490         var sb = new StringBuilder();
491         sb.append("HEX: [");
492         for (byte b : mACACOframe2) {
493             sb.append(String.format("%02X ", b));
494         }
495         sb.append("]");
496         flag = true;
497         return sb.toString();
498     }
499 }