[NEEDS UPDATING] Presence Detection using DD-WRT Router Script

Similar to many users, I wasn’t happy with the smartthings mobile app presence detection. I just moved back from a veralite which I had setup my router to run a script to toggle a virtual presence switch. That was a little easier to setup than with smartthings as there was already a good discussion thread to work off of. See:
Micasaverde Forums

To get this to work, you need:

  • DD-WRT (possibly Tomato) based router to run the devices tracking script.
  • Virtual Presence Toggle device for the router to interact with (with endpoint access)

First, the virtual presence device is similar to others that have been posted. I set mine up with a virtual switch and a virtual presence tile/capabilities. This allows you to manually override or set the presence. See the following for the basic device code.

/**
 *  Presence Toggle
 *
 *  Author: impliciter
 *
 *  Date: 2015-04-01
 */
metadata {
	// Automatically generated. Make future change here.
	definition (name: "Presence Toggle", namespace: "impliciter", author: "impliciter", oauth: true) {
		capability "Actuator"
		capability "Switch"
		capability "Sensor"
        capability "Presence Sensor"
	}

	// simulator metadata
	simulator {
	}

	// UI tile definitions
	tiles {
		standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "off", label: 'Away', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "on"
			state "on", label: 'Home', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "off"
		}
        standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) {
			state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
            state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
		}
		main(["button","presence"])
		details(["button","presence"])
	}
}

def parse(String description) {
}

def on() {
	sendEvent(name: "switch", value: "on")
    sendEvent(name: 'presence', value: 'present')
}

def off() {
	sendEvent(name: "switch", value: "off")
    sendEvent(name: 'presence', value: 'not present')
}

Next, you need to setup OAUTH endpoint access. That is covered elsewhere.

The router script is taken from the afforementioned micasaverde discussion 1. A few changes were made. The wget command was changed to a curl command as the wget command didn’t seem to work with HTTPS requests. Add the following script as a custom script in DD-WRT.

#!/bin/sh
WATCHDOG_SLEEP_SEC=2
MAC_ADDRESS_1="MAC" # Phone 1 MAC
c1_last_state="0"
MAC_ADDRESS_2="MAC" # Phone 2 MAC
c2_last_state="0"
PHONE1ON="https://graph.api.smartthings.com/api/..."
PHONE1OFF="https://graph.api.smartthings.com/api/..."
PHONE2ON="https://graph.api.smartthings.com/api/..."
PHONE2OFF="https://graph.api.smartthings.com/api/..."
x=0
y=0
offcount1=0
offcount2=0

while sleep $WATCHDOG_SLEEP_SEC
do
	if [ "$x" == 180 ]; then
		# Every 30 minutes or so we do them all again, just in case Vera missed something
		x=0
		c1_last_state="0"
		c2_last_state="0"
		h1_last_state="x"
		h2_last_state="x"
		h3_last_state="x"
		h4_last_state="x"	
		h10_last_state="x"	
	fi
	x=$(( $x + 1 ))

	c1_new_state=`wl assoclist | grep $MAC_ADDRESS_1`
	if [ "$c1_new_state" == "$c1_last_state" ] ; then
		sleep 0
	else
		if [ "$c1_new_state" == "assoclist $MAC_ADDRESS_1" ]; then
			c1_last_state="$c1_new_state"
			curl "${PHONE1ON}" -k
			offcount1=0
		else
			offcount1=$(( $offcount1 + 1 ))
		fi
	fi

	c2_new_state=`wl assoclist | grep $MAC_ADDRESS_2`
	if [ "$c2_new_state" == "$c2_last_state" ] ; then
		sleep 0
	else
		if [ "$c2_new_state" == "assoclist $MAC_ADDRESS_2" ]; then
			c2_last_state="$c2_new_state"
			curl "${PHONE2ON}" -k
			offcount2=0
		else
			offcount2=$(( $offcount2 + 1 ))
		fi
	fi

	if [ $offcount1 -lt 2 ]; then
		# give not responding devices 1 minute to respond
		sleep 0
	else
		c1_last_state="$c1_new_state"
		curl "${PHONE1OFF}" -k
		offcount1=0
	fi

	if [ $offcount2 -lt 2 ]; then
		#give not responding devices 1 minute to respond
		sleep 0
	else
		c2_last_state="$c2_new_state"
		curl "${PHONE2OFF}" -k
		offcount2=0
	fi

