iPhone Presence Detection with hping3 and ARP

Hi!

Thanks for the script @Maurits28 . This helped me out a lot and improved my presence, I’m using a modified version, posting data using MQTT. Basically I have followed @rlkoshak presence guide: Fine tuning of wifi presence detection

Thought I would share it in case someone is looking to modify in a similar manner

Some differences

  • Loop to be able to do presence for multiple devices, I run this on a standalone Linux Server
  • I only flush the arp table for the device I’m looking for (not all, since my server have a lot of arp entries)
  • I look the MAC-address up, you only need to know the IP, which could be good and bad, this solution might not be as robust
  • I only arp the specified device, probably not an issue to do arp -an for all.
  • Relaying on mosquitto_pub to publish messages, which is a bit hard coded.
#!/bin/bash

 #Enter your ip of the devices here, separator space
DEVICES="10.1.1.20 10.1.1.21"
MQTT_SERVER="some ip"

for i in `echo $DEVICES`; do
    # Change dev and eth1 if needed
    ip neigh flush dev eth1 $i
    hping3 -2 -c 10 -p 5353 -i u1 $i -q >/dev/null 2>&1
    sleep 1
    # Only arp specific device, grep for a mac-address
    status=`arp -an $i | awk '{print $4}' | grep "..:..:..:..:..:.."`
    statusMessage="OFF"
    #A mac will be 17 characters including the ":"
    if [ ${#status} -eq 17 ]; then
        echo "Phone $i is detected!"
        statusMessage="ON"
    else
        echo "Phone $i is not present"
        statusMessage="OFF"
    fi
    if [ $i == "10.1.1.20" ]; then
        mosquitto_pub -h $MQTT_SERVER -m $statusMessage -t presence_sensors/network/person1 -q 1
	echo "Pub: $statusMessage -t presence_sensors/network/person1 -q 1"
    fi
    if [ $i == "10.1.1.21" ]; then
	mosquitto_pub -h $MQTT_SERVER -m $statusMessage -t presence_sensors/network/person2 -q 1
	echo "Pub: $statusMessage -t presence_sensors/network/person2 -q 1"
    fi  
done
3 Likes

hping3 doesn’t appear to be waking up my wife’s iphone 5s, the arp tables shows an incomplete mac address 90% of the time:

root@openhab:/opt/openhab# hping3 -2 -c 10 -p 5353 -i u1 192.168.2.19 -q
HPING 192.168.2.19 (wlan0 192.168.2.19): udp mode set, 28 headers + 0 data bytes

--- 192.168.2.19 hping statistic ---
10 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms



root@openhab:/opt/openhab# arp -an
? (192.168.2.19) at <incomplete> on wlan0

Any ideas?

I have problems a iPhone 6 is put to deep sleep, Currently I have a timer set to 19 mins delay before it is marked as not present. With the 19 mins delay it is working.

My detection with iPhone 6 is not flawless yet either, I also will include a delay to make the feedback more robust. I have been doing quite some research, but did not find yet the solution to solve this satisfactorily.

Edit: since this post the initial post has been updated and the presence detection works well now.

For whatever it is worth, we have a iphone7 and one of the latest nexus android phones and both show drop-outs with this method that I cannot reproduce yet. I have to use this script with the Nexus android phone and iphone7 (both phone are fully updated) as the network binding does not work for my android phone (seems to have same no-reponse-to-ping issue, when sleeping). I’ve played around with some parameters, but to make the iphone respond reliably takes much longer and more frequent hping3’s than in the current script…tried it only manually as not to ruin battery life…but frequent hping3 pings for about a 1min or 2 seem to be necessary at times. Android seems to be less reliable. At times the presence works rock-solid for both phones at times it does not work at all…mmmhhh

I have a One Plus 3 Android Phone (Android 6.0.1), for that one I need a timeout of 10 mins, compared to the 19 with the iPhone 6.
I have a SetPoint in my sitemap for the timeout, so can easily adjust the timers.
Usually I notice timeouts during the night, but for the last couple of days it has been stable using those settings.

@Seaside Your script is great and seems to work flawlessly for users. However, not everyone uses MQTT or should be forced to install a broker. Would you alternatively add a way to communicate with the openHAB REST API? Integration via stdout might also be possible. Unsure how to best handle the multiple devices feature that way. I’d probably move the loop to openHAB but I get that your goal was different.

Would be great to finally have ONE solution for all users :sparkles:

@Maurits28 maybe you could update the first posting with this solution. Setting of the privileges should be less insecure by following this recommendation.

1 Like

@Maurits28 Thank you for your example of presence detection for the iPhone.
I tried your solution, and i run into, i think, a permission error:

[12:06:20] pi@openHABianPi:/opt$ ./IphoneD.sh
Failed to send flush request: Operation not permitted
Away

It relates to the command in your script:

ip neigh flush all

Besides this, i got the feeling that the changing of Permissions in your description might be wrong?

Now you need to change the permissions:

sudo chown 777 IphoneD.sh

I do not know of a user-name 777, so i changed that command to:

sudo chown openhab IphoneD.sh
sudo chmod +x IphoneD.sh

Further, with sudo visudo, i gave both my users (pi and openhab) the rights to execute the commands ip and hping3:

openhab ALL=NOPASSWD: /sbin/ip
pi ALL=NOPASSWD: /sbin/ip
openhab ALL=NOPASSWD: /usr/sbin/hping3
pi ALL=NOPASSWD: /usr/sbin/hping3

I am able to perform the command ip neigh from the command prompt, but when i add the flush all argument i receive the error message.

[12:22:47] pi@openHABianPi:/opt$ ip neigh
192.168.178.40 dev eth0  FAILED
192.168.178.22 dev eth0 lladdr 00:27:02:10:7b:74 STALE
192.168.178.130 dev eth0  FAILED
(lines left out)
fe80::5e49:79ff:fe51:781c dev eth0 lladdr 5c:49:79:51:78:1c router STALE
fe80::ca60:ff:fe1b:fb46 dev eth0  FAILED
2001:982:2a43:1:4819:72f3:34e6:1afb dev eth0  FAILED
[12:24:12] pi@openHABianPi:/opt$ ip neigh flush all
Failed to send flush request: Operation not permitted
[12:24:20] pi@openHABianPi:/opt$

Btw, my setup is running on an RPi2.
Any idea what is wrong with my setup?

Hi @deltabert ,

Thanks for your comments, I recently upgraded to openHABian 2.0.0 and just put presence detection back in business again :sweat_smile:

Good point, I changed / added both your suggestions in the initial post.

I have /bin/ip and /bin/bash in the sudo visudo file for user openhab.

Try executing the script (or ip neigh flush dev eth0 xxx.xxx.xxx.xxx for testing) with sudo:
sudo bash /opt/IphoneD.sh.

I’m not an expert on this, but is your network device linked to eth0? You can get more info with sudo ifconfig. Did you change the ip address in the IphoneD.sh script to the ip address of your Iphone? You can also try to use the updated solution that you can find at the top of the page.

Let me know how you progress with it.

@Seaside, @ThomDietrich On another note, I updated the initial solution at the top of the page with the great work @Seaside has done, I just need to add the timeout rule of 19 minutes.

1 Like

I have a problem with the hping3 command. This is not working (iPhone 5S).
At first i thought it is the hping3 program itself, but that is working OK in my network:

[11:37:01] pi@openHABianPi:~$ sudo hping3 -S 192.168.178.40 -c 2 -p 80
HPING 192.168.178.40 (eth0 192.168.178.40): S set, 40 headers + 0 data bytes
len=46 ip=192.168.178.40 ttl=64 id=59134 sport=80 flags=RA seq=0 win=0 rtt=638.2 ms
len=46 ip=192.168.178.40 ttl=64 id=11411 sport=80 flags=RA seq=1 win=0 rtt=17.9 ms

--- 192.168.178.40 hping statistic ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 17.9/328.0/638.2 ms

This is a command send to my iPhone, which is awake btw.
But sending the command from the tutorial to the iPhone (still awake) gives me this:

[11:39:58] pi@openHABianPi:~$ sudo hping3 -2 -c 10 -p 5353 -i u1 192.168.178.40 -q
HPING 192.168.178.40 (eth0 192.168.178.40): udp mode set, 28 headers + 0 data bytes

--- 192.168.178.40 hping statistic ---
10 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
[11:41:26] pi@openHABianPi:~$

Any ideas?
EDIT-1. I tried also with an Android phone (LG-OB) and even with my NAS, both gave the same results… So, it is not related to the iPhone 5S, which i read somewhere…

Hi Bert,

The feedback from hping is ok, it is only meant to wake up the Iphone, I get the same ‘100% packet loss’.

I just updated the initial post with the rule to allow for a time-out before setting presence status to ‘Away’. I tested it a few days and it seems to be working well.

Side note: I would be interested to hear from you which is according to your experience the time-out needed (currently at 19 tries).

@Maurits28: Thanks for your script. I’ve a comprehensive question: I like the approach of the maxcounter timeout. But when I understand the rule right, this means that after a “real” absence (so no false non-presence) it takes 19 minutes (as the script is only executed once a minute) until the presence switch is finally switched to OFF. So I have a quite big delay after leaving the house (in case I’m further using the on/off presence switch for other things (alarm activation etc.).

And another question:
I’ve just created the script and all necessary settings. Just for testing I did several starts of the script from the shell. I have defined two IPs, one is present at the moment (.37), the other one is abscent (.32). .32 is reported correctly every time, but .37 only gets one correct state in about 10 tries. Is this “normal behavior”?

[15:00:30] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:00:33] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:00:36] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:00:39] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:00:42] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:09:45] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:09:50] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:10:06] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is detected!
Phone 192.168.178.32 is not present
[15:10:10] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:10:14] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:10:18] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:10:22] pi@openHABianPi:/opt$ sudo ./IphoneD.sh
Phone 192.168.178.37 is not present
Phone 192.168.178.32 is not present
[15:10:35] pi@openHABianPi:/opt$

