Hacking the Philips Hue with KRL


Summary

Philips Hue is a connected lighting system. No sooner did I see it that I wanted to control my lights from my personal cloud. This blog post is about my initial experiments with controlling Hue using KRL.

Hue Logo

I believe that connected things and personal clouds are going to usher in a new approach to home automation. Home automation has always been too hard and too expensive. Now with simple connected devices and cloud-based control systems, we can automate more and more of the things in our homes. The biggest problem, however, is getting these things to work together. Almost all of them come with a connection to their manufacturer's Web site which is fine as far as it goes, but ignores interaction between things.

For example, I have a CT-30 Wi-Fi thermostat. I can control it from an iPhone app. I also have four Philips Hue lightbulbs that I can control from an iPhone app. Does the future of home automation really consist of my phone being cluttered with dozens of apps that my finger as the integration point? I hope not.

If you've been following my blog, you'll recall that I recently set up my CT-30 thermostat so that I can program it from Google calendar using KRL. In this post, I'll show you how I did the same thing for the Philips Hue light system and demonstrate a simple interaction between the thermostat and the lights.

The Hue API

Philips has done a nice job of creating a user-friendly system with the Hue. Plug everything in, install the iPhone app, push the big button on the top of the Hue's hub and you're all set up. Very nice. What they don't tell you (yet) is that there's an API for the Hue and that the hub has an embedded HTTP server allowing direct interaction. Philips has a developer page promising more later.

Thankfully unofficial sites like EveryHue and Ross McKillop's Hue API documentation give early hackers enough to get going. The API is pretty simple. You need to set up an "application" on the hub to get an key (McKillop details how to do this) and then you just make HTTP Huts to the interface to change almost everything about the bulbs.

Using KRL to Control Hue

I wrote a simple KRL module to provide access to the Hue's API. The heart of the module is an action called control_bulb that takes

control_bulb = defaction(bulb, command) {
   base_api_url = "http://"+hub_ip_addr+ ":" + 
                  hub_port +"/api/"+hub_key;
   bulb_url = base_api_url + "/lights/#{bulb}/state";
   http:put(bulb_url) with
      body = command and 
      headers = {"content-type": "application/json"}
}

The parameters hub_ip_addr, hub_port, and hub_key are module parameters and configured when the module is declared. The command parameter is a KRL map that is turned into JSON an PUT to the hub's URL. The map should match the JSON command to be issued to the hub.

Because KRL needs to PUT to the hub, I had to use port mapping to make it available outside my firewall. Ultimately I need some kind of IoT firewall that provides me with more control over this and better security.

For example, I wrote a simple action to flash a bulb using control_bulb() called flash_bulb():

flash_bulb = defaction(bulb) {
  action = {"alert" : "select"};
  control_bulb(bulb, action);       
}

So far, I've kept it pretty simple. I'll undoubtedly add more as I get into specific features.

Simple Thermostat Interaction

When I wrote the KRL for controlling CT-30 thermostat, one of the things I had it do was write it's most recent data (including calculated information like the temperature trend) into the personal data store in the cloud where it's installed. The rule does this by raising a pds:new_map_available event that the PDS service is listening for.

rule save_temp_values {
  select when thermostat temperature
  always {
    raise pds event new_map_available with
      namespace = 'thermostat' and
      mapvalues = {
        'last_temp_time' : ent:last_temp_time,
        'last_temp' : ent:last_temp,
        'last_temp_trend' : ent:last_temp_trend || 'unknown',
        'avg_temp' : ent:avg_temp || 'unknown',
        ...
      }
  }
}

The blackboard pattern used here makes it easy for any other rule in my cloud to see the temperature data and do something with it. The CT-30 has an LED on the front that shows red, green, or yellow. When I programmed the CT-30, I change the color of the LED to reflect the temperature trend based on the average of the last five readings: "red" is "getting warmer", "yellow" is "constant", and "green" is "getting cooler". So the variable last_temp_trend always contains a color value representing the trend.

I decided to have the color of a Hue light reflect this same trend data. The rule is pretty simple:

rule set_temp_color {
  select when thermostat temperature
  pre {
    trend = pds:get_item('thermostat','last_temp_trend');
    huev = trend eq 'red'   => 5000 |
           trend eq 'green' => 25000 |
                               17500;
  }
  hue:control_bulb(1, {"sat": 254, "hue": huev});
}

This rule simply grabs the trend from the PDS and then sends a control command to the Hue light. The hardest part is figuring out hue and saturation values that (roughly) correspond to "red", "yellow", and "green". In order to get brilliant whites with lots of color temperature variation, the Hue lights don't work with RGB values that most programmers are more familiar with.

Debugging

A note about debugging all this. The Hue is pretty good about giving back error messages. Seeing those easily in KRL is (still) difficult, so I used a pastebin to see what I was sending to the hub and then curl to send those directly to the hub and read the error messages. Not ideal, but it works.

Also, the Sky Event console was invaluable in seeing the results of various events and the debugging output. The console works as a Chrome app.

Limitations

There are a few things to keep in mind while hacking the Hue:

  • Since Philips hasn't documented any of this yet, the API could change, although you'd need to flash your hub to see the changes, so if you're relying on the API, don't flash your hub until you know what it might do to the API.
  • At present, the API gives no way to control more than one bulb at a time, so you have to issue multiple PUTs. This isn't usually a problem, but the Hue does have some rate limiting (see McKillon's docs for more on this).
  • When you turn a Hue bulb off and then on, the hub must know that it's just rejoined and yet, the hub doesn't resend state data, so the bulb just comes on white until the next time the hub issues a command.
  • Due to a limitation of KRL, you can't send JSON with boolean values using the KRL HTTP library and the Hue bulbs can only be turned on and off using a boolean value. I'm working this.

Conclusion

Getting the Hue bulbs to work with KRL and personal clouds was easier than I thought. Once I found the API, the fact that it was running a Web server for direct control made this really easy. This is the same model that the CT-30 had and one of the reasons it was so easy to work with.

While changing the light color to match the temperature trend from the thermostat isn't all that practical, it's a nice demo of how two different connected things can be made to work together with a little KRL. I'm sure I'll be playing with this some more and making improvements.