Ecobee Thermostat (by eZLO) Plugin

Sadly, both of the available Ecobee plug-ins have serious flaws. The old one (which I use) loves to fall out of authorization, which is a major pain, but I set up a Reactor rule that notifies me whenever that happens. So it’s manageable.

Both are obviously affected by the random system outages reported at ecobee’s website, which leave ALL their customers unable to control ANY devices for hours. And it’s things like that which keep serious “local only” (i.e. abandon cloud dependencies) proponents away from their products altogether.

But I have to say, again, the ezlo version of this plugin comes out of the box with so many bugs that it’s just not even ready for alpha, much less beta or daily-driver usage. Once users get a whiff of in-house (not even 3rd party!) creations like this making it out the door without sufficient QC, they begin to question lots of other decisions. Like why attempt to roll your own when there was a perfectly serviceable example ready for adoption in the Apps market?

Thus, while I applaud the effort, I’m sticking with the devil I know, which is the old ecobee plug-in, mercifully kept alive by @rafale77 (unceremoniously ousted from the Forum last year) and @toggledbits (the most helpful user ever!) since its creator left the Forum. Without those guys, ecobee thermostats would be a no-go with Vera, even six years on.

2 Likes

Do you mind sharing your rule for detecting when the auth has failed? At least that way this integration would be somewhat usable.

Oh, is it just “status”? Mine currently sits at 1. I’m assuming if auto fails it’ll go to 0? Or is it the selectionType which is set to “registered”?

Thanks

Sure! The parameter I watch (using Multi System Reactor, but you can achieve the same with Reactor for Vera) is ‘TSK’ on the ecobee parent device.

Whenever any of the following patterns partially matches the contents of ‘TSK’, Reactor (MSR, in my case) fires off an email message to me via SMTP:

Trying or Error or Not yet authorized or Register at ecobee

I hope that’s enough information for you to create an appropriate reaction.

I’m also using this app… I’d like it to put the thermostat in away or vacation mode indefinitely (or just different thermostat setpoints) as a scene and then back into normal mode / normal schedule in another scene.

It seems we can only set it to home/away/sleep/vacation as a hold itself. You can’t turn off holds and resume normal scheduling? I can’t find how to trigger that. Or am I misunderstanding “Home for now hold”?

Yes, you can cause the ecobee thermostat to “Resume” (i.e. stop a “Hold”). It’s the “ResumeProgram” action under the “Ecobee1” service. For this, I use Reactor or MSR, but one could just as easily get the job done using Lua or Advanced Scenes.

I installed Ecobee Thermostat(by eZLO ) Plugin about a year ago. I am just getting around to integrating the various temperatures(hourly forecast, thermostat set point and current house temp ) to control windows and shades in my house. I just noticed that the CurrentTemperature variable is always at 71.8 no matter that the Ecobee 5 is at 79 or 65 on its face. It appears that the plugin isn’t actually reporting the thermostat correctly. Has anyone else seen this? Is there a work around?
I am running Plugin Version 1.3. I have an Vera Plus running 1.7.5186 (7.31).

2 Likes

Hi Tim,

We are aware of the issue and will continue giving you support via email.

Best regards!

Does anyone know how to execute a ‘Resume’ using the eZLO plugin? I am trying to turn the fan on for 2 hours then back to Auto. I have no problem turning it on, but I cannot cancel the fan hold that is created nor change it to ‘Auto’.
Any code snip-its that can be used for this?

I started debugging this tonight.

One issue I found in the lua source code is this regex:

local server_matching_regex = ".*Server_Account=([%w%p%d]+).*Server_Account_Alt=([%w%p%d]+).*Use_Server_Account_Alt=(%d)"

Use_Server_Account_Alt does not exist in my /etc/cmh/servers.conf, so it fails with:

“Could not retrieve the account servers”

This really should be three separate regexes with a default for the last one.

I added a line, Use_Server_Account_Alt=0 and did the recommended process at Ecobee Thermostat (by eZLO) Plugin - #54 by slackner and got the url back …

I can verify at that point that the auth token and the refresh token are in /etc/cmh/user_data.json.lzo

But now, there is a startup error:

LuaUPnP.log:01  03/14/24 20:05:38.766   LuaInterface::CallFunction_Startup-1 device 198 function Main failed [string "local dkjson = require("dkjson")..."]:348: attempt to concatenate local 'response_code' (a nil value) <0x76be6520>

