How can i self reference the state of the binary_sensor to conditionally set the attribute?

I have this giant trigger-based sensor for my freezers. I have a bit of false positive issues that I want to trace to which issue caused it to trigger the alert. I want to set the attribute to a key of issue but I want to clear that issue attribute once the alert is false. How do I self reference the state of this entity when the template renders?

The main part I am focused on is the last part of this template sensor which is this part. Should i be using the is_state() or this.state?

      attributes:
        issue:>
          {% if is_state('') %}
            "{{ trigger.id }}"
          {% endif %}

The rest of the trigger based template binary sensor.

# Big Freezer
- trigger:
    - trigger: state
      id: switch_on
      entity_id: switch.big_freezer_plug_blue
      to:
        - 'on'
    - trigger: state
      id: switch_off
      entity_id: switch.big_freezer_plug_blue
      to:
        - 'unavailable'
        - 'off'
    - trigger: numeric_state
      id: high_temp
      entity_id: sensor.big_freezer_temperature
      above: 10
      for:
        minutes: 60
    - trigger: numeric_state
      id: low_temp
      entity_id: sensor.big_freezer_temperature
      below: 10
    - trigger: state
      id: temperature_malfunction
      entity_id: sensor.big_freezer_temperature
      to: 'unavailable'
    - trigger: homeassistant
      id: ha_start
      event: start
    - trigger: state
      id: no_change
      entity_id: sensor.big_freezer_temperature
      for:
          minutes: 5
  binary_sensor:
    - name: "Big Freezer Alert"
      unique_id: 402a23de-6628-42fa-a753-2c52457586b4
      device_class: problem
      state: >-
        {% if trigger.id == 'switch_off' %}
          true
        {% elif trigger.id == 'low_temp' and (is_state('switch.big_freezer_plug_blue', 'unavailable') or is_state('switch.big_freezer_plug_blue', 'off'))%}
          true
        {% elif trigger.id == 'high_temp' or trigger.id == "temperature_malfunction" or trigger.id =="no_change" %}
          true
        {% elif trigger.id == 'ha_start' and (is_state('switch.big_freezer_plug_blue', 'unavailable') or is_state('switch.big_freezer_plug_blue', 'off') or is_state('sensor.big_freezer_temperature', 'unavailable') or this.state == 'on') %}
          true
        {% elif trigger.id =='switch_on' and states('sensor.big_freezer_temperature')|float > 10 %}
          true
        {% else %}
          false
        {% endif %}
      attributes:
        issue:>
          {% if is_state('') %}
            "{{ trigger.id }}"
          {% endif %}

Move the template used in state to a variable then reference the variable in state and in issue.

# Big Freezer
- trigger:
    - trigger: state
      id: switch_on
      entity_id: switch.big_freezer_plug_blue
      to:
        - 'on'
    - trigger: state
      id: switch_off
      entity_id: switch.big_freezer_plug_blue
      to:
        - 'unavailable'
        - 'off'
    - trigger: numeric_state
      id: high_temp
      entity_id: sensor.big_freezer_temperature
      above: 10
      for:
        minutes: 60
    - trigger: numeric_state
      id: low_temp
      entity_id: sensor.big_freezer_temperature
      below: 10
    - trigger: state
      id: temperature_malfunction
      entity_id: sensor.big_freezer_temperature
      to: 'unavailable'
    - trigger: homeassistant
      id: ha_start
      event: start
    - trigger: state
      id: no_change
      entity_id: sensor.big_freezer_temperature
      for:
          minutes: 5
  variables:
    mode: >
      {% if trigger.id == 'switch_off' %} 
        true
      {% elif trigger.id == 'low_temp' and (is_state('switch.big_freezer_plug_blue', 'unavailable') or is_state('switch.big_freezer_plug_blue', 'off'))%}
        true
      {% elif trigger.id == 'high_temp' or trigger.id == "temperature_malfunction" or trigger.id =="no_change" %}
        true
      {% elif trigger.id == 'ha_start' and (is_state('switch.big_freezer_plug_blue', 'unavailable') or is_state('switch.big_freezer_plug_blue', 'off') or is_state('sensor.big_freezer_temperature', 'unavailable') or this.state == 'on') %}
        true
      {% elif trigger.id =='switch_on' and states('sensor.big_freezer_temperature')|float > 10 %}
        true
      {% else %}
        false
      {% endif %}
  binary_sensor:
    - name: "Big Freezer Alert"
      unique_id: 402a23de-6628-42fa-a753-2c52457586b4
      device_class: problem
      state: "{{ mode }}"
      attributes:
        issue: "{{ trigger.id if mode | bool else '' }}"

Reference

Trigger-based Template Sensor - Variables


EDIT

Correction. Added bool filter as suggested by Didgeridrew. For some reason, the true/false values in an if-then-else are treated as strings instead of booleans.

2 Likes

Damn well you are a wizard harry. I’m going to test it tomorrow as my SO is asleep and can’t wake her up again lol or else i will have other alerts going off lol

Sounds like I’m not the only wizard toiling away in my laboratory at this hour. :laughing:

OK so it didn’t work entirely well and i’m not sure why.

