Are Ecobee thermostats supported?

Andrew Morsillo shared this question 7 years ago
Need Answer

Are Ecobee thermostats supported by Zipato? If not are they on the roadmap to being supported?

Replies (12)

photo
1

Not yet, but if they talk Zigbee, you may try to join them with the Zipabox with Zigbee module and there is a good chance that you will succeed. You still won't be able to use them, but then there is a possibility for me to support them remotely

photo
1

They don't support zigbee. They are wifi devices. Here is the API documentation https://www.ecobee.com/home/developer/api/introduction/index.shtml

photo
1

Hm... I checked here https://www.ecobee.com/how-we-are-smarter-smart-si/ and under "Standard complient" part it says something about Zigbee support.


I am not familliar with those devices though

photo
1

They do have remote sensors which I might guess are zigbee powered. I don't know about controlling the thermostat itself via zigbee. Other platforms seem to integrate via http.

photo
2

Can you provide an ETA on when Ecobee thermostats will be supported via their http API?

photo
4

they are the best seller in the usa and the most highly regarded smart thermostat, this should be integrated asap. But, again it will take time.


Just develop a plug in page zipato.

photo
1

Presumably you can control it via HTTP requests just like the Tado?


I had a quick look at the API and couldn't see any way to set A/C or heating mode. Does anyone have experience with controlling this thermostat?

photo
1

Ok, got it.


settings/hvacMode

photo
2

you the man dav. you the man

photo
1

@David - are you able to share any info on how to integrate it?

photo
1

I've taken a look at the API here:

https://www.ecobee.com/home/developer/api/documentation/v1/auth/pin-api-authorization.shtml


and the OpenHab integration here:

https://github.com/openhab/openhab1-addons/wiki/Ecobee-Binding


and the bad news is that it's not (apparently) as simple as the Tado. With the Tado, I can send HTTP commands directly from a Zipabox rule with my password and username in the string, and change the temperature.


I was about to detail how to do it from Google App Script, but it seems to me like the EcoBee requires something much more interactive, as if the user had to enter a time-limited PIN in a web page for the script to use. Maybe Adrian Van Der Heijden has more information?

photo
1

Ok, I think I've got it now. This explains it a bit more:


https://community.openhab.org/t/cant-get-pin-for-ecobee-binding/3432/40


First you register an app key at the EcoBee site. Then you specify a time-limited PIN for a particular application, also in the EcoBee web portal, and you use the app key and the PIN to make a one-time-only call to the API to get an authorisation code for that particular application. I think you then need to go back to the portal to register the application using that code within ten minutes.


Then, you call the API again to get an access and refresh token. I'm not clear whether these tokens are permanent or have to be re-requested every so often.

photo
1

If the tokens are permanent, then once you have gone through this arduous procedure, you can simply first off commands from Zipabox rules to the EcoBee API using HTTP commands.

If they are not permanent, you'll need a script running to request new access/refresh tokens and send them to the Zipabox.


In any event, to read the temperature and other data from the thermostat, you'll need a script running to receive the data from the HTTP GET commands and send it on to a virtual meter in the Zipabox. I use a Google App Script for this, which runs every ten minutes.

photo
1

To give you an idea, here's my Tado script. It does a bit more than just send data to the Zipabox. It also sends all the readings to a Google Spreadsheet and updates a "date-time" virtual meter on the Zipabox with date and time codes that I use to calculate date and time stuff.


// Send failure response with code/message
function sendResponse(code, message) 
{
  return {
      status: 
      {
        code: code,
        message: message
      },
      payload: 
      {
      }
    };
}

