When developers submit their products for Works with Nest product review, the basics are usually there, but the "housekeeping" is often missing.

In this codelab, you will walk through our Python sample app, so you can learn how to build applications using best practices for authorization, user data security, and user experience.

What you'll learn

What you'll need

There are two ways to get the code. You can clone the repository from GitHub:

$ git clone https://github.com/nestlabs/nest-python.git

Or alternatively, click the following link to download the code for this codelab:

Download Source Code

This will unpack a root folder (nest-python if you cloned the repository, or nest-python-master if you downloaded the zip file.)

For this codelab, you'll navigate to the root folder.

Open your terminal and cd into the nest-python (or nest-python-master) directory.

$ cd ~/nest-python

This project uses Python's PIP to manage dependent packages. Go to the nest-python directory and run the following commands.

  1. (Optional) Install Virtualenv and start a virtual environment:
$ pip install virtualenv
$ virtualenv env
$ . env/bin/activate
  1. Install the dependencies.
(env) $ pip install -r requirements.txt

Nest Home Simulator allows you to simulate up to two structures. Inside each structure, you can simulate Nest Cams, Nest Thermostats, and Nest Protects.

  1. Download Nest Home Simulator
  2. In Nest Home Simulator, log in with the same account credentials used in home.nest.com.
  3. Create two structures and add some devices to each structure.

Learn more about Nest Home Simulator.

To run the sample app, you need an OAuth client ID and client secret.

Before you begin


  1. In console.developers.nest.com, sign in with your Nest account credentials, and click Create new OAuth client.
  2. Fill in the client details and permissions.
  1. Click CREATE.

After your client is created, click the client to view the OAuth client ID and client secret.

To keep your secret keys safe and secure, set two environment variables (one for the client ID and the other for the client secret).

Why do this?

It's important to securely store and transmit access tokens, authorization codes, and client secrets.

Environment variables help you avoid accidentally exposing sensitive details by hard-coding them into your scripts and then pushing them to GitHub.

What is an environment variable?

An environment variable is a KEY=value pair that is stored on the local system where your code/app is being run.

Once set, the environment variables are accessible from within your code. In Step 8, we show you how to access them.

How to set them temporarily (this session only)

Copy the OAuth client ID and client secret (from Step 5) to the environment where the application will run. For instance, on Linux or MacOS, enter the following lines in a shell.

$ export PRODUCT_ID='Your OAuth client ID here'
$ export PRODUCT_SECRET='Your OAuth client secret here'

How to set them permanently

Modifying your shell startup script causes the changes to persist across terminal sessions.

  1. Copy the OAuth client ID and client secret (from Step 5) and add them to your /etc/environment file (for Linux):
PRODUCT_ID='Your OAuth client ID here'
PRODUCT_SECRET='Your OAuth client secret here'

or ~/.bash_profile (for Mac OS):

export PRODUCT_ID='Your OAuth client ID here'
export PRODUCT_SECRET='Your OAuth client secret here'
  1. Save and close your startup file.
  2. Reload your startup file in the current shell:

Did it work?

Run the following command to make sure.

$ printenv | grep PRODUCT

The output should contain your OAuth client ID and client secret.

  1. In the nest-python directory, start the sample app.
python app.py

If you're prompted, allow incoming network connections.

  1. Open http://localhost:5000 in a browser.
  2. If prompted, log in to your home.nest.com account.
  1. Accept the authorization request.
    If the account has two structures, accept the authorization request for both structures: Continue > Select the structures > Accept

Now that you have set the OAuth client ID and client secret in environment variables, you can access these variables in your code.


In the sample app, notice that sensitive information is obfuscated by using variables instead of hard-coded values.

In the following Python code, we get the PRODUCT_ID and PRODUCT_SECRET from environment variables.


import os

# OAuth2 client ID and secret copied from 
# https://console.developers.nest.com/products/(selected product)
# Keep product ID and product secret private 
#(don't store this in a public location).
product_id     = os.environ.get("PRODUCT_ID", None)
product_secret = os.environ.get("PRODUCT_SECRET", None)


product_id = settings.product_id
product_secret = settings.product_secret