What I am stumped at now is this always returns all nil’s:

local one, response_code, response_headers, response_status = RequestFunction {                                                                                                         
                url = url,                                                                                                                                                                      
                method = method,                                                                                                                                                                
                headers = request_headers,                                                                                                                                                      
                source = source,                                                                                                                                                                
                sink = sink,                                                                                                                                                                    
                protocol = "tlsv1_2"                                                                                                                                                            
        }

Best I can tell after an evening of debugging is that ssl.https on the vera is broken. This works on my Linux box (with an appropriate 500 error), but not on vera:

https = require('ssl.https')                                                    
                                                                                
print('hello world')                                                            
                                                                                
local res = {}                                                                  
local one, two, three, four = https.request {                                   
    url="https://api.ecobee.com/1/thermostat?json=foo",                         
    -- url="https://google.com",                                                
    method="GET",                                                               
    protocol = "tlsv1_2",                                                       
    sink=ltn12.sink.table(resp),                                                
    verify="none",                                                              
    mode="client",                                                              
    options="all",                                                              
}                                                                               
print(tostring(one))                                                            
print(tostring(two))                                                            
print(tostring(three))                                                          
print(tostring(four))

More debugging this morning.

If I point my test script to a local https server on my network, it works:

root@MiOS_50009988:~/debug# ./foo.s 
hello world
1
200
table: 0x7e34a8
HTTP/1.1 200 OK

I extracted the environment from /proc for the vera Lua process and replicated the env while testing:

foo.lua:https = require('ssl.https')
foo.lua:print('hello world')
foo.lua:local res = {}
foo.lua:local one, two, three, four = https.request {
foo.lua:        -- url="https://api.ecobee.com/1/thermostat?[redacted]",
foo.lua:        -- url="https://google.com",
foo.lua:        url="https://10.13.54.100",
foo.lua:        method="GET",
foo.lua:        protocol = "tlsv1_2",
foo.lua:        --sink=ltn12.sink.table(resp),
foo.lua:        verify="none",
foo.lua:        --mode="client",
foo.lua:        --options="all",
foo.lua:}
foo.lua:      
foo.lua:print(tostring(one))
foo.lua:print(tostring(two))
foo.lua:print(tostring(three))
foo.lua:print(tostring(four))
foo.sh:export LUA_PATH="./?.lua;/usr/share/lua/?.lua;/usr/share/lua/?/init.lua;/usr/lib/lua/?.lua;/usr/lib/lua/?/init.lua;/etc/cmh-ludl/?.lua;/etc/cmh-lu/?.lua"
foo.sh:export LD_LIBRARY_PATH=/usr/lib/cmh:/mios/usr/lib/cmh:/rom/usr/lib/cmh
foo.sh:export HOME=/
foo.sh:export TERM=linux
foo.sh:export PATH=/usr/sbin:/usr/bin:/sbin:/bin
foo.sh:cd /
foo.sh:lua /root/debug/foo.lua

I think this is failing during the socket handshake.

I’m a little concerned how old the cacert.pem file is:

-rw-r--r-- 1 root root 253453 Aug 14 2014 /etc/mios/sslcerts/CA/cacert.pem

But curl works with google, so I’m skeptical of the age of the file being a problem. They likely share the same certs? But curl doesn’t use the openssl lib.

I wonder if maybe the older vera lua libraries don’t support a newer cypher that google, ecobee, etc. requires. But somehow curl supports it still.

And here is someone who tends to think the same thing:

If you’re doing this in Vera firmware…
You may be dealing with the server demanding a level of encryption that the libraries cannot provide. This has been an increasingly common issue for many plugins, and Google was leading the charge in dropping weaker ciphers. In some of my own plugins, I’ve had to offer curl as an alternative mechanism, because the built-in curl uses different libraries and can be a workaround if the Lua libraries aren’t cutting it.

It’s also the case that the CA chain is now out of date on all Vera firmware, and as old CA certificates are now expiring, and Vera/EZlo has not yet produced firmware with upgraded CA stores, you could be running into a certificate validation failure due to the outdated or expired data. The -k on curl is a possible cure for this. In the Lua SSL library, you can set verify to none in your request table.

The issues have affected, in addition to my plugins (SiteSensor, Reactor for Vera, etc.) the Honeywell and MyQ plugins, and I think GCal3 has already had one round of retrofit as a result of Google’s earlier changes. It’s going to get worse, not better, unless they update the firmware (or we do…)

Well, here’s my fix. Curl doesn’t seem to suffer from the ssl issue that the lua ssl does. So this is a mad hack:

$ diff L_ecobee2.lua.orig L_ecobee2.lua.fix 
116c116,118
<       local server_matching_regex = ".*Server_Account=([%w%p%d]+).*Server_Account_Alt=([%w%p%d]+).*Use_Server_Account_Alt=(%d)"
---
>       local server_acount_regex = ".*Server_Account=([%w%p%d]+)"
>       local server_account_alt_regex = ".*Server_Account_Alt=([%w%p%d]+)"
>       local using_server_account_alt_regex = ".*Use_Server_Account_Alt=(%d)"
136c138,140
<       server_account, server_account_alt, using_server_account_alt = string.match(server_data, server_matching_regex)
---
>       server_account = string.match(server_data, server_account_regex)
>       server_account_alt = string.match(server_data, server_account_alt_regex)
>       using_server_account_alt = string.match(server_data, using_server_account_alt_regex)
442a447,454
> 
>       if request_body then
>          request_body = request_body:gsub("[\n\r]", " ")
>          request_body = request_body:gsub("%s+", " ")
>       end
> 
>       Log("EcobeeRequest url=" .. tostring(url) .. " method=" .. tostring(method) .. " request_body=" .. tostring(request_body))
> 
452,455c464,476
<       local request_headers = {
<               ["Content-Type"] = "application/json;charset=utf-8",
<               ["Authorization"] = "Bearer " .. access_token
<       }
---
>       content_type = "application/json;charset=utf-8"
>       authorization = "Bearer " .. access_token
> 
>       local cmd
> 
>       if method == "GET" then
>           cmd = string.format("/usr/bin/curl -X GET -s -H 'Content-Type: %s' -H 'Authorization: %s' '%s'", content_type, authorization, url)
>       end
> 
>       if method == "POST" then
>          cmd = string.format("/usr/bin/curl -X POST -s -H 'Content-Type: %s' -H 'Authorization: %s' --data '%s' '%s'", content_type, authorization, request_body, url)
>       end
> 
461c482,484
<               local response_code, response_body = Request(url, method, request_headers, request_body)
---
> 
>               Log("Running " .. cmd)
>               fh = io.popen(cmd)
463,464c486,494
<               if not response_code then
<                       Log("Can't make request to ecobee")
---
>               if not fh then
>                       Log("Can't make request to ecobee via curl")
>                       return
>               end
> 
>               local response_body = fh:read("*all")
> 
>               if not response_body then
>                       Log("Response from curl was empty")
545a576,577
>  
>         Log("target: " .. new_mode_target .. ", can_execute=" .. tostring(can_execute))

I just calll out to curl for the ecobee API’s. The authtoken stuff still goes through vera’s own oath servers, which are still compatible with lua’s ssl.

You can apply the diff to the original file by lzo decompressing to a temporary directory, and then patching:

$ ll
total 44
-rw-r--r-- 1 rrauenza rrauenza 37909 Mar 15 15:42 L_ecobee2.lua.orig
-rw-rw-r-- 1 rrauenza rrauenza  2223 Mar 15 15:42 patch
[rrauenza@tendo test]$ patch L_ecobee2.lua.orig patch
patching file L_ecobee2.lua.orig
$ ll
total 44
-rw-r--r-- 1 rrauenza rrauenza 38866 Mar 15 15:43 L_ecobee2.lua.orig
-rw-rw-r-- 1 rrauenza rrauenza  2223 Mar 15 15:42 patch
[rrauenza@tendo test]$

Anyone currently successfully using an Ecobee Thermostat with a new Ezlo hub?

Not the old Vera firmware hub.

Apparently there is an Ecobee plugin in-built in the Ezlo hubs firmware, which is why I could not see an Ecobee plugin in their Ezlogic web UI plugins area.

Apparently you have to add the Ecobee Thermostat via the Mios mobile app Add Devices, like you would for a Z-Wave device.