I glimpsed a post in the RSS feeds today on how to connect Nest data with a Shiny dashboard and was compelled to post a less brute-force way to get data from the Nest API. The authors of the Shiny+Nest post used
system calls to
curl and regular expression character vector operations to slice, dice & work with the Nest API/data. However, we have packages like
httr that all make
system calls unnecessary. Here’s how to use the Nest API in a more R-like (or
Nest & OAuth 2.0
Nest uses OAuth 2.0 for authentication. As the authors of the other post pointed out, you’ll need to go to
https://developer.nest.com/clients (create an account if you don’t already have one) to setup a new client, making sure to leave the
OAuth Redirect URL field blank. Copy the Client ID for use later and store the Client secret in your
.Renviron file as
NEST_CONSUMER_SECRET=... (restart any open R sessions so R will re-read the
Once your API client information is setup, you’ll need a way of working with it. We first create a Nest OAuth endpoint that has the core URLs that
httr will use to help authorize the client with. Unfortunately, not all OAuth 2.0 endpoints work the same way. For the Nest API an initial
state parameter is required even if using PIN-based authentication (which we are in this example since Nest doesn’t honor a dynamic callback URL as far as I can tell). This is how top setup the
library(httr) library(jsonlite) nest <- oauth_endpoint( request=NULL, authorize="https://home.nest.com/login/oauth2?state=login", access="https://api.home.nest.com/oauth2/access_token" )
Now, we need to setup the “app”. This is more of an “R” need than an “OAuth” need. Use the Client ID you copied earlier.
httr will pull the secret from the environment variable you created.
nest_app <- oauth_app("nest", key="a8bf6e0c-89a0-40ae-869a-943e928316f5")
With that out of the way, now we authorize the client. Because we’re using PIN-based authentication, the user will have to cut/paste the URL displayed in the R Console into a browser then cut/paste the PIN displayed in the browser back into the R Console. With
cache=TRUE this will be a one-time event.
nest_token <- oauth2.0_token(nest, nest_app, use_oob=TRUE, cache=TRUE)
We can now use our shiny new token to make Nest API calls.
Using the Nest API
Since this is not a detailed introduction to the Nest API in general, you may want to take the time to read their documentation. Here’s how to get a list of all the devices for the account and then read the data from the first thermostat. I’m gaming this a bit since I only have one Nest device and it is a thermostat, but you can use their simulator to play with more data.
To get all the devices in use, it’s just a call do the
devices path. Again, not all OAuth 2.0 APIs work the same way, so instead of embedding the access token into the http request headers, you need to specify it in the query parameters:
req <- GET("https://developer-api.nest.com", path="devices", query=list(auth=nest_token$credentials$access_token)) stop_for_status(req) devices <- fromJSON(content(req, as=text))
devices contains a very ugly list (most JSON APIs return really ugly responses) but we can easily get the ID of the first thermometer (which we’ll need to use in the next API call) by doing:
first_thermostat <- names(devices$thermostats)
str(devices) to see what else is there for use.
Now, to get the data from thermostat, all you have to do is:
req <- GET("https://developer-api.nest.com/", path=sprintf("devices/thermostats/%s", first_thermostat), query=list(auth=nest_token$credentials$access_token)) stop_for_status(req) thermo <- data.frame(fromJSON(content(req, as="text")), stringsAsFactors=FALSE)
And, you can display the current temperature/humidity via:
cat(thermo$ambient_temperature_f, "F / ", thermo$humidity, "%", sep="")
Ideally, one would wrap this into a package (which I may do but feel free to take this code and make one before I get the cycles to get to it) and add more error checking and convenience functions for working with the data. But, you can now adapt the Nest+Shiny dashboard post code to use proper API calls and data structures vs
A contiguous version of the above code is in this gist.