Use NetworkHealth Binding for iphone detection

I know there are already multiple ways to accomplish iphone presence detection, but I thought I would share what I did. The NetworkHealth binding is exactly what I wanted except that the the iphone does not respond to pings when it goes to sleep. However, the iphone does responds to UDP packets on port 5353. So I modified the NetworkHealth binding so that it could optionally execute a command before doing a ping. This may be a bit of a hack, but I figured someone else might like this.

I installed hping3 for issuing the command to wake up the iphone. Then I just created the binding config below. The last parameter being the command (which is optional).

*Switch Presence_James "James" <present> (Presence) { nh="james_phone_ip:0:0:sudo hping3 -2 -c 10 -p 5353 -i u1 james_phone_ip" }*

Below are the changes I made to the NetworkHealth binding:

a/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/NetworkHealthBindingProvider.java
+++ b/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/NetworkHealthBindingProvider.java
@@ -39,4 +39,9 @@ public interface NetworkHealthBindingProvider extends BindingProvider {
 	 */
 	public int getTimeout(String itemName);
 	
+	/**
+	 * @return the corresponding cmd of the given <code>itemName</code>
+	 */
+	public String getCmd(String itemName);
+	
 }

a/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/internal/NetworkHealthBinding.java
+++ b/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/internal/NetworkHealthBinding.java
@@ -13,6 +13,7 @@ import java.net.SocketTimeoutException;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import org.openhab.binding.networkhealth.NetworkHealthBindingProvider;
 import org.openhab.core.binding.AbstractActiveBinding;
@@ -76,7 +77,18 @@ public class NetworkHealthBinding extends AbstractActiveBinding<NetworkHealthBin
 				if (provider.getTimeout(itemName) > 0) {
 					timeout = provider.getTimeout(itemName);
 				}
-				
+
+				if (provider.getCmd(itemName) != null) {
+					String cmd = provider.getCmd(itemName);
+					try {
+						Process process = Runtime.getRuntime().exec(cmd);
+						process.waitFor(timeout, TimeUnit.MILLISECONDS);
+					} catch (IOException | InterruptedException e) {
+						e.printStackTrace();
+						logger.debug("Failed to execute process ['{}']", new Object[] {cmd});
+					}
+				}
+
 				boolean success = false;
 				
 				try {

a/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/internal/NetworkHealthGenericBindingProvider.java
+++ b/bundles/binding/org.openhab.binding.networkhealth/src/main/java/org/openhab/binding/networkhealth/internal/NetworkHealthGenericBindingProvider.java
@@ -64,8 +64,8 @@ public class NetworkHealthGenericBindingProvider extends AbstractGenericBindingP
 		super.processBindingConfiguration(context, item, bindingConfig);
 		
 		String[] configParts = bindingConfig.trim().split(":");
-		if (configParts.length > 3) {
-			throw new BindingConfigParseException("NetworkHealth configuration can contain three parts at max");
+		if (configParts.length > 4) {
+			throw new BindingConfigParseException("NetworkHealth configuration can contain four parts at max");
 		}
 		
 		NhBindingConfig config = new NhBindingConfig();
@@ -78,6 +78,9 @@ public class NetworkHealthGenericBindingProvider extends AbstractGenericBindingP
 		if (configParts.length > 2) {
 			config.timeout = Integer.valueOf(configParts[2]);
 		}
+		if (configParts.length > 3) {
+			config.cmd = configParts[3];
+		}
 		addBindingConfig(item, config);
 	}
 	
@@ -105,6 +108,14 @@ public class NetworkHealthGenericBindingProvider extends AbstractGenericBindingP
 		return config != null ? config.timeout : 0;
 	}
 	
