Ecobee3 API

  • Intro

    One of my newest projects entails introducing my home to the IoT (Internet of Things). My first venture into this, was installing an Ecobee3 system into my home. For those that dont know, the Ecobee3 is a wifi enabled thermostat similar to the Nest product. One of the cooler things about this product is the open API that allows you to develop your own software to manage your thermostat. Suddenly an idea sparked in my head, I decided to write a quick script that I can use to grab information from my thermostate to output into my local installation of Nagios (more on that later). For this tutorial, I will be developing a Perl application on a Linux based system.

  • How Does The API Work?

    Ecobee describes its API as an http-based interface for control and access to the ecobee thermostats. Integrators will be allowed to read, update and poll information about their thermostat(s). ecobee aims to be backward compatible with previous versions of the API and to protect implementations from breaking when new features or changes are implemented. The API also strives to be self-documenting, easy to understand and use. Once I started digging into the API, I soon started to realize that the integration is somewhat complex. The API requires two keys to be stored, the first, known as the access-token is used to communicate with the thermostat. The second key is called the refresh-token, this is used to refresh the access-token which is only available for one hour. This makes coding a bit more complex, as you will need to make the decision of how to manage tokens. More advanced code can take the access-token and keep track of the expiry, and only refresh the token once the token has expired. For our use case, we will refresh the token every time we run the script.

  • Let's Get Started

    The first thing that we need to do before we can do any sort of application development, is to grab some API keys and authenticate our application. The first thing you will need to do is get a developer account at Ecobee.com. You can grab one from the following link: https://www.ecobee.com/developers/ Next, Ecobee provides a nifty little tool to get you started with your API key and initial tokens. Go ahead and follow the steps here to get started: https://www.ecobee.com/home/developer/api/examples/ex1.shtml. Follow the tutorial and grab your access_token and refresh_token from Step 3.

  • Perl and Curl

    Because the API is web based, we will be using Curl quite a bit to make our requests and consume our responses. Remember, that this application is linux based so tweaks will need to be made for Windows based applications. The first thing that we will do, is set up our core application information.

    #!/usr/bin/perl
    
    ########################################################################
    #       NormalException.net
    #       Author: Paimon Sorornejad
    #       Date: 1/4/2016
    #       Purpose: Quick perl script written to communicate with the
    #                ecobee3 api to obtain information about an
    #                installed thermostat
    ########################################################################
    
    use strict;
    use warnings;
    
    my $silent = 0;
    my $apiKey = "heYlook9someSecretKeyIAM";
    my $accessToken;
    my $refreshToken = "n7CBbSZuNo88TF0rLIfbzzoH9xaI5xJM";
    my $readToken;
    my $refresh_filename = "/tmp/refresh_token";
    my $ecobee_filename = "/tmp/ecobee_data";
    						
    We are defining a few core variables here:
    • $silent - This will define if we are running in silent mode (no printing to console)
    • $apiKey - This is your application's ApiKey which you should have obtained through the ecobee tutorial. Make sure to keep this secret and encrypt if you plan on distributing your app
    • $accessToken - We will store our access token locally here
    • $refreshToken - We will store our refresh token locally here
    • $readToken - This is the entire token we read from our temp file
    • $refresh_filename - Our temp file to hold our access/refresh tokens
    • $ecobee_filename - Our temp file to hold our ecobee data

    # Allow the user to provide an argument.  At this time we accept any
    # argument to symbolize that we are going to put this app in silent
    # mode.
    if($#ARGV + 1 > 0) {
            $silent = 1;
    } else {
            print "Please Add An 's' to the command line to silence";
    }
                            
    This part of our code is used to easily read any additional parameter to signify that we want to run the application in a silent mode. We can eventually expand on this if we want to do more complex things like pass in the data object that we want to retrieve instead of what we will perform in this example.

    # Read our current tokens from our temporary storage file.  This
    # file is expected to contain a single line with two tokens separated
    # by a space.  The first token is the access token, the second is the
    # refresh token.
    if(-e "/tmp/refresh_token") {
            open my $fh, '<', "/tmp/refresh_token";
            $readToken = <$fh>;
            close $fh;
    }
    
    # Hopefully we read a token, this is really ony expected to fail the first
    # time we run the application.  After that, we need our token from the
    # file.  Else this defaults to the tokens above, which wont work
    if(defined $readToken) {
            print "I Read Token From File: $readToken\n" if !$silent;
            ($accessToken, $refreshToken) = split(/ /, $readToken);
    }                        
                            
    This part of our code will read our tokens from our temp files. Remember we need to store our tokens locally so that we can constantly refresh and ensure that we have non expired tokens. We will create these initial temp files at the end of this tutorial, but for now, anticipate that they will be there and contain data. Our refresh_token file will contain two strings separated by a space on a single line.

    # Next, we are going to call the ecobee3 api, and perform a refresh.  If we
    # were a smarter application, we would keep track of when the token actually
    # expires and only run this whenever we actually expired.
    my $token_curl = `curl -s --data "grant_type=refresh_token&&code=$refreshToken&client_id=$apiKey" "https://api.ecobee.com/token"`;
    print "My Curl Output: $token_curl\n" if !$silent;
    
    # We need to extract two values, the access_token, and the refresh_token
    print "Extracting Token\n" if !$silent;
    my ($token,$refresh) = $token_curl =~ /\"access_token\": \"(.*?)\"[.\S\s]*\"refresh_token\": \"(.*?)\"/;
    print "Token: $token, Refresh Token: $refresh\n" if !$silent;                        
                            
    The next thing we are doing, is actually calling the ecobee API to do a refresh of our token. The curl request in this block is doing a POST as defined by our --data parameter. We are using our refresh_token and our APIKey here to perform our refresh. The expected response here is a JSON response that contains our tokens and expiry time:


    {
      "access_token": "NvqCvcf3qrrv85jcTTo3NvAWOgEoJ7KT",
      "token_type": "Bearer",
      "expires_in": 3599,
      "refresh_token": "LF2O50ovJvWLXybow6amsRY3sqZzsJqU",
      "scope": "smartWrite"
    }
                            

    # Assuming we got the token (we should have, else something bad happened)...
    if(defined $token) {
            # Let's write the two new tokens to our temporary file so that we can
            # use them for the next iteration of our application
            print "Writing Response To File\n" if !$silent;
            open my $fh, ">", $refresh_filename or die("Could not open file. $!");
            print $fh "$token $refresh";
            close $fh;
    
            # Now lets ask the Ecobee3 for some information.  This call will return
            # all runtime information from the Ecobee portal
            print "Now Getting Thermostat Data Using $token\n" if !$silent;
            my $thermostat_curl = `curl -s -H 'Content-Type: text/json' -H 'Authorization: Bearer ${token}' 'https://api.ecobee.com/1/thermostat?format=json&body=\\{\"selection\":\\{\"selectionType\":\"registered\",\"selectionMatch\":\"\",\"includeRuntime\":true\\}\\}'`;
            print $thermostat_curl . "\n" if !$silent;
    
            # Make sure that we actually got a response
            if(defined $thermostat_curl) {
                    # Write that information to a file so that we can read outside the app
                    print "Writing Thermostat Data To File\n" if !$silent;
                    open my $fh, ">", $ecobee_filename or die("Could not open file. $!");
                    print $fh $thermostat_curl;
                    close $fh;
            }
    }                        
                            
    Now that we have refreshed our tokens, we need to store them so we can use them again. The first thing we do in the above block, is save the tokens to our temporary file. This is the file we read from earlier so take note that this is how we are closing the loop. Next, we will use our access_token (which we have defined as $token from our curl request), to communicate to the ecobee web portal to get our runtime information. The data that is returned we store in our temporary ecobee_data file.

    At this point, we expect a result that gives us all of our thermostat runtime data. The data that returns should look similar to the following data:

    {
      "page": {
        "page": 1,
        "totalPages": 1,
        "pageSize": 1,
        "total": 1
      },
      "thermostatList": [
        {
          "identifier": "1241412412412",
          "name": "Home",
          "thermostatRev": "160104223841",
          "isRegistered": true,
          "modelNumber": "athenaSmart",
          "brand": "ecobee",
          "features": "HomeKit",
          "lastModified": "2016-01-04 22:38:41",
          "thermostatTime": "2016-01-04 22:06:32",
          "utcTime": "2016-01-05 03:06:32",
          "runtime": {
            "runtimeRev": "160105030609",
            "connected": true,
            "firstConnected": "2015-12-30 19:34:53",
            "connectDateTime": "2016-01-02 23:05:01",
            "disconnectDateTime": "2015-12-30 19:52:06",
            "lastModified": "2016-01-05 03:06:09",
            "lastStatusModified": "2016-01-05 03:06:09",
            "runtimeDate": "2016-01-05",
            "runtimeInterval": 33,
            "actualTemperature": 699,
            "actualHumidity": 24,
            "desiredHeat": 700,
            "desiredCool": 760,
            "desiredHumidity": 36,
            "desiredDehumidity": 60,
            "desiredFanMode": "auto"
          }
        }
      ],
      "status": {
        "code": 0,
        "message": ""
      }
    }                        
                            
  • Creating Our Temporary Files

    Before you get ready to run the application and see your results, we need to make sure we create our two temporary files. The first one is easy and will be a blank file called '/tmp/ecobee_data'. The second file will actually contain data which will simply be our initial access_token and refresh_token.

                            iowfki9e48403jhfhufspsmGyQOzIUZHP1 tOo052abbsid938383hrbYkzaUK