2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.lgwebos.internal;
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;
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;
34 * Class with utility functions to support Wake On Lan (WOL)
36 * @author Arjan Mels - Initial contribution
37 * @author Sebastian Prehn - Modification to getMACAddress
41 public class WakeOnLanUtility {
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;
47 private static final String COMMAND;
49 String os = System.getProperty("os.name").toLowerCase();
50 LOGGER.debug("os: {}", os);
51 if ((os.indexOf("win") >= 0)) {
52 COMMAND = "arp -a %s";
53 } else if ((os.indexOf("mac") >= 0)) {
56 if (checkIfLinuxCommandExists("arp")) {
58 } else if (checkIfLinuxCommandExists("arping")) { // typically OH provided docker image
59 COMMAND = "arping -r -c 1 -C 1 %s";
67 * Get MAC address for host
69 * @param hostName Host Name (or IP address) of host to retrieve MAC address for
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.");
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 Matcher matcher = MAC_REGEX.matcher(response);
81 String macAddress = null;
83 while (matcher.find()) {
84 String group = matcher.group();
86 if (group.length() == 17) {
92 if (macAddress != null) {
93 LOGGER.debug("MAC address of host {} is {}", hostName, macAddress);
95 LOGGER.debug("Problem executing command {} to retrieve MAC address for {}: {}",
96 String.format(COMMAND, hostName), hostName, response);
102 * Send single WOL (Wake On Lan) package on all interfaces
104 * @macAddress MAC address to send WOL package to
106 public static void sendWOLPacket(String macAddress) {
107 byte[] bytes = getWOLPackage(macAddress);
110 Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
111 while (interfaces.hasMoreElements()) {
112 NetworkInterface networkInterface = interfaces.nextElement();
113 if (networkInterface.isLoopback()) {
114 continue; // Do not want to use the loopback interface.
116 for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
117 InetAddress broadcast = interfaceAddress.getBroadcast();
118 if (broadcast == null) {
122 DatagramPacket packet = new DatagramPacket(bytes, bytes.length, broadcast, 9);
123 try (DatagramSocket socket = new DatagramSocket()) {
125 LOGGER.trace("Sent WOL packet to {} {}", broadcast, macAddress);
126 } catch (IOException e) {
127 LOGGER.warn("Problem sending WOL packet to {} {}", broadcast, macAddress);
132 } catch (IOException e) {
133 LOGGER.warn("Problem with interface while sending WOL packet to {}", macAddress);
138 * Create WOL UDP package: 6 bytes 0xff and then 16 times the 6 byte mac address repeated
140 * @param macStr String representation of the MAC address (either with : or -)
141 * @return byte array with the WOL package
142 * @throws IllegalArgumentException
144 private static byte[] getWOLPackage(String macStr) throws IllegalArgumentException {
145 byte[] macBytes = new byte[6];
146 String[] hex = macStr.split("(\\:|\\-)");
147 if (hex.length != 6) {
148 throw new IllegalArgumentException("Invalid MAC address.");
151 for (int i = 0; i < 6; i++) {
152 macBytes[i] = (byte) Integer.parseInt(hex[i], 16);
154 } catch (NumberFormatException e) {
155 throw new IllegalArgumentException("Invalid hex digit in MAC address.");
158 byte[] bytes = new byte[6 + 16 * macBytes.length];
159 for (int i = 0; i < 6; i++) {
160 bytes[i] = (byte) 0xff;
162 for (int i = 6; i < bytes.length; i += macBytes.length) {
163 System.arraycopy(macBytes, 0, bytes, i, macBytes.length);
169 private static boolean checkIfLinuxCommandExists(String cmd) {
171 return 0 == Runtime.getRuntime().exec(String.format("which %s", cmd)).waitFor();
172 } catch (InterruptedException | IOException e) {
173 LOGGER.debug("Error trying to check if command {} exists: {}", cmd, e.getMessage());