Cheap alternative to Phillips Hue LED Strip

I have some RGB LED strips in my bedroom to light an area other lighting in the room doesn’t reach. The strips I bought were inexpensive, but they only interact with the included infrared remote. I wanted to be able to control these lights with SmartThings. There are a couple of ways you can do this:

Easy and spendy

Phillips has a bunch of products that integrate nicely with SmartThings. The obvious contender here is this guy. However, for this to work, you’d also need a Phillips Hue Bridge. In total, this is going to run you somewhere between $150 and $250, depending on how many feet of LED strip you want.

Partially because this seemed unreasonably expensive, but especially considering I’d already glued LED strips to my walls, this solution wasn’t appealing.

Cheap and complicated

Browsing around, I found a cheap ($30) LED controller advertising “WiFi” control (link):

It was exactly what I was hoping for. It has a tiny TCP server that allows network control. The official mobile app is actually quite good, but it doesn’t integrate with the rest of my SmartThings stuff. I toyed around a little bit and managed to reverse engineer the protocol. I put it in a rubygem, available here.

This allowed me to programmatically control the LEDs, but obviously still no integration with SmartThings. Fortunately, that wasn’t very hard either.

Design

The overall design looks something like this:

7VfLU/M2EP9rcmzGD/LgSEJoD/QbpmGm7VGxha1BtjyS8uKvZ2WtbCkGmg6kH4fmkEg/rVf7+O1uPEqX1eFXSZryd5FTPkqi/DBKb0dJcj25gm8DHC0wmVxboJAst1DcA2v2QhGMEN2ynKpAUAvBNWtCMBN1TTMdYE+Ch1c0pHDqe2CdET5E/2S5Li06T6Y9/htlRemuiafozIZkz4UU2xrvGyXpU/uxxxVxutCrQ2S3bn/EPaprSB0Y9CJEFQCSqj5S6CwLna/JLriSs/o5DNlGyJxKTyhdQSKlEKDIrKrDknKTTJeodJpF8SzJKZ0m82iS/2JV350r3oVX0hqN/bTK1OpU+uiSSHPIKW6F1KUoRE34qkcXbaKoUQlhX5S64rCMYQlmyeNfiLebv81mPIGt0kTqGynFHqCME6VY5uA7BlZaFdYcY8MJpT7wGWWU2MoMHxvIYBnBbQV9T8+kSyJUIxUVBQdasnCi2S40iCAXik6ue/RBMDAVSIoPpI6kWMG4dRqsSfhQnzsIFTG3O7HGCChfAhaenT3U5vzM/GNYdoRv0b/71e3qx+pxlExJ1UBC6o0yP7DnELdFznawLMxyKSDDgnOoATyE673zAbM8puyo1Awaxz3ZUP4gFNNMmJrdCK1NrS64OVh0bWEpuLCl5hpDr+OGs8I8q4UxVIFNz137gaQg4mmIonl0Zyj6BLx7S7MqSd7y1AjlRJUd2eGkMd5Uh8K06zETajZm0DzVGKwo9WbLN53rxkDqWHAuj4f8Qw1OBdIoadmaRPu+0c6w/ZVej71Gsbf4GhDp37AGdXqs+WO1BspEayrB6UHi9yXTdN2Qtjr3ELiwbVwiWvE8rLorbHReuGLX2v14TU+q80viNR/Eqy5YffgOcUq/U5yQv16chtOpzt8ZIkP4zMlED0ybo1YO1v3JpYYR1s+Hw6jlzDBtXl7cIPHT4rDPzqyQFKfJts4NRtZAjXsO1Tit/zD5vmKuuas8Kq0rIMljyerCTtGAVZzDH2Lz38Y1+IyLLShf/ISSnJyUpPPFL0kXWT/3cTK7QE3G6NAlivI/LzxsxB8Wng33/5X3Jl9g27/gWPH+tTVdvQI=

I’ll elaborate on each of these components in the following sections.

REST server

The TCP API works nicely, but I wanted to wrap it in something that’d be easier to interface with. I wrote a really small REST gateway using sinatra. This serves two functions:

  1. Easy access. Obviously, integrating directly with a TCP server kind of sucks in comparison to making a REST call.
  2. Security. I added a before block in the sinatra to verify HMAC codes computed using a shared secret. This prevents unauthorized parties from using this server. Wouldn’t want randos turning my lights off and on!

This little server listens for requests on port 8000.

nginx

I use nginx as the externally facing endpoint because I have a bunch internal webservers, and nginx makes it easier to manage all of them. It also adds the ability to address the webserver using a subdomain instead of a custom port. The config looks like this:

Notice the server is listening on port 81. My router opens port 80, and forwards it to port 81 on my home server. I do this because internal services I don’t want to expose to the outside world run on port 80, but I’d prefer to use port 80 from the outside world. The request chain looks something like:

Integrating with SmartThings

