[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>
pull/11479/head
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")
return
} 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
private final Integer port;
private byte @Nullable [] magicPacket;
private final Consumer<byte[]> magicPacketSender;
private final Consumer<byte[]> magicPacketMacSender;
private final Consumer<byte[]> magicPacketIpSender;
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.hostname = hostname;
this.port = port;
this.magicPacketSender = this::sendMagicPacket;
this.magicPacketMacSender = this::sendMagicPacketViaMac;
this.magicPacketIpSender = this::sendMagicPacketViaIp;
}
/**
* Used for testing only.
*/
public WakeOnLanPacketSender(String macAddress) {
logger.debug("initialized WOL Packet Sender (mac: {}", macAddress);
this.macAddress = macAddress;
this.hostname = 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.hostname = null;
this.port = null;
this.magicPacketSender = magicPacketSender;
this.magicPacketMacSender = magicPacketSender;
this.magicPacketIpSender = this::sendMagicPacketViaIp;
}
public void sendPacket() {
byte[] localMagicPacket = magicPacket;
if (localMagicPacket == null) {
localMagicPacket = createMagicPacket(createMacBytes(macAddress));
magicPacket = localMagicPacket;
}
public void sendWakeOnLanPacketViaMac() {
byte[] magicPacket = createMagicPacket();
this.magicPacketMacSender.accept(magicPacket);
}
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) {
@ -102,23 +120,24 @@ public class WakeOnLanPacketSender {
return HexUtils.hexToBytes(hexString);
}
private byte[] createMagicPacket(byte[] macBytes) {
byte[] bytes = new byte[MAGIC_PACKET_BYTE_SIZE];
Arrays.fill(bytes, 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, bytes, i, macBytes.length);
private void sendMagicPacketViaMac(byte[] magicPacket) {
try (DatagramSocket socket = new DatagramSocket()) {
logger.debug("Sending Wake-on-LAN Packet via Broadcast");
broadcastMagicPacket(magicPacket, socket);
} 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()) {
if (StringUtils.isEmpty(hostname)) {
broadcastMagicPacket(magicPacket, socket);
} else {
if (!StringUtils.isEmpty(this.hostname)) {
logger.debug("Sending Wake-on-LAN Packet via IP Address");
SocketAddress socketAddress = new InetSocketAddress(this.hostname,
Objects.requireNonNullElse(this.port, WOL_UDP_PORT));
sendMagicPacketToIp(magicPacket, socket, socketAddress);
} else {
throw new IllegalStateException("Hostname is not set!");
}
} catch (SocketException 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,
WOL_UDP_PORT);
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());
} catch (IOException e) {
logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})", macAddress,
broadcastAddress.getHostAddress(), e);
logger.error("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})",
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) {
@ -146,9 +165,9 @@ public class WakeOnLanPacketSender {
try {
socket.send(packet);
} 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() {
@ -156,7 +175,7 @@ public class WakeOnLanPacketSender {
try {
return InetAddress.getByName(address);
} 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;
}
}).filter(Objects::nonNull);

View File

@ -47,17 +47,36 @@ public class NetworkActions implements ThingActions {
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.")
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;
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 {
logger.warn("Failed to send Wake-on-LAN packet (handler null)");
}
}
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);
}
public void sendWakeOnLanPacket() {
public void sendWakeOnLanPacketViaIp() {
// Hostname can't be null
wakeOnLanPacketSender.sendWakeOnLanPacketViaIp();
}
public void sendWakeOnLanPacketViaMac() {
if (handlerConfiguration.macAddress.isEmpty()) {
throw new IllegalStateException(
"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",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket();
sender.sendWakeOnLanPacketViaMac();
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",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket();
sender.sendWakeOnLanPacketViaMac();
assertValidMagicPacket(HexUtils.hexToBytes("6F-70-65-6E-48-41", "-"), actualPacket);
}
@ -77,7 +77,7 @@ public class WakeOnLanPacketSenderTest {
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841",
bytes -> System.arraycopy(bytes, 0, actualPacket, 0, bytes.length));
sender.sendPacket();
sender.sendWakeOnLanPacketViaMac();
assertValidMagicPacket(HexUtils.hexToBytes("6f70656e4841"), actualPacket);
}
@ -93,13 +93,13 @@ public class WakeOnLanPacketSenderTest {
}
@Test
public void sendWithHostnameNullAndPortNull() throws IOException, InterruptedException {
sendWOLTest(null, null);
public void sendWithHostnameNullAndPortNull() {
assertThrows(IllegalStateException.class, () -> sendWOLTest(null, null));
}
@Test
public void sendWithHostnameNull() throws IOException, InterruptedException {
sendWOLTest(null, 4444);
public void sendWithHostnameNull() {
assertThrows(IllegalStateException.class, () -> sendWOLTest(null, 4444));
}
private void sendWOLTest(String hostname, Integer port) throws InterruptedException, IOException {
@ -112,36 +112,41 @@ public class WakeOnLanPacketSenderTest {
Thread.sleep(100);
}
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", hostname, port);
sender.sendPacket();
try {
WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", hostname, port);
sender.sendWakeOnLanPacketViaIp();
// This Test is only applicable for IP Requests
if (hostname != null && port != null) {
socket.receive(datagramPacket);
// This Test is only applicable for IP Requests
if (hostname != null && port != null) {
socket.receive(datagramPacket);
}
Assertions.assertTrue(datagramPacket.getData().length > 0);
} finally {
socket.close();
}
socket.close();
Assertions.assertTrue(datagramPacket.getData().length > 0);
}
@Test
public void sendWithEmptyMacAddressThrowsException() {
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("").sendPacket());
assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("").sendWakeOnLanPacketViaMac());
}
@Test
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
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
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());
}
}