My name is Aaron Tinsley

An eclectic collection of thoughts and projects on a variety of (mostly) tech topics.

OpenHAB Presence Detection with a DD-WRT router

WRT54G Router

Above photo of WRT54G by webhamster / Foter / CC BY

Overview of Presence Detection

Presence detection is an essential part of any home automation system, but it’s also one that many people struggle to get right.  I went through several iterations of presence detection for my OpenHAB system, but was ultimately unimpressed with the performance and/or reliability of each of my previous methods.  The method described in this post has achieved 100% reliability over the past six months and triggers no more than 2 seconds after I’ve connected to Wi-Fi.  Awesome!

The following script is constantly running on my DD-WRT router and scanning the routing table for the MAC addresses of our cell phones and our TV (so my home automation system knows when we’re watching TV).  When any of the devices connect or disconnect, the script notifies my OpenHAB system so it can respond accordingly.

I discovered the base script in a forum after a lot of Googling methods of presence detection, but it was originally designed for a router with a Broadcom chipset and would update an Insteon ISY-994.  I modified it to work instead with OpenHAB and to run on my Atheros router.

Here is the link to the forum post that I found the original script on.

OpenHAB Setup

You’ll need to create an OpenHAB switch item for each device you want to track.  This switch can be turned ON or OFF.  The DD-WRT script will turn the switch ON when you get home and OFF when you leave. Here’s an example of how to create the item.  Put it in your .items file.

Switch aaronPhone

You can then react to changes in its status by creating rules like the following examples.

rule "Aaron arrives home"
when
	Item aaronPhone received command ON
then
	//Put your actions here
	sendCommand(Light_GF_Living_Room, ON) //Turns the living room lamp on
end
rule "Aaron leaves home"
when
	Item aaronPhone received command OFF
then
	//Put your actions here
	sendCommand(Light_GF_Living_Room, OFF) //Turns the living room lamp on
end

DD-WRT Script

If you have a Broadcom router, make the following modification to my script below.  Replace

maclist=$(wl_atheros -i ath0 assoclist | cut -d" " -f2)

with

maclist=$(wl -i $(nvram get wl0_ifname) assoclist | cut -d" " -f2)

Paste the following code into your DD-WRT web control panel under the Administration > Commands tab.  You’ll then want to click the “Save Startup” button so the script runs as soon as your DD-WRT router boots.  There are several places you’ll need to modify for your specific setup, so make sure you read each line carefully.

Warning: This script does not use SSL. You should only use it on your own internal home network (with a password on your Wi-Fi!). If you need to notify a remote OpenHAB installation, you’ll want to modify the script to use SSL.
#!/bin/sh
###############################################################
# Proximity detection
#
# A script designed to run on a router running DD-WRT to detect certain devices connected to the router.
# It runs at startup and runs continually, checking for a specific list of devices (phones/laptop, etc)
# that connect wirelessly to the router.  Once a device is connected, the OpenHAB status will
# be updated with either an ON or OFF.  Make sure you set up a switch item in OpenHAB for each device
# you want to track.
#
# The searching frequency can be adjusted to be slower/faster depending on your requirements. Searching too fast
# could burden your router.  Too slow might not update the status as necessary for the application.
#
 
  
# Make changes below
# MAC address of each device to watch. Don't leave blank. 
# For security purposes, if your router requires a password, even if someone could clone the MAC of your 
# phone, they would still require the password of your network to link to your router. 
macdevice1="00:00:00:00:00:00"    #Aaron Phone
macdevice2="00:00:00:00:00:00"    #Device 2
macdevice3="00:00:00:00:00:00"    #Device 3
macdevice4="00:00:00:00:00:00"    #Device 4
 
#OpenHAB username, password, and IP Address
username="OPENHAB_USERNAME"
password="OPENHAB_PASSWORD"
IPAddr="OPENHAB_IP_ADDRESS"
port="OPENHAB_PORT"
 
# OpenHAB switch items to be updated for each tracked MAC
item1="aaronPhone"
item2="DEVICE_2"
item3="DEVICE_3"
item4="DEVICE_4"
 
 
# Occupied and unoccupied delay in seconds to check status
# Adjust for shorter/longer wait times.  For instance, when one device is already 
# connected, you might want to check less frequently.  This could also delay the 
# notification of a disconnect.
delay_occupied=4
delay_unoccupied=2
 