SmartThings has what they call “Virtual Devices“, which is a way to define a custom device in terms of its capabilities. A Virtual Device can, for example, declare that it’s a switch (giving it an on/off toggle), a switch level (giving it a dimmer slider), or a “color control” (giving it a hue/saturation control). One can also insert code that’s called whenever one of the controls changes value. Perfect!

I created the following Virtual Device based on the Phillips Hue device template that interacts with the REST server mentioned previously.

All one needs to do at this point is create a new device within SmartThings that makes use of this Virtual Device.

Conclusion

This ended up working out way better than I expected. From what I can tell, it behaves exactly like a first-class citizen within SmartThings. I’m super happy with how easy (and fun!) it was.

Updates

  1. [Oct-21, 2015] — I noticed there was a bug in the SmartThings Virtual Device signature generation method. It wasn’t properly padding signatures beginning with 0. I fixed this by using the built in byte[].encodeHex().

Words With Friends Dictionary

I’ve been doing a bit of work with a move generator for Zynga’s popular variant on Scrabble, Words With Friends (WWF). To be any good at that, you need a list of words what WWF considers valid. Fortunately, they mention on their website that they use a slightly modified version of ENABLE, which is freely available.

Unfortunately, I noticed that sometimes WWF would complain about words I generated not being valid. After ensuring that the generated words were indeed in the ENABLE dictionary, it became obvious that Zynga’s dictionary removes some words from ENABLE. From experience, these generally included words that some people might consider offensive. For example, the word ABO, which is a pejorative for an Aboriginal Australian, is not recognized by WWF. In addition to this, ENABLE includes words that are longer than 15 letters. Since WWF’s board is 15×15, it’s impossible to form a word longer than 15 letters. Strangely enough, WWF still recognizes these words, despite them not being valid plays.

In an attempt to get a dictionary that’s as close to possible as the one WWF is actually using, I used an open source library I’ve been developing to ping them with all of the words shorter than 16 letters in the ENABLE dictionary. They removed about 50 words from the standard ENABLE dictionary.

A quick poll of a few “cheat” websites indicated that they were using the vanilla ENABLE dictionary, which includes the removed words.

You can find the updated dictionary here: wwf-dictionary.

Minification and disk caching

It’s probably pretty obvious that resource minification is a good thing. The bandwidth savings this affords you are probably negligible, but the fact that your site has fewer objects to load means faster page load times, and fewer requests sent to your server.

Ideally, you’ll have one file for all of your javascript source, and one more for all of your stylesheets. There are some pretty nifty WordPress plugins that do all of the work for you. This is great. Whenever you update your javascript or stylesheets, these plugins will generate a fresh minified file for you automatically. How lovely.

One has to be careful, though. Gathering up all of your source, minifying it, and throwing it all into a single file is a slightly costly operation. It’s a good thing, then, that all of the plugins I mentioned earlier provide some sort of caching mechanism. Some are a little less thoughtful of performance, however. Consider, for example, Better WordPress Minify. Here’s what the link to my minified javascript file looked like:

http://blog.christophermullins.net/wp-content/plugins/bwp-minify/min/?f=wp-includes/js/jquery/jquery.js,wp-content/plugins/crayon-syntax-highlighter/js/util.js,wp-content/plugins/crayon-syntax-highlighter/js/crayon.js,wp-content/plugins/crayon-syntax-highlighter/js/jquery.popup.js,wp-content/plugins/crayon-syntax-highlighter/js/fancybox/jquery.fancybox.pack.js,wp-content/plugins/crayon-syntax-highlighter/js/crayon_admin.js,wp-includes/js/thickbox/thickbox.js,wp-content/plugins/crayon-syntax-highlighter/util/tag-editor/crayon_te.js

This is clearly getting passed to PHP every time someone requests the page. Why is that bad? Because PHP is a resource hog. Serving a static file is almost always cheaper! This is especially true when PHP is just going to read a cached file from the disk anyway. As far as I can tell, that’s exactly what this plugin does. I can’t seem to find any way to change the caching mechanism without editing the source. Here’s how it serves cached files:

In contrast, here’s what the minified javascript link looks like under W3 Total Cache’s minify:

http://blog.christophermullins.net/wp-content/w3tc/min/92903ecb.6593de.js

This looks much nicer! This is, in fact, a static file. We could even serve it from a more lightweight web server, or push it to a CDN. W3TC supports doing both of these things. Here’s the magic: if the file exists, it’ll be served normally. If it hasn’t been generated, the request will eventually hit this rewrite rule:

rewrite ^/wp-content/w3tc/min/(.+\.(css|js))$ /wp-content/w3tc/min/index.php?file=$1 last;

This forwards the request to W3TC’s minify handler, which generates and caches the minified resource. From that point forward, it’ll be served as a static file. Nice!

Incidentally, W3TC’s page cache does exactly the same thing. When configured properly, this plugin makes it pretty easy to withstand pretty heavy traffic spikes.

You might not notice the difference in performance when your server is under a light load, but trust me. If you let PHP handle the caching, you’ll get a visit from the 502 Bad Gateway fairy in no time.

Of course, this all assumes you’re on a single host and have no interest in stuff like memcached.