When i turn off the switch that powers the freezer i see issue: switch_off, but when i turn it back on it clears the issue so binary_sensor is false now, but issue now says switch_on which is odd since your if statement is testing if mode is true from what I can tell.

I had tested a simplified version of your Trigger-based Template Binary Sensor. It contained just enough to confirm that a variable defined in variables is computed after the trigger occurs and its value is accessible in state and attributes. In other words, it produces an updated value that’s accessible where needed for your application. (Compare that to this.state. It contains the entity’s state value before the trigger occurs; it’s the entity’s previous state)

If the end-result is that it’s not behaving as expected, then you’ll need to examine the state and issue templates.

For example, this.state represents the entity’s state value before the trigger occurred. Any template that uses it is checking the entity’s previous state. In other words or this.state == 'on' checks if the entity was on before the entity was triggered (not the computed new state but its previously computed state).

I don’t know if that’s responsible for the unexpected behavior you have observed, but it’s the kind of thing you need to check in the templates.

To make it easier to debug, I suggest you temporarily expose all the values, used in the templates, as attributes.



binary_sensor:
  - name: "Big Freezer Alert"
    unique_id: 402a23de-6628-42fa-a753-2c52457586b4
    device_class: problem
    state: "{{ mode }}"
    attributes:
      issue: "{{ trigger.id if mode else 'zilch' }}"
      mode: "{{ mode }}"
      id: "{{ trigger.id}}"
      freezer_plug: "{{ states('switch.big_freezer_plug_blue') }}"
      freezer_temp: "{{ states('switch.big_freezer_temperature') }}"
      previous: "{{ this.state }}"

Try it with the bool filter:

issue: "{{ trigger.id if mode | bool else '' }}"

Your suggestion to cast the value with bool made me second-guess myself; is a variable’s type, such as boolean, preserved when used elsewhere in the Trigger-based Template entity?

I performed the following experiment.

I defined four variables in a Trigger-based Template Sensor.

    variables:
      t1: "{{ true }}"
      t2: "{{ false}}"
      t3: true
      t4: false

I defined four attributes that check if the variable’s value is boolean and another four that confirm they contain the expected boolean value.

        attributes:
          test1: "{{ t1 is boolean }}"
          test2: "{{ t2 is boolean }}"
          test3: "{{ t3 is boolean }}"
          test4: "{{ t4 is boolean }}"
          test1a: "{{ t1 == true }}"
          test2a: "{{ t2 == false }}"
          test3a: "{{ t3 == true }}"
          test4a: "{{ t4 == false }}"

All eight attributes reported true. It confirms a variable’s type (in this case boolean) is preserved (although not if it’s a datetime object).

Using a setup more analogous to OP’s:

trigger:
  - trigger: time_pattern
    minutes: /1
variables:
  mode: |
    {% if now().minute is even %}
      true
    {% else %}
      false
    {% endif %}
sensor:
  - name: Test variable typing
    state: "{{ now() }}"
    attributes:
      bool_test: "{{ mode is boolean }}"
      tf_test: "{{ mode == true }}"
      string_test: "{{ mode is string }}"
      tf_string: "{{ mode == 'true' }}"

Only tf_string changes, the other attributes are always:

bool_test: false
tf_test: false
string_test: true
1 Like

Very interesting!

This produced boolean values.

t1: "{{ true }}"
t3: true

Whereas this produced strings.

mode: |
  {% if now().minute is even %}
    true
  {% else %}
    false
  {% endif %}

Not quite sure why native typing recognized true to be a boolean in the first example but not in the second one.

Given that behavior then, as per your recommendation, mode needs to be cast as a boolean using bool.


Alternative is to wrap true/false in double braces but casting with bool is neater.

Tried an Immediate If in your example and it returns booleans.

variables:
  mode: |
   {{ iif(now().minute is even, true, false) }}

It appears that native typing follows slightly different rules for values reported by an if-then-else. Unless the values are explicitly wrapped in double braces, true/false are reported as strings.


EDIT

I am beginning to suspect native typing may have a bug. If the same if-then-else is used to return numbers (i.e. replace true/false with 88/77, it correctly reports the values as integers (not strings).

mode: |
  {% if now().minute is even %}
    88
  {% else %}
    77
  {% endif %}

Alsi confirmed it correctly reports [88] as a list. It’s true/false that it doesn’t automatically cast as boolean. That seems like an oversight.

This discussion is great! Thank you both for putting your heads together and experiment! I learned a lot!

1 Like

UPDATE

I reported the issue and the observed behavior is normal.


When using if-else in a template, Home Assistant’s native typing feature recognizes True/False to represent booleans and true/false to represent strings.

In other words, if the text is in titlecase then it is understood to represent a boolean whereas if it’s in lowercase then it’s a string.

In the following example, the resulting value’s type will be string.

variables:
  test: >
    {% if some_condition_goes_here %}
      true
    {% else %}
      false
    {% endif %}

In this example, the resulting value’s type will be boolean.

variables:
  test: >
    {% if some_condition_goes_here %}
      True
    {% else %}
      False
    {% endif %}

The same rule applies for none (string) and None (null object).

very interesting! I will make the changes to mind even though I have already piped it to a bool or filtered it through a bool check.