def get_access_token(authorization_code):
    data = urllib.urlencode({
        'client_id': product_id,
        'client_secret': product_secret,
        'code': authorization_code,
        'grant_type': 'authorization_code'

Watch the video!

Don't assume your customer has only one structure (home).

Suppose the user account has two structures. Your app should display both structures and allow the user to configure per-structure settings.

Sample Works with Nest structure picker:

Before you begin:


  1. In the sample app, see both of your structures.
  2. Open the structures to view the devices that you created.

In the following Jinja template, we display a structure element for each structure in the user account.


<div id="structure-picker" class="accordion" role="tablist">
{% for struct_id, struct in data.get_structures().iteritems() %}
    <div class="card">
      <div class="card-header structure-card" 
              id="heading{{ loop.index }}">
          <a data-toggle="collapse" 
            <i class="fa fa-home" aria-hidden="true"></i>
            {{ struct.name }}
            <i class="fa fa-chevron-right" aria-hidden="true"></i>
      </div> <!-- card header -->
     ... structure details ...
     ... device details ...
    </div> <!-- card -->
  {% endfor %} <!-- for each structure →

Watch the video!

Make sure that your app updates within seconds after changes to device/structure labels, temperatures, setpoints, ambient temperatures, alarm states, and motion/sound events.

Your app should listen to updates from devices and update real-time or near real-time.

For example, with REST polling, your app should poll the API server at least every 30 seconds. With REST streaming, your app can listen for updates from the API server.

Before you begin:


  1. In the sample app, expand a structure and the devices for which you want to see updates.
  2. In the Nest Home Simulator or on a physical device, make some changes to the settings.
  3. In the sample app, wait for the app to poll or receive updates and then verify those changes appear.

In the following JavaScript code, we show how to poll or listen to the Works with Nest app server.

REST polling


    const DEFAULT_POLL_INTERVAL = 30000; // 30 seconds
    var pollIntervalId = 0;
    // Either poll or listen for updates 
    // - disable either pollRESTClient or listenRESTClient
    pollRESTClient();            // call sample WWN app server  
    //listenRESTStreamClient();  // listen to sample WWN app server

    function pollRESTClient() {
        // fetch the data and update results
        let contentURL = resultsCard.data('api-url');

        sendRequest({ url: contentURL});

        if (!pollIntervalId) { // repeat polling in specified interval
            pollIntervalId = window.setInterval(pollRESTClient,

REST streaming

If you prefer even quicker updates, you can use REST streaming server-sent events instead of REST polling.

  1. In static/js/app.js, un-comment listenRESTStreamClient(), and comment out pollRESTClient().
  2. In requirements.txt, uncomment sseclient-py and urllib3 and repeat Install dependencies step 2.


    // Either poll or listen for updates 
    // - disable either pollRESTClient or listenRESTClient
    //pollRESTClient();            // call sample WWN app server  
    listenRESTStreamClient();  // listen to sample WWN app server

    function listenRESTStreamClient() {
        var source = new EventSource('/apicontent_stream');
        source.onmessage = function (event) {
            let data = JSON.parse(event.data);

Watch the video!

Here's how to handle Home/Away states in the structure.

Before you begin:


  1. In the developer console, make sure your client is configured with the Away read/write permission.

  1. In the sample app, deauthorize and reauthorize the integration.
  2. Expand a structure and select Home or Away.
  3. Verify the change in the Nest Home Simulator or on the physical device.

In the following Jinja template, we generate a form to update the Away setting for each structure.


<form method="POST" action="/apiupdate/structures/{{ struct_id }}" role="form">
  <span><strong>{{ msgmap.labels['away'] or 'Home/Away' }}:</strong>
    <div class="btn-group radio-group">
      <input type="radio" value="home" name="away"
        {%- if struct['away'] == 'home' %} 
          class="active" checked=true 
        {% endif %} > Home
      <input type="radio" value="away" name="away" style="margin-left:2px"
        {%- if struct['away'] == 'away' %} 
          class="active" checked=true 
        {% endif %} > Away
  .. extra form elements ..

Watch the video!

Your client should confirm user actions and scheduled events—such as, when switching Home/Away states or when turning a Nest Cam on/off.

Before you begin:


  1. In the sample app, select a structure in the schedule form.
  2. Set the Home/Away setting to Home or Away.
  3. Choose a scheduled time to make the update, such as 1 minute from now.
  4. Click Schedule.
  5. Notice that a confirmation pop-up appears when the scheduled time arrives.

The following JavaScript code enables the user to update a device property by clicking a Schedule button. The UI then prompts the user for confirmation at the scheduled time. We use jQuery for the $ functionality.


$('#schedAwayForm').on('submit', function(ev) {
     Product asks using confirmation for an automated action, such as 
     when switching Home/Away states and turning Camera on/off.
     let confirmSchedule = function() {
         let confirmQMsg = "Set structure " + structureName 
                         + " home/away setting to '" + away + "'?";
         let cancelMsg = "Scheduled request to set home/away to '" 
                       + away + "' has been cancelled.";
             createAlertContent('info', confirmQMsg), 
             'Structure Schedule',
                     type: 'POST', url: url, data: "away="+away 
             function(){ showMsg('info', cancelMsg); }
      let millseconds = (minutes || 5) * 60 * 1000; // default 5 minutes
      var timerId = window.setTimeout(confirmSchedule, millseconds);
      showMsg('info', "Request to set home/away to '" + away 
                      + "' has been scheduled.");

Watch the video!

Your client integration should have clear error messaging when actions fail. Learn how to handle common errors that occur when writing back to Nest devices, for example, locked thermostat and rate-limit errors.

Before you begin:


  1. In the Nest Developers console, navigate to your client permissions, and remove the Away read/write permission. If you don't have this permission set, skip this step.
  2. In the sample app, deauthorize and reauthorize the client integration.
  3. Expand a structure and try to change Away to Home or Home to Away.
  4. Click OK in the confirmation.
  5. Verify that an error message appears, indicating you don't have write permission.

The following Python code shows the error messages that can appear in the UI when the Nest API returns an error. These error messages supplement the information returned by the Nest API.


class APIError(Error):
    def __init__(self, code, result):
        self.code = code
        self.result = result

def get_error_msg_help(code, default=""):
    help_msgs = {
        # HTTP Redirect code handled by application, not sent to browser.
        # This error may show as a 401 Unauthorized,
        # if not handled by the WWN client.  
        # See 
# https://developers.nest.com/documentation/cloud/how-to-handle-redirects
        # Adding here to show all error codes from the API server.
        307: "The WWN API has sent a redirect endpoint",

        # INVALID REQUEST  - This is a general error but the message
        # returned from the API will be more specific,
        # such as "Thermostat is not online", "Not writable",
        # "Invalid value for Away", etc.
        # Only use the following message to append to the message returned 
        # from the Nest API server, not replace it.
        400: """The request made by this client was not valid.
             Please revise this request before repeating it.""",

        # https://developers.nest.com/documentation/cloud/how-to-auth
        401: """The Works with Nest connection has been removed.
             Please re-authorize to get a valid token, 
             then retry the request.""",

        # FORBIDDEN
        403: """The credentials being used to access the Nest service 
                are invalid. This request should not be repeated.""",

        # NOT FOUND
        404: "The requested path could not be found.",

        # https://developers.nest.com/documentation/cloud/data-rate-limits
        429: """The WWN client has reached its rate limits.
             Please retry this request at a later time. """,

        500: """The WWN API has reported an internal server error (500).
             Please review the request and re-attempt at a 
             later point in time.""",

        503: """The WWN API is not currently available to service 
                client requests.  Please retry at a later time."""

    return help_msgs.get(code, default)

Learn more about Nest API error messages.

Watch the video!

Make sure your client integration removes all cached data as soon as the user removes a device or a structure with devices.

Refresh your app when devices/structures are removed.

Remove cached data that is older than 10 days.

Before you begin:


  1. In the sample app, expand a structure and devices for which you want to see updates.
  2. In the Nest Home Simulator or at home.nest.com, delete one or more devices.
  3. In the sample app, wait for the app to poll and then verify that the device data and the device are deleted.

The following Python code removes cached data that's older than 10 days. It also deletes Thermostat data if the Thermostat is deleted (or is not returned) in the user account.


hist_days_allowed = 10

def process_data_changes(nestData):
    if prev_therms:
        for therm_id, thermostat in prev_therms.iteritems():
            if not curr_therms or therm_id not in curr_therms:
                delete_device_files("thermostats", therm_id)
    # delete history older than 10 days (checking with each request)

Learn more about managing user data.

Watch the video!

Ensure that your client integration removes any images/GIFs after the camera is removed (or if the subscription is removed from the camera). Learn how to refresh your app when cameras/subscriptions are removed.

Before you begin:


  1. In the sample app, expand a structure and the camera.
  2. In the Nest Home Simulator or at home.nest.com, delete the camera.
  3. In the sample app, wait for the app to poll and then verify that the camera data and the camera are removed from the app.

The following Python code deletes camera images if the Camera is not returned or if the Camera doesn't have a subscription.


def process_data_changes(nestData):
    if curr_cams:
        for camera_id, camera in curr_cams.iteritems():
            if not models.NestData.camera_has_subscription(camera):
                # remove images if the subscription was removed
                # Cache camera images for sample app

    if prev_cams:
        for camera_id, camera in prev_cams.iteritems():
            if not curr_cams or camera_id not in curr_cams:
                # remove any images after the camera was removed

Watch the video!

Revoking tokens is an important token-management feature. When the user has explicitly logged out, your application needs to revoke their access and prompt for reauthorization.

Implement the deauthorization API when the user disconnects or logs out.

Before you begin:


  1. In the sample app, click Deauthorize/Remove Works with Nest Connection.
  2. From home.nest.com, make sure that the Works with Nest connection is deleted:
    Select a structure and then select Settings > Works with Nest. The Works with Nest window should not contain an entry for the sample app.

The following Python code deletes the access token when the user logs out of the sample app. It also removes all user data.


<a id="logout" class="nav-link" href="{{url_for('logout')}}">
      <i class="fa fa-sign-out" aria-hidden="true"></i>
      <strong> Deauthorize/Remove Works with Nest Connection</strong>


def logout():
    """ Product handles logout/deauthorization, reverting to a disconnected
state and removing all Nest user data, then redirects to enable a new oauth2 flow.
    return redirect(url_for(next_route))


def remove_access():
    token = fetch_token()
    if token:
        # delete user token using the Nest API
        except Exception as ex:
            print "Error deleting access token: ", ex

        # delete token and user data from persistent storage and cache

        print 'Not signed in.'

def delete_user_data():
    print "delete persistent user data"
    if cameras:
        print 'deleting any camera files saved'
        for camera_id, camera in cameras.iteritems():

    if thermostats:
        print 'deleting any thermosatat files saved'
        for therm_id, thermostat in thermostats.iteritems():
            delete_device_files("thermostats", therm_id)

    print "delete user session"


def delete_access_token(token):
    path = nest_tokens_path  + token
    req = urllib2.Request(nest_api_root_url + path, None)
    req.get_method = lambda: "DELETE"
    response = urllib2.urlopen(req)
    return True

Watch the video!

Sometimes an end user revokes access to your app through the user's Nest app account settings. If you don't handle revoked auth, your app will appear to be working, but the data will become stale because it won't get refreshed.

When authorization is revoked, your client integration must listen for and handle the 401 Unauthorized status code, refresh the app, and enable a new OAuth flow.

Before you begin:


  1. In home.nest.com, remove the Works with Nest connections:
    Select a structure and then select Settings > Works with Nest > Remove all connections.

  2. In the sample app, wait for it to poll or refresh the browser.
    A 401 Unauthorized alert appears.
  3. Close the pop-up.
  4. The sample app redirects to the Authorize screen.

When the app receives a 401 Unauthorized error, the following Python and JavaScript code handles the error by removing user data, displaying the error, and redirecting the user to the Authorize screen.


def process_api_err(err):
    if err.code == 401:
    return jsonify(err.result)


    function handleData(data) {
        clearMsg(); // clear current message
        if (data.error) {
            if (/401/.test(data.error)) {
                    createAlertContent('error', data.error), 
                    '401 - Unauthorized', 
            } else {
                showMsg('error', data.error);

Watch the video!

You've finished the codelab and successfully explored the best practices for Works with Nest app integrations.

Hopefully you have not only understood how these concepts and features can be implemented in an app but also understand why these best practices are important.

The following are some additional topics and links you might want to explore to amplify your skills even further!