# initial testing loop count - uncomment the counter near the bottom of the script for testing only. 
limit=120
 
###############################################
# do not change below here
###############################################
 
sleep
#initialize internal variables
 
# status of each MAC. 0=disconnected. 1=connected.  -1 initially forces isy update first loop
macconnected1=-1
macconnected2=-1
macconnected3=-1
macconnected4=-1
connected=-1
# total number of currently conencted devices.   
currentconnected=0
counter=1
 
# Initial testing loop.  Will run continually after testing is complete
while [ $counter -lt $limit ]; do
 
#maclist stored mac listing in router from status screen
maclist=$(wl_atheros -i ath0 assoclist | cut -d" " -f2)
 
#reset current status. Two variables are used for each device.  The past known status and the current 
# status.  Only a change is reported to the ISY.  Otherwise, it would constantly be updating the ISY with 
# the current status creating unnecessary traffic for both the router and the ISY
maccurrent1=0;
maccurrent2=0;
maccurrent3=0;
maccurrent4=0;
 
 
# compare each device that is currently connected to the MAC devices we want to watch.
for mac in $maclist; do
case $mac in
   "$macdevice1") maccurrent1=1;;
   "$macdevice2") maccurrent2=1;;
   "$macdevice3") maccurrent3=1;;
   "$macdevice4") maccurrent4=1;;
esac
done
 
# Look for a change in status from the old known to the current status.
# If it changed, update the ISY. Otherwise it leaves it as is. 
if [ $macconnected1 -ne $maccurrent1 ]; then
     if [ $maccurrent1 -eq 1 ]; then
         macstatus1="ON";
     else
         macstatus1="OFF";
     fi
     curl -X POST -d $macstatus1 -H "Content-Type: text/plain" -i http://$username:$password@$IPAddr:$port/rest/items/$item1
fi
 
if [ $macconnected2 -ne $maccurrent2 ]; then
     if [ $maccurrent2 -eq 1 ]; then
         macstatus2="ON";
     else
         macstatus2="OFF";
     fi
     curl -X POST -d $macstatus2 -H "Content-Type: text/plain" -i http://$username:$password@$IPAddr:$port/rest/items/$item2
fi
 
if [ $macconnected3 -ne $maccurrent3 ]; then
     if [ $maccurrent3 -eq 1 ]; then
         macstatus3="ON";
     else
         macstatus3="OFF";
     fi
     curl -X POST -d $macstatus3 -H "Content-Type: text/plain" -i http://$username:$password@$IPAddr:$port/rest/items/$item3
fi
   
if [ $macconnected4 -ne $maccurrent4 ]; then
     if [ $maccurrent4 -eq 1 ]; then
         macstatus4="ON";
     else
         macstatus4="OFF";
     fi
     curl -X POST -d $macstatus4 -H "Content-Type: text/plain" -i http://$username:$password@$IPAddr:$port/rest/items/$item4
fi
 
# Update the known status from the current.  Ready for the next loop. 
macconnected1=$maccurrent1;
macconnected2=$maccurrent2;
macconnected3=$maccurrent3;
macconnected4=$maccurrent4;
 
# Total up the number of devices connected. 
let currentconnected=$macconnected1+$macconnected2+$macconnected3+$macconnected4
 
connected=$currentconnected
 
# Delay (sleep) depending on the connection status. 
# No devices connected could delay less.  Once a device is connected, it could delay longer. 
if [ $connected -gt 0 ]; then
    sleep $delay_occupied
    else
    sleep $delay_occupied
fi
 
#for testing only - uncomment to have the looping stop at X loops defined in variable:  limit. 
#let counter=$counter+1 
done