done

Fill out the necessary MAC address info and endpoint addresses you setup previously. Also, add the following startup script in DD-WRT:

while [ ! -e "/tmp/custom.sh" ]; do
  sleep 1 # wait till /tmp/custom.sh gets generated
done
/tmp/custom.sh &

You can add more devices to track pretty easily. I have it setup to track 2 phones. It’s functionality is easily extendable as you could have it track any devices connect to your router. For example, you could trigger an action whenever a laptop, smart tv, etc connect. Additionally, because the virtual presence is driven by a virtual switch, you could manually override/set presence with a physical switch or IFTTT.

One extension I’ve thought of is triggering a “Watching TV” mode or scene whenever the TV connects to the network. Seems like this could be quite useful.

Hopefully this was helpful. Its a little light on details but most of the discussion from the micasaverde forum1 applies.

12 Likes

How presence should truly be handled. Good job. Wish My router were supported. Just looked into the DD-WRT firmware today.

1 Like

Do you have some sample places where this is covered at?

Thanks!

Tutorial: Creating a REST SmartApp Endpoint

1 Like

Thanks for this! After much poking around, trial-and-error, and some modification to the script, I finally got it all the pieces of this running on my system. I have a Netgear R7000 running DD-WRT, running V24-sp2. For me, the following command didn’t work properly, and would just return a blank:

wl assoclist
I needed to specify the interface for it to return the list of associated MAC addresses:
wl -i eth1 assoclist
Also, the curl commands didn’t seem to work for me. I would get a 301 error, so I tried replacing the curl commands with wget, and that seems to be working as intended.

Now, I have four virtual presence toggles being controlled by this script, representing the other four members’ presence, all without having to get them to install Smartthings on their phones.

1 Like

I use OpenWRT on a TP-Link router and had to make a few mods to your shell script. I posted my version to a github repo here

1 Like

with that smart app, the router isn’t even necessary. I was able to get reliable presence sensing two different ways using that device type: Way 1: I had two ifttt recipes that turned on or off the virtual switch as i connected or disconnected from my home wifi. Way 2: works exactly the same as way 1, but i used tasker instead of ifttt. The only issue i had is my router would boot me off for a few seconds sometimes (maybe once a week) so it would trigger leaving and hello home if i was the only person home. Adding a five minute delay to the leaving apps fixed all issues.

2 Likes

Thanks for the nice suggestion, I did something similar:

#!/bin/sh

WATCHDOG_SLEEP_SEC=2
WATCHDOG_DISCONNECT_TRY=30 # give not responding devices 1 minute to respond
WGET=curl #or use wget

while ! which ${WGET}; do
  echo no $WGET > /tmp/$MAC_ADDRESS.state
  sleep $WATCHDOG_SLEEP_SEC
  ipkg update
  ipkg install $WGET
done

if [ $WGET = curl ]; then
  WGET="curl -k"
else
  WGET="wget-ssl --random-file /dev/urandom --no-check-certificate -O -"
fi

MAC_ADDRESS="$1"
APP_TOKEN="$2"
ACCESS_TOKEN="$3"
last_state="0"
PHONEON="https://graph.api.smartthings.com/api/smartapps/installations/${APP_TOKEN}/iPhone/here?access_token=${ACCESS_TOKEN}"
PHONEOFF="https://graph.api.smartthings.com/api/smartapps/installations/${APP_TOKEN}/iPhone/away?access_token=${ACCESS_TOKEN}"
offcount=0

[ -z "$MAC_ADDRESS" -o -z "$APP_TOKEN" -o -z "$ACCESS_TOKEN" ] && exit 1

while sleep $WATCHDOG_SLEEP_SEC; do
        new_state=`arp | grep " $MAC_ADDRESS "`
        [ -n "$new_state" ] && new_state=off || new_state=on
        echo $new_state > /tmp/$MAC_ADDRESS.state
        if [ "$new_state" != "$last_state" ] ; then
                if [ "$new_state" == "on" ]; then
                        last_state="$new_state"
                        ${WGET} "${PHONEON}"
                        offcount=0
                else
                        offcount=$(( $offcount + 1 ))
                fi
        fi

        if [ $offcount -ge $WATCHDOG_DISCONNECT_TRY ]; then
                last_state="$new_state"
                ${WGET} "${PHONEOFF}"
                offcount=0
        fi
