SmartThings Community

Presence sensor... device associated with local wifi

project_presence

(John S) #1

This is going to be oddball, but

given my router knows when a device is connected to wifi

and

given I know the device mac (or dns name)

I should be able to make a presence device type that polls the local network for the existence of said MAC or DNS name currently associated with the AP, and if so…

PRESENT

So on linux something like this works on an Asus router/AP

arp | cut -d ' ' -f 2,4 | tr A-F a-f | sort -t ' ' -k 2 | grep -v incomplete | awk  '{ print $2
 " " $1}' > /tmp/f1
cat /var/lib/misc/dnsmasq.leases | cut -d ' ' -f 2,4 | sort  > /tmp/f2
awk 'NR==FNR {h[$1] = $2; next} {print $1,h[$1]}' /tmp/f2 /tmp/f1 | grep -q <MAC or DNS NAME>
echo $?

would output 0 if the user is present or 1 otherwise. Make this thing a script that is run via an http call, and you’d have a presence value that is fairly strong. Has anyone done anything like this? A similar thing could be done using a bluetooth scan, if known BT device MACs are used, to “see” phones when they are present (vs geofencing…)


Generic handler for legacy devices
Virtual Presence Device
Problem with Cell Phone as presence Sensor
FAQ: The Many Ways of Detecting Presence
#2

Yes, some other people have done this, if you search the forums you should find some examples. Or hopefully those with personal experience will chime in.

The biggest issue is the same old one: if the device loses connection, the code thinks you’re away when you’re not. And if you lose WIfi altogether, when it restarts all your “just arrived” actions may trigger.

The challenge is always how to build in some bounce tolerance while still maintaining coherence. Even when your family is making microwave popcorn. :wink:. (FWIW, SmartThings staff have told me ST requires 5 consecutive fails in a preset timeframe before declaring a device “away.” Other protocols use other debounce algorithms. But you’ll likely need something, few WiFi connections are solid 24/7.)

BTW, WiFi connection is already used in most geolocation phones these days. It helps, it’s not perfect. But if you’re using Life360 or IFTTT location, they’re using wifi connection as part of their algorithms. That’s also how the Hue bridge geofencing works.

From the Hue FAQ:

Can I set my home location without having access to my home Wi-Fi?
No, you need to be connected to the wi-fi router your bridge is connected to, in order to be able to set up your home location.


(Patrick Stuart [@pstuart]) #3

It would be much easier if ST mobile apps would just allow us to use WiFi AP’s as presence devices.

Android and iOS already have Network change registration / permissions.

So on network change, get SSID, if SSID = then person is at home.

The issue though gets complicated by the fact that if you turn off wifi, you are no longer “home”.

WiFi ultimately isn’t a good presence detector and nor are mobile phones without BLE or some other way to say, I’m home or I am not home.

Realistically, I think ST needs to rethink the role of presence and how it plans to make Users a part of the home. Identifying an individual is critical to smart programming, not just if a device is on the network or not.

Users should be a separate layer of interaction, not linked to devices, IMO. But that’s another discussion for another day.


(John S) #4

Started with “Mobile Presence” device template - which oddly didn’t include any way for the device to have its presence value set, added metatdata

    command "setPresence"

and a method

def setPresence(String v) {
    sendEvent(displayed: true,  isStateChange: true, name: "presence", value: v, descriptionText: "$device.displayName is $v")
}

So I now have a device type that I can set presence to “present” or “not present” via a REST endpoint. Verified that if I use that as my presence device, ST app shows me present or not present based on my setting of the attribute.

Now off to do some guessing about my presence… I can detect wifi, bluetooth, etc and I can “decay” the values much like ST does for the geofence presence to smooth out dropouts. Will give this a go and post updates…


(John S) #5

With a few additional tweaks, I have this now working. It’s (somewhat) specific to my infrastructure, but what I’ve done is create a presence device type that supports getting/setting the presence - I then create one of these for each person in the home’s phone.

I have an Asus AP/router that provides wifi, it has a telnet interface, and so I have a small script installed on it to generate the currently associated wifi devices by DNS name/mac address, and then look for a supplied value in that list to return if it is present or not.

On yet another computer (the ole linux server) I have a cron job check every 2 minutes for the three user’s phone mac addresses, and to update the presence values for each user (using a REST endpoint into ST)

Bottom line, we now know who is home and who is not home. If the wifi goes down, then everything is down (including the hub) and I don’t have anything dangerous happening on arrival (like unlocking doors)

It’s cool you can do this, but it’s not remotely “consumer friendly”.


#6

What happens when someone turns off their phone? Say a reboot?


(John S) #7

Currently they will show as not present - when I get around to doing the delayed change, nothing unless they leave it off for more than X minutes, then not present Which is the same thing for the geofence anyway… phone off == no location ping


(John S) #8

