Using variables and macros with templating

Thought I’d share the following yaml code snippet which uses both macros as well as temporary variables to make things just a little bit more readable.

One tricky bit is that you need to watch out for when the output of macros get converted to strings and so you need to explicitly put in the |float conversion.

sensor 3:
    platform: template
    sensors:
        basement_on:
            friendly_name: 'Basement On Point'
            value_template: >
                {% set living = states.sensor.living_room_humidity.state|float %}
                {% set onoff_threshold = states.input_slider.onoff_threshold.state|float %}
                {% set highhumidity_threshold = states.input_slider.highhumidity_threshold.state|float %}
                {% macro min(X, Y) -%} {{ X if X|float < Y|float else Y }} {%- endmacro %}
                {% macro max(X, Y) -%} {{ X if X|float > Y|float else Y }} {%- endmacro %}
                {{ max(living,min(living+onoff_threshold,highhumidity_threshold)) }}
        basement_off:
            friendly_name: 'Basement Off Point'
            value_template: >
                {% set living = states.sensor.living_room_humidity.state|float %}
                {% set onoff_threshold = states.input_slider.onoff_threshold.state|float %}
                {% set highhumidity_threshold = states.input_slider.highhumidity_threshold.state|float %}
                {% macro min(X, Y) -%} {{ X if X|float < Y|float else Y }} {%- endmacro %}
                {% macro max(X, Y) -%} {{ X if X|float > Y|float else Y }} {%- endmacro %}
                {{ min(living,max(living-onoff_threshold,highhumidity_threshold-onoff_threshold)) }}
4 Likes

Nice! I love how open ended this stuff is - there are so many cool things yet to be discovered. I had something just the other day I wanted to use variables for but hadn’t got around to figuring it out yet - thanks.

@apetryk Thanks for sharing this, but could you explain a bit more what your goal was and how this accomplishes it? I think that will make this an even more useful resource!Q

Background is I’ve got a dehumidifier in the basement. My goal with the dehumidifier is to maintain relative humidity compared to the living room as well as some measure of absolute humidity.

Of course a dehumidifier off the shelf only has a humidity set point to engage (I’ve got mine set at 40%) above which it turns on and off.

So the components of my system include:

  1. Nest thermostats around the house - from which I can read humidity levels
  2. Z-wave power switch via Vera home automation
  3. input slider which defines the threshold for a “high humidity day” (default to 55%)
  4. input slider which defines the on vs off humidity distance so that the dehumidifier doesn’t constantly cycle on and off. (default to 4%)
  5. template variables which calculate the absolute level of basement humidity at which I’d turn on the dehumidifier (if it is off) and turn it off (if it is on and running). These are the template variable I provided the code for in my original post.

The logic I want to apply is:

If it’s a low humidity day, turn the dehumidifier on if the basement humidity is >= livingroom humidity + threshold.
If it’s a high humidity day, turn the dehumidifier on if the basement humidity is >= high humidity threshold
If it’s really humid out, turn on the dehumidifier if the basement humidity is >= livingroom humidity (without the threshold)

With offsetting logic for turning off.

Writing out the above logic with fully qualified variable names and a bunch of if statements would be a big mess of yaml. Turns out you can work out the logic with a MAX(A,MIN(B,C)) type statement as is contained in my code. Unfortunately Jinja doesn’t have a | max or | min filter and instead of creating my own filter I figured I’d just write the macros above.

I also figured it would make the statement more readable to use temp variables (e.g. “living” instead of “states.sensor.living_room_humidity.state|float”)

Some notes worth mentioning:

  1. Things have a tendacy to get converted to strings which cause errors when trying to do a < or > against a number. This is why I intersperse |float just about everywhere.
  2. I couldn’t figure out a way to make the macro definitions “global” which is why I cut and paste the same macros twice for the on and off points.
  3. Ditto the variable definitions (no way to make them global).

Then the on/off automation events are in my code snippet below. I set them up to only check humidity levels every 5 minutes - this was mainly in the event there was some weird problem with my state variables and I wanted to avoid cycling the dehumidifier on and off rapidly.

You may note below I also only turn off the humidifier if it is pulling > 100 watts. This was an extra step I threw in there as our dehumidifier has a delay timer. Sometimes when my son is in the basement gaming he will set the dehumidifier on delay (it’s fairly loud). If he’s done this, I’d rather leave the dehumidifier on (so the timer can expire) and only turn it off if the compressor is actually running.

automation:
    trigger:
        platform: time
        minutes: '/5'   
        seconds: 0
    condition:
        condition: and
        conditions:
            - condition: state
              entity_id: switch.dehumidifier_power
              state: 'off'
            - condition: state
              entity_id: binary_sensor.basement_humidity_ge_on_threshold
              state: 'on'
    action:
        - service: homeassistant.turn_on
          entity_id: switch.dehumidifier_power
        - service: notify.notify
          data:
            message: 'Turned on dehumidifier.  Basement: {{ states.sensor.basement_humidity.state }} Livingroom: {{ states.sensor.living_room_humidity.state }}'
            
automation 2:
    trigger:
        platform: time
        minutes: '/5'   
        seconds: 0
    condition:
        condition: and
        conditions: 
        - condition: state
          entity_id: binary_sensor.basement_humidity_le_off_threshold
          state: 'on'
        - condition: state
          entity_id: switch.dehumidifier_power
          state: 'on'
        - condition: numeric_state
          entity_id: sensor.dehumidifier_watts
          above: 100
    action:
        - service: homeassistant.turn_off
          entity_id: switch.dehumidifier_power
        - service: notify.notify
          data:
            message: 'Turned off dehumidifier.  Basement: {{ states.sensor.basement_humidity.state }} Livingroom: {{ states.sensor.living_room_humidity.state }} Watts: {{ states.sensor.dehumidifier_watts.state }}'
2 Likes

Thanks for taking the time to explain this, @apetryk! This is really well laid out - you should consider posting this to Share Your Projects! I can see a specific instance this would work for me if I can get access to my new Frigidaire A/C 's api.

I started a reply which ended by being off-topic.

Here’s also other things you could already try.

Thanks to @apetryk for sharing, it also took me a while to discover!