done
  • The script takes three arguments, the MAC address, the app token and the access token. The latter two can be found in the notification the above Toggle Presence Device apps sends at setup.
  • My startup script looks like:
while [ ! -e "/tmp/custom.sh" ]; do
  sleep 1 # wait till /tmp/custom.sh gets generated
done
/tmp/custom.sh XXX YYY ZZZ &
/tmp/custom.sh AAA BBB CCC &
  • This makes it super-easy to add more phones as needed.
2 Likes

The beauty of using the router script is that you can set up presence for people who don’t even have the ST app installed, and might not even have a smart phone.

I’m betting I can install this script directly on AsusWRT without anything special. It’s already fairly open.

3 Likes

Hi I will try this. But I like to know what I have to write after PHONE1ON and PHONE1OFF

Thanks

Yes, it is a beauty, but there is also a security problem, somebody with access to your wifi and knowledge of a MAC address from one of the trusted devices can basically disable your smartthings alarm system!

Midyear66’s Toggle Presence Device will send you the needed URL after setup.

1 Like

Thanks for your reply, I think I’m getting closer to the solution.
When I run your script I receive this error: [: (192.168.137.76): unknown operand
192.168.137.76 is the IP of my cellphone.

I think is in this line [ -n $new_state ] && new_state=off || new_state=on

What can be happening?

Also, my router is getting some time to be aware when a MAC ADDRESS is not in the network. Yo you know if there is a command to force update that?

Thanks

Well, I guess that means arp | grep " $MAC_ADDRESS " does not return anything meaningful in your case and hence new_state is empty and an error occurs in the next line.You could try to use wl assoclist | grep $MAC_ADDRESS instead (like in the original post), however this won’t work for devices, which aren’t directly connected over wifi, but over e.g a bridge or a repeater.

To increase the time between scans, just increase WATCHDOG_SLEEP_SEC.

Thanks, I made it. I used a combination of both scripts and seems like works fine.

Now my virtual presence devices are working fine. The only detail is in the “recent” actions for each device I see several “toggle presence device” lines (one for each watchdog iteration) . What can I do to avoid that and get only the relevant actions.
Thanks

1 Like

Thanks for everyone’s hard work on this. I have the smartapp and device handlers set up. When I go to the URL in my browser it works perfectly in changing the presence senor within the SmartThings app. I must be doing something wrong on the DD-WRT side of things. Is there any settings I need to change to make the script work. I tried it with both curl and wget. Is there a way to see a log file of what is happening in the router scripts? How do I know if I have curl installed. I am running Firmware: DD-WRT v24-sp2 (03/25/13) mega.

The steps I took to install the script is as follows:

Copied the startup script and then clicked “saved startup”

Then I copied the custom script and clicked “run command”

It appears to then add a “custom script” section on the bottom of the page which is blank. But the script remains in the Command Shell section.

If I click “Save Custom Script” nothing changes.

I have looked all over the place but can’t seem to find a more descriptive way of installing a script.

Thanks in advance.

Hello, I recomend you to run the commands in console (Curl and wget) and see if are working in your router. I was not able to get those commands working (stock version) and I had to install the optware version .

This is my first time trying anything regarding router scripts etc. So i am not familiar with running the commands in the console. I believe i have wget on my router but not curl. I tried installing the ipkg via telnet but it doesn’t seem to be updating and then when i try ipkg list it tells me to run ipkg update so something isn’t working correctly. You need ipkg to install Optware correct? Trying to make sense of what I have been reading on forums and ddwrt wikis.

Thanks again

  1. Yes, you need ipkg to install Optware software.

  2. As I told you I had to install curl from Optware, so, to get the command working I must call curl this way:

/opt/bin/curl -k ${PHONE1ON}

(I installed wget in /opt/bin and ${PHONE1ON} is the variable where I have the URL). When I tried to run wget or curl command with the default version that came with the router, I received an error because that version is not compatible with HTTPS.

  1. I used this tutorial to install Optware: http://tomatousb.org/tut:optware-installation (the tutorial is for a router running TomatoUSB but I think it should work in DDWRT)

  2. If your router is compatible, I would recommend you to install tomatousb and not DDWRT, at my experience is more reliable. In the past I used DDWRT and has some problems running Optware software.

  3. Sorry for my bad english, I’m from Costa Rica and my native language is spanish.

1 Like