Remote control, 24/24h, under all circumstances via Google pubsub

For my home alarm system, I defined the following important requirements:

  • It should be possible to contact and control remotely (e.g. turn off/on the alarm while not at home) the system.
  • The latency for reacting to remote control commands should be as fast as possible (e.g. not more than 1-2 seconds).
And there I go, starting to implement the security of my home service HTTP endpoints... configuring the Ruby Sinatra framework to use SSL, to have basic HTTP authentication... setting up port forwarding on the home gateway...

That sounds ok, works ok, until thinking about using a "backup network"; what I have always foreseen in my home alarm system, is to use an external WiFi external access-point as fallback when my home gateway network is down; nowadays it is indeed easy and common to use your ISP special WiFi access points, available everywhere in the country. A quick scan around shows already 3 of these access points in the neighbourhood (one of course being linked to my home router). Well, that's definitively an additional requirement to take into account:
  • In the special circumstance where the default home networking is not available, it should still be possible to contact and control the system.
A potential solution would be to simply use polling: poll against a dedicated HTTP endpoint on my home server (running on Google App Engine). Not a good idea, as there are some restrictions and long polling is not really possible on GAE. Therefore I started looking for simple alternatives, looking like the Amazon SQS service (that I used for work).
My first findings were the "Task queue" and "Channels" services of the GAE platform. Unfortunately, if the Channel service was a good match, it lacked a Ruby client library (Javascript only, why why why?). A pity that Google Cloud Messaging can't be used to communicate with my home service: it works great to communicate with my Android phone...

Luckily I discovered that recently Google launched a service that would be perfect for my requirements: Pubsub. It is still a beta, but it does exactly what I need, and there is a [very beta] Ruby gem that makes it possible to use it in a simple way:
  • A few lines of Java to push a message from my GAE application.
  • Even less lines to pull the message from my Ruby client application; and it can be using long HTTP polling: great.
The only difficulties I faced while experimenting with the Pubsub service were that my GAE application was too old to work properly with it; I had to re-create it!!!
If you have an existing project and want to associate a Google App Engine application with it, you will need to create a new project to do so; be sure to enable Google Cloud Pub/Sub for the new project in step 2.
I am not sure why, but in the beginning the Ruby client authorization was unreliable, sometimes working fine, sometimes returning, randomly, some HTTP 403 and "User not authorized to perform this action" exceptions when performing API calls. May be something wrong in my use of the Google API client gem. But at the end, this works pretty well:

require "google/api_client"

key = Google::APIClient::KeyUtils.load_from_pkcs12('my_project.p12', 'notasecret')
client = Google::APIClient.new(application_name: "my_project",
                               application_version: "1-0-snapshot")

client.authorization = Signet::OAuth2::Client.new(
  token_credential_uri: "https://accounts.google.com/o/oauth2/token",
  audience:             "https://accounts.google.com/o/oauth2/token",
  scope:               ["https://www.googleapis.com/auth/pubsub",
                        "https://www.googleapis.com/auth/cloud-platform"],
  issuer:               keyfile["client_email"],
signing_key: key)

client.authorization.fetch_access_token!
pubsub_api = client.discovered_api "pubsub", "v1beta2"

result = client.execute(api_method: pubsub_api.projects.topics.list,
                        parameters: { project: 'projects/my_project' })

Comments

Popular posts from this blog

ZWay: getting the device list.

First steps with the ZWay library

Libcurl: perform a REST HTTP PUT