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