My first experience using BlueVia APIs

I wrote yesterday about a quick hack I did at Over The Air using the BlueVia API. I thought it was worth a quick post to show just how simple it was.

Read yesterday’s post for background to the idea behind the hack, but in essence, what I wanted was:

  • monitor the location of my mobile phone
  • send an SMS to a different mobile number when my phone goes into a predefined known area

BlueVia provides an API that let me doing this using network operator data. In other words, nothing needs to run on my phone itself as location data is obtained from where O2 thinks my phone is.

This means there is no battery-life impact on the phone for this monitoring.

It also means this will work with any phone – from iPhones and Androids to cheap feature phones.

The whole thing took me less than an hour and needed only 90 lines of Python.

This is how I did it.

1. Downloaded the zip file from BlueVia’s github page

2. Opened the Readme.html file in the examples/python/Sample Code folder

3. Installed the Python prereqs listed in the Readme.html file using easy_install

4. Created an app at bluevia.com requesting the Send SMS and the Location API

screenshot

I got keys and IDs immediately.

5. The Readme.html contained enough sample code to show me how to do their OAuth process:

# overtheair-auth.py

import bluevia

consumer = 'XXXXXXXXXXXXXX'
secret = 'XXXXXXXXXXXXX'

o3 = bluevia.BlueViaOauth(consumer, secret)
urlobj = o3.fetch_request_token()

code = urlobj[0]
url = urlobj[1]

if code == 200:
    access_token = raw_input('go to ' + url + ' then come back here and enter the verifier\n\nverifier: ')
    o3.fetch_access_token(access_token)
    o3.saveAccessToken("blueviaauthtok.pkl")
else:
    print 'fail ' + code

6. The Readme.html file also contained enough sample code to show me how to get my mobile’s current location

# overtheair-location-attempt1.py

import bluevia
import pprint

l = bluevia.BlueViaLocation()
l.loadAccessToken("blueviaauthtok.pkl")
location = l.locateTerminal()

pprint.pprint(location)

7. That wasn’t very accurate.

It gave me a location somewhere in Slough, which was nowhere near me in Bletchley Park. That confused me for a minute until I remembered that O2 are in Slough.

Comparing the location I got out of the BlueVia API with the O2 UK Head Office…

map

Ah… I guessed it wasn’t returning me inaccurate location data. It was returning me canned data, which they’ve set to their head office location.

Which is when I remembered that in the BlueVia APIs talk I went to, they mentioned that the REST API included a sandbox endpoint for testing and development.

I took a quick peek at the source code of the Python bluevia library I downloaded from github, and spotted that the BlueViaLocation function had an optional sandbox parameter which defaulted to ‘sandbox’.

A quick tweak:

# overtheair-location-attempt2.py

import bluevia
import pprint

l = bluevia.BlueViaLocation(sandbox="")
l.loadAccessToken("blueviaauthtok.pkl")
location = l.locateTerminal()

pprint.pprint(location)

Bingo – I had the latitude/longitude of my location in Bletchley Park.

8. I needed to know how to see how far this location was from the predefined known area. A quick Google turned some public domain Python for doing this at johndcook.com – so big thanks to John D. Cook for saving me having to do any complicated maths.

9. Back to the Readme.html file, to get sample code for sending an SMS:

# overtheair-sendsms.py

import bluevia
import pprint

targetMobileNumber = "447654123456"

s = bluevia.BlueViaOutboundSms(sandbox="")
s.loadAccessToken("blueviaauthtok.pkl")
r = s.sendSMS([targetMobileNumber], "message to send")
pprint.pprint(s.deliveryStatus(r[1]))

10. I did a little tinkering to dynamically set a polling frequency based on my distance from the target

# 1 miles per hour = 0.44704 metres per second
# my max speed = 70 mph
# my max speed = (70 * 0.44704) metres per second
pollinterval = ((70 * 0.44704) / distanceInMetres)
# lower limit - don't poll more frequently than once a minute
if pollinterval < 60:
    return 60
else:
    return pollinterval

11. Putting it all together, I had this:

# overtheair.py

import bluevia
import pprint
import math
import time

# borrowed with thanks from http://www.johndcook.com/python_longitude_latitude.html
def distance_on_unit_sphere(lat1, long1, lat2, long2):

    # Convert latitude and longitude to 
    # spherical coordinates in radians.
    degrees_to_radians = math.pi/180.0
        
    # phi = 90 - latitude
    phi1 = (90.0 - lat1)*degrees_to_radians
    phi2 = (90.0 - lat2)*degrees_to_radians
        
    # theta = longitude
    theta1 = long1*degrees_to_radians
    theta2 = long2*degrees_to_radians
        
    # Compute spherical distance from spherical coordinates.
        
    cos = (math.sin(phi1)*math.sin(phi2)*math.cos(theta1 - theta2) + 
           math.cos(phi1)*math.cos(phi2))
    arc = math.acos( cos )

    # return in metres
    return arc * 6373 * 1000