Fairly trivial change to the device type, implemented 1 minute delay before changing presence. Todo: make delay configurable in the device tile. I’ll publish the code to github once I’ve shaken it out, but so far it’s working way better than geofencing (funny story - it takes a week or so after you move before Apple and Google decide to move your wifi address to your new home, so you’ll be “away” via geofence whenever you associate to your home wifi… until they update their wifi locations. This is one reason why I decided to do this :slight_smile: )


(John S) #9

I never did post the code up.

So that’s a fairly dumb presence device. Create as many devices as you need using this device type, and assign those devices as presence devices to the people who carry said devices.

Next, you’ll need a REST endpoint. I’ll leave that as an exercise for the reader - I’ve posted one myself here ( https://github.com/schettj/echostadapter/blob/master/webApi.groovy), it’s a great starting point if you don’t have one and it’s what I use with some modifications, which are shown below:

// add this to webApi, or if you roll your own, do something like this:

in method updateItem(), in the case block handling the actual command to perform, add

case 'presence':
    // accept 'presence':'not' as the same as 'not present' - simplifies passing values around
    if (v == "not") v = "not present"
    def curr = findPresence(params.id)
    def changecount = 0
    if (curr != v) {
      // how many times was it different?
      changecount = findPresenceDiff(params.id)
      if (changecount > 0) {  // poll rate 2 min, so 4 min of change. Might go as high as 5 someday...
        device.setPresence(v)
        changecount = 0; // reset change count
        savePresence(params.id, v);
      } else {
         changecount++ // bump it. If different twice, actually change it
      }
  }
  setPresenceDiff(params.id, changecount)
  break

And add the support methods that keep track of state:

 // do we have presence info for this device?
// lookup a registration
def findPresence(String id) {

    // do we have state data for this id?
    if (state.presence != null)
        state.presence[id]
    else
        ""
}

def savePresence(String id, v) {
    // do we have state data for this id?
    if (state.presence == null)
        state.presence = [base:1]
    state.presence[id] = v
}

def findPresenceDiff(String id) {
//log.debug "findPresenceDiff"
    // do we have state data for this id?
    if ((state.presencediff != null) && (state.presencediff[id] != null))
        state.presencediff[id]
    else
        0
}

def setPresenceDiff(String id, v) {
    if (state.presencediff == null)
        state.presencediff = [base:1]
    state.presencediff[id] = v
}

And then all you need to do is call your REST endpoint for each device id every couple minutes to report presence state as {‘state’: {‘presence’:‘present’}} or {‘state’: {‘presence’:‘not’}}

I do that in a cron job, which I outlined above. The wee bit of code that pushes the state to the REST endpoint is the usual curl mojo. For me the cron job runs a bash script with three devices defined something like this

#!/bin/bash

device="some mac address"
stdevice="the guid of the device in smartthings from my REST Smart App"
state=presence.sh $device | grep state |  tr -d '\r\n'`

curl -s -X PUT --header "Content-Type: application/json"  --header "Authorization: Bearer MY_TOKEN"  "https://graph.api.smartthings.com/api/smartapps/installations/MYRESTENDPOINT /$stdevice" --data-binary  ${state}

The presence.sh is a horrible hack that calls another script on my Asus router using expect. I’ll spare you that horror. Eventually the router does what I outlined way back at the beginning to find out if the mac address passed in is actually associated with the wifi right now.

If you haven’t run screaming from this thread, the basic idea is

cron runs a script that polls router every 2 min for some mac addresses - the result being present or not, which is pushed via a curl call to a REST endpoint, which checks to see if the value has changed or not from the last time. If it has changed, then a counter is fetched. If the counter is >0, then we push the new state to the device and reset the counter to 0. If it’s not >0 we just bump it. If it’s not changed at all, we reset the change count to 0.


(then the boot kicks the bucket releasing the ball, that flips the man into the tub, releasing the trap, which drops the trap onto the mouse…)

So, if someone leaves, we will see it in ~ 4 minutes - the cron job has to report the new presence twice in a row before the endpoint will change it in the device. This clears up most false positives. You could bump the change count if you want a longer delay before changing presence.

I know it’s a really icky hack, but frankly it has been spot on, and at least in my house you have to be pretty close to the front of the house before you pick up the wifi.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #10

So… Could the *nix side of this run entirely under OpenWRT?


(John S) #11

Quite probably - most of it is running on my Asus stock router firmware (linux/busybox) - assuming you have curl or wget it should be fine… and cron? - the stock asus firmware looks like it can do it if I get optware enabled, which is possible apparently


(jaytarang92) #12

Thanks so much schettj. I made a github repo on how I achieved this. https://github.com/jaytarang92/STwifiPresence


#13

I just have this idea about using network connection as presence sensor and after some search I found this thread. I don’t know much about programming so I think I’ll need some hand holding…

It looks like that I’ll need a wrt router but I don’t have one. However I have a small server running archlinux. Would it work if I run the python code on the linux box which is connected to the router?