38 comments

  1. I did something very similar in my DDWrt device and its really a comfortable presence detection solution. But it has one big disadvantage: While my Android device enters the wifi area, it’s usually in deep sleep mode and does not connect to wifi. Even if policy is ‘WLAN on in standby’. I’m experimenting with tasker/automagic to wake up the device periodically when my home cell ids are detected, but thats an akku killer so far. How die you face the Problem of deep sleeping devices?

    1. Snyder,

      I’ve definitely faced that issue as well. The way I’ve dealt with it is that I have an “at home” profile in Tasker using the cell tower IDs, just like you have. It’s somewhat different, though, in that it doesn’t explicitly wake up the device, but just turns on WiFi. “At home” is used a loose term here. Generally my WiFi will turn on when I’m a couple miles from home, so the device hasn’t yet gone back to sleep when I pull in my driveway. It connects fairly quickly because of that.

  2. Hi Arron!
    Thanks for sharing this great work!
    I have slight problem, because im newb :)
    I save the script on my dd-wrt with that difference, that i commented 2 rows with openhab username and password, because im not using them, is that ok?

    Another thing,
    example:
    macdevice1=”00:00:00:00:00:00″- are quotes necessary?
    There is any update of switch status in my openhabs cmd window!
    Thank you

    1. Osmi,

      If you’re not using HTTP Auth with your OpenHAB installation, there are a couple steps you should take:
      1) Comment out lines 27 and 28 where the username and password are stored (you said you already did that)
      2) Make this change on lines 99, 108, 117, and 126: Change http://$username:$password@$IPAddr to http://$IPAddr

      To answer your second question, yes, you need quotes around the MAC address.

      Hope this helps!

      Aaron

      1. Thanks Aaron!
        I have made changes as you suggessted, but still it doesnt work.
        Somewhere on the net, i red that Optware should be installed on router, before script configuration.
        Is that true?

        1. Osmi,

          The only important package that you need is Curl. You can ssh into the router and try typing “curl” then enter. If the router complains that it’s an unknown command, you can use Optware to install it.

          Good luck! I hope you figure it out.

          1. Thank you Aaron!
            I installed Optware then Curl and…it’s working :)
            On the forum post from your link, is upgraded script for detecting wired clients.
            Thank you again!

  3. hi Aaron,

    Thank you so much for the script! I modified it to work on my tomato R7000 netgear router on 5Ghz in combination with Pimatic instead of Openhab.

    I was looking for such a solution, because i was using ping presence before, but that prevented my phone to go into deep sleep.

    happy days:)

  4. Hej Arron,

    I’m looking at your script to implement it in a ASUS router with Merlin.

    I guess it will not create any issue.

    Just 3 questions :

    – Line 144 should be delay unoccupied no??

    – I would not need to check for 4 devices but more for 2. What should be the best option for you? Commenting the lines or letting mac adresses at 0000….. ?

    – Last but not least I do not need to update each and every device. I just need to check if there is at least one device -> send a command
    Zero device -> Another command
    What do you think about that?

    Thx,

    1. MGB,

      1. Yes, you’re correct. That should have been unoccupied.
      2. I would recommend setting the MAC addresses to all 0s. I’ve done that in my own script.
      3. This will require a slight modification of the script. I use OpenHAB to trigger events based on phones coming and going. If you don’t want to use OpenHAB, you can place your event triggers in the if/else block on lines 141-145.

      Thanks,

      Aaron

  5. I spent most of my weekend working on getting this to work. After initial setup, I discovered that my DD-WRT build did not include curl, nor did any other builds for my router. DD-WRT also uses busybox, which provides the wget binary. Busybox’s stripped-down wget doesn’t support POST requests. I even tried getting it to install curl through ipkg (to a smb share on my local Ubuntu box, since the onboard memory of the router is too small to have any jffs space). No luck, curl didn’t like ulibc, and even following various instructions for DD-WRT ulibc fixes didn’t fix it.

    My final solution is kind of interesting. I have apache and curl on the Ubuntu box, so I decided to enable cgi-bin to LAN devices and wrote cgi scripts to send curl requests for each state for each LED (so I have 6 different .sh files for my 3 LED (for me and my 2 roomates)). Now all DD-WRT has to do is wget the correct page and Ubuntu uses curl to send the POST request to openhab. I am now seeing similar speed results compared to the author’s (within seconds of the device (dis)connecting, the LED changes state).

    The only thing left to do now is move the cgi server to the raspberry pi that is hosting openhab so the Ubuntu box will not be part of the equation any more. My thanks to the author for the router script, and for providing me a good starting point for openhab. I learned a lot about DD-WRT and openhab while getting this setup to work, and I came away with a finished solution. So thanks!

    1. Phoenix,

      There’s also an alternative solution (one that I wasn’t aware of until after I wrote this post). You can actually control OpenHAB items using a GET request, though it’s not currently noted in the OpenHAB documentation. You could use wget to directly control OpenHAB and cut out the “middleman”.

      The format for the address would be as follows.:
      http://$username:$password@$IPAddr:$port/CMD?$item1=$macconnected1

      Here’s an example URL:
      http://username:password@192.168.1.1:8080/CMD?Lights=ON

      Hope this helps! You can refer to my post about controlling OpenHAB items with the Amazon Echo for more information on controlling OpenHAB with GET instead of POST.

      Feel free to comment back if you need more info.

      Thanks,

      Aaron

  6. Thanks for the reply Aaron!

    I was not aware of the option to use GET requests. This greatly simplifies my setup. It will now be essentially similar to yours.

    Thanks again!

  7. Hey Aaron, just one more quick question. My network is set up with several other routers serving wifi throughout the house, and MAC addresses on those wifi networks are not listed by the wl command (which is to be expected since it’s specifically for the wireless adpater) (I have a DIR-655 (Broadcom) router). The other routers are also not running DD-WRT. They’re connected via the LAN ports with their DHCP servers disabled so that they are basically just ethernet and wifi switches.

    Do you know if there is a way to list all IP or MAC addresses of connected clients in DD-WRT, or do you know of any other possible solutions?

    1. Phoenix,

      Unfortunately I’m not sure of a solution for your problem :(. I only have the one router so it’s not something I’ve run into. I’m not awae of a command that would list all clients on your network.

      If I think of anything, I’ll comment back. Good luck!

      Aaron

  8. Hi

    I am missing curl on my WNDR3700 v2 dd-wrt router.
    I have searched, but nothing works to install opware on my router.
    Can someone point me in the right direction.

    1. Mike,

      Does your build of DD-WRT include wget? If so, you could make minor modifications to the script to use that. I gave Phoenix the same advice a little bit further up in the comments.

      Comment back and I’ll do my best to help you through it!

      Aaron

  9. Hey Aaron,
    I recently discovered OpenHAB and I’m in the process of setting it up. I would like to try this presence detection script. I already have it setup in the startup of my ddwrt router. My router doesn’t have curl installed and it only has a 4meg flash. I wanted to try the Wget method you posted. How would I change your script to get it working? Where do you enter “http://$username:$password@$IPAddr:$port/CMD?$item1=$macconnected1”. Thanks for any help

    1. Mike,

      You’ll want to make modifications on lines 99, 108, 117, and 126. Instead of the following command:

      curl -X POST -d $macstatus1 -H "Content-Type: text/plain" -i http://$username:$password@$IPAddr:$port/rest/items/$item1

      You’ll want something like this.

      wget -q http://$username:$password@$IPAddr:$port/CMD?$item1=$macstatus1 &> /dev/null

      Keep in mind that line 108 needs to have $item2=$macstatus2, line 117 needs $item3=$macstatus3, etc.

      That command will run an HTTP GET request using wget, put it in quiet mode (no console output), and will redirect any downloaded files to /dev/null. /dev/null is basically a garbage can… nothing in that directory is permanently saved.

      I really think you’ll like OpenHAB! The learning curve is steep, but it’s extremely flexible and already has many plugins (called bindings) for popular home automation products.

      Thanks,

      Aaron

      1. Aaron,
        I made the changes but I still can’t get it working. Could you take a look at the following to
        see if you can find what I’m doing wrong.
        In my OpenHAB items files I have this:
        Switch brandonphone “Brandon’s Phone” (GDevices,GRoom19)
        Switch mikephone “Mike’s Phone” (GDevices,GRoom19)
        Switch seanphone “Sean’s Phone” (GDevices,GRoom19)
        Switch kimphone “Kim’s Phone” (GDevices,GRoom19)

        I then put the switches on the sitemap so I could see if they went on.
        I didn’t make any rules yet.

        The router CPU model I have is a Qualcomm Atheros QCA9533 rev 1.1 (0x0141)
        Here is the script I put in to the startup in DD-WRT. I blanked out the mac addresses. I also don’t have
        a user name or password for my OpenHAB, so I made those changes in the script.

        # Make changes below
        # MAC address of each device to watch. Don’t leave blank.
        # For security purposes, if your router requires a password, even if someone could clone the MAC of your
        # phone, they would still require the password of your network to link to your router.
        macdevice1=”xx:xx:xx:xx:xx:xx” #Brandon Phone
        macdevice2=”xx:xx:xx:xx:xx:xx” #Mike Phone
        macdevice3=”xx:xx:xx:xx:xx:xx” #Sean Phone
        macdevice4=”xx:xx:xx:xx:xx:xx” #Kim Phone

        #OpenHAB username, password, and IP Address
        #username=””
        #password=””
        IPAddr=”192.168.0.50″
        port=”8080″

        # OpenHAB switch items to be updated for each tracked MAC
        item1=”brandonphone”
        item2=”mikephone”
        item3=”seanphone”
        item4=”kimphone”

        # Occupied and unoccupied delay in seconds to check status
        # Adjust for shorter/longer wait times. For instance, when one device is already
        # connected, you might want to check less frequently. This could also delay the
        # notification of a disconnect.
        delay_occupied=4
        delay_unoccupied=2

        # initial testing loop count – uncomment the counter near the bottom of the script for testing only.
        limit=120

        ###############################################
        # do not change below here
        ###############################################

        sleep
        #initialize internal variables

        # status of each MAC. 0=disconnected. 1=connected. -1 initially forces isy update first loop
        macconnected1=-1
        macconnected2=-1
        macconnected3=-1
        macconnected4=-1
        connected=-1
        # total number of currently conencted devices.
        currentconnected=0
        counter=1

        # Initial testing loop. Will run continually after testing is complete
        while [ $counter -lt $limit ]; do

        #maclist stored mac listing in router from status screen
        maclist=$(wl_atheros -i ath0 assoclist | cut -d” ” -f2)

        #reset current status. Two variables are used for each device. The past known status and the current
        # status. Only a change is reported to the ISY. Otherwise, it would constantly be updating the ISY with
        # the current status creating unnecessary traffic for both the router and the ISY
        maccurrent1=0;
        maccurrent2=0;
        maccurrent3=0;
        maccurrent4=0;

        # compare each device that is currently connected to the MAC devices we want to watch.
        for mac in $maclist; do
        case $mac in
        “$macdevice1”) maccurrent1=1;;
        “$macdevice2”) maccurrent2=1;;
        “$macdevice3”) maccurrent3=1;;
        “$macdevice4″) maccurrent4=1;;
        esac
        done

        # Look for a change in status from the old known to the current status.
        # If it changed, update the ISY. Otherwise it leaves it as is.
        if [ $macconnected1 -ne $maccurrent1 ]; then
        if [ $maccurrent1 -eq 1 ]; then
        macstatus1=”ON”;
        else
        macstatus1=”OFF”;
        fi
        wget -q http://$IPAddr:$port/CMD?$item1=$macstatus1 &> /dev/null
        fi

        if [ $macconnected2 -ne $maccurrent2 ]; then
        if [ $maccurrent2 -eq 1 ]; then
        macstatus2=”ON”;
        else
        macstatus2=”OFF”;
        fi
        wget -q http://$IPAddr:$port/CMD?$item2=$macstatus2 &> /dev/null
        fi

        if [ $macconnected3 -ne $maccurrent3 ]; then
        if [ $maccurrent3 -eq 1 ]; then
        macstatus3=”ON”;
        else
        macstatus3=”OFF”;
        fi
        wget -q http://$IPAddr:$port/CMD?$item3=$macstatus3 &> /dev/null
        fi

        if [ $macconnected4 -ne $maccurrent4 ]; then
        if [ $maccurrent4 -eq 1 ]; then
        macstatus4=”ON”;
        else
        macstatus4=”OFF”;
        fi
        wget -q http://$IPAddr:$port/CMD?$item4=$macstatus4 &> /dev/null
        fi

        # Update the known status from the current. Ready for the next loop.
        macconnected1=$maccurrent1;
        macconnected2=$maccurrent2;
        macconnected3=$maccurrent3;
        macconnected4=$maccurrent4;

        # Total up the number of devices connected.
        let currentconnected=$macconnected1+$macconnected2+$macconnected3+$macconnected4

        connected=$currentconnected

        # Delay (sleep) depending on the connection status.
        # No devices connected could delay less. Once a device is connected, it could delay longer.
        if [ $connected -gt 0 ]; then
        sleep $delay_occupied
        else
        sleep $delay_unoccupied
        fi

        #for testing only – uncomment to have the looping stop at X loops defined in variable: limit.
        #let counter=$counter+1
        done

        1. Mike,

          At first glance, that looks good to me! There are a couple things you can try to narrow down what’s happening. First, open a browser window and go to http://192.168.0.50:8080/rest/items. There you’ll be able to easily see whether the state of your OpenHAB item is changing. You can open a second browser window and navigate to http://192.168.0.50:8080/CMD?mikephone=ON or http://192.168.0.50:8080/CMD?mikephone=OFF to make sure that your item is properly turning ON and OFF.

          Then telnet or SSH into your router and run the following commands and report back with their output. You can Google how to do this if you don’t know how already:

          Report back and I’ll help you through it. Don’t forget to censor your MAC addresses when posting the output of the wl_atheros command!

          Thanks,

          Aaron

          1. Aaron,
            I played around with it a little bit more and it seems to be working. Do the MAC address’s have to be capital letters? Not sure if that fixed it or something else I did. Anyways, now that its working I think I would like to set-up one more device in it. I’ll give it a try and if I have any problems I might check back with you for help. Thanks for your help! Hope your having a Merry Christmas.

          2. Mike,

            Yes, Linux is caps-sensitive so the MAC addresses would need to be caps. That’s probably what fixed it!

            I’m glad to hear it’s working for you. I’m always available to help if you need anything.

            Merry Christmas!

            Aaron

  10. Quick question to check if the presence detection script is working the way its suppose to. After I made some changes to my OpenHAB configuration I rebooted it. I then watched my switches set up for each MAC address. None of them were lighting it up. I have one setup for my Samsung TV. The TV was on the whole time, before and after the reboot but the switch wasn’t lighting up after the reboot. Same thing with the phones. I went to my router setup and all the phones were active. I then disconnected my phone from the wifi and then turned it back on, this finally made the switch turn on. Is this the normal operation of the script that it only picks up new connections?

      1. One solution, if you don’t mind a few extraneous commands getting sent to the openhab server, is to have it resend the state after the first few (in this case 100, but you can change it if you want) loops, in case it doesn’t detect the correct initial configuration.
        http://pastebin.com/PVKy6WrN

    1. George,

      Sorry about that. I missed your comment somehow.

      I’m not actively using this code in my setup right now so it’s not a good time for me to package it. If anyone else is willing to do so, please feel free.

      Thanks.

      Aaron

  11. In your post you note the following:

    DD-WRT-SCRIPT

    If you have a Broadcom router, make the following modification to my script below. Replace

    1
    maclist=$(wl_atheros -i ath0 assoclist | cut -d” ” -f2)

    with

    1
    maclist=$(wl -i $(nvram get wl0_ifname) assoclist | cut -d” ” -f2)

    Do you know what I would need to put for a Netgear R7000 router?

  12. R7000 (like my R6300v2) is dual band so assuming default settings
    wl0 is the 2.4 GHz band radio
    wl1 is the 5 GHz band radio

    maclist=$(wl -i $(nvram get wl0_ifname) assoclist | cut -d” ” -f2)
    vs.
    maclist=$(wl -i $(nvram get wl1_ifname) assoclist | cut -d” ” -f2)

    Haven’t given much thought to attempting to combine the assoclist outputs into a single list. Also this doe snot allow for anything hard wired like say your smart TV (if like me you ran twisted pair all over the house)

    Also haven’t attempted to play with vlans like a guest wi-fi network at wl0.1 to see what happens.

Leave a Reply

Your email address will not be published. Required fields are marked *