diff --git a/bundles/org.openhab.binding.network/README.md b/bundles/org.openhab.binding.network/README.md index 7fc819372df..a742e80c95c 100644 --- a/bundles/org.openhab.binding.network/README.md +++ b/bundles/org.openhab.binding.network/README.md @@ -54,6 +54,12 @@ Use the following options for a **network:pingdevice**: - **timeout:** How long the ping will wait for an answer, in milliseconds. Default: `5000` (5 seconds). - **refreshInterval:** How often the device will be checked, in milliseconds. Default: `60000` (one minute). - **useIOSWakeUp:** When set to true, an additional port knock is performed before a ping. Default: `true`. +- **useArpPing:** When set to true if the presence detection is allowed to use arp ping. + This can speed up presence detection, but may lead to inaccurate ping latency measurements. + Switch off if you want to use this for ping latency monitoring. Default: `true`. +- **useIcmpPing:** When set to true if the presence detection is allowed to use icmp ping. + When also using arp ping, the latency measurements will not be comparable. + Switch off if you rather want to use arp ping latency monitoring. Default: `true`. - **networkInterfaceNames:** The network interface names used for communicating with the device. Limiting the network interfaces reduces the load when arping and Wake-on-LAN are used. Use comma separated values when using textual config. Default: empty (all network interfaces). @@ -190,6 +196,7 @@ demo.things: ```java Thing network:pingdevice:devicename [ hostname="192.168.0.42", macAddress="6f:70:65:6e:48:41", useIOSWakeUp="false" ] +Thing network:pingdevice:router [ hostname="192.168.0.1", useArpPing="false" ] Thing network:speedtest:local "SpeedTest 50Mo" @ "Internet" [url="https://bouygues.testdebit.info/", fileName="50M.iso"] ``` @@ -199,6 +206,8 @@ demo.items: Switch MyDevice { channel="network:pingdevice:devicename:online" } Number:Time MyDeviceResponseTime { channel="network:pingdevice:devicename:latency" } +Number:Time MyRouterResponseTime { channel="network:pingdevice:router:latency" } + String Speedtest_Running "Test running ... [%s]" {channel="network:speedtest:local:isRunning"} Number:Dimensionless Speedtest_Progress "Test progress [%d %unit%]" {channel="network:speedtest:local:progress"} Number:DataTransferRate Speedtest_ResultDown "Downlink [%.2f %unit%]" {channel="network:speedtest:local:rateDown"} @@ -218,6 +227,10 @@ sitemap demo label="Main Menu" Text item=MyDeviceResponseTime label="Device Response Time [%s]" } + Frame { + Text item=MyRouterResponseTime label="Router Response Time [%s]" + } + Frame label="SpeedTest" { Text item=Speedtest_Start Switch item=Speedtest_Running diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkHandlerConfiguration.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkHandlerConfiguration.java index 378b8f836b7..d19faa4bfc1 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkHandlerConfiguration.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkHandlerConfiguration.java @@ -32,5 +32,7 @@ public class NetworkHandlerConfiguration { public Integer refreshInterval = 60000; public Integer timeout = 5000; public boolean useIOSWakeUp = true; + public boolean useArpPing = true; + public boolean useIcmpPing = true; public Set networkInterfaceNames = Set.of(); } diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java index d0677afa651..260be92d039 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java @@ -71,8 +71,10 @@ public class PresenceDetection implements IPRequestReceivedCallback { private String ipPingState = "Disabled"; protected String arpPingUtilPath = ""; private ArpPingUtilEnum arpPingMethod = ArpPingUtilEnum.DISABLED; - protected @Nullable IpPingMethodEnum pingMethod = null; + protected @Nullable IpPingMethodEnum pingMethod = IpPingMethodEnum.DISABLED; private boolean iosDevice; + private boolean useArpPing; + private boolean useIcmpPing; private Set tcpPorts = new HashSet<>(); private Duration refreshInterval = Duration.ofMinutes(1); @@ -188,7 +190,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { public void setUseIcmpPing(@Nullable Boolean useSystemPing) { if (useSystemPing == null) { ipPingState = "Disabled"; - pingMethod = null; + pingMethod = IpPingMethodEnum.DISABLED; } else if (useSystemPing) { final IpPingMethodEnum pingMethod = networkUtils.determinePingMethod(); this.pingMethod = pingMethod; @@ -220,12 +222,17 @@ public class PresenceDetection implements IPRequestReceivedCallback { * Sets the path to ARP ping. * * @param enable enable or disable ARP ping - * @param arpPingUtilPath enableDHCPListen(useDHCPsniffing); + * @param arpPingUtilPath path to Arping tool + * @param arpPingUtilMethod Arping tool method */ public void setUseArpPing(boolean enable, String arpPingUtilPath, ArpPingUtilEnum arpPingUtilMethod) { - setUseArpPing(enable, destination.getValue()); - this.arpPingUtilPath = arpPingUtilPath; - this.arpPingMethod = arpPingUtilMethod; + if (!enable) { + arpPingMethod = ArpPingUtilEnum.DISABLED; + } else { + setUseArpPing(enable, destination.getValue()); + this.arpPingUtilPath = arpPingUtilPath; + this.arpPingMethod = arpPingUtilMethod; + } } public String getArpPingState() { @@ -256,6 +263,36 @@ public class PresenceDetection implements IPRequestReceivedCallback { iosDevice = value; } + /** + * Return true if the device presence detection is also performed using arp ping. This gives + * less accurate ping latency results when used for an IPv4 destination host. + */ + public boolean isUseArpPing() { + return useArpPing; + } + + /** + * Set to true if the device presence detection should also be performed using arp ping. This gives + * less accurate ping latency results when used for an IPv4 destination host. + */ + public void setUseArpPing(boolean useArpPing) { + this.useArpPing = useArpPing; + } + + /** + * Return true if the device presence detection is also performed using icmp ping. + */ + public boolean isUseIcmpPing() { + return useIcmpPing; + } + + /** + * Set to true if the device presence detection should also be performed using icmp ping. + */ + public void setUseIcmPing(boolean useIcmpPing) { + this.useIcmpPing = useIcmpPing; + } + /** * Return the last seen value as an {@link Instant} or null if not yet seen. */ @@ -329,7 +366,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { Set interfaceNames = null; detectionChecks = tcpPorts.size(); - if (pingMethod != null) { + if (pingMethod != IpPingMethodEnum.DISABLED) { detectionChecks += 1; } if (arpPingMethod.canProceed) { @@ -385,7 +422,7 @@ public class PresenceDetection implements IPRequestReceivedCallback { } // ICMP ping - if (pingMethod != null) { + if (pingMethod != IpPingMethodEnum.DISABLED) { addAsyncDetection(completableFutures, () -> { Thread.currentThread().setName("presenceDetectionICMP_" + hostname); if (pingMethod == IpPingMethodEnum.JAVA_PING) { 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 0f90414cfe5..ac1cf09102b 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 @@ -183,8 +183,9 @@ public class NetworkHandler extends BaseThingHandler presenceDetection.setIOSDevice(handlerConfiguration.useIOSWakeUp); // Hand over binding configurations to the network service presenceDetection.setUseDhcpSniffing(configuration.allowDHCPlisten); - presenceDetection.setUseIcmpPing(configuration.allowSystemPings); - presenceDetection.setUseArpPing(true, configuration.arpPingToolPath, configuration.arpPingUtilMethod); + presenceDetection.setUseIcmpPing(handlerConfiguration.useIcmpPing ? configuration.allowSystemPings : null); + presenceDetection.setUseArpPing(handlerConfiguration.useArpPing, configuration.arpPingToolPath, + configuration.arpPingUtilMethod); } this.retries = handlerConfiguration.retry.intValue(); diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/LatencyParser.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/LatencyParser.java index 8a58fc9c0b0..efdd317a573 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/LatencyParser.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/LatencyParser.java @@ -12,7 +12,7 @@ */ package org.openhab.binding.network.internal.utils; -import static org.openhab.binding.network.internal.utils.NetworkUtils.millisToDuration; +import static org.openhab.binding.network.internal.utils.NetworkUtils.*; import java.time.Duration; import java.util.regex.Matcher; @@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory; @NonNullByDefault public class LatencyParser { - private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?ms"); + private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?(u|m)s.*"); private final Logger logger = LoggerFactory.getLogger(LatencyParser.class); // This is how the input looks like on Mac and Linux: @@ -43,18 +43,30 @@ public class LatencyParser { // 1 packets transmitted, 1 packets received, 0.0% packet loss // round-trip min/avg/max/stddev = 1.225/1.225/1.225/0.000 ms + // This is an example of an arping response on Linux: + // arping -c 1 -i eth0 192.168.0.1 + // ARPING 192.168.0.1 + // 60 bytes from xx:xx:xx:xx:xx:xx (192.168.0.1): index=0 time=792.847 usec + // + // --- 192.168.0.1 statistics --- + // 1 packets transmitted, 1 packets received, 0% unanswered (0 extra) + // rtt min/avg/max/std-dev = 0.793/0.793/0.793/0.000 ms + /** - * Examine a single ping command output line and try to extract the latency value if it is contained. + * Examine a single ping or arping command output line and try to extract the latency value if it is contained. * - * @param inputLine Single output line of the ping command. - * @return Latency value provided by the ping command. null if the provided line did not contain a - * latency value which matches the known patterns. + * @param inputLine Single output line of the ping or arping command. + * @return Latency value provided by the ping or arping command. null if the provided line did not + * contain a latency value which matches the known patterns. */ public @Nullable Duration parseLatency(String inputLine) { logger.debug("Parsing latency from input {}", inputLine); Matcher m = LATENCY_PATTERN.matcher(inputLine); - if (m.find() && m.groupCount() == 1) { + if (m.find() && m.groupCount() >= 2) { + if ("u".equals(m.group(2))) { + return microsToDuration(Double.parseDouble(m.group(1).replace(",", "."))); + } return millisToDuration(Double.parseDouble(m.group(1).replace(",", "."))); } diff --git a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java index e22a984d479..c9fe6b1a8cd 100644 --- a/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java +++ b/bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java @@ -55,9 +55,10 @@ import org.slf4j.LoggerFactory; public class NetworkUtils { /** - * Nanos per millisecond. + * Nanos per millisecond and microsecond. */ private static final long NANOS_PER_MILLI = 1000_000L; + private static final long NANOS_PER_MICRO = 1000L; /** * Converts a {@link Duration} to milliseconds. @@ -84,6 +85,17 @@ public class NetworkUtils { return Duration.ofNanos((long) (millis * NANOS_PER_MILLI)); } + /** + * Converts a double representing microseconds to a {@link Duration} instance. + *

+ * + * @param micros the microseconds to be converted + * @return a {@link Duration} instance representing the given microseconds + */ + public static Duration microsToDuration(double micros) { + return Duration.ofNanos((long) (micros * NANOS_PER_MICRO)); + } + private final Logger logger = LoggerFactory.getLogger(NetworkUtils.class); private LatencyParser latencyParser = new LatencyParser(); @@ -286,6 +298,7 @@ public class NetworkUtils { } public enum IpPingMethodEnum { + DISABLED, JAVA_PING, WINDOWS_PING, IPUTILS_LINUX_PING, @@ -414,7 +427,25 @@ public class NetworkUtils { // The return code is 0 for a successful ping. 1 if device didn't respond and 2 if there is another error like // network interface not ready. - return new PingResult(proc.waitFor() == 0, Duration.between(execStartTime, Instant.now())); + int result = proc.waitFor(); + if (result != 0) { + return new PingResult(false, Duration.between(execStartTime, Instant.now())); + } + + PingResult pingResult = new PingResult(true, Duration.between(execStartTime, Instant.now())); + try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { + String line = r.readLine(); + while (line != null) { + Duration responseTime = latencyParser.parseLatency(line); + if (responseTime != null) { + pingResult.setResponseTime(responseTime); + return pingResult; + } + line = r.readLine(); + } + } + + return pingResult; } /** diff --git a/bundles/org.openhab.binding.network/src/main/resources/OH-INF/i18n/network.properties b/bundles/org.openhab.binding.network/src/main/resources/OH-INF/i18n/network.properties index 4b03ccc0039..787f3f95931 100644 --- a/bundles/org.openhab.binding.network/src/main/resources/OH-INF/i18n/network.properties +++ b/bundles/org.openhab.binding.network/src/main/resources/OH-INF/i18n/network.properties @@ -41,8 +41,12 @@ thing-type.config.network.pingdevice.retry.label = Retry thing-type.config.network.pingdevice.retry.description = How many refresh interval cycles should a presence detection should take place, before the device is stated as offline thing-type.config.network.pingdevice.timeout.label = Timeout thing-type.config.network.pingdevice.timeout.description = States how long to wait for a response (in ms), before if a device is stated as offline +thing-type.config.network.pingdevice.useArpPing.label = Use ARP Ping +thing-type.config.network.pingdevice.useArpPing.description = Set to true if the presence detection is allowed to use arp ping. This can speed up presence detection, but may lead to inaccurate ping latency measurements. Switch off if you want to use this for ping latency monitoring. thing-type.config.network.pingdevice.useIOSWakeUp.label = Use iOS Wake Up thing-type.config.network.pingdevice.useIOSWakeUp.description = Set to true if the device presence detection should be performed for an iOS device like iPhone or iPads. An additional port knock is performed before a ping. +thing-type.config.network.pingdevice.useIcmpPing.label = Use ICMP Ping +thing-type.config.network.pingdevice.useIcmpPing.description = Set to true if the presence detection is allowed to use icmp ping. If you are monitoring network latency using arping, you should switch this off to prevent mixing results with arp ping results. thing-type.config.network.servicedevice.hostname.label = Hostname or IP thing-type.config.network.servicedevice.hostname.description = Hostname or IP of the device thing-type.config.network.servicedevice.macAddress.label = MAC Address diff --git a/bundles/org.openhab.binding.network/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.network/src/main/resources/OH-INF/thing/thing-types.xml index 5ee4b44cc53..25d3799bd36 100644 --- a/bundles/org.openhab.binding.network/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.network/src/main/resources/OH-INF/thing/thing-types.xml @@ -70,6 +70,23 @@ true + + + true + Set to true if the presence detection is allowed to use arp ping. This can speed up presence detection, + but may lead to inaccurate ping latency measurements. Switch off if you want to use this for ping latency + monitoring. + true + + + + + true + Set to true if the presence detection is allowed to use icmp ping. If you are monitoring network + latency using arping, you should switch this off to prevent mixing results with arp ping results. + true + +