function GetTadoData() 
{
  //Logger.log("GetTadoData");
  
  const sZIPABOX_REMOTING_PREFIX = "https://my.zipato.com/zipato-web/remoting/attribute/set?serial=xxxxSERIAL_NUMBER_HERExxxx&apiKey=";
  const sZIP_DATETIMEMETER_SUFFIX = "xxxxDATE_TIMER_METER_ID_HERExxxx";
  
  var nResponseCode = 0;
  var sResultText;
    
  // Prepare standard HTTP POST stuff
  var options =
      {
        "contentType" : "text/plain",
        "method" : "post"
      };
      
  // Get current date/time
  var dateNow = new Date();
  var nYear = dateNow.getFullYear();
  var nMonth = dateNow.getMonth() + 1;
  var nDay = dateNow.getDate();
  var nHour = dateNow.getHours();
  var nMinute = dateNow.getMinutes();
  
  var bOddMinutes = true;
  var bIsEarlyHours = false;
  
  // Take minutes and divide by ten, removing fractions
  var nMinuteTens = Math.floor(nMinute / 10);
  if (nMinuteTens % 2 == 0)
  {
    bOddMinutes = false;
  }
  
  // If it's the early hours of the morning, also restrict the call frequency
  if (nHour > 2 && nHour < 7)
  {
    bIsEarlyHours = true;
  }    
  
  // Call once every 20minutes during the early hours
  if (!bIsEarlyHours || bOddMinutes)
  {
    // Create date-time value for the Zipabox (number of hours since 1 January 1970)
    var nZipaboxDateTime = dateNow.valueOf() / 1000 / 60 / 60;
    
    // Create time value (seconds since midnight)
    var dateTodayMidnight = new Date(nYear, dateNow.getMonth(), dateNow.getDate(), 0, 0, 0);
    var nZipaboxTimeValue = (dateNow.valueOf() - dateTodayMidnight.valueOf()) / 1000;
    
    // Send values to Zipabox date-time meter
    var resultZipaboxDateTime = UrlFetchApp.fetch(sZIPABOX_REMOTING_PREFIX+sZIP_DATETIMEMETER_SUFFIX+
                                    "&value9=" + parseFloat(nZipaboxDateTime) + "&value1=" + parseInt(nZipaboxTimeValue), options);
                                      
    // Borrowed heavily from https://gist.github.com/mhawksey/1276293
    
  //   var lock = LockService.getPublicLock();
  //   lock.waitLock(30000);  // wait 30 seconds before conceding defeat.
    
     // Set up fields for Tado data
     //var fields = {'success':true,'operation':true,'autoOperation':true,'operationTrigger':true,'insideTemp':true,'setPointTemp':true,'controlPhase':true,'boxConnected':true,'wConnected':true,'tsConnected':true,'currentUserPrivacyEnabled':true,'currentUserGeoStale':true,'deviceUpdating':true,'homeId':true,'pendingDeviceInstallation':true};
    
     var bSuccess = false;
    
     var result = UrlFetchApp.fetch("https://my.tado.com/mobile/1.4/getCurrentState?username=xxxxTADOUSERNAMEHERExxxx&password=xxxxTADOPASSWORDHERExxxx");
     //var nResponseCode = result.getResponseCode();
    
     if (result.getResponseCode() == 200)
     {
       var arrTadoData  = JSON.parse(result.getContentText());
       
       var resultHumidity = UrlFetchApp.fetch("https://my.tado.com/api/v1/home/17732/hvacState?username=xxxxTADOUSERNAMEHERExxxx&password=xxxxTADOPASSWORDHERExxxx");    
      // nResponseCode = resultHumidity.getResponseCode();
       
       if (resultHumidity.getResponseCode() == 200) 
       {
         var arrTadoDataHumidity  = JSON.parse(resultHumidity.getContentText());
         
         var doc = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/xxxxGOOGLESPREADSHEETIDHERExxxx/edit');
         
         // Position ourselves on row after last row with content
         var sheet = doc.getSheets()[0];
         var nLastRow = sheet.getLastRow();
         //sheet.insertRowsAfter(nLastRow, 1);
         
         // Get header titles
         var arrHeaderTitles = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
         
         // Create array to contain the row data
         var arrRowData = []; 
         
         var nBoxConnectedPos = 0;
         
         // loop through the header titles. For each, find the corresponding value from the JSON data
         for (nHeaderCounter in arrHeaderTitles)
         {
           // special case if you include a 'Timestamp' column
           if (arrHeaderTitles[nHeaderCounter] == "Timestamp")
           { 
             arrRowData.push(new Date());
           } 
           else if (arrHeaderTitles[nHeaderCounter] == "humidity")
           {
             var nHumidityPercentage = arrTadoDataHumidity.humidity.percentage;
             
             // Add humidity value
             arrRowData.push(nHumidityPercentage); 
           }
           else 
           { 
             // else use header name to get data
             arrRowData.push(arrTadoData[arrHeaderTitles[nHeaderCounter]]);
           }
           
           // Note position of boxConnected column
           if (arrHeaderTitles[nHeaderCounter] == "boxConnected")
           { 
             // Unbelievably, if you don't put + before the variable, it gets handled as a string
             nBoxConnectedPos = +nHeaderCounter + 1; // Header counter doesn't include the row header column
           } 
           
         }
         
         // Insert timestamp in first col of next empty row
         //var cell = sheet.getRange(nLastRow + 1, 1);
         //cell.setValue(new Date());
         
         // more efficient to set values as [][] array than individually
         sheet.getRange(+nLastRow + 1, 1, 1, arrRowData.length).setValues([arrRowData]);
         
         // Send inside temp and set pt temp values to Zipabox virtual meter
    /*     var options =
             {
               "contentType" : "text/plain",
               "method" : "post"
             };
  */       
         // Now also update home/away/sleep auto mode
         
         var nState = 0; // Home mode
         
         if (arrTadoData["autoOperation"] == "SLEEP")
         {
           nState = 1;
         }
         else if (arrTadoData["autoOperation"] == "AWAY")
         {
           nState = 2;
         }
         
         // And manual/auto mode
         
         var bManualMode = 0; // Auto
         
         if (arrTadoData["operation"] == "MANUAL")
         {
           bManualMode = 1;
         }
         
         // And whether the termostat is heating
         var bIsHeating = 0;
         
         if (arrTadoData["controlPhase"] == "HEATUP")
         {
           bIsHeating = 1;
         }       
                
         const sZIP_TADO_SUFFIX = "xxxxTADO_VIRTUAL_METER_ID_HERExxxx";  
         
         var sSuffixes =       
             "&value9=" + arrTadoData["setPointTemp"] +
             "&value1=" + arrTadoData["insideTemp"] +
             "&value8=" + parseFloat(nHumidityPercentage) +
             /*"&value7=" + parseInt(nState) +*/
             "&value6=" + parseInt(bManualMode) +
             "&value5=" + parseInt(bIsHeating) + 
             "&value4=" + parseFloat(nZipaboxDateTime);
             
         // "Home" is no longer useful information, since the Tado is always on "home" unless Geolocation is activated
         if (nState != 0)
         {
           sSuffixes = sSuffixes + "&value7=" + parseInt(nState);
         }
         
         var resultTado = UrlFetchApp.fetch(sZIPABOX_REMOTING_PREFIX+sZIP_TADO_SUFFIX+sSuffixes, options);
         nResponseCode = resultTado.getResponseCode();
         // var params = JSON.parse(resultTado.getContentText());
         
         //var nResponseCode = resultSetPoint.getResponseCode();
         
         // Get value of boxConnected column on next-to-last row and next-to-next-to-last row
         var bPreviousRowConnectionState = true;
         var bPreviousButOneRowConnectionState = true;
         
         if (nBoxConnectedPos != 0 && nLastRow > 0)
         {
           bPreviousRowConnectionState = sheet.getRange(nLastRow, nBoxConnectedPos, 1, 1).getValue();
           bPreviousButOneRowConnectionState = sheet.getRange(nLastRow - 1, nBoxConnectedPos, 1, 1).getValue();
         }
         
         // Warn via email if box is disconnected (but only if the previous state was also disconnected, i.e. it's not a temporary blip, 
         // and if the previous-but-one state was connected, to avoid a continual bombardment of emails)
         if (arrTadoData["boxConnected"] == false && bPreviousRowConnectionState == false && bPreviousButOneRowConnectionState == true)
         {
           var recipients = "xxxxEMAIL_RECIPIENTS_HERExxxx";
           
           var subject = "Tado offline! Electricity off?";
           var body = "The Tado is currently offline. Check to see if the electricity is off.";
           MailApp.sendEmail(recipients, subject, body);     
         }
         
       }
     }
    
  //   lock.releaseLock();
    
     if (nResponseCode == 200) 
     {
       sResultText = "Tado data sent to Zipabox correctly";
       // var sResultText = result.getContentText();
       //var params = JSON.parse(result.getContentText());
      
       //Logger.log(params.name);
       //Logger.log(params.blog);
      
       bSuccess = true;
     }
     else
     {
       sResultText = "Tado data not sent to Zipabox correctly. Error.";
      
       bSuccess = false;
     }  
   }
   else
   {
     sResultText = "Skipped Tado update this time";
     bSuccess = true;
   }
  
   //Logger.log(sResultText);
  
   var response = sendResponse("200", sResultText);
  
   return ContentService.createTextOutput(JSON.stringify(response))
    .setMimeType(ContentService.MimeType.JSON); 
 }

photo
1

Most of that you won't need, but the calls to the Ecobee may (or may not!) be straightforward once you've set everything up.


I would add subroutines to the Google App Script to make each of the calls to set up the authorisation, subroutines that you can trigger by hand as needed during the initial set-up.


https://www.ecobee.com/home/developer/api/documentation/v1/auth/pin-api-authorization.shtml

photo
1

According to this:

https://www.ecobee.com/home/developer/api/documentation/v1/auth/token-refresh.shtml


the access and refresh tokens expire after 1 year. So I guess the best thing is to stick everything in the script and have it first refresh the tokens, and then use the new access token for the calls.


This is all theory. Someone who's actually tried this will know more.

photo
photo
1

@David - thank you for the follow-up. Seems it’s doable but not as seamless as I was hoping :( Too bad as I was counting on our provincial smart thermostat rebate that I could use with my ZipaTiles.

photo
2

Would like to see ecobee supported natively as well. This is my vote.

photo
1

I'm looking at switching to Zipato from smartthings but native Ecobee support is a must. Hopefully you support it soon because I like lot of features of your product.

photo
1

I have a Ecobee thermostat with 4 remote sensors. I'd also like to see support for this.

Leave a Comment
 
Attach a file