]> git.basschouten.com Git - openhab-addons.git/blob
35f74cd71654457a6238314f79d0badcb9562354
[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.samsungtv.internal;
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.InterfaceAddress;
20 import java.net.NetworkInterface;
21 import java.time.Duration;
22 import java.util.Enumeration;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import java.util.stream.Stream;
26
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.core.io.net.exec.ExecUtil;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 /**
34  * Class with utility functions to support Wake On Lan (WOL)
35  *
36  * @author Arjan Mels - Initial contribution
37  * @author Laurent Garnier - Use improvements from the LG webOS binding
38  * @author Nick Waterton - use single ip address as source per interface
39  *
40  */
41 @NonNullByDefault
42 public class WakeOnLanUtility {
43
44     private static final Logger LOGGER = LoggerFactory.getLogger(WakeOnLanUtility.class);
45     private static final int CMD_TIMEOUT_MS = 1000;
46     private static String host = "";
47
48     /**
49      * Get os command to find MAC address
50      *
51      * @return os COMMAND
52      */
53     public static String getCommand() {
54         String os = System.getProperty("os.name");
55         String COMMAND = "";
56         if (os != null) {
57             os = os.toLowerCase();
58             LOGGER.debug("{}: os: {}", host, os);
59             if ((os.contains("win"))) {
60                 COMMAND = "arp -a %s";
61             } else if ((os.contains("mac"))) {
62                 COMMAND = "arp %s";
63             } else { // linux
64                 if (checkIfLinuxCommandExists("arp")) {
65                     COMMAND = "arp %s";
66                 } else if (checkIfLinuxCommandExists("arping")) { // typically OH provided docker image
67                     COMMAND = "arping -r -c 1 -C 1 %s";
68                 } else {
69                     LOGGER.warn("{}: arping not installed", host);
70                 }
71             }
72         } else {
73             LOGGER.warn("{}: Unable to determine os", host);
74         }
75         return COMMAND;
76     }
77
78     /**
79      * Get MAC address for host
80      *
81      * @param hostName Host Name (or IP address) of host to retrieve MAC address for
82      * @return MAC address
83      */
84     public static @Nullable String getMACAddress(String hostName) {
85         host = hostName;
86         String COMMAND = getCommand();
87         if (COMMAND.isEmpty()) {
88             LOGGER.debug("{}: MAC address detection not possible. No command to identify MAC found.", hostName);
89             return null;
90         }
91
92         Pattern MAC_REGEX = Pattern.compile("(([0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2})");
93         String[] cmds = Stream.of(COMMAND.split(" ")).map(arg -> String.format(arg, hostName)).toArray(String[]::new);
94         String response = ExecUtil.executeCommandLineAndWaitResponse(Duration.ofMillis(CMD_TIMEOUT_MS), cmds);
95         String macAddress = null;
96
97         if (response != null) {
98             Matcher matcher = MAC_REGEX.matcher(response);
99             while (matcher.find()) {
100                 String group = matcher.group();
101
102                 if (group.length() == 17) {
103                     macAddress = group;
104                     break;
105                 }
106             }
107         }
108         if (macAddress != null) {
109             LOGGER.debug("{}: MAC address of host {} is {}", hostName, hostName, macAddress);
110         } else {
111             LOGGER.debug("{}: Problem executing command {} to retrieve MAC address for {}: {}", hostName,
112                     String.format(COMMAND, hostName), hostName, response);
113         }
114         return macAddress;
115     }
116
117     /**
118      * Send single WOL (Wake On Lan) package on all interfaces
119      *
120      * @param macAddress MAC address to send WOL package to
121      */
122     public static void sendWOLPacket(String macAddress) {
123         byte[] bytes = getWOLPackage(macAddress);
124
125         try {
126             Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
127             while (interfaces != null && interfaces.hasMoreElements()) {
128                 NetworkInterface networkInterface = interfaces.nextElement();
129                 if (networkInterface.isLoopback() || !networkInterface.isUp()) {
130                     continue; // Do not want to use the loopback or down interface.
131                 }
132                 for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
133                     InetAddress broadcast = interfaceAddress.getBroadcast();
134                     if (broadcast == null) {
135                         continue;
136                     }
137
138                     InetAddress local = interfaceAddress.getAddress();
139                     DatagramPacket packet = new DatagramPacket(bytes, bytes.length, broadcast, 9);
140                     try (DatagramSocket socket = new DatagramSocket()) {
141                         socket.send(packet);
142                         LOGGER.trace("Sent WOL packet from {} to {} {}", local, broadcast, macAddress);
143                         break;
144                     } catch (IOException e) {
145                         LOGGER.warn("Problem sending WOL packet from {} to {} {}", local, broadcast, macAddress);
146                     }
147                 }
148             }
149
150         } catch (IOException e) {
151             LOGGER.warn("Problem with interface while sending WOL packet to {}", macAddress);
152         }
153     }
154
155     /**
156      * Create WOL UDP package: 6 bytes 0xff and then 16 times the 6 byte mac address repeated
157      *
158      * @param macStr String representation of the MAC address (either with : or -)
159      * @return byte array with the WOL package
160      * @throws IllegalArgumentException
161      */
162     private static byte[] getWOLPackage(String macStr) throws IllegalArgumentException {
163         byte[] macBytes = new byte[6];
164         String[] hex = macStr.split("(\\:|\\-)");
165         if (hex.length != 6) {
166             throw new IllegalArgumentException("Invalid MAC address.");
167         }
168         try {
169             for (int i = 0; i < 6; i++) {
170                 macBytes[i] = (byte) Integer.parseInt(hex[i], 16);
171             }
172         } catch (NumberFormatException e) {
173             throw new IllegalArgumentException("Invalid hex digit in MAC address.");
174         }
175
176         byte[] bytes = new byte[6 + 16 * macBytes.length];
177         for (int i = 0; i < 6; i++) {
178             bytes[i] = (byte) 0xff;
179         }
180         for (int i = 6; i < bytes.length; i += macBytes.length) {
181             System.arraycopy(macBytes, 0, bytes, i, macBytes.length);
182         }
183
184         return bytes;
185     }
186
187     private static boolean checkIfLinuxCommandExists(String cmd) {
188         try {
189             return 0 == Runtime.getRuntime().exec(String.format("which %s", cmd)).waitFor();
190         } catch (InterruptedException | IOException e) {
191             LOGGER.debug("{}: Error trying to check if command {} exists: {}", host, cmd, e.getMessage());
192         }
193         return false;
194     }
195 }