I made a screencast demonstrating the home automation system prototype I wrote about this week.
You can also view it on Screencast.com.
I made a screencast demonstrating the home automation system prototype I wrote about this week.
You can also view it on Screencast.com.
This is the third in a series about the home automation prototype system. Find the whole series here.
If you remember from this post, there is an HTTP proxy that sits between the local home devices and the Internet. It can talk the protocols the devices understand as well as HTTP that KNS understands. Here’s the system diagram again:
Right now, we don’t have any physical devices (this is still a prototype), so rather than communicating with the devices, the server simply prints out the effect that the directives would have on them.
If you remember the functions I described in the previous post, you’ll get a feel for what the server protocol looks like. Here they are again:
Sinatra makes it very easy to write the server (although you could use something like Bottle or Flask if you prefer). We use global variables (in the settings object) to keep track of “device” state. The server looks like this:
Once we get further in this project, we plan to get some real hardware and make it talk to the server. For now, though, we just have to read the output from Sinatra’s logs to see how the directives are taking effect.
Here’s an example interaction when the phone:incoming_call event was raised. The media_state and light_level are checked, and then they are both set. (See the rule that handles this event here.)
eridanus:home-emulator snay2$ ruby -rubygems server.rb ==Sinatra/1.2.6 has taken the stage on 4567 for development with backup from Mongrel 127.0.0.1 - - [20/Jul/2011 12:24:15] "GET /media_state HTTP/1.0" 200 23 0.0003 127.0.0.1 - - [20/Jul/2011 12:24:16] "GET /light_level HTTP/1.0" 200 19 0.0003 Media state is now stop. Was stop. 127.0.0.1 - - [20/Jul/2011 12:24:16] "GET /media_state/stop HTTP/1.0" 200 23 0.0004 Light level now 10. Was 10. 127.0.0.1 - - [20/Jul/2011 12:24:16] "GET /light_level/10 HTTP/1.0" 200 19 0.0005
This is the second in a series about the home automation prototype system. Find the whole series here.
The home automation prototype we’re building in my research lab has an HTTP proxy that translates communication between the in-home devices (DVD player and light dimmer in our example) and KNS. This proxy is running on the local home network and may not have a public IP address or domain name. That causes problems when we want a KRL ruleset to be able to talk directly to the proxy.
I first heard about localtunnel over a month ago when Twilio announced it. It’s a service that lets you tunnel any port on your local machine to a publicly accessible domain name. The domain name is randomly generated, providing some security by obscurity. This is the perfect solution for letting KNS talk to the local proxy in our system.
There’s still one problem, though. Every time the localtunnel is reestablished, the domain name changes. I could go manually edit the KRL each time that occurs so that it always points to the right place, but that is unreasonable in a production environment.
The KRL
The solution is to have the proxy server ping a webhook on the same KRL ruleset every time the server gets a new localtunnel address. Then the ruleset simply keeps that address around in an app variable. Here’s what the KRL looks like for that:
To use it, we simply reference the app variable. These are the functions I mentioned in the last post that handle communication with the proxy server:
The proxy server
The HTTP proxy itself is running Sinatra, so it listens on port 4567 by default. To use localtunnel, I’d normally just write this:
$ localtunnel 4567
Which spits out something like this:
This localtunnel service is brought to you by Twilio.
Port 4567 is now publicly accessible from http://3pjg.localtunnel.com ...
To automate it, however, I need to get that URL programmatically. Parsing the output wouldn’t work, because the localtunnel instance stays running as long as the tunnel is open. I ultimately decided to fork the repo on Github and add the feature myself. Here’s the original repo, and here’s my fork.
What I added was a function that pings a webhook once the URL has been obtained from the localtunnel web service. Take a look at the diff.
Now all I have to do is call LocalTunnel::Tunnel.new() and pass the webhook URL as well. Like this:
Every time I start the Sinatra server, I also run this script to open the localtunnel and notify the KRL ruleset of the change. No manual updating, and everything works great.
In a production system, we’d want to authenticate everything so that a rogue server can’t come in and redirect our traffic to a different place. But for the prototype, this is sufficient.
Note: If you want to follow the whole project, you can find it on Github.
This is the first in a series about the home automation prototype system. Find the whole series here.
In my research lab, we’re developing a prototype of a home automation system that uses Kynetx to glue all the pieces together. The two main components of that system are things (devices) that raise events and things that respond to directives. Let’s take this example from Phil Windley:
One of the significant benefits of evented systems is that they exhibit extreme loose coupling. To understand this consider the situation where you want your DVD player to pause when you get a phone call. Assuming each has an API, programming your phone to send a “pause” command to the DVD is easy enough to do. Now suppose you decide you want to also raise the room lights when the phone call comes in; you have to program the phone to also send a “raise lights” command to the room lights. Each new interaction requires an explicit command from one system to another. Such point-to-point connections make changes difficult. Change out the DVD player and every device sending it commands has to be reprogrammed.
Now, imagine instead that the phone merely raises a
phone:inbound_callevent. The DVD and lights can both listen for such events and do the right thing. Add something else to the mix and it can easily listen for thephone:inbound_callevent and do whatever is right for it. Nothing else needs to be reprogrammed or even told its there as long as it can listen for events of interest. Each actor in the event-driven system interprets the event according to its own context and purpose. Loose coupling leads to better resilience in the face of errors and configuration mistakes.
(Phil also discusses this example here and here.)
To get that loose coupling, the devices raising the events must be thought of separately from those responding to events. The phone receives a call; it might raise that event over the home Wi-Fi connection, or it might do it over its 3G connection. The DVD player and lights that are listening for that event are sitting behind a proxy that can speak several protocols: it knows how to communicate with the dimmer and the DVD player (perhaps through a proprietary API, as Phil suggests), and it knows how to speak HTTP so it can raise events and receive directives from KNS. This is what it looks like:
A sample interaction:
phone:incoming_call event to the home automation ruleset in KNSphone:incoming_call event, which do the following things:
set_media_state("pause") directive to the DVD playerset_light_level(8) directive to the dimmerThe phone could also raise a phone:hung_up event to signal that the call had finished. When that happens, we restore the state of the DVD player and lights to what they were before, using the same process.
Here’s an example implementation in KRL for the ruleset responding to the events. I use several functions that communicate with the HTTP proxy; their details are not given here, but the behavior is self-explanatory. Also, the rules are using app variables to store the previous state of the DVD player and dimmer so they can be restored later.
Future posts will explain in more detail the connection between KRL and the HTTP proxy and how the HTTP proxy is implemented.
I needed to write a very simple web server today. I maintained a small Sinatra server when I was the TA for CS 462. The code was clean and sensible, so I decided to give it a try.
However, liking Python as I do, I went searching for a Sinatra lookalike for Python. There are a few, but I tried Bottle.
I implemented the server using both frameworks for comparison. Here’s what the code for each looks like, with Bottle on the left and Sinatra on the right (click to enlarge):
The Bottle version of the server is several lines longer and not quite as succinct. While I’m still new to Ruby, there are several things I like better about its syntax than Python’s (e.g., the .to_i operator, beestings, better handling of “global” variables–settings in this case).
I think Sinatra wins here. What do you think?
Here is a list of the best articles about Google+ that I’ve read so far:
As a bonus: the Google announcement about pervasive design changes across all the Google properties.
You may remember my previous two posts about dialoguing. I illustrated two methods for doing it: form submission with watch() and web events. I’ve been using the second of those methods so much that I wanted to abstract it out to a module. a163x121 is the result.
Documentation
Here’s how it works. First, there are two configuration options that you need to specify when you import the module:
There is one method that does it all, called get_data(). Here are the parameters:
event:param("...").Example
I retrofitted the example from this post to use the new module, so the code may look familiar to you. First, I added one line to the meta block:
use module a163x121 alias dialogue with app_id=”a163x104″ and app_version=”dev”
Then I rewrote the rule a bit:
The original byufb_dir_search() function contained two things: First there were some lines to unhides the search results box and show a progress bar; second there was the dialoguing code. Since the module only handles the dialoguing code, I broke out those first two lines into a new function called byufb_dir_search_prep(), which in turn calls byufb_dir_search(). The “Search” button calls the prep function. I passed the name “byufb_dir_search” to the dialoguing module’s get_data() action so it knows what to name the JavaScript function.
The rule that handles the web event is exactly the same as before; no changes were necessary.
Conclusion
This simplifies the code by abstracting out the mundane JavaScript to get data from the page and raise a web event containing that data.
What do you think?