The Nest Thermostat is a Wi-Fi enabled device that can optimize usage of heating and cooling systems using weather information and local sensor data. This device can also be controlled remotely so you can do cool things like set the house to heat / cool while on a drive home from work. The device is a bit of a black box though, and uses some underlying machine learning mechanisms (from what I hear anyway) to create a model of your particular usage habits and tries to determeine whether you are home or not.
The problem is that the Nest does not always determine home / away properly. For example, I have two Nest units controlling two HVAC units at home (one upstairs which controls the left-side of my house and another downstairs which controls the right-side). Oftentimes, I find myself working in my upstairs office (located on the right-side of the house) and the AC shuts off due to thinking I am away from the house. This is a problem and often involves manually firing off the Nest controller via the mobile / web application or running out of the office and tripping the motion sensor on one of my other Nest Devices.
The Nest is a Wi-Fi enabled device, so why not just tell it when you are home using motion sensing or something similar? Nest is aware of this issue and sells a motion sensor and an additive monitoring thermostat for controlling temperatures in rooms outside of the Nest controller location. These sensors are really expensive and they are only compatible with the Nest v3 thermostats (many of us have v2 models). The Nest v2 has enabled Wi-Fi, so you would think a Wi-Fi based device would exist to solve this issue, but it doesn't.
SolutionAll we really need is a method of informing the Nest that we are home using some form of hardware based trigger (PIR motion / reed switch / something more sophisticated). One would think that If This Then That would support this feature with their Nest integration, but there is nothing specific to setting the Home / Away status of the Nest at this time. That's not a deal-breaker, we can just roll our own.
To create our own mechanism for updating the Nest Home / Away status we need only a few ingredients:
- A valid Oauth Token to allow us to make API requests to update Home / Away status through Nest's developer API
- A device which can detect presence and send an event to trigger this API call. We will use a Particle Core / Photon and a PIR motion sensor along with an integration to forward messages to an Azure IoT Hub.
- A Service to make the actual call to the Nest API. We will use a serverless Azure function to make the call to the Nest API which will be triggered when events arrive through the aforementioned Azure IoT Hub.
A diagram outlining the overall architecture is provided below:
1. You will need to create an active Nest Developer account at - https://developer.nest.com/
2. Create a new OAuth Client at - https://console.developers.nest.com/products/new
Make sure to enable the "Away read / write" setting!
3. Obtain the Authorization URL value for your OAuth client from the screen below and select "Allow". After allowing your application, you will receive a pin code, make sure to copy this down. The Authorization URL can be obtained from the Overview screen for your OAuth Client as shown below.
4. In the Overview screen where you obtained the Authorization URL, take note of the 'Client ID' and 'Client Secret' values. Next, from a suitable command prompt with "curl" installed, execute the following command to obtain your AUTH_TOKEN:
curl -X POST "https://api.home.nest.com/oauth2/access_token?client_id=<YOUR_CLIENT_ID>&code=<YOUR_PIN_CODE>&client_secret=<YOUR_CLIENT_SECRET>&grant_type=authorization_code"
5. Finally, we need to obtain your desired STRUCTURE_ID which is a representation of your Home which associates to specific Nest devices. The following will return a json payload. You will want to take note of the value for the structure_id property.
curl --location-trusted \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \ -X GET "https://developer-api.nest.com/
Constructing the HardwareWe will use Particle Photon or Particle Core device available from http://particle.io along with a digital PIR sensor to create a device which monitors for motion and is capable of publishing events to Particle.io.
1. Begin by obtaining a Digital PIR sensor and connecting to an available Particle device according to the following schematic:
2. Create a new program in the particle Web IDE at https://build.particle.io/build
You will need to flash the attached nest-motion.ino and be sure to include the associated CircularBuffer.h
The program uses a CircularBuffer to allow for monitoring of Sensor readings over a Sample Window. This allows us to very precisely control the sensitivity of motion trigger events from the hardware. Typically, PIR sensors are very prone to producing false-positive events without a mechanism to evaluate sample readings according to a governor. For higher sensitivity, reduce the 'PostiveSamplesToTriggerMotion' variable. To require a longer window of motion for trigger , you can adjust 'SampleWindowSize'.
nest-motion.ino is provided below for convenience:
/*
*********************
Nest Motion Detection
*********************
A tunable program for determining motion events using a PIR sensor, with an emphasis on reducing false-positives and minimizing energy consumption.
When motion is detected, an event is published to Particle.io.
This event is then forwarded to an Azure IoT Hub which calls a Serverless function informing Nest to set Away Status to "Home"
Instruction for end-to-end configuration are avaiable @
*/
#include "CircularBuffer.h"
#define PIRPIN D0 //The Pin attached to the PIR Sensor
const int CalibrationTimeInSeconds = 30; //Seconds to await for calibration of the PIR sensor
const int SampleWindowSize = 10; //Ex: SampleWindowSize is the amount of samples to keep track of for evaluating the occurence of a motion event
const int PostiveSamplesToTriggerMotion = 10; //Ex: A value of x will require that at least x samples produced within the sampleWindow are postive to trigger a motion event
const int SleepIntervalInSeconds = 600; //The amount of time to go into deep sleep mode afer motion is reported, 600 seconds equates to a max of 10 alerts per hour
CircularBuffer<bool, SampleWindowSize> sampleWindow;
char output[50];
//Per PIR spec, allow 30s to calibrate (warm up) the sensor
void CalibrateSensor()
{
Serial.print("Calibrating Sensor... ");
for(int i = 0; i < CalibrationTimeInSeconds; i++){
delay(1000);
}
Serial.println("PIR Sensor Calibrated");
}
void setup() {
Serial.begin(9600);
Serial.println("***************************************");
Serial.println(" Nest Motion Detection Started ");
Serial.println("***************************************");
pinMode(PIRPIN, INPUT);
CalibrateSensor();
}
void loop() {
sampleWindow.push(SamplePIR());
if(CheckSampleWindowForMotion())
{
Serial.print("Publishing motion event... ");
//Motion accurately detected, time to inform Nest that we are we are home
Particle.publish("motion", "true", PRIVATE); //Trigger the integration
delay(1000); //Extra sleep to ensure message delivery
Serial.println("Motion event published");
Serial.println("Going to sleep now...");
System.sleep(SLEEP_MODE_DEEP, SleepIntervalInSeconds); //Go into deep sleep low-power mode for SleepIntervalInSeconds seconds
CalibrateSensor();//Recalibrate Sesnor on awaken
}
}
//Takes ten readings per second, returns true if a postive reading is encountered
bool SamplePIR() {
Serial.print("Sampling PIR... ");
int val = 0;
for(int i = 0; i < 10; i += 1)
{
if(val == LOW)
val = digitalRead(PIRPIN);
delay(100);
}
if(val)
{
Serial.println(" Motion Detected in sample!");
return true;
}
else
{
Serial.println(" No Motion Detected in sample");
return false;
}
}
//Loops through the sampleWindow, returns true if enough positive samples are found
bool CheckSampleWindowForMotion()
{
Serial.print("Checking Sample Window... ");
int positiveSamples = 0;
for(int i = 0; i < SampleWindowSize ; i++){
if(sampleWindow.pop() == true)
positiveSamples++;
}
Serial.print(positiveSamples);
Serial.println(" positive samples were found in sample window");
if(positiveSamples >= PostiveSamplesToTriggerMotion)
return true;
else
return false;
}
Setup Particle Integration to forward events to an Azure IoT HubThis step will require an active Azure account. The services that we will create will be free for or cost next to nothing to operate.
1. Create an Azure IoT Hub at http://azure.com
2. Create the Particle Integration
Create a new particle integration at https://console.particle.io/integrations/azure-iot-hub/create. Be sure to follow all of the instructions and pay particular attention to the part about creating a "Shared Access Policy". Use the value "motion" for the Event Name on the resulting setup page.
When you have filled in the appropriate information, enable the integration.
Now when Particle.publish("motion", "true", PRIVATE);
is called in nestmotion.ino, the event will be sent to Particle.io and then forwarded to your Azure Iot Hub.
We are almost done, we simply need to trigger a call to Nest's API to set the Nest 'Away' status to home when motion is detected.
I've done my best to make this as easy as possible with a premade Azure function which performs this task.
1. Create a new Azure Function app at https://ms.portal.azure.com/#create/Microsoft.FunctionApp
2. Within Visual Studio Code, open the Nest-Controller folder included in the source repo at https://github.com/toolboc/Auto-Away-Assist-for-NEST-Thermostat
You will need to edit the local.settings.json config file:
ACCESS_TOKEN and STRUCTURE_ID should be set to the values obtained in steps 4 & 5 of 'Obtaining an Oauth Token to access the Nest API'
In the azure Portal, navigate to your IoT Hub and select the "Built-in endpoints' section under 'Settings'.
<EventHub-Compatible-Endpoint> should be changed to the value of 'Event Hub-compatible endpoint'.
You are now able to debug and test the Azure Function. You should see the Function trigger when a motion event is sent from the Particle device.
When you are able to verify that the function is working as intended, you can publish the Function to Azure within Visual Studio by right-clicking the project and selecting 'Deploy to Function App'.
Optional: If you know what you are doing, you could create the Function entirely in the Azure portal using the code provided below. Be aware that you will have to install the 'request' dependency in the kudu cli by visiting http://<YOUR_FUNCTION_SITENAME>.scm.azurewebsites.net
, navigating to 'wwwroot', and issuing the command 'npm install request -g' to satisfy the dependency for 'request.js'.
module.exports = function (context, eventInput) {
context.log('Triggered!');
if(eventInput.event === "motion" && eventInput.data === "true");
{
var url = 'https://developer-api.nest.com/structures/' + process.env.STRUCTURE_ID + '/away?auth=' + process.env.ACCESS_TOKEN;
var request = require('request');
request({ method: 'PUT', url: url, followAllRedirects : true, 'content-type': 'application/json', body: '"home"' }, function (err, res, body) {
context.log('Away status set to: ' + JSON.parse(body));
context.done();
});
}
}
4. Update the function AppSettings to use your values for
ACCESS_TOKEN, STRUCTURE_ID, and particle-iot-hub_events_IOTHUB should be set to the values of your local.settings.json config file which were configured in Step 2 of 'Set Nest 'Away' status with Azure Function'.
You can test the Function using the contents of sample.dat within the Azure Portal:
And there you have it! You should now have a full pipeline in Azure for processing messages from Hardware => Particle.io => Azure IoT HuB => Azure Function => Nest API.
You can monitor motion events to the particle integration point at https://console.particle.io/integrations/ then select your integration and scroll down to history. Be sure to check this area to ensure you are not sending false-positive motion events:
My sensor is employed in my office and I was coincidentally out of town for the last 5 days. It's great to see that ZERO false-positives were sent!
ConclusionNest has a pretty awesome line of products as-is, however, we have shown that using access to their developer API, we can make them even better! I no longer have to worry that my AC will shut off when working upstairs. I can simply flash the nest-motion.ino code to multiple particle devices and alert Nest of home occupancy in multiple rooms in the house. Currently, I have one device attached to the usb port of my office printer (always-on), another in my brother's room that is plugged into the wall, and one attached to the usb port of my downstairs TV which only turns on when the TV is on. This setup allows the Nest to REALLY know when we are home or not.
The services we have employed are all free, and for around $22 you can build as many devices as you need. The hardware is energy conscientious as it goes into a deep sleep mode for 10 minutes by default after a motion detect event occurs, consuming only a mere 200uA. In addition, we can expand on the features by employing a variety of additional sensors. We could even create our own Nest Thermostat by employing a temperature sensor, and display.
Let us know in the comments what you think and feel free to share your ideas to improve on the design!
Comments