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:
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:
- Easy access. Obviously, integrating directly with a TCP server kind of sucks in comparison to making a REST call.
- 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:
1 2 3 4 5 6 7 8 |
server { listen 81; server_name <server_name> location /leds { proxy_pass http://127.0.0.1:8000; } } |
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:
1 2 3 4 |
SmartThings --(http: port 80)--> router --(http: port 81)--> nginx --(http: port 8000)--> REST server |
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
- [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().