diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java index 603c640ed1c7a..4e9da808cda49 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/WakeOnLanPacketSender.java @@ -13,16 +13,13 @@ package org.openhab.binding.network.internal; import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.SocketException; -import java.net.UnknownHostException; +import java.net.*; import java.util.Arrays; import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.net.NetUtil; @@ -50,12 +47,28 @@ public class WakeOnLanPacketSender { private final Logger logger = LoggerFactory.getLogger(WakeOnLanPacketSender.class); private final String macAddress; + + @Nullable + private final String hostname; + + @Nullable + private final Integer port; + private byte @Nullable [] magicPacket; private final Consumer magicPacketSender; + public WakeOnLanPacketSender(String macAddress, @Nullable String hostname, @Nullable Integer port) { + this.macAddress = macAddress; + this.hostname = hostname; + this.port = port; + this.magicPacketSender = this::sendMagicPacket; + } + public WakeOnLanPacketSender(String macAddress) { this.macAddress = macAddress; - this.magicPacketSender = this::broadcastMagicPacket; + this.hostname = null; + this.port = null; + this.magicPacketSender = this::sendMagicPacket; } /** @@ -63,6 +76,8 @@ public WakeOnLanPacketSender(String macAddress) { */ WakeOnLanPacketSender(String macAddress, Consumer magicPacketSender) { this.macAddress = macAddress; + this.hostname = null; + this.port = null; this.magicPacketSender = magicPacketSender; } @@ -96,26 +111,46 @@ private byte[] createMagicPacket(byte[] macBytes) { return bytes; } - private void broadcastMagicPacket(byte[] magicPacket) { + private void sendMagicPacket(byte[] magicPacket) { try (DatagramSocket socket = new DatagramSocket()) { - broadcastAddressStream().forEach(broadcastAddress -> { - try { - 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, - broadcastAddress.getHostAddress()); - } catch (IOException e) { - logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})", - macAddress, broadcastAddress.getHostAddress(), e); - } - }); - logger.info("Wake-on-LAN packets sent (MAC address: {})", macAddress); + if (StringUtils.isEmpty(hostname)) { + broadcastMagicPacket(magicPacket, socket); + } else { + SocketAddress socketAddress = new InetSocketAddress(this.hostname, + Objects.requireNonNullElse(this.port, WOL_UDP_PORT)); + sendMagicPacketToIp(magicPacket, socket, socketAddress); + } } catch (SocketException e) { logger.error("Failed to open Wake-on-LAN datagram socket", e); } } + private void broadcastMagicPacket(byte[] magicPacket, DatagramSocket socket) { + broadcastAddressStream().forEach(broadcastAddress -> { + try { + 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, + broadcastAddress.getHostAddress()); + } catch (IOException e) { + logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, broadcast address: {})", macAddress, + broadcastAddress.getHostAddress(), e); + } + }); + logger.info("Wake-on-LAN packets sent (MAC address: {})", macAddress); + } + + private void sendMagicPacketToIp(byte[] magicPacket, DatagramSocket socket, SocketAddress ip) { + DatagramPacket packet = new DatagramPacket(magicPacket, MAGIC_PACKET_BYTE_SIZE, ip); + try { + socket.send(packet); + } catch (IOException e) { + logger.debug("Failed to send Wake-on-LAN packet (MAC address: {}, address: {})", macAddress, ip, e); + } + logger.info("Wake-on-LAN packets sent (MAC address: {}, IP address: {})", macAddress, ip); + } + private Stream broadcastAddressStream() { return NetUtil.getAllBroadcastAddresses().stream().map(address -> { try { diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/handler/NetworkHandler.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/handler/NetworkHandler.java index a2cf48c1517fa..f27fe42033c70 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/handler/NetworkHandler.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/handler/NetworkHandler.java @@ -198,7 +198,8 @@ void initialize(PresenceDetection presenceDetection) { presenceDetection.setRefreshInterval(handlerConfiguration.refreshInterval.longValue()); presenceDetection.setTimeout(handlerConfiguration.timeout.intValue()); - wakeOnLanPacketSender = new WakeOnLanPacketSender(handlerConfiguration.macAddress); + wakeOnLanPacketSender = new WakeOnLanPacketSender(handlerConfiguration.macAddress, + handlerConfiguration.hostname, handlerConfiguration.port); updateStatus(ThingStatus.ONLINE); presenceDetection.startAutomaticRefresh(scheduler); diff --git a/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/WakeOnLanPacketSenderTest.java b/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/WakeOnLanPacketSenderTest.java index 505c9d0331a68..517521d4464bd 100644 --- a/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/WakeOnLanPacketSenderTest.java +++ b/bundles/org.openhab.binding.network/src/test/java/org/openhab/binding/network/internal/WakeOnLanPacketSenderTest.java @@ -17,9 +17,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.openhab.binding.network.internal.WakeOnLanPacketSender.*; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.util.Arrays; -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.openhab.core.util.HexUtils; @@ -29,7 +32,7 @@ * * @author Wouter Born - Initial contribution */ -@Timeout(value = 10, unit = TimeUnit.SECONDS) +@Timeout(value = 10) public class WakeOnLanPacketSenderTest { private void assertValidMagicPacket(byte[] macBytes, byte[] packet) { @@ -79,6 +82,49 @@ public void sendWithNoSeparatedMacAddress() { assertValidMagicPacket(HexUtils.hexToBytes("6f70656e4841"), actualPacket); } + @Test + public void sendWithHostnameAndPort() throws IOException, InterruptedException { + sendWOLTest("127.0.0.1", 4444); + } + + @Test + public void sendWithHostnameAndPortNull() throws IOException, InterruptedException { + sendWOLTest("127.0.0.1", null); + } + + @Test + public void sendWithHostnameNullAndPortNull() throws IOException, InterruptedException { + sendWOLTest(null, null); + } + + @Test + public void sendWithHostnameNull() throws IOException, InterruptedException { + sendWOLTest(null, 4444); + } + + private void sendWOLTest(String hostname, Integer port) throws InterruptedException, IOException { + DatagramSocket socket = new DatagramSocket(4444); + + byte[] buf = new byte[256]; + DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); + + while (socket.isClosed()) { + Thread.sleep(100); + } + + WakeOnLanPacketSender sender = new WakeOnLanPacketSender("6f70656e4841", hostname, port); + sender.sendPacket(); + + // This Test is only applicable for IP Requests + if (hostname != null && port != null) { + socket.receive(datagramPacket); + } + + socket.close(); + + Assertions.assertTrue(datagramPacket.getData().length > 0); + } + @Test public void sendWithEmptyMacAddressThrowsException() { assertThrows(IllegalStateException.class, () -> new WakeOnLanPacketSender("").sendPacket());