[Network] Added param to differentiate between mac and IP WOL Request (#11387)

* Added possiblity to send WOL Requests to configured Hostname, also removed unnecessary unit from Timeout Annotation in WakeOnLanPacketSenderTest
* Introduced parameter to decide whether to send WOL via IP or MAC
* Added two Methods for user clarity, marked old method as deprecated, adjusted README
* Updated internal methods to use explicit calls as well, added deprecated method call to README

Signed-off-by: Jonathan Saxen <jonathan@saxen.info>
This commit is contained in:
Jonathan S 2021-10-31 09:16:00 +01:00 committed by GitHub
parent 3bd2939b6c
commit 1d07dbe1f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 54 deletions

View File

@ -250,6 +250,11 @@ if (actions === null) {
logInfo("actions", "Actions not found, check thing ID") logInfo("actions", "Actions not found, check thing ID")
return return
} else { } else {
actions.sendWakeOnLanPacket() // Send via MAC address
actions.sendWakeOnLanPacketViaMac()
actions.sendWakeOnLanPacket() // deprecated
// Send via IP address
actions.sendWakeOnLanPacketViaIp()
} }
``` ```

View File

@ -54,21 +54,28 @@ public class WakeOnLanPacketSender {
@Nullable @Nullable
private final Integer port; private final Integer port;
private byte @Nullable [] magicPacket; private final Consumer<byte[]> magicPacketMacSender;
private final Consumer<byte[]> magicPacketSender; private final Consumer<byte[]> magicPacketIpSender;
public WakeOnLanPacketSender(String macAddress, @Nullable String hostname, @Nullable Integer port) { public WakeOnLanPacketSender(String macAddress, @Nullable String hostname, @Nullable Integer port) {
logger.debug("initialized WOL Packet Sender (mac: {}, hostname: {}, port: {}", macAddress, hostname, port);
this.macAddress = macAddress; this.macAddress = macAddress;
this.hostname = hostname; this.hostname = hostname;
this.port = port; this.port = port;
this.magicPacketSender = this::sendMagicPacket; this.magicPacketMacSender = this::sendMagicPacketViaMac;
this.magicPacketIpSender = this::sendMagicPacketViaIp;
} }
/**
* Used for testing only.
*/
public WakeOnLanPacketSender(String macAddress) { public WakeOnLanPacketSender(String macAddress) {
logger.debug("initialized WOL Packet Sender (mac: {}", macAddress);
this.macAddress = macAddress; this.macAddress = macAddress;
this.hostname = null; this.hostname = null;
this.port = null; this.port = null;
this.magicPacketSender = this::sendMagicPacket; this.magicPacketMacSender = this::sendMagicPacketViaMac;
this.magicPacketIpSender = this::sendMagicPacketViaIp;
} }
/** /**
@ -78,17 +85,28 @@ public class WakeOnLanPacketSender {
this.macAddress = macAddress; this.macAddress = macAddress;
this.hostname = null; this.hostname = null;
this.port = null; this.port = null;
this.magicPacketSender = magicPacketSender; this.magicPacketMacSender = magicPacketSender;
this.magicPacketIpSender = this::sendMagicPacketViaIp;
} }
public void sendPacket() { public void sendWakeOnLanPacketViaMac() {
byte[] localMagicPacket = magicPacket; byte[] magicPacket = createMagicPacket();
if (localMagicPacket == null) { this.magicPacketMacSender.accept(magicPacket);
localMagicPacket = createMagicPacket(createMacBytes(macAddress)); }
magicPacket = localMagicPacket;
}
magicPacketSender.accept(localMagicPacket); public void sendWakeOnLanPacketViaIp() {
byte[] magicPacket = createMagicPacket();
this.magicPacketIpSender.accept(magicPacket);
}
private byte[] createMagicPacket() {
byte[] macBytes = createMacBytes(this.macAddress);
byte[] magicPacket = new byte[MAGIC_PACKET_BYTE_SIZE];
Arrays.fill(magicPacket, 0, PREFIX_BYTE_SIZE, (byte) 0xff);
for (int i = PREFIX_BYTE_SIZE; i < MAGIC_PACKET_BYTE_SIZE; i += MAC_BYTE_SIZE) {
System.arraycopy(macBytes, 0, magicPacket, i, macBytes.length);
}
return magicPacket;
} }
private byte[] createMacBytes(String macAddress) { private byte[] createMacBytes(String macAddress) {
@ -102,23 +120,24 @@ public class WakeOnLanPacketSender {
return HexUtils.hexToBytes(hexString); return HexUtils.hexToBytes(hexString);
} }
private byte[] createMagicPacket(byte[] macBytes) { private void sendMagicPacketViaMac(byte[] magicPacket) {
byte[] bytes = new byte[MAGIC_PACKET_BYTE_SIZE]; try (DatagramSocket socket = new DatagramSocket()) {
Arrays.fill(bytes, 0, PREFIX_BYTE_SIZE, (byte) 0xff); logger.debug("Sending Wake-on-LAN Packet via Broadcast");
for (int i = PREFIX_BYTE_SIZE; i < MAGIC_PACKET_BYTE_SIZE; i += MAC_BYTE_SIZE) { broadcastMagicPacket(magicPacket, socket);
System.arraycopy(macBytes, 0, bytes, i, macBytes.length); } catch (SocketException e) {
logger.error("Failed to open Wake-on-LAN datagram socket", e);
} }
return bytes;
} }
private void sendMagicPacket(byte[] magicPacket) { private void sendMagicPacketViaIp(byte[] magicPacket) {
try (DatagramSocket socket = new DatagramSocket()) { try (DatagramSocket socket = new DatagramSocket()) {
if (StringUtils.isEmpty(hostname)) { if (!StringUtils.isEmpty(this.hostname)) {
broadcastMagicPacket(magicPacket, socket); logger.debug("Sending Wake-on-LAN Packet via IP Address");
} else {
SocketAddress socketAddress = new InetSocketAddress(this.hostname, SocketAddress socketAddress = new InetSocketAddress(this.hostname,
Objects.requireNonNullElse(this.port, WOL_UDP_PORT)); Objects.requireNonNullElse(this.port, WOL_UDP_PORT));
sendMagicPacketToIp(magicPacket, socket, socketAddress); sendMagicPacketToIp(magicPacket, socket, socketAddress);
} else {
throw new IllegalStateException("Hostname is not set!");
} }
} catch (SocketException e) { } catch (SocketException e) {
logger.error("Failed to open Wake-on-LAN datagram socket", e); logger.error("Failed to open Wake-on-LAN datagram socket", e);
@ -131,14 +150,14 @@ public class WakeOnLanPacketSender {
DatagramPacket packet = new DatagramPacket(magicPacket, MAGIC_PACKET_BYTE_SIZE, broadcastAddress, DatagramPacket packet = new DatagramPacket(magicPacket, MAGIC_PACKET_BYTE_SIZE, broadcastAddress,
WOL_UDP_PORT); WOL_UDP_PORT);
socket.send(packet); socket.send(packet);
logger.debug("Wake-on-LAN packet sent (MAC address: {}, broadcast address: {})", macAddress, logger.debug("Wake-on-LAN packet sent (MAC address: {}, broadcast address: {})", this.macAddress,
broadcastAddress.getHostAddress()); broadcastAddress.getHostAddress());
} catch (IOException e) { } catch (IOException e) {
logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})", macAddress, logger.error("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})",
broadcastAddress.getHostAddress(), e); this.macAddress, broadcastAddress.getHostAddress(), e);
} }
}); });
logger.info("Wake-on-LAN packets sent (MAC address: {})", macAddress); logger.info("Wake-on-LAN packets sent (MAC address: {})", this.macAddress);
} }
private void sendMagicPacketToIp(byte[] magicPacket, DatagramSocket socket, SocketAddress ip) { private void sendMagicPacketToIp(byte[] magicPacket, DatagramSocket socket, SocketAddress ip) {
@ -146,9 +165,9 @@ public class WakeOnLanPacketSender {
try { try {
socket.send(packet); socket.send(packet);
} catch (IOException e) { } catch (IOException e) {
logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, address: {})", macAddress, ip, e); logger.error("Failed to send Wake-on-LAN packet (IP address: {})", ip, e);
} }
logger.info("Wake-on-LAN packets sent (MAC address: {}, IP address: {})", macAddress, ip); logger.info("Wake-on-LAN packets sent (IP address: {})", ip);
} }
private Stream<InetAddress> broadcastAddressStream() { private Stream<InetAddress> broadcastAddressStream() {
@ -156,7 +175,7 @@ public class WakeOnLanPacketSender {
try { try {
return InetAddress.getByName(address); return InetAddress.getByName(address);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
logger.debug("Failed to get broadcast address '{}' by name", address, e); logger.error("Failed to get broadcast address '{}' by name", address, e);
return null; return null;
} }
}).filter(Objects::nonNull); }).filter(Objects::nonNull);

View File

@ -47,17 +47,36 @@ public class NetworkActions implements ThingActions {
return handler; return handler;
} }
/**
* @deprecated Use sendWakeOnLanPacketViaMac or sendWakeOnLanPacketViaIp instead.
*/
@Deprecated
@RuleAction(label = "send a WoL packet", description = "Send a Wake-on-LAN packet to wake the device.") @RuleAction(label = "send a WoL packet", description = "Send a Wake-on-LAN packet to wake the device.")
public void sendWakeOnLanPacket() { public void sendWakeOnLanPacket() {
sendWakeOnLanPacketViaMac();
}
@RuleAction(label = "send a WoL packet", description = "Send a Wake-on-LAN packet to wake the device via Mac.")
public void sendWakeOnLanPacketViaMac() {
NetworkHandler localHandler = handler; NetworkHandler localHandler = handler;
if (localHandler != null) { if (localHandler != null) {
localHandler.sendWakeOnLanPacket(); localHandler.sendWakeOnLanPacketViaMac();
} else {
logger.warn("Failed to send Wake-on-LAN packet (handler null)");
}
}
@RuleAction(label = "send a WoL packet", description = "Send a Wake-on-LAN packet to wake the device via IP.")
public void sendWakeOnLanPacketViaIp() {
NetworkHandler localHandler = handler;
if (localHandler != null) {
localHandler.sendWakeOnLanPacketViaIp();
} else { } else {
logger.warn("Failed to send Wake-on-LAN packet (handler null)"); logger.warn("Failed to send Wake-on-LAN packet (handler null)");
} }
} }
public static void sendWakeOnLanPacket(ThingActions actions) { public static void sendWakeOnLanPacket(ThingActions actions) {
((NetworkActions) actions).sendWakeOnLanPacket(); ((NetworkActions) actions).sendWakeOnLanPacketViaMac();
} }
} }

