Use template for binary_sensor threshold?

Hi,

using the binary_sensor threshold, we are forced to use a hardcoded value for upper or lower. Probably fr hysteresis too, havent checked.

it would be really nice if we could do something like this:

binary_sensor:
- platform: threshold
  name: 'ZP Opbrengst threshold'
  entity_id: sensor.zp_actuele_opbrengst
  upper: >
    {{(states('input_number.zp_opbrengst_threshold') | float)}}

instead of this:

binary_sensor:
- platform: threshold
  name: 'ZP Opbrengst threshold'
  entity_id: sensor.zp_actuele_opbrengst
  upper: 1500

I have circumvented this now by using a binary_sensor template:

- platform: template
  sensors:
    zp_opbrengst_threshold_input:
      friendly_name: 'ZP Opbrengst threshold input'
      value_template: >
        {{(states('sensor.zp_actuele_opbrengst') | float) >
          (states('input_number.zp_opbrengst_threshold') | float)}}

which works fine, but obviously is a bit of a hacky workaround, and doesnt use the hysteresis. Maybe I could try and fiddle an extra template in there to approach the same, but it wouldn’t have the same elegance the true threshold sensor has.

Can this be done somehow, or am I looking a a feature request here.

thx,
Marius

3 Likes

Thanks for the workaround, with hysteresis (fixed 1.0 in this case) it looks like this:

- platform: template
  sensors:
    should_open_windows:
      friendly_name: "My variable binary threshold sensor with hysteresis"
      value_template: >
         {{
          (states('sensor.my_temperature_sensor') | float) >
          (states('sensor.my_variable_threshold') | float)
          + (1.0 if is_state('sensor.should_open_windows', false) else 0)
          - (1.0 if is_state('sensor.should_open_windows', true) else 0)
         }}
2 Likes