EDIT: I’ve done a little more research. Both iPhones (5s and SE) are only detected when they are in active use. But as soon as I lay them down and the screen goes off, the detection doesn’t work anymore (or everything but reliable).

Any thoughts? Does this mean that the hping3 isn’t working? hping3 is installed and I can run hping3 from bash.

Hi Stefan, your understanding is correct, and to prevent ‘false’ away detections a time-out of 19 is in use. The issue with the Iphone is that when it is in sleep mode, it is hard to wake it up. For that we use Hping3, to wake it up just before we detect it’s presence via arp. Even then it does not always wake up, hence the ‘time-out threshold’ of 19 tries.

I see that you tried once every few seconds, my experience with such an interval is also that it does not detect the iPhone lots of times. You might want to try an interval of 30 seconds instead of one minute, and see empirically how many times you’ll need to have a reliable presence detection. Post your results in this thread so we can learn from your experience! :slight_smile:

The ip neigh command requires dig which is not installed on an RPi by default. Use sudo apt-get update && sudo apt-get install dnsutils first.

Hello,

Thanks for the material. I am installing openHAB2 and found your script educational. My question to you is why don’t you use the arp command to obtain the IP address of Apple devices since the MAC addresses of the latter have a well known prefix. (ref: https://nmap.org/book/nmap-mac-prefixes.html.

Regards.

Hi Matha

It is an interesting thought, however for me personally the additional work needed to implement does not weigh against the advantages. And I see a few things that still would need ‘manual’ work:

  • Assigning of a specific Apple device to a specific person in your household
  • You do not want to track all Apple devices like AppleTV, iPad, MacBook as you are interested in the fact if somebody is in the house or not.

But hey, why don’t you give it a try and tell us how it works? There are always users that would benefit from your proposed solution.

Cheers!

Hi seaside,
I like your shared bash script, I turn to your script as it can check multi iPhone in single run,
after few days operate, I think it’s not necessary to flush arp in every run

ip neigh flush dev eth1 $i

, I use crontab to flush arp every 10 minutes and run your script every minute, in openhub items time out in 4 minutes, result is pretty accurate, may be 1 time false “away” per day, as my home only got 1 door to leave, so door must open when someone leave, than I set the offline rules turn TRUE if door also opened within 10 minutes which makes the detection 100% right in my home. you may also go for a try.

1 Like

Hi,

I am really thankful for this thread (surprisingly it seems that there is not much discussion going on in the web at all about this topic) as it seems to be the only way that works in a stable manner.
I chose perl to implement the above concept on a Raspi2. I am sure the script can be improved (I am not exactly a perl expert), but it works fine for me.

Input: IP Address
Output: 1=IP detected, 0=IP not deteced

I use 4 cron scripts in parallel (every 5 minutes) to detect presence of the 4 phones in my household, each cron-script invoking the below’s perl script and depending on the result triggers an openhab-rest-api to set a switch…

Script to detect iphone “detect_ip.pl”:

#!/usr/bin/perl -w
#
# script will try to reach ip address 30 times (hping3 not always "wakes up" the iphone) 
# wait time between the pings: a random number of 1 - 10 secs (would probably work if you fix the sleep to 10 secs as well)
#
#
# Input-Parameter: Ip-Address
# Output-Parameter: 1 = ip-device deteced, 0 = not detected
#


use strict;

my $ip = $ARGV[0];
if (not defined $ip) { die "no IP address!\n"; }

exit (&checkIPpresence);




sub checkIPpresence {

  # first flush arp entry for ip
  system "sudo ip neigh flush dev eth0 $ip";

  #### hping3 not always wakes up iphone - trying 30 times should be enough to be stable...

  my $counter = 30;

  while ($counter > 0) {

     $counter--;

     # hping3 to ip
     system "sudo hping3 -2 -c 10 -p 5353 -i u1 $ip -q >/dev/null 2>/dev/null";

     # wait 0.5 sec then check if mac address shows up in arp
     select(undef, undef, undef, 0.5);

     if (&checkARP) {
        return 1;  # 1 = true, 0 = false;
     }

     #wait between 1 and 10 secs then arp-check if mac is found
     sleep (1 + int rand(10));

  }

  return 0;  # 1 = true, 0 = false;

}



sub checkARP {

  my $arp = "sudo arp -an $ip";

  open (ARP, "$arp |") or die "cannot run arp '$arp'\n$!\n";

  my $i = 0;

  if ( defined (my $line = <ARP>) ) {
     chomp($line);
     close ARP;

     if ($line =~ /(.+:.+:.+:.+:.+:.+)/) {
          return 1;  # 1 = true, 0 = false;
     }

   }

   close ARP;
   return 0;  # 1 = true, 0 = false;

}

The cron-scripts executed every 5:

#!/usr/bin/perl -w

use strict;
use warnings;
use Data::Dumper;

# define openhab server IP, openhab switch-item name, and ip of iphone to be checked
my $openhabServerIP = "<your openhab server ip address>";
my $openhab_item = "<your openhab switch>";    
my $ip = "<ip address of iphone to be tested>";

# define system commands to turn switch on/off and IP of openhab server
my $openhabON  = "curl --header \"Content-Type: text/plain\" --request POST --data 'ON' http://$openhabServerIP:8080/rest/items/$openhab_item";
my $openhabOFF = "curl --header \"Content-Type: text/plain\" --request POST --data 'OFF' http://$openhabServerIP:8080/rest/items/$openhab_item";

# run the detection script and pass along the ip address of the phone,
# depending on the return code, set openhab item to on / off
system ("/home/pi/perl.sources/find_iphone/detect_ip.pl $ip");
my $returnCode = $? >> 8;
if ($returnCode) {      system ("$openhabON"); }
else {                  system ("$openhabOFF"); }

Dear peter_h!
Can you provide your cron script too. I am not good in Linux, so I need to use this script to turn Switch using rest api on windows Openhab server.
Also, what i need to add to the clean Raspberian to use that script? About the same way I worked with 1-wire, so I have a additional raspberry server with only owfs.