Things used in this project

Hardware components

Software apps and online services

Microsoft Azure

Story

Introduction

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.

Solution

All 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.

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:

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.

Constructing the Hardware

We 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:

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'.

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 azure-cli -g' to satisfy the dependency for 'request.js'.

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!

Conclusion

Nest 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!

Schematics

Circuit Diagram

Code

nest-motion.ino

C/C++

Used to control NEST device using PIR Motion Sensor and Particle Azure IoT-Hub integration.

/**********************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 SensorconstintCalibrationTimeInSeconds=30;//Seconds to await for calibration of the PIR sensorconstintSampleWindowSize=10;//Ex: SampleWindowSize is the amount of samples to keep track of for evaluating the occurence of a motion eventconstintPostiveSamplesToTriggerMotion=10;//Ex: A value of x will require that at least x samples produced within the sampleWindow are postive to trigger a motion event constintSleepIntervalInSeconds=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 hourCircularBuffer<bool,SampleWindowSize>sampleWindow;charoutput[50];//Per PIR spec, allow 30s to calibrate (warm up) the sensorvoidCalibrateSensor(){Serial.print("Calibrating Sensor... ");for(inti=0;i<CalibrationTimeInSeconds;i++){delay(1000);}Serial.println("PIR Sensor Calibrated");}voidsetup(){Serial.begin(9600);Serial.println("***************************************");Serial.println(" Nest Motion Detection Started ");Serial.println("***************************************");pinMode(PIRPIN,INPUT);CalibrateSensor();}voidloop(){sampleWindow.push(SamplePIR());if(CheckSampleWindowForMotion()){Serial.print("Publishing motion event... ");//Motion accurately detected, time to inform Nest that we are we are homeParticle.publish("motion","true",PRIVATE);//Trigger the integrationdelay(1000);//Extra sleep to ensure message deliverySerial.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 secondsCalibrateSensor();//Recalibrate Sesnor on awaken}}//Takes ten readings per second, returns true if a postive reading is encounteredboolSamplePIR(){Serial.print("Sampling PIR... ");intval=0;for(inti=0;i<10;i+=1){if(val==LOW)val=digitalRead(PIRPIN);delay(100);}if(val){Serial.println(" Motion Detected in sample!");returntrue;}else{Serial.println(" No Motion Detected in sample");returnfalse;}}//Loops through the sampleWindow, returns true if enough positive samples are foundboolCheckSampleWindowForMotion(){Serial.print("Checking Sample Window... ");intpositiveSamples=0;for(inti=0;i<SampleWindowSize;i++){if(sampleWindow.pop()==true)positiveSamples++;}Serial.print(positiveSamples);Serial.println(" positive samples were found in sample window");if(positiveSamples>=PostiveSamplesToTriggerMotion)returntrue;elsereturnfalse;}

CircularBuffer.h

C Header File

Used to provide a sample windows for tracking motion sensor samples over time

/* CircularBuffer.h - circular buffer library for Arduino. Copyright (c) 2009 Hiroki Yagita. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */#ifndef CIRCULARBUFFER_h#define CIRCULARBUFFER_h#include<inttypes.h>template<typenameT,uint16_tSize>classCircularBuffer{public:enum{Empty=0,Half=Size/2,Full=Size,};CircularBuffer():wp_(buf_),rp_(buf_),tail_(buf_+Size),remain_(0){}~CircularBuffer(){}voidpush(Tvalue){*wp_++=value;remain_++;if(wp_==tail_)wp_=buf_;}Tpop(){Tresult=*rp_++;remain_--;if(rp_==tail_)rp_=buf_;returnresult;}intremain()const{returnremain_;}private:Tbuf_[Size];T*wp_;T*rp_;T*tail_;uint16_tremain_;};#endif

Auto-Away-Assist-for-NEST-Thermostat

The Source Code for the "Auto Away Assist for Nest Thermostat" project