Custom Widget: Ecobee thermostat

LATEST UPDATE:

Thanks so much to @RedOranges, here is a version that fixes the problems when using Celsius. This version works for Fahrenheit as well, so I would suggest everyone use this version:

Ecobee.widget.json (15.7 KB)


Original post:

This is an implementation of a widget that can (mostly) control an ecobee thermostat.

First, a few screenshots. If the thermostat is running its normal program, the widget looks like:

You can see the current temperature, humidity, and the temp of the remote sensor. The slider on the right shows what the current temp limit is set to. If you move that slider, it will change from programming mode to the hold mode. The widget will update to show that:

You can click the X button to remove the hold temperature, and return to normal programming mode. You can also click the mode icon to switch between heat, cool, auto and off modes. Here are a couple of final screen shots, showing menu to switch modes, and 1 showing how it looks in cooling mode:

The widget now uses settings for all the various items. (There are quite a few that are needed)

Installation instructions:
First, you will need v. 1.9 of the Ecobee binding and Ecobee action installed.

There are quite a few items that are needed, as well as a few rules For reference, here is a copy of my items file; It has a 1-to-1 mapping with the items needed by the widget. Note that you donā€™t have to use these item names, but you do need items defined for each item in this list.

/* Ecobee binding items (replace 123456789012 with your thermostat ID) */

String ecobee_name             "Ecobee Name [%s]"                 { ecobee="=[123456789012#name]" }

Group All
Group gSettings (All)

String ecobee_hvacMode "Ecobee hvacMode [%s]"     (gSettings) { ecobee="=[123456789012#settings.hvacMode]" }
Switch ecobee_useCelsius "Ecobee useCelsius [%s]" (gSettings) { ecobee="=[123456789012#settings.useCelsius]" }

Group gRuntime (All)

Number ecobee_temperature  "Ecobee Temperature [%.1f Ā°F]"  (gRuntime) { ecobee="<[123456789012#runtime.actualTemperature]" }
Number ecobee_humidity     "Ecobee Humidity [%d %%]"       (gRuntime) { ecobee="<[123456789012#runtime.actualHumidity]" }

Number ecobee_desiredCool  "Ecobee Desired Cool [%.1f Ā°F]" (gRuntime) { ecobee="<[123456789012#runtime.desiredCool]" }
Number ecobee_desiredHeat  "Ecobee Desired Heat [%.1f Ā°F]" (gRuntime) { ecobee="<[123456789012#runtime.desiredHeat]" }

String ecobee_schedule "Ecobee Scheduler"           { ecobee="<[123456789012#runningEvent.holdClimateRef]", autoupdate="false" }

Number ecobee_userCool "User Selected Heat [%.1f Ā°F]"
Number ecobee_userHeat "User Selected Cool [%.1f Ā°F]"


/* runningEvent.* is first event marked running, available as of openHAB 1.8 */
String ecobee_eventType "Running event type [%s]"                            (gEvents)       { ecobee="<[123456789012#runningEvent.type]" }

Group gRemoteSensors (All)

/* remote sensor named Office connected to an ecobee3 with ID 123456789012 */
Number ecobee_office_temperature "Ecobee Office Temperature [%.1f Ā°F]" (gRemoteSensors) { ecobee="<[123456789012#remoteSensors(Office).capability(temperature).value]" }
Switch ecobee_office_occupancy "Ecobee Office Occupied [%s]"           (gRemoteSensors) { ecobee="<[123456789012#remoteSensors(Office).capability(occupancy).value]" }

You also need the following rules:

import org.openhab.core.library.types.*

rule "Populate userHeat from desiredHeat"
when
  Item ecobee_desiredHeat received update
then
  if ( ((ecobee_hvacMode.state.toString == "heat")  ||
 (ecobee_hvacMode.state.toString == "auto")) && 
 
          ecobee_desiredHeat.state instanceof DecimalType) {
    
    ecobee_userHeat.postUpdate(ecobee_desiredHeat.state)
  
  }

end

rule "Populate userCool from desiredCool"
when
  Item ecobee_desiredCool received update
then
  if ( ((ecobee_hvacMode.state.toString == "cool")  ||
 (ecobee_hvacMode.state.toString == "auto")) && 
 
          ecobee_desiredCool.state instanceof DecimalType) {
    
    ecobee_userCool.postUpdate(ecobee_desiredCool.state)
  
  }

end

rule UserHeatHold
when
  Item ecobee_userHeat received command
then
  switch ecobee_hvacMode.state.toString {
    case "heat" : ecobee_desiredHeat.sendCommand(receivedCommand)
    case "auto" : ecobee_desiredHeat.sendCommand(receivedCommand)
    case "cool" : logWarn("UserHeatHold", "in cool mode, heat setpoint ignored")
    case "off"  : logWarn("UserHeatHold", "in off mode, heat setpoint ignored")
  }