I was looking for a templatable threshold and stumbled upon this old post.
For anyone looking for the same thing, here’s my version incorporating the new features:

  - binary_sensor:
      - name: environment_cold
        unique_id: environment_cold
        icon: mdi:thermometer-low
        availability: |
          {{ not false in
             [states('sensor.environment_temperature'),
              states('input_number.environment_cold_diff'),
              states('sensor.home_temperature')]|map('is_number')
          }}
        state: |
          {% set entity=states('sensor.environment_temperature')|float(0) %}
          {% set hysteresis=states('input_number.environment_cold_diff')|float(0) %}
          {% set limit=states('sensor.home_temperature')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
          {{ entity < limit }}

2 Likes

Isn’t there the platform missing?
I tried to add the binary_sensor in the configuration.yaml, however it fails when checking the config. I tried to add the platform key template or threshold, but no luck.

It is template sensor following the new syntax so it should be in the section

Template:

There is no platform required as it is defined by the section.

It has been a while since I was in need of a new (template-)sensor, lol, haven’t used the new syntax since the introduction of it.
Got it working now, thank you!

1 Like

I get an error on this.state. Possibly I am supposed to substitute something here.

Any thoughts about how to do a threshold with input_number for upper and lower limits with hysteresis on both?

Thanks

“This” will not work in devtools, because “this” will not be known.

This.state should not give an error, unless you are on an older version of home assistant.

Here is a version that should work with upper and lower limits:

[code deleted, because wrong]

You would have to replace the values in the first four lines with references to your entities.

[EDIT] on second thought. This does not work. hold on

This should work:

{% set current_state=this.state %}
{% set referenced_state=18 %}
{% set limit_upper=24 %}
{% set limit_lower=20 %}
{% set hysteresis=1 %}
{% set threshold_upper = limit_upper - (hysteresis if (current_state=='off' and referenced_state>limit_upper) else -hysteresis)  %}
{% set threshold_lower = limit_lower + (hysteresis if (current_state=='off' and referenced_state<limit_lower) else -hysteresis)  %}
{{ (referenced_state >= threshold_lower) and (referenced_state <= threshold_upper) }}

Again, you would have to replace the numbers in line 2-5 with references to your entity-states like

{% set referenced_value=states('input_number.your_input')|float(0) %}

Thanks seanomat. I tried that out and handles the upper and lower thresholds. I’m going to modify it to be a sensor by adding the logic to have above, in_range, and below as the values returned vs just on and off. I’m controlling a fan to help offload my AC and I need to know all 3 states to turn it on and off properly (for instance if it is out of range above, I want the fan on, but If it is out of range below, the fan can’t heat so I don’t want to turn it on.) With your code it was easy to make it a sensor and it seems to work in the template editor. I need to collect some data and see if it turns on and off properly. I’m not sure if using two single ended binary sensors or this single sensor is best. Gotta try it. Thanks for your help with this.

I case if your fan you could have stayed with the original template with just the upper limit, as you only need two states: ON and OFF.

I think, there is still an error in the last template.

It should be something like this (not tested!):

{% set threshold_upper = limit_upper - (hysteresis if (current_state=='off' and referenced_state>(limit_upper - hysteresis)) else -hysteresis)  %}
{% set threshold_lower = limit_lower + (hysteresis if (current_state=='off' and referenced_state<(limit_lower + hysteresis)) else -hysteresis)  %}

I came to the same conclusion to use a single threshold. I modified your code to this and it seems to be working.

{% set entity=states('sensor.outside_temperature')|float(0) %}
{% set hysteresis=states('input_number.inside_temp_hysteresis')|float(0) %}
{% set limit=states('sensor.inside_temperature')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
{{ entity < limit }}

Thanks for your help

2 Likes

@seanomat I tested this for a while now and it works, but I wanted to do a modification but could not get the modification working.

Currently, its works as follows, assuming an inside temp of 21°C and hysteresis of 0.5°C
Trigger to off at 21.5°C (rising)
Trigger to on at 20.5°C (subsiding)

My goal is to
Trigger to off (risig temp) at 20.5°C
and
Trigger to on at 21.5°C (subsiding temp)
so exactly the other way around, switching states before it reaches the actual temperature.

However it looks like I’m missing something. I thought I would just flip the hysteresis algebraic signs, but that does for whatever reason not work. I also tried other variants, but no luck.
Here is the snippet of what I think should work, but doesn’t.

{% set limit=states('sensor.home_temperature')|float(0) + (-hysteresis if this.state=='on' else +hysteresis)  %}
{{ entity < limit }}

can anyone help?

A threshold is meant to avoid flapping (frequent state changes). So the state change has to occur at the “further” limit in regard to the direction the underlying sensor is moving and there is an overlap in the middle.

What you want is the direct opposite, so this will not work.

Say the temp is 20, the state is on, the limit is 20.5.
The temp rises to 21, your limit has been reached, so the state is off, but since it’s off the limit now changes to 21.5.
Since the state is defined as entity< limit the state changes to on again. Which changes the limit again, and so on.

It seems to me you are making this more complicated then necessary.

My use case for this FR:

    name: "Sun Azimuth SE"
    unique_id: sun_azimuth_se
    lower: '{{ states("input_number.azimuth_se_lower") }}'
    upper: '{{ states("input_number.azimuth_se_upper") }}'
    entity_id: sensor.sun_azimuth

  - platform: threshold
    name: "Sun Azimuth South"
    unique_id: sun_azimuth_south
    lower: '{{ states("input_number.azimuth_south_lower") }}'
    upper: '{{ states("input_number.azimuth_south_upper") }}'
    entity_id: sensor.sun_azimuth
    
  - platform: threshold
    name: "Sun Azimuth SW"
    unique_id: sun_azimuth_sw
    lower: '{{ states("input_number.azimuth_sw_lower") }}'
    upper: '{{ states("input_number.azimuth_sw_upper") }}'
    entity_id: sensor.sun_azimuth

rather than:

- binary_sensor:    

  - name: "Sun Azimuth SE"
    unique_id: sun_azimuth_se
    state: '{{ states( "input_number.azimuth_se_lower") < states("sensor.sun_azimuth") < states( "input_number.azimuth_se_upper") }}'

  - name: "Sun Azimuth South"
    unique_id: sun_azimuth_south
    state: '{{ states( "input_number.azimuth_south_lower" ) < states("sensor.sun_azimuth") < states( "input_number.azimuth_south_upper" ) }}'
   
  - name: "Sun Azimuth SW"
    unique_id: sun_azimuth_sw
    state: '{{ states( "input_number.azimuth_sw_lower" ) < states("sensor.sun_azimuth") < states( "input_number.azimuth_sw_upper" ) }}'

Also would like to see unique_id: added to threshold sensors.

you are absolutely right, my misjudgment…
I have kind of a workaround by adding + 1°C to the environment temperature, rather than playing wronlgy with the hysteresis +/-.

          {% set entity=states('sensor.environment_temperature')|float(0) + 1 %}

That works kind of, it triggers sooner in the morning, however also later in the evening. I could create two sensors, one with and one without that “+1°C”, and then combine the two with an automation, but ideally I wanted to create only one sensor. :slight_smile:

I’m not that advanced with the binary-sensors, is it possible to trigger the binary sensor from on to off when one condition (environment +1°C) would fire a on-to-off event (in the morning), and to trigger from off to on if the second condition (environment +0°C) fires off-to-on event (in the evening)?

I have illustrated this in the 3rd line on the below graphic

The code for the first line/sensor

state: |
          {% set entity=states('sensor.environment_temperature')|float(0) %}
          {% set hysteresis=states('input_number.environment_cold_diff')|float(0) %}
          {% set limit=states('sensor.home_temperature')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
          {{ entity < limit }}

The code for the second line/sensor

state: |
          {% set entity=states('sensor.environment_temperature')|float(0) + 1 %}
          {% set hysteresis=states('input_number.environment_cold_diff')|float(0) %}
          {% set limit=states('sensor.home_temperature')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
          {{ entity < limit }}

You don’t need a hysteresis in your case:

{% set temp_home = states('sensor.home_temperature')|float(0) %}
{% set temp_env = states('sensor.environment_temperature')|float(0) %}
{% set diff = 0.5 %}
{{ (temp_env >= temp_home + diff ) or (temp_env <= temp_home - diff ) }}

This sensor will be on above 21.5 and below 20.5 provided your home_temperature is 21.0.
You might have to switch this around for your case. I am a littel confused about what you really want.

I saw your post from yesterday but you have deleted it :smiley: anyway, I found a way now which works fabulous for my requirement (I definitely need the threshold, as you mentioned in your deleted post).

I have modified your sensor a bit, adding an offset to the outside temp. The threshold is set to 1°C in my case.

- binary_sensor:
      - name: Inside vs outside temp
        icon: mdi:thermometer-low
        availability: |
          {{ not false in
             [states('sensor.outside_temp),
              states('input_number.threshold'),
              states('sensor.inside_temp')]|map('is_number')
          }}
        state: |
          {% set hysteresis=states('input_number.threshold')|float(0) %}
          {% set entity=states('sensor.outside_temp')|float(0) + states('input_number.offset')|float(0)  %}
          {% set limit=states('sensor.inside_temp')|float(0) + (hysteresis if this.state=='on' else -hysteresis)  %}
          {{ entity < limit }}

For the offset-temperature, I have created an automation, it includes also a safety margin (x2.5 instead of x2). In the morning, the offset in my case is at +2°C, which increases the outside temp artificially. In the evening, the offset is at -1°C, which decreasees the outside temp artificially.
Since there is the threshold in the binary sensor, you dont have any flapping.

alias: Offset on/off
description: ''
trigger:
  - platform: template
    value_template: >-
      {{ (states('sensor.outside_temp')|float(0) -
      states('sensor.inside_temp')|float(0)) > (2.5 *
      states('input_number.threshold')|float(0)) }}
    id: off
  - platform: template
    value_template: >-
      {{ (states('sensor.inside_temp')|float(0) -
      states('sensor.outside_temp')|float(0)) > (2.5 *
      states('input_number.threshold')|float(0)) }}
    id: on
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: off
        sequence:
          - data_template:
              value: >-
                {{ states('input_number.threshold')|float(0) *
                -1 }}
            entity_id: input_number.offset
            service: input_number.set_value
      - conditions:
          - condition: trigger
            id: on
        sequence:
          - data_template:
              value: >-
                {{ states('input_number.threshold')|float(0) *
                2 }}
            entity_id: input_number.offset
            service: input_number.set_value
    default: []
mode: single

The only problem was, that if the outside temperature does not go below the inside temp for the defined [offset-threshold], the offset will remain as it was. For this, I have created an additional trigger at 5 AM in the above automation, which will check this, including a bit of safety margin (0.5°C):

      - conditions:
          - condition: trigger
            id: time
          - condition: template
            value_template: >-
              {{ (states('sensor.inside_temp')|float(0) -
              states('sensor.outside_temp')|float(0)) <= (2.5 *
              states('input_number.threshold')|float(0)) }}
          - condition: state
            entity_id: binary_sensor.invise_vs_outside_temp
            state: 'on'
        sequence:
          - data_template:
              value: >-
                {{ (states('sensor.inside_temp')|float(0)
                - states('sensor.outside_temp')|float(0) +
                states('input_number.threshold')|float(0) +
                0.5)|round(1) }}
            entity_id: input_number.offset
            service: input_number.set_value

This is the result (the offset would change dynamically at 5AM in this scenario, you can see inside vs outside temp was almost the same during the night):

Cheers and thanks for the help and input!

Yeah, turned out my last post created the exact loop that I described a few posts above :thinking:

Yes, I guess you could solve the problem with an automation. I was hoping to to avoid that.