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