Tuesday, September 18, 2012

Further Optimizations to Presence Detection using Sockets

As I've gone over in past posts, I've got my OpenWRT running some pretty basic presence detection for when my wife and I come and go. My plan is to eventually use this information to do things like adjust the thermostat, or enable an IP camera so we can keep an eye on things when we're both away.

However, for now, all it's doing is keeping a log of when we come and go, until I am satisfied with the general software architecture of my system. So basically, I've had it working for a while now, but have kept changing the mechanism to what I feel is a little cleaner, and learning a bit along the way.

This time? Sockets!

My current method involves the router running a script every minute on a cron job that looks through its list of associated wireless clients. It stores the state in a file on the file system and when the current state changes from the stored state, it sends an event out a TCP socket using netcat (helpfully built into the BusyBox implementation running on my WNDR-3700). I also wanted to experiment a bit by writing a shell script that doesn't make me want to stab myself in the eye.

My Raspberry Pi is then running a Python server that is listening on the port for specific message formats, and parsing the event text and subsequently acting on it. If you're interested in the Python piece, it's probably something that I'll post about later, when I'm more happy with it.

But for now, here's the script that runs every minute on the router and will send events over a TCP socket when they're detected:
#!/bin/sh

# Constants
STATE_FILE="/root/.presence_state"
MAC1="00:00:00:00:00:00"
MAC2="00:00:00:00:00:01"
PORT_NUM=1337

# Variables
oldState=0
curState=0

################################################################################
# FUNCTION: read_state_from_file
#
# A function that reads the current state from the presence file, creating it
# if necessary with the current state
################################################################################
read_state_from_file()
{
   # First check if the file exists, creating it if it does not
   if [ ! -f $STATE_FILE ]
   then
      echo "$STATE_FILE does not exist, creating!"
      echo -ne $curState > $STATE_FILE
   fi

   # Now read in the state from the file
   oldState=`cat $STATE_FILE`

   #echo "The old state is \"$oldState\""
}

################################################################################
# FUNCTION: check_current_state
#
# Checks the current state by grepping through the list of associated clients
# for the MAC addresses
################################################################################
check_current_state()
{
   #echo "Checking the current state"
   local mac1Present=0
   local mac2Present=0

   iw dev wlan0 station get $MAC1 > /dev/null 2>&1
   if [ $? -eq 0 ]
   then
      mac1Present="1"
   fi

   iw dev wlan0 station get $MAC2 > /dev/null 2>&1
   if [ $? -eq 0 ]
   then
      mac2Present="1"
   fi

   curState=$(($mac1Present + 2 * $mac2Present))

   #echo "The current state is \"$curState\""
}

################################################################################
# FUNCTION: main
#
# Handles and future command line args and runs the rest of the script
################################################################################
main()
{

   # Check the current state by grepping the station list
   check_current_state

   # Read the old state from the file
   read_state_from_file

   # Compare the old state to the current one, acting if they are different
   if [ "$curState" != "$oldState" ]
   then
      #echo "The states are different!"
      echo -ne $curState > $STATE_FILE
      echo -ne "presence state=$curState" | nc raspi:$PORT_NUM
   fi
}

# Run the main method
main "$@"


3 comments:

  1. Replies
    1. I haven't done too much work in the past few weeks. I've updated the presence detection to notify me when the state changes, mostly so I can weed out if there are any anomalies before I start using the data to control the thermostat, and so far it's been pretty solid.

      There was a little hiccup once when my wife got back in from being out of town for a few days and her phone would rapidly connect/disconnect from the wifi causing a flurry of notifications, but a reboot of her phone fixed it.

      As for the notifications, I've got basic scripts set up for three different types of notifications: email, SMS, and iOS Push notifications using BoxCar with the Growl API.

      If you're interested in seeing any of that code, let me know, and I'll do a post containing it.

      Delete
  2. Have been thinking about the same thing, not running a OpenWRT system, so the options I have come up with so far are and tested;

    - Linux DNS server and just tailing the logs, this seems to work quite nicely, but haven't worked out how to write the script yet.
    - Bonjour, this works really well with python and the bonjour package but you need to have a jailbroken iPhone and ssh running on it.
    - Just plain old pinging of the device, this is what I am currently using.

    Matt.

    ReplyDelete