Skip navigation

Category Archives: Authentication

I glimpsed a [post](http://blagrants.blogspot.com/2015/09/nest-thermostat-and-r-creating-shiny.html) 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 `RCurl`, `curl` and `httr` that all make `system` calls unnecessary. Here’s how to use the Nest API in a more R-like (or `httr`-like) fashion.

### 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 `.Renviron` file).

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 `httr` endpoint.

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](https://developer.nest.com/documentation/topics/api-guides-and-reference). 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))

Now, `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)[1]

Use `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="")

### Fin

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](http://blagrants.blogspot.com/2015/09/nest-thermostat-and-r-creating-shiny.html) post code to use proper API calls and data structures vs `system(“curl…”)` and `gsub()`.

A contiguous version of the above code is in [this gist](https://gist.github.com/hrbrmstr/2da2312170a84340c14f).