Using my Nest Thermostat as a Combination Lock
TL;DR: I turned my Nest Thermostat into a combination lock, using a series of temperature settings in a particular sequence to disarm my alarm system. You can check out the final code here. If you enjoy these types of fun hacks and want to spend your time working on something way more important, come join us at LendUp.
Update: Someone on Reddit pointed out that I basically built this:
Why Would You Do This?
My wife sleep-eats. I’m an extremely deep sleeper, and every morning between 1:00 and 3:00 am she gets out of bed, sneaks right past me, and walks down the stairs into our kitchen. She then proceeds to eat whatever unhealthy sugary or carb-heavy treats she can find: a half-pound of chocolate chips intended to be used for baking cookies; a whole box of granola. Given how hard she works during the day to manage her diet, she is understandably frustrated when she wakes up in the morning having scarfed down an entire bag of tortilla chips. And I was frustrated too, because her solution (until recently) was to avoid having almost anything yummy in our entire house.
A few weeks ago she came up with an idea: why didn’t we turn on the motion sensors for our alarm system each night? The panel to disarm the alarm is across the kitchen from the stairs, so there’s no way to disable it without walking through the room and setting it off, and setting it off is likely to wake me up and stop her. She’d be trapped upstairs, unable to gorge herself on snacks. And since I usually wake up first in the mornings, I could use my companion iPhone app to disable the alarm before heading downstairs to make coffee. This seemed like a smart solution; we decided to try it.
The first night, I enabled the motion sensors before heading to sleep, and woke up excited to see if our plan had worked. I opened the app, disabled the alarm, and headed downstairs, only to find crumbs all over the kitchen floor. My wife had tiptoed over to my side of the bed, grabbed my phone off the nightstand, and unlocked it (she has my passcode). She then used the app to disable the alarm and… proceeded to eat an entire box of Rice Krispies.
At this point I started to marvel at her ingenuity and question whether she was actually asleep during these mid-night escapades. But it didn’t really matter: her willpower in the middle of the night was clearly very low, we needed to fix this, and we were back to square one.
Our Basic Solution
Luckily, the alarm iOS app had the option to add a passcode within the app itself. I enabled that feature and set it with a code that she didn’t know. That night my wife’s attempts to sate herself on junk food were finally thwarted. We left things this way for a week, and amazingly, there was no secret late night snacking! Success: my wife was thrilled.
This, however, left me with two minor inconveniences:
- First, I had to remember to disable the alarm each morning before walking down the stairs. This happened about half the time during the first week — but when I forgot, I would set off the alarm and wake my wife, my dogs, and half the neighborhood. I began setting random items at the top of the stairs as a mnemonic to remind me to disable the alarm, but this was unsightly and a tripping hazard.
- Second, turning off the alarm was now a real pain. My iPhone 6s Plus is horribly slow on iOS 11, and disabling the alarm requires pressing Touch ID, waiting for the phone to unlock, loading the alarm app, waiting for the second lock screen to show up, entering another code, pressing the disarm button, and waiting for the alarm system to announce that it has been deactivated. The whole process could take 30 seconds, and often times the app (or my entire phone) would crash halfway through. I’d be stuck, groggy and grumbling at the top of the stairs, longing for coffee.
Enter the Nest
I started brainstorming other options. I had already built a RESTful web interface to arming and disarming my alarm for the purpose of integrating with my Amazon Echo, so I could disable the alarm programmatically — all I needed was an internet-connected device at the top of the stairs.
I had an Amazon IoT Dash button lying around and considered using that, but there was no way to prevent my wife from using it as well. I considered requiring that the Dash be tapped in a particular sequence, but that seemed cumbersome and potentially error prone. I also considered just sticking the button in a hidden location, but I was worried my wife would find it and secretly start using it to snack again. Plus, there wasn’t really any good place to hide it at the top of the stairs.
My next idea was to get a Raspberry Pi with a keypad. At that point, I realized I was essentially rebuilding an alarm console, just with a different, secret, alarm code. The whole thing seemed overly complicated. I stood at the top of my stairs and scratched my head, looking around. What could I use to do the job? And suddenly, it was obvious — my Nest thermostat.
The thermostat is internet-connected, and positioned perfectly at the top of our stairs. Best of all, it lights up when I walk by, conveniently reminding me each morning to disable the alarm. All I needed to do was configure a system that monitored my thermostat for temperature changes, and when it identified a series of particular temperatures set in a particular order, it would disable the alarm. Heck, the thing even looked like a darned combination lock. It was perfect!
Nest API Integration
The first thing I needed to do was to find out if the Nest Developer API supported webhooks. Nope, but after diving into the docs I found the Nest REST Streaming service, which essentially allows your app to long-poll the NEST API, leaving a persistent HTTP connection open and receiving events when they occur. This is exactly what I needed.
First, I had to create a new Nest product using the Nest Developer Console, giving my product only the Thermostat Read v6
permission. I then visited the OAuth Authorization URL of the new product, which allowed me to perform an OAuth connect to my own personal Nest account. The authorization URL is available on the product overview page, and looks like this:
https://home.nest.com/login/oauth2?client_id=<PRODUCTID>&state=STATE
Since I never set up a login URL for my new product, this page automatically directed me to a Nest handler page with a pin-code to be used for finalizing the authentication. The page looks like this (this particular code is already expired, so you can’t use it to create a token on my account):
I took this code and plugged it into a HTTP request to exchange it for an OAuth access token, which could then be used to communicate with the Nest API:
curl 'https://api.home.nest.com/oauth2/access_token' -d 'client_id=<PRODUCT-ID>&client_secret=<PRODUCT-SECRET>&code=<PINCODE>&grant_type=authorization_code'
That command returned the following JSON:
{"access_token":"<TOKEN>","expires_in":315360000}
Great, I now had an OAuth token that would last me ten years. I first obtained my thermostat ID by performing the following:
curl -L 'https://developer-api.nest.com/?auth=<TOKEN>'
This returned me a JSON block with information on my devices, including my thermostat and its ID. If you have jq
installed, you can get the IDs of your thermostats with this command:
curl -L 'https://developer-api.nest.com/?auth=<TOKEN>' | jq '.devices.thermostats | keys[0]'
I then tested the Nest API streaming mode with cURL, using the following command. Note the -L
flag, which will follow redirects provided by the Nest API, and the -H 'Accept: text/event-stream
which is a client header that puts the Nest API into long-polling mode:
curl -L -H 'Accept: text/event-stream' 'https://developer-api.nest.com/devices/thermostats/<THERMOSTAT-ID>/target_temperature_f?auth=<TOKEN>‘
This immediately began streaming the target temperature of my thermostat. I walked over to the thermostat and turned the knob, and sure enough, out popped the new target temperatures:
event: put
data: {"path":"/devices/thermostats/<THERMOSTAT-ID>/target_temperature_f","data":79}event: put
data: {"path":"/devices/thermostats/<THERMOSTAT-ID>/target_temperature_f","data":82}
Now I replicated this in Python. I started with the example code provided by Nest using Python’s sseclient-py
library. Very quickly, I had an app that was spewing out numbers from any change on my thermostat. I already had a REST API set up to disarm my alarm, so now I just had to decide when to call it.
Building a Combination Lock
The next step was to build code that analyzed the incoming numbers and looked for a particular pattern. I thought it would be fun to learn to use Kinesis and Kinesis Analytics for this (hey, I’m analyzing a time-series, right?), but after playing around with Kinesis for a bit, I found there was a decent delay while my Kinesis Analytics application applied the SQL across the Kinesis stream. I wanted the unlock to happen as instantaneously as possible, since I’d be standing at the top of the stairs, waiting for the alarm to shut off. So I decided to simplify.
I settled on using an in-memory object in my Python script to analyze the numbers and match the combination (the numbers should be entered quickly, and in the unlikely event the script got restarted in the middle of entering the combination, I would just have to start over again from the beginning and I’d be fine). My initial implementation was simple: maintain a deque
named tumbler
that was the same length as the combination, and call tumbler.append(temp)
each time a new temperature came in. Then I could simply match the tumbler
against the combination
using simple the basic python operator ==
, like this:
This worked great while testing my script using the Nest app on my iPhone, however, when I moved to testing by actually rotating the physical thermostat, I realized there was a problem: the Nest was spewing out numbers very quickly as I turned the dial, which meant that the stream of temperatures would have extra numbers in between the numbers representing the combination, and I couldn’t just do a simple match.
My first instinct was to look at the time between the numbers, to determine if a particular number had been landed on for a significant period of time. But this would slow down entering the combination and seemed arbitrary. Instead, I thought about how an actual combination lock works, and settled on recognizing numbers the same way — by only focusing on numbers where the rotational direction of the wheel has reversed.
This was now easy enough to implement— my code looped through the historical numbers and pulled out a list of only the digits where the wheel had changed direction, then checked to see if the combination was present in that list. This required that every digit of the combination reverse the direction of the wheel (i.e., you could not have a combination like 87–86–85
). But that was an acceptable limitation, and it worked great:
And finally, the last step was zero-ing out the tumbler after the disarm was tripped, forcing the user to enter the combination again to disarm it again. Suddenly I had a perfectly working combination lock on my Nest:
Conclusion
All in all, this was a very silly but very fun project, and works great for our needs. What I think is so cool about this is that it’s hiding the lock in plain sight, like pulling a special book on a bookshelf, or a button that unlocks a secret door hidden inside of a bust. If I owned a web-connected hidden safe in the wall, this would be a really slick way to make the secret panel pop open — it’s very secret agent. I’m pretty darn pleased with myself.
Feel free to grab the final application here and tinker with it or adapt it for your needs. This is also something that could easily be built into a module for something like HomeAssistant (or IFTTT, or Workflow, etc) as a module where you can configure the appropriate combination and associated resulting action.
I hope you enjoyed reading this ! If you love stuff like this and you are interested in building things that might have a slightly bigger impact on society, consider joining us at LendUp!
Join the conversation on Hacker News.