def getlocationtoken():
    l = bluevia.BlueViaLocation(sandbox="")
    l.loadAccessToken("blueviaauthtok.pkl")
    return l

def getlocation(l):
    loc = l.locateTerminal()
    return loc

def getdistanceinmetres(loc, target):
    currentlat = float(loc[1]['terminalLocation']['currentLocation']['coordinates']['latitude'])
    currentlon = float(loc[1]['terminalLocation']['currentLocation']['coordinates']['longitude'])
    return distance_on_unit_sphere(target['latitude'], target['longitude'], currentlat, currentlon)

def sendsms(place):
    s = bluevia.BlueViaOutboundSms(sandbox="")
    s.loadAccessToken("blueviaauthtok.pkl")
    r = s.sendSMS(["447654123456"], "I'm near " + place['name'] + " now. Do you need me to get you anything?")
    pprint.pprint(s.deliveryStatus(r[1]))

def choosesleeptime(metres):
    # 1 miles per hour = 0.44704 metres per second
    # my max speed = 70 mph
    # my max speed = (70 * 0.44704) metres per second
    quickesttraveltime = ((70 * 0.44704) / metres)
    # fix lower limit - don't poll more frequently than once a minute
    if quickesttraveltime < 60:
        return 60
    else:
        return quickesttraveltime

loctok = getlocationtoken()

while True:
    loc = getlocation(loctok)

    closestPlace = None

    places = [ { 'latitude' : 51.99684, 'longitude' : -0.7384, 'name' : 'Bletchley Park' },
               { 'latitude' : 50.96948, 'longitude' : -1.35246, 'name' : 'Sainsburys' },
               { 'latitude' : 50.97966, 'longitude' : -1.34928, 'name' : 'Tesco Express' } ]

    for place in places:
        place['distance'] = getdistanceinmetres(loc, place)
        if closestPlace is None:
            closestPlace = place
        elif closestPlace['distance'] > place['distance']:
            closestPlace = place
    
    if closestPlace is None:
        print "something went wrong. fail."
        exit()

    # network location wont be precise, so tolerate anything within 500 metres
    goodenough = 500
    
    if closestPlace['distance'] < goodenough:
        sendsms(closestPlace)
        print "finished - don't do this again for at least 12 hours"
        time.sleep(12 * 60 * 60) # 12 hours
    else:
        seconds = choosesleeptime(closestPlace['distance'])
        print str(closestPlace['distance']) + " from nearest place. waiting for " + str(seconds) + " seconds before checking again"
        time.sleep(seconds)

12. It worked. 🙂

photo of a hack

Okay, so it was a silly idea.

But the point is - it was amazingly easy to get this working. To do this by writing a native mobile app would have needed much more effort, and would have only worked on one platform.

The downside? You need to be on a mobile network that supports BlueVia - which in the UK means O2. I couldn't have done this if I was on Vodafone. Which restricts the usability a little.

But still. It is pretty cool.

Tags: , , , , , , ,

8 Responses to “My first experience using BlueVia APIs”

  1. […] Dale Lane on using BlueVia APIs […]

  2. Hammad says:

    Hey dude,

    Well done, Im a stakeholder in BV and good to know you’ve managed to get it beyond what it was designed for.

    Would be great if you could walk me thru this on android….

    bladerunner204@gmail.com

    cheers

    Hammad

  3. dale says:

    I’m not sure that Android would be any different to any other platform – what sort of thing did you have in mind?

    Kind regards, D

  4. Hammad says:

    Hey Dale

    apoligies for the late reply, I would like what you have setup on android, when I get near a location a text to go out to the Mrs etc… w

    Thanks in advance

    Hammad

  5. dale says:

    Hi Hammad

    All the source code to do it is on this page.

    Unless the API has changed significantly in the five months since I did this, you shouldn’t need anything else in order to do that.

    Kind regards, D

  6. Immo Huneke says:

    Interesting article – I am primarily reading it in order to find out how best to document APIs similar to this one.

    I notice that your formula for “polling interval” gives a frequency in units of s^-1, which is fine, but you then treat it as an interval as if it were in units of seconds. No wonder that it gives an interval of 0 for distances from target of anything over 35 metres! Your guard condition (if pollinterval < 60) will always fire unless your mobile device is right on top of the transmission mast. 🙂

  7. Nicole says:

    Where do you get the blueviaauthtok.pkl file? If you made it, how does it look inside? I’m having a problem with this part since it’s my first time to encounter a .pkl file… Thank you

  8. Nicole says:

    I’m sorry… but I got the answer already. I didn’t see the o3.saveAccessToken part.