12

How can I have my Raspberry Pi continually scan for a MAC address on my network then perform a task when it detects a specific MAC address?

I use my Pi to run various home automation tasks. I'd like it to perform a task when it senses that my phone connects to the Wi-Fi, (meaning I just got home). What is the best way to have the Pi scan for my phone's MAC Address continually? I know I can use arp-scan to scan for MAC Addresses, but I need this to be continually scanning. I'm sure there's a better method then having arp-scan run in a loop, or using cron to have it run every minute.

Darth Vader
  • 4,206
  • 24
  • 45
  • 69
gsears
  • 123
  • 1
  • 7
  • 2
    If you don´t need realtime response cron is the best way. First you write script which return something like true or false in sh (status code is he best option to privide response) then script which will store previous value somewhere to /tmp and check for change. If change happend it will log it. – Misaz May 30 '17 at 19:36

2 Answers2

3

Why not add a reservation to your phone/s ip address and simply do a ping via a cron job every few minutes.

    #!/bin/bash
    HOSTS="x.x.x.1 x.x.x.2"
    COUNT=10
    for myHost in $HOSTS
    do
      count=$(ping -c $COUNT $myHost | grep 'received' | awk -F',' '{ print $2 }' | awk '{ print $1 }')
      if [ $count -eq 10 ]; then
        # 100% response
        # let the cat out
      fi
    done

I accomplish this on my rpi using fhem, it has a built in presence detection module that lets me set resident statuses that can be queried to influence home automation tasks.

paj
  • 311
  • 1
  • 6
3

PhoneHome

My phone acts differently, as all phones has some difference. A ping response only happens if the phone is awake. If the Pi is rebooted and the phone is in sleep mode, multiple pings will place its ip and mac addresses in the arp table, with 100% packet loss. I just learned that the arp command is obsolete, ip neighbor is used now.

pi@RPi0:~ $ ip neighbor
169.254.65.43 dev eth0 lladdr 64:31:00:00:00:00 REACHABLE
192.168.0.1 dev wlan0 lladdr ac:b3:00:00:00:00 STALE
fe80::aeb3:13ff:fe00:000 dev wlan0 lladdr ac:b3:00:00:00:00 router STALE

pi@RPi0:~ $ ping 192.168.0.22
PING 192.168.0.22 (192.168.0.22) 56(84) bytes of data.
From 192.168.0.10 icmp_seq=1 Destination Host Unreachable
From 192.168.0.10 icmp_seq=2 Destination Host Unreachable
From 192.168.0.10 icmp_seq=3 Destination Host Unreachable
--- 192.168.0.22 ping statistics ---
34 packets transmitted, 0 received, +3 errors, 100% packet loss, time 34303ms

pi@RPi0:~ $ ip neighbor
192.168.0.1 dev wlan0 lladdr ac:b3:00:00:00:00 REACHABLE
169.254.65.43 dev eth0 lladdr 64:31:00:00:00:00 REACHABLE
192.168.0.22 dev wlan0 lladdr ac:37:00:00:00:00 REACHABLE
fe80::aeb3:13ff:fe00:000 dev wlan0 lladdr ac:b3:00:00:00:00 router STALE

After testing, my solution would be to have two loops inside of a forever loop. the first inside loop would be to do a ping on a range of ip addresses, multiple times, that would be possible for my phone. My router has reserved the first 19 ip address and I may have about a half dozen address that DHCP will assign, including my phone, starting at address 192.168.0.20. I will ping a dozen ip address once, in background mode, wait one second for response, and throw the results away as junk. I will wait eight seconds on the arp table, and run the ip neighbor command, grep the mac address for the ip address. The router and phone will keep this same ip address unless something unusual happens. The arp table will remain in the Pi, but will change states from REACHABLE, STALE, and FAILED from pings and time.

The second inside loop will ping and check the arp table every five minutes to determine if the phone is at home. With three ping 'FAILED' in a row, the phone is not at home. One 'REACHABLE', when phone is not at home, will make the phone return home (do something). There are checks to validate the ip address and return to the first inside loop if corrections are required.

#!/bin/bash
# A script to do something when Phone returns Home.

mac="ac:37:00:00:00:00"    # Your phone mac address
ip_addr=""                 # Leave blank or ip for test
network="192.168.0.0"      # Your network (Class C only)
range="20 32"              # ip address possible range
pgm='echo "do something"'  # program to exec when Phone returns Home

start=$(echo "$range" | cut -d " " -f1)
stop=$(echo "$range" | cut -d " " -f2)
network=$(echo "$network" | cut -d. -f1-3)

echo "Start  $(date)"
while [ 1 ]; do
    cnt=0
    fail=0
    [ "$ip_addr" ] || while [ ! "$ip_addr" ]; do
        for x in $(seq "$start" "$stop"); do
            (junk=$(ping -c1 -W1 "$network"."$x") & )
            wait
        done
        sleep 8
        ip_addr=$(ip neighbor | grep "$mac" | cut -d " " -f1)
        ((cnt++))
        if (( $cnt > 15 )); then
            cnt=0
            echo "--- Phone not Home  $(date)"
            sleep 300      # 5 minutes
        fi
        if [ "$ip_addr" ]; then
            echo "--- Phone is Home, Count = $cnt, Date = $(date)"
            echo "Phone ip = $ip_addr  mac = $mac"
        fi
    done

    while [ "$ip_addr" ]; do
        junk="$(ping -c1 -W1 $ip_addr)"
        sleep 8
        home_nw="$(ip neighbor | grep $ip_addr | cut -d ' ' -f 1,5,6)"
        echo "$home_nw - $(date)"
        is_home=$(echo "$home_nw" | cut -d " " -f3)
        if [ "$is_home" == "REACHABLE" ] && (( "$fail" >= 3 )); then
            echo "--- Phone returned Home - $(date)"
            $pgm
        fi
        [ "$is_home" == "REACHABLE" ] && fail=0
        mac_stat=$(echo "$home_nw" | cut -d " " -f2)
        if [ "$mac_stat" == "FAILED" ]; then
            (( "$fail" < 10 )) && ((fail++))
            ip_test="$(ip neighbor | grep $mac | cut -d ' ' -f1)"
            if [ "$ip_test" ]; then
                [ "$ip_test" == "$ip_addr" ] || ip_addr=""
            fi
            if (( "$fail" == 3 )); then
                echo "--- Phone not at Home  $(date)"
            fi
        else
            if [ "$mac_stat" != "$mac" ]; then
                ip_addr=""
            fi
        fi
        sleep 300          # 5 minutes
    done
done
bstipe
  • 534
  • 3
  • 5