Thursday, July 12, 2012

Proximity Sensing Using Latitude and VBScript

Are You Home Yet?
The System Knows...
NOTICE: With the killing of Latitude by Google in 2013, this approach no longer works. Just be advised - too bad, it was a handy solution IMHO. - Stephen

I have played with how to tell my Home Automation system if I was home or away for quite some time. Sure I can press a "home" button and an "away" button but what fun is that for a "Smart" home? I wanted the ability for the system to "know" if I'm home or not "automagically" without my sometimes forgetful intervention.

Back before moving to an iPhone I had a bluetooth solution "semi-working". The phone was paired with a bluetooth adapter on the server and some software was running that could tell if the phone was in range or not. A script would then spin through the status file, a simple flat text file, and see if my phone was local and adjust the status in my system. Kludgy but usually worked.

When I moved to an iPhone I was UNhappy to find out that the same bluetooth option would not work so I was back to figuring out a new solution. This time I wrote a script that "pinged" the IP my phone had picked up on the WiFi and if it was there then I was home, if not, away. That worked but I had to make sure I always had WiFi enabled on the phone and the script simply fired off a command ping which was very slow. Also sometimes the ping would time out anyhow if my phone was not active so I would suddenly be "away" when I was really home.

Google Latitude Tracking

The more recent solution is a combination of using Google Latitude (http://www.google.com/latitude), which I was already experimenting with, and a local VBScript that provides the linkage between Latitude data and my HA system. Latitude is available for various mobile phones and although obviously very "Big Brotherish" it provides a nice framework to handle the GPS tracking piece of a proximity system. It also provides all kinds of mobility analytics such as how many miles you've traveled, how much time you spend "at work" and "at home" and other rather interesting pieces of information.

To be sure, Latitude is not perfect. It usually has my GPS location pretty well tied down but you will always see occasional "way out there" points when it defaults to the closest cell tower instead of a tight GPS point. But overall it's pretty good at keeping up with where you're at. Personally I haven't see any major battery drainage issues either but I am unrelenting on charging my devices every night no matter how they were used that day.

Of course the first thing you have to do is download the Latitude app for your particular mobile device. Visit the Latitude page and/or your app store and get it working on your device first. There are apps for many of the devices out there so check out the page / links. Make sure the Detect Your Location and Background Updating are turned on in the Settings area so the app is constantly updating location data.

To make Latitude your friend for getting data out, takes a special configuration that isn't overly obvious. You have to expose the data using the "Google Public Location Badge" feature. Sounds like you're giving away the farm but this is just an option if you WANT to publish everywhere you go all the time. The key is, this lets you build the data link but you DON'T HAVE TO share the link. The public location badge allows you to publish either a road map (KML), JSON or ATOM feeds of your location data but we are just going to use the data for our own purposes and not publish to the world.

To get your data link, log into Latitude on the web, click on the gear icon in the upper right, and go to Application Settings. At the very bottom of the screen is a "Developer Information" link. Click on that and you will see the linkage information and your own special username code in the various formats. We are using the JSON format here so the URL looks like this:

https://www.google.com/latitude/apps/badge/api?user={Your Code}&type=json

Once you have this link you can leave the rest of the page alone and move on. Just make sure the Enable and Show Best Location radio button is check in the configuration and click Save. That's after you've checked out all the other tidbits of data you'll have available about your movements of course.

Mobile Status Screen for
My Home Automation System

Getting the Data

Now we need to get the data feed and do some location checking. This is where the VBScript comes into play. You can do this in any other scripting or other language if you want, this is just the easiest to write and understand for me. This script is then scheduled on my HA server to run every five (5) minutes to see where I'm at. I actually have it log my Lat/Lon into my own local database and then check for within a certain range of my home address.

VBScript Sample

The key features of the sample script are the GetLatitude and the GetDistance functions. GetLatitude uses your provided Google Latitude user code and pulls the current lat/lon data out of your feed. The GetDistance function takes two lat/lon pairs and returns the distance, in miles, between them. I am using a free online web API to do the distance comparison here but you could spin your own code if you wanted to. GeoCoder.us is free for the low level personal use I am dealing with here. With those two functions you can have your script do whatever you want with the results. The sample script below uses a CheckDistance variable to compare against the results and take action.

You'll have to clean up some of the &Amp code issues if you copy/paste the script but the overall process is very simple. Also if you are behind a corporate proxy, the URL gets may or may not work. I've run an enhanced version of this script that links to my Home Automation system and toggles my status for months now and it's been quite stable. The code could obviously use some error handling or at least what to do if we get 0,0 back as location or odd distances but that's up to you creative people out there.

'** GetLatitude.vbsGet Google Latitude Lat/Lon for the user and log to the Log database
'v1.00 - SWN - 10/29/2011

'** Debugging flag - True to see messages
MyDebug = TRUE

'** Your information and location to test against
'** Google Latitude User ID
UserID  = "{Your Latitude Code Here}"

'** Location Latitude to test against
LocLat  = 35.32

'** Location Longitude to test against
LocLon  = -96.92

'** If you are within 1/2 Mile of LocLat/LocLon then If triggers
CheckDistance = .5

'** First get the user (YOU) current location from the Latitude JSON feed
MyLonLat = GetLatitude(UserID)

'** Debug stuff - show it if debugging
If MyDebug then MsgBox "Raw Coords: " & MyLonLat

'** Split out the Lat/Lon from the results
MyCoordsA = SPLIT(MyLonLat)
MyLon = MyCoordsA(0)
MyLat = MyCoordsA(1)

'** Now do a distance check between the points
'** Note this uses another free web API, GeoCoder.us to calc this for us
'** You could build your own dist compare if you wanted... but why if this is there?
'** Distance returned is in Miles so you can convert as desired from there
MyDistance = GetDistance(MyLat, MyLon, LocLat, LocLon)

'** Show distance if debugging
If MyDebug then MsgBox "Distance: " & MyDistance

'** Now take action on the distance check results
'** Obviously you will have to have some session state here so this doesn't fire on every check
'** In my HA system, it sets a Home flag so my code checks if the should be changed and takes action if needed
If MyDistance < CheckDistance then
 '** Do something cool because you're close
 MsgBox "You are within " & MyDistance & " of your target location."
Else
 '** Do something else because you're not
 MsgBox "You are OUTSIDE of your target location by " & MyDistance - CheckDistance & " miles."
End If

'** get the Lat/Lon data from your Google Latitude Feed
Function GetLatitude(strUserID)
    
 Set objHttp = CreateObject("Msxml2.ServerXMLHTTP")
 objHttp.Open "GET", "http://www.google.com/latitude/apps/badge/api?user=" & strUSERID & "&type=json&callback=parse", False
 objHttp.Send
 html = objHttp.ResponseText
 Lines = Split(html, "[", -1, 1)
 Count=UBound(Lines)
 MyCoords = lines(2)
 MyCoords = LEFT(MyCoords,INSTR(MyCoords,"]") - 1) 
 GetLatitude = MyCoords
 
 Set objHTTP = nothing 'Release the object 
End Function

'** Calc the distance between two lat/lon points and return as MILES
Function GetDistance(Lat1, Lon1, Lat2, Lon2)
 Set objHttp = CreateObject("Msxml2.ServerXMLHTTP")
 GetLink = "http://geocoder.us/service/distance?lat1=" & Lat1 & "&lat2=" & Lat2 & "&lng1=" & Lon1 & "&lng2=" & Lon2
 objHttp.Open "GET", GetLink, False
 objHttp.Send
 html = objHttp.ResponseText
 
 '** Parse out the information here
 StartPos  = Instr(html,"=")
 EndPos   = Instr(html,"mile")
 GetDistance = Ltrim(Rtrim(Mid(html,StartPos + 1, EndPos-StartPos-1)))
 GetDistance = GetDistance * 1
 Set objHttp = Nothing

End Function