end

rule UserCoolHold
when
  Item ecobee_userCool received command
then
  switch ecobee_hvacMode.state.toString {
    case "cool" : ecobee_desiredCool.sendCommand(receivedCommand)
    case "auto" : ecobee_desiredCool.sendCommand(receivedCommand)
    case "heat" : logWarn("UserCoolHold", "in heat mode, cool setpoint ignored")
    case "off"  : logWarn("UserCoolHold", "in off mode, heat setpoint ignored")
  }
end

rule HeatHold
when
  Item ecobee_desiredHeat received command
then
  logInfo("HeatHold", "Setting heat setpoint to " + receivedCommand.toString)
  val DecimalType desiredHeatTemp = receivedCommand as DecimalType
  var DecimalType desiredCoolTemp
  if (ecobee_desiredCool.state instanceof DecimalType) {
    desiredCoolTemp = ecobee_desiredCool.state as DecimalType
  } else {
    desiredCoolTemp = new DecimalType(90)
  }

  // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
  ecobeeSetHold("319281956903", desiredCoolTemp, desiredHeatTemp, null, null, null, null, null)
end

rule CoolHold
when
  Item ecobee_desiredCool received command
then
  logInfo("CoolHold", "Setting cool setpoint to " + receivedCommand.toString)
  val DecimalType desiredCoolTemp = receivedCommand as DecimalType
  var DecimalType desiredHeatTemp
  if (ecobee_desiredHeat.state instanceof DecimalType) {
    desiredHeatTemp = ecobee_desiredHeat.state as DecimalType
  } else {
    desiredHeatTemp = new DecimalType(50)
  }

  // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
  ecobeeSetHold("319281956903", desiredCoolTemp, desiredHeatTemp, null, null, null, null, null)
end

// rule supported in 1.9 onwards using the new ecobeeSetHold method signature:
rule FanHold
when
  Item ecobee_desiredFan received command
then
  logInfo("FanHold", "Setting fan hold to " + receivedCommand.toString)
  val params = newLinkedHashMap(
    'isTemperatureAbsolute'-> false,
    'isTemperatureRelative' -> false,
    'isCoolOff' -> true,
    'isHeatOff' -> true,
    'coolHoldTemp' -> 90,
    'heatHoldTemp' -> 50,
    'fan' -> receivedCommand.toString)
  ecobeeSetHold("319281956903", params, null, null, null, null)
end

rule ComfortHold
when
  Item ecobee_schedule received command
then
  if (receivedCommand.toString.equals("resume")) {
    // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
    ecobeeResumeProgram("319281956903", true)
  } else {
    // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
    ecobeeSetHold("319281956903", null, null, receivedCommand.toString, null, null, null, null)
  }
end

If you used different item names that are in my file, you will need to edit that file to match.

Once that is done, you can install the widget itself. The icons are available here

Once downloaded, extract the files to your conf/html directory. You should end up with a sub directory named images containing the extracted files.

Finally, import the following custom widget:

Ecobee.widget.json (10.5 KB)

There are still a few bugs to work out. The slider values donā€™t update correctly every time. It can always be fixed by reloading the widget (either by reloading the page, or going to a different page and going back) but I am still working through how to force updates. There are also a couple of spacing/formatting issues to work out. More updates after I learn more and fix some bugs.

7 Likes

Thanks for sharing!

Awesome! Looks great. Looking forward to try this (although I have a Nest :slight_smile: )

Could you add #widget tag to this thread so itā€™s easier to find?

Could you possibly update the template so that it uses the config items rather than hardcoded items please?

I am actually working on that, along with moving the css into the widget itself. As this was the first real widget I did, as well as the first time I have used javascript, I just blindly followed whatever examples I could find, and I am now refactoring each time I learn something new.

Unfortunately, I just got my new touchscreen display for my RPI, so I will be distracted for a day or two setting that up and updating my HABpanel setup to run there, etc but I hope to get back to this by the weekend.

Just saw the new version - really nice piece of work you did there @Signal11!
Thanks for sharing it.

1 Like

Tim, congratulations for your widget. But somehow the vertical slider doesnā€™t work in my configuration, see screenshot.

Using the standard slider widget, vertical sliders works as intended.
Anyone having any idea?

Not sure what is going on ther @DEy; The slider is not just a minimal size, it is also orange, when it should be blue when the system is set to cooling. Thats very strange.

I have nothing to really help, but I do have a more ā€˜finalā€™ version of the widget. It uses the widget-slider instead of just the direct rzslider from angularJS. This makes the slider update actually work, and the whole thing seems to be a lot better.

