As seen previously (more), I’ve got our garage door hooked up to the internet. Now that we’ve both got Siri though, it seemed like I ought to be able to ask her to open the thing. Apple doesn’t yet provide any kind of API into or out of Siri, but you can get her to send texts.
Which brings us to Twilio, a fantastically easy to use SMS and voice menu API. It’s about as simple as I can imagine it being to set up a number on Twilio that, when texted, forwards the text along to a web URL of your choosing. So all I needed to write was a tiny web app to bridge SMS messages from Twilio to Indigo‘s web interface.
Since I’ve been working on Buzz, a tiny little web framework written in Lua, I decided to use that for my bridge.
So given all the various pieces were already in place, this is all the new code I had to write for this entire project. As a bonus, I can turn the lamp behind me in my office on and off too (mostly because it was a lot more comfortable sitting at my desk writing this than sitting on the garage floor!)
With this app running and Twilio set up to send texts to it, I made a contact on my phone named “House” with my new Twilio number, and can now tell Siri “Text house open the garage”, and the door will open. Technically it will open or close, it’s just a toggle, but you can see how to modify this code to do more stuff, or be smarter about it, pretty easily. Until Apple opens up the Siri API, this is a pretty good poor man’s substitute.
require('buzz') local ffi=require('ffi') ffi.cdef [[ int system(const char *command); ]] function homecontrol(request) _,_,body=request.uri:find('[?&]Body=([a-zA-Z+]*)') request.headers['Content-Type'] = 'text/xml' body = body:lower() if body:find('lamp') then ffi.C.system('curl -X PUT -d toggle=1 http://sideshowbob.local:8176/devices/OfficeLamp') response='Turned the office lamp on or off' elseif body:find('garage') then ffi.C.system('curl -X PUT -d outputBinaryStates="1,0,0,0,0,0,0,0" http://sideshowbob.local:8176/devices/GarageDoor') response='Activated the garage door' end buzz.response(request, '<?xml version="1.0" encoding="UTF-8"?>\n'.. '<Response><Sms>'..response..'</Sms></Response>\n') end buzz.get('^/homecontrol/\?.*$', homecontrol) buzz.run(8080)
Some issues with the above code:
- There’s no security at all, other than the phone number being a secret. Anyone could open the garage if they knew the right number to send a text to, or the right URL to send a Twilio-like request to. There are several really simple things that could be done to greatly increase security, if one were worried about it.
- It should really be set up as a PUT or POST request, as something is OBVIOUSLY changing state as a result of the request! But Buzz only does GET requests right now. Luckily Twilio can deal with that.
- It requires luajit, rather than working with plain old lua, since I’m using ffi to make a system() call to curl. There are better ways to do that, but ffi makes things like that really easy for experienced C programmers. Buzz itself requires luajit for the socket layer anyway, though I don’t think it will forever.