iPhone Presence Detection with hping3 and ARP

I have improved my iPhone detection, based on the above information. The solution allows detection of multiple phones. You can integrate this in your current presence solution.

Required steps:

  1. Install hping3
  2. Allow use of sudo commands
  3. Create script
  4. Create items and rules

Install hping3

sudo apt-get install hping3

Allow use of sudo commands

Instead of adding directly to the sudoers file, I decided to add an inlude file /etc/sudoers.d/openhab, that is automatically picked up by the system.

sudo visudo -f /etc/sudoers.d/openhab

Paste this text in the file:

/etc/sudoers.d/openhab

openhab ALL=(ALL) NOPASSWD: /sbin/ip
openhab ALL=(ALL) NOPASSWD: /usr/sbin/hping3

If you use openHABian or a regular pi, and like to use the script from the commandline, you can duplicate these lines and replace openhab with openhabian or pi respectively.

Create script

The script iphone.sh will hping3 the iphone to wake it from deep sleep. It is required to be pinged more than once. Instead of retrying through a rule as posted above, I decided to loop in the script itself. The script is called by a “Time cron” rule in openHAB at an interval depending on your needs. Mine is 5 minutes. I have tried 1 minute. This works, but will have more impact on your phone’s battery life. The script will try 20 times (this is configurable in the script) to ping the device. If it still fails, the device is considered ‘away’.

The script can best be located in the OH scripts folder. For an apt-get based installation, including openHABian, this is /etc/openhab2/scripts.

cd /etc/openhab2/scripts
touch iphone.sh
chmod +x iphone.sh
nano iphone.sh

/etc/openhab2/scripts/iphone.sh

#!/bin/bash

# detect iphone by IP and MAC address.
# use MAC address too, to prevent false positives if IP might change
# return ON or OFF so output can be directly bound to a switch item

# number of retries, less is faster, but less accurate
MAXRETRIES=20

# exit immediately if no parameters supplied
if [ $# -lt 2 ]
  then
    echo "UNDEF"
    exit 1
fi

# Set variables
IP=$1
MAC=$2

COUNT=0
while [ ${COUNT} -lt ${MAXRETRIES} ];
do

  # Change dev and eth0 if needed
  sudo ip neigh flush dev eth0 ${IP}

  sudo hping3 -q -2 -c 10 -p 5353 -i u1 ${IP} >/dev/null 2>&1
  sleep .1

  # Only arp specific device, grep for a mac-address
  STATUS=`arp -an ${IP} | awk '{print $4}' | grep "${MAC}"`

  if [ ${#STATUS} -eq 17 ]; then
      # exit when phone is detected
      echo "ON"
      exit 0
  fi
  let COUNT=COUNT+1
  sleep .1
done

# consider away if reached max retries 
echo "OFF"

Create items and rules

iphone.items

Switch Phone1 "iPhone 1 [%s]" <present>
Switch Phone2 "iPhone 2 [%s]" <present>
Switch Phone3 "iPhone 3 [%s]" <present>

iphone.rules

rule "Find My iPhone"
when
    Time cron "0 */5 * * * ? *" // every 5 minutes
then
    val Number timeout = 10*1000 // give the script some time to finish, to capture the result

    // replace 0.0.0.0 with your ip address
    // replace 00:00:00:00:00:00 with your mac address
    var String result1 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)
    var String result2 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)
    var String result3 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)

    Phone1.postUpdate(result1)
    Phone2.postUpdate(result2)
    Phone3.postUpdate(result3)

end

TODO:

  • DRY: use lambdas for reusing code per phone.
  • get Exec2 binding working to replace current cron based rule
  • optimize detection timing
  • (perhaps) use REST api to sets items directly (with $3 for item)
3 Likes