View File

@ -242,11 +242,16 @@ public class NetworkHandler extends BaseThingHandler
return Collections.singletonList(NetworkActions.class); return Collections.singletonList(NetworkActions.class);
} }
public void sendWakeOnLanPacket() { public void sendWakeOnLanPacketViaIp() {
// Hostname can't be null
wakeOnLanPacketSender.sendWakeOnLanPacketViaIp();
}
public void sendWakeOnLanPacketViaMac() {
if (handlerConfiguration.macAddress.isEmpty()) { if (handlerConfiguration.macAddress.isEmpty()) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot send WoL packet because the 'macAddress' is not configured for " + thing.getUID()); "Cannot send WoL packet because the 'macAddress' is not configured for " + thing.getUID());
} }
wakeOnLanPacketSender.sendPacket(); wakeOnLanPacketSender.sendWakeOnLanPacketViaMac();
} }
} }

View File

@ -53,7 +53,7 @@ public class WakeOnLanPacketSenderTest {
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f:70:65:6e:48:41", WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f:70:65:6e:48:41",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length)); bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket(); sender.sendWakeOnLanPacketViaMac();
assertValidMagicPacket(HexUtils.hexToBytes("6f:70:65:6e:48:41", ":"), actualPacket); assertValidMagicPacket(HexUtils.hexToBytes("6f:70:65:6e:48:41", ":"), actualPacket);
} }
@ -65,7 +65,7 @@ public class WakeOnLanPacketSenderTest {
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6F-70-65-6E-48-41", WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6F-70-65-6E-48-41",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length)); bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket(); sender.sendWakeOnLanPacketViaMac();
assertValidMagicPacket(HexUtils.hexToBytes("6F-70-65-6E-48-41", "-"), actualPacket); assertValidMagicPacket(HexUtils.hexToBytes("6F-70-65-6E-48-41", "-"), actualPacket);
} }
@ -77,7 +77,7 @@ public class WakeOnLanPacketSenderTest {
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length)); bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket(); sender.sendWakeOnLanPacketViaMac();
assertValidMagicPacket(HexUtils.hexToBytes("6f70656e4841"), actualPacket); assertValidMagicPacket(HexUtils.hexToBytes("6f70656e4841"), actualPacket);
} }
@ -93,13 +93,13 @@ public class WakeOnLanPacketSenderTest {
} }
@Test @Test
public void sendWithHostnameNullAndPortNull() throws IOException, InterruptedException { public void sendWithHostnameNullAndPortNull() {
sendWOLTest(null, null); assertThrows(IllegalStateException.class, () -> sendWOLTest(null, null));
} }
@Test @Test
public void sendWithHostnameNull() throws IOException, InterruptedException { public void sendWithHostnameNull() {
sendWOLTest(null, 4444); assertThrows(IllegalStateException.class, () -> sendWOLTest(null, 4444));
} }
private void sendWOLTest(String hostname, Integer port) throws InterruptedException, IOException { private void sendWOLTest(String hostname, Integer port) throws InterruptedException, IOException {
@ -112,36 +112,41 @@ public class WakeOnLanPacketSenderTest {
Thread.sleep(100); Thread.sleep(100);
} }
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", hostname, port); try {
sender.sendPacket(); WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", hostname, port);
sender.sendWakeOnLanPacketViaIp();
// This Test is only applicable for IP Requests // This Test is only applicable for IP Requests
if (hostname != null && port != null) { if (hostname != null && port != null) {
socket.receive(datagramPacket); socket.receive(datagramPacket);
}
Assertions.assertTrue(datagramPacket.getData().length > 0);
} finally {
socket.close();
} }
socket.close();
Assertions.assertTrue(datagramPacket.getData().length > 0);
} }
@Test @Test
public void sendWithEmptyMacAddressThrowsException() { public void sendWithEmptyMacAddressThrowsException() {
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("").sendPacket()); assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("").sendWakeOnLanPacketViaMac());
} }
@Test @Test
public void sendWithTooShortMacAddressThrowsException() { public void sendWithTooShortMacAddressThrowsException() {
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("6f:70:65:6e:48").sendPacket()); assertThrows(IllegalStateException.class,
() -> new WakeOnLanPacketSender("6f:70:65:6e:48").sendWakeOnLanPacketViaMac());
} }
@Test @Test
public void sendWithTooLongMacAddressThrowsException() { public void sendWithTooLongMacAddressThrowsException() {
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("6f:70:65:6e:48:41:42").sendPacket()); assertThrows(IllegalStateException.class,
() -> new WakeOnLanPacketSender("6f:70:65:6e:48:41:42").sendWakeOnLanPacketViaMac());
} }
@Test @Test
public void sendWithUnsupportedSeparatorInMacAddressThrowsException() { public void sendWithUnsupportedSeparatorInMacAddressThrowsException() {
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("6f=70=65=6e=48=41").sendPacket()); assertThrows(IllegalStateException.class,
() -> new WakeOnLanPacketSender("6f=70=65=6e=48=41").sendWakeOnLanPacketViaMac());
} }
} }