+	/**
+	 * {@inheritDoc}
+	 */
+	public String getCmd(String itemName) {
+		NhBindingConfig config = (NhBindingConfig) bindingConfigs.get(itemName);
+		return config != null ? config.cmd : null;
+	}
+	
 	
 	/**
 	 * This is an internal data structure to store information from the binding
@@ -115,6 +126,7 @@ public class NetworkHealthGenericBindingProvider extends AbstractGenericBindingP
 		public String hostname;
 		public int port;
 		public int timeout;
+		public String cmd;
 	}

I have a similar solution for IPhone detection. Can I just ask if the IPhone you are detecting is WIFI synced with any of the other computers on the same WIFI network? When I was getting my system setup I had issues with detecting the IPhone even with this method until I WIFI synced it with a machine on the same network.

Cheers

No, it is not WiFi synced.

Very Interesting.
I always thought that the iphone is switching of the WLAN to save power and only from time to time at an interval which is much to long for presence detect is waking up to check for events.

Question: with your approach what reaction time are you getting. Stated differently, how long f.e. does it take when you are in the reach of your Wifi home network to detect that you are at home?

I have the NetworkHealth binding setup to poll every minute (“networkhealth:refresh=60000” in openhab.cfg). So to answer your question it takes at most one minute to detect the iphone. While not perfect, I find that this works very well. I could try something quicker like 30 seconds, but I’m not sure how such a quick interval would affect the battery life and one minute is quick enough for me. I have not noticed any difference in battery so far.

I also confirmed the one minute detection by pinging the iphone from my computer. Every minute when the NetworkHealth binding sends the hping3 command to the iphone I see 5 pings get through. Then the iphone doesn’t respond to pings until the NetworkHealth binding polls again.

I have some rules such as open door alerts when no one is home. For these, I just make sure no one is home for at least one minute before sending out an alert.

[quote=“martin_klimke, post:4, topic:3825, full:true”]
I always thought that the iphone is switching of the WLAN to save power and only from time to time at an interval which is much to long for presence detect is waking up to check for events.[/quote]

That exactly is what I see with my son’s iPhone, so for me it’s useless for home presence detection.
Our Android phones can be switched to “Wifi always on” and that is a lot better, didn’t find this option yet on iOS.

Interesting. Thanks for the sharing this.

Although you say it explicitly, it wasn’t completely clear to me at first that you’re using hping3 only to wake up the phone and still using ping/ICMP to detect presence.

I’ve seen reports that this only works in the first 30 minutes after the iPhone goes to sleep. The theory is that after 30 minutes the phone goes into a deep sleep and doesn’t respond to 5353.

I plan to do more experimentation with this because I’d really like this to work. I live in a building with concrete walls and Bluetooth LE range is very short so that’s not an option for me.

I’m also wondering about the effect on battery life.

Yeah, my modified version of the NetworkHealth binding can execute a command before doing the ICMP ping. The command I use to wake the iphone is the hping3 command.

The affects on batttery life with a one minute hping3/ping interval is negligible right now. At least I haven’t noticed any difference.

After 30 minutes this approach still works fine. However, looking at the graph from OpenHab over the past day where the phone should have been present the whole time, I do see some missed pings. There is also a 12 hour interval where there are no missed pings at all.

And looking at the hourly graph, you see this is just for one minute intervals:

I’m thinking that sometimes the 3 UDP packets that get sent sometimes get dropped. I am going to modify the hping3 command to send out more packets. Hopefully, this will fix the blips where there is one minute of no presence: hping3 -2 -c 10 -i u1 -p 5353 james_phone_ip

1 Minute reaction time is worse than what I am getting if I am using IFTTT and signaling via myopenhab.
The disadvantage though is that myopenhab is sometimes down and thereby the reliability is not so great.

IFTTT sounds like a good solution, too, but I would need access to the phone. The iPhone is actually my wife’s phone and she doesn’t want me installing anything on it. So using the NetworkHealth binding is much more transparent with no setup on the iPhone end. Plus, I can also setup presence for the person who cleans our house so she won’t trip any alerts. This works because I know she setup her phone to use my Wifi.

I’m having mixed results. I have an iPhone 5s and it work sometimes but not reliably. When it works, the subsequent ping latency is sometimes multiple seconds. That makes me think the phone is is probably in some low power (reduced clock speed?) mode and processing the network inputs much more slowly than usual. My wife is using an iPhone 4 and this technique doesn’t with that phone. We’re both running iOS 9.

It would also be an issue if I need root permissions to run hping3 since my openHAB server doesn’t run with those privileges. Maybe I can write a program to send the UDP packet without hping3.

My wife has an iPhone 6. We also have an ipod touch that looks like an iPhone 5. I have no idea what generation it is, but I am going to test pinging that device, too.

Yeah, I actually have sudo in my binding config command. On Ubuntu, I just created a sudoers file to allow openhab to run hping3.
/etc/sudoers.d/hping3:
openhab ALL=NOPASSWD: /usr/sbin/hping3

Jmckenna how quickly do find the IPhone is detected when returning to the house/wifi network?

Worst case, one minute with my current configuration.

Hi jmckenna, thanks a lot for sharing this solution!
I would really like to try this myself because iBeacons are proving troublesome for me :frowning:
However, I don’t have access to a development environment where I could perform the modifications on the binding source. Could you maybe share your compiled binding file?

I would also appreciate that!

Maybe I’m wrong but I think Network Health Binding main usage is to detect presence by pinging Phones or other devices. Would it be possible to make it officially iPhone compatible?

The problem is NOT the binding, the problem is the iPhone, because it goes to sleep mode during wifi activity.
Android phones and all other devices are working great with the binding.

So the question is, can Apple make the iPhone compatible!

I understand you sihui, but I think Apple drives the world of smartphones so we need to adapt to her.

The patch above is meant to be generic and not specific to iPhones. You can include a command in the binding config to execute before each ping. The command I used was to wake up an Apple device.

That being said, I can’t think of any other use cases where executing a command before a ping would be helpful. And with so many Apple devices out there (iPhones, iPods, iPads), maybe the NetworkHealth binding should officially support these types of devices.

Apple surely isn’t going to bend over backwards to accommodate OpenHab.