Here is a screenshot:

Here is the updated json:
Ecobee.widget.json (13.6 KB)

Finally, I made 1 change to the rules file, so that the ā€œresuming scheduleā€ thing works. Update the ComfortHold rule to look like the following:

rule ComfortHold
when
  Item ecobee_schedule received command
then
  if (receivedCommand.toString.equals("resume")) {
    // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
    ecobeeResumeProgram("319281956903", true)
    ecobee_eventType.postUpdate(receivedCommand.toString)
  } else {
    // the 1.9 onwards action bundle uses a selection string instead of an item as the first parameter
    ecobeeSetHold("319281956903", null, null, receivedCommand.toString, null, null, null, null)
  }
end

I donā€™t see how that change could fix your issue, but give it a shot, and if itā€™s still not working, we can try some debugging.

Great, itā€™s working nowā€¦
It probably might not have been connected to the new implementation of the slider. I donā€™t have an Ecobee and simulated most items; maybe Iā€™ve made a mistake there.
But thanks a lot for your help @Sensor11 which will hopefully help me a lot in implementing other widgets - itā€™s still a steep learning curve for me as Iā€™m neither a css nor JavaScript expertā€¦

Hi,
I just got started with OH because i wanted to see what i can do with my ecobee.
This widget is great, thanks for making it.

I have absolutely 0 experience with angularJS, but I managed to add all three of my sensors to your widget.
However, Iā€™m having problems with using Celsius.

The sliderā€™s bounds are hard coded to F
I could just make a second copy in the code under a

but my gut tells me thatā€™s kind of garbage code (unless thats just how things are done in AngularJS). Any chance you could provide a cleaner solution?
Also that would only get me past the first problem, i think i would then have to convert back to F to pass into the API

Hello,
Yes, the slider bounds are set to Fahrenheit. Well, specifically, the limits are something like 50 to 90. I tried to do something clever, like setting the limits as 15 below and 15 above the current setting, but that went badlyā€¦ as the value changed, the limits changed, and it all went terribly wrong.

I think you can set your ecobee up for Fahrenheit or Celsius. There is a setting for it, and I use that for the main display to show either F or C in the main widget.

My suggestion would be to make sure the ecobee is set to Celsius, and then to modify the widget. Once you import it, you can edit the code and change all the ā€œfloorā€ and ā€œceilā€ settings; Those are the ones that set the low and high end of the sliders.

If I get some time, I will try to set my ecobee to Celsius and see if that works, or if you have to do the conversion; If so, I can probably make a Celsius version of the widget for anyone with the same issues.

Hi,
I modified your file to support Celsius a little better.
Ecobee.widget.json (15.7 KB)

It just copies your code with different values if using Celsius, It could use a little refactoring but it seems to work for me. No need to make any item or rule changes luckily.

I seem to be missing something - I have it changing heat/cool/auto properly, but the slider seems to do nothing. The sensor also refuses to read for some reason. Should I post my rules/items files?

EDIT: I got it! Never mind. Now Iā€™m looking into a fan control method.

I got something working to mimic the quick settings menu, and I eventually want to expand it and add custom comfort settings.

But I need an Icon and some guidance on layout, the menu opens downward and it would work better going up.
Anyone want to help me out?

Here is the snippet for a quick settings row.
Notice Iā€™m using the wrong icon, and the menu opens down which might be a problem.

  <tr>
    <td class="quicksettings-row" colspan=3 align="right">
        <div class="quickSettings" uib-dropdown>
          <button id="single-button" class="mode" uib-dropdown-toggle>
            <img class="mode-icon" src="/static/images/ecobee/{{itemValue(config.hvac_mode) | lowercase}}.png"/>
          </button>
          <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
            <li role="menuitem"><a ng-click="sendCmd(config.schedule, 'home')"><b>Home and hold</b></a></li>
            <li role="menuitem"><a ng-click="sendCmd(config.schedule, 'away')"><b>Away and hold</b></a></li>
            <li role="menuitem"><a ng-click="sendCmd(config.schedule, 'sleep')"><b>Sleep and hold</b></a></li>
          </ul>
        </div>
    </td>
  </tr>

If you get this working for Nest I would love to see it :slight_smile:

1 Like

Any update on the mapping to Nest?

TypeError: [sprintf] expecting number but found string vendor.js

I see this error in the viewer and the widget has 2 slider one for heat and one for cool not the single one in your pic copied your code a number of times and seem to be going no where fast any thoughts running 2.1

I havenā€™t encountered this myself, but I havenā€™t updated OpenHab in a while.

Do you see an issue with me repurposing this for a Nest thermostat widget or is there a lot of specific Ecobee variables?