I've had an Ecobee thermostat in my house and now in my apartment for a number of years. It's a touchscreen thermostat equipped with 802.11 wireless that can be remotely adjusted and monitored from Ecobee's website as well as iPhone and Android applications. While the expected use case might be monitoring the temperature of one's home while at work, I often lazily use the phone applications while at home when I'm too cold to get out of bed to turn the heat up. Also, while Ecobee's website touts its "green" features and energy savings, working from home has always meant being unable to use automated work/home schedules and instead having to hold the same temperature all day. With some Ruby code and SNMP, I am now able to automatically detect when I am home and when I leave the apartment, and adjust the temperature automatically.
My Ecobee thermostat. Yes, I realize it's currently cooler outside than in my apartment. I just opened a window, okay?
When I first installed the Ecobee years ago, I watched its network traffic to try to figure out how it worked, hoping to create a proxy that would allow me to automate temperature changes or at least read the data coming from the unit. The thermostat maintains a persistent TCP connection out to Ecobee's server, over which it receives temperature and setting changes made remotely from the website, and pushes out local changes made from the display. Local temperature and humidity conditions are also sent to Ecobee to be logged and alerted on if necessary. The wire protocol used between the thermostat and the Ecobee server was not immediately discernible, so I looked to automate interacting with the Ecobee website. But after seeing their use of DWR and not wanting to work with it, I gave up and moved on to other projects. With a recent need to fetch local humidity information, I thought to look at Ecobee's recently released Android application to see how it communicated with their servers.
jcs@air:~> ruby ecobee.rb checking thermostat 121107XXXXXX hvac mode is: cool hold temp is: 76.0F room temp is: 76.2F room humidity is: 46%
With that working, I thought about whether I could automate temperature changes in response to my leaving the apartment. Since I always have my phone on me, the easiest solution was to monitor whether my phone was on my local wireless network (a modern version of my blueping utility) and do temperature adjustments accordingly.
The simplest solution that came to mind was to just ping the phone every so often:
jcs@air:~> ping 192.168.1.46 PING 192.168.1.46 (192.168.1.46): 56 data bytes Request timeout for icmp_seq 0 Request timeout for icmp_seq 1 64 bytes from 192.168.1.46: icmp_seq=0 ttl=64 time=2090.010 ms 64 bytes from 192.168.1.46: icmp_seq=1 ttl=64 time=1089.269 ms 64 bytes from 192.168.1.46: icmp_seq=2 ttl=64 time=89.664 ms
But because the phone goes into various states of sleep, it is not always responding to layer 3 traffic. Sometimes it doesn't respond at all, even though it's still maintaining an association with my Airport Extreme access point (as verified by its
Next, I thought about using SNMP against the Airport, fetching the list of MAC addresses of associated clients, and looking for my phone's entry. I hoped that this would be more accurate than pinging because the phone wouldn't even have to respond. This would also mean less traffic has to be processed by the phone when it is awake, resulting in better battery performance. (Due to an annoying problem with my Nexus S taking a long time to re-associate to the wireless network after waking up, I changed a setting months ago so it keeps its wireless radio on all the time. The battery performance hasn't been affected much, surprisingly.)
After working around a weird data caching problem^1^ where the Airport would still show my phone in its previous state long after it changed, I had a working script.
With my phone's wireless disabled, the program sees me as gone and lets the temperature get up to 78:
jcs@air:~/ecobee> ruby ecobee.rb 2011-08-30 01:01:55 - querying airport access point at 192.168.1.51 2011-08-30 01:01:56 - initialized phone (B4:07:F9:XX:XX:XX) connection state to not connected 2011-08-30 01:01:56 - establishing ecobee session 2011-08-30 01:01:56 - found ecobee thermostat id 121107XXXXXX 2011-08-30 01:01:57 - hvac mode cool, hold temp 78.0F, room temp 74.9F, humidity 47% 2011-08-30 01:01:57 - sleeping for 30 seconds 2011-08-30 01:02:27 - querying airport access point at 192.168.1.51 2011-08-30 01:02:28 - phone still not connected, leaving temperature as-is 2011-08-30 01:02:28 - sleeping for 30 seconds [...]
Once I re-enable the wireless and associate to the access point simulating me re-entering the apartment, it lowers the hold temperature to 74:
[...] 2011-08-30 01:06:33 - querying airport access point at 192.168.1.51 2011-08-30 01:06:34 - updating ecobee thermostat 2011-08-30 01:06:34 - hvac mode cool, hold temp 78.0F, room temp 74.9F, humidity 47% 2011-08-30 01:06:34 - phone (B4:07:F9:XX:XX:XX) has just associated, changing cool hold temperature to 74.0F 2011-08-30 01:06:35 - updating ecobee thermostat 2011-08-30 01:06:35 - hvac mode cool, hold temp 74.0F, room temp 74.9F, humidity 47% 2011-08-30 01:06:35 - sleeping for 30 seconds 2011-08-30 01:07:05 - querying airport access point at 192.168.1.51 2011-08-30 01:07:06 - phone still connected, leaving temperature as-is 2011-08-30 01:07:06 - sleeping for 30 seconds [...]
I later removed the hard-coded temperatures and let it adapt to the thermostat's hold temperature. This way if I am home and bump the temperature up 2 degrees to 76, and then leave, when I come back, it will change it back down to 76. Some persistent storage of these values would be useful, as well as support for heat hold temperatures once it gets cold enough to need it. I will eventually release this code on GitHub.
.184.108.40.206.220.127.116.1118.104.22.168.1.1.17where cached data seems to be poorly expired, walk
.22.214.171.124.126.96.36.1991.3.2to force it to refresh the data.