Preventing on/off/on/off cycles with very short phases

Dear group,

I would like to protect my lights from flickering, that is, from on/off/on/off cycles whose phases are very short, for example because “light on” is immediately followed by sunrise or someone repeatedly toggles a switch very quickly.

What I came up with is the following template helper:

template:
  - binary_sensor:
      - name: "Anti-Flickering Unit"
        icon: mdi:sine-wave
        state: |
          {% if (now() - this.last_changed).total_seconds() < 10.0 %}
            {{ this.state }}
          {% else %}
            {{ states(binary_sensor.light_control_logic) }}
          {% endif %}

The implementation idea was:

  • If the entity’s state changed less than 10.0 seconds ago, just stay with the previous state to keep it unchanged.
  • Otherwise, have it have the state of the “controlling” entity (for example another binary sensor).
    • If this state happens to be the same as the one before the 10.0 seconds mark, nothing happens.
    • Otherwise, the state changes, this.changed is updated, and all begins anew: the new state is kept for at least another 10.0 seconds.

(Please note that this is different from e. g. the delay in a group helper, because it can change its state immediately after the 10.0 seconds mark.)

The problem is that this generates two log messages exactly once per minute:

  • Anti-Flickering Unit became unavailable
  • Anti-Flickering Unit turned off triggered by state of Anti-Flickering Unit became unavailable

Why does it generate these messages (why does it become unavailable?) and how can I prevent it?

Woah, sorry!
It seems it should have been one of these:

{{ states('binary_sensor.light_control_logic') }}
{{ states.binary_sensor.light_control_logic.state }}

With one of these, it works. Sorry for the noise! :blush:


Now I have a follow-up question though:
When I put the wrong line, i. e.

{{ states(binary_sensor.light_control_logic) }}   # WRONG

into the Developer Tools > Template live editor, it yields a nice error message (“‘binary_sensor’ is undefined”)

:arrow_right: Why does the error message not make it into the logs?

It’s assuming that since it has no quotes, it’s a variable. It’s trying to look for a variable named binary_sensor, with a property called light_control_logic, neither of which exist, so you get an error that binary_sensor doesn’t exist.

As to why you didn’t get that error in your logs, is because HA doesn’t do a full stack trace type of error. Your template threw an error, which caused the state attribute to have an unknown value, which is what it reported as the error by saying the entity became unavailable (had an unknown state).

{{ states('binary_sensor.light_control_logic') }} 

Also template binary sensors have delay_off and delay_on options that may simplify your binary sensor.

Thanks for your reply! Unfortunately, delay_on and delay_off work in the opposite sense (“The amount of time (e.g. 0:00:05) the template state must be met before this sensor will switch to on.”) than what I pursue: Outside of the 10.0 seconds window, I want my template to switch to the new state immediately and only then apply the delay. I’ve not found any built-in mechanism that works in that way.

Thanks for your reply and your detailed explanation!

Would that be worth a feature request?

If I had the error message of binary_sensor being undefined seen in the log, I would probably have been able to fix the error early and would not even have posted here. :wink:

So just use delay off.

But that’s just a mirror of delay_on, isn’t it?

Both for delay_on and delay_off, the given time must pass before the sensor switches to the new state, on or off, respectively. That’s different from switching to the new state immediately and then keeping the new state fixed for a given time. Or am I overlooking something?

The delay_on and delay_off basically impose a required delay, before switching on and off respectively, by checking the state of your sensor.

I think you’d get what you want if you did:

template:
  - binary_sensor:
      - name: "Anti-Flickering Unit"
        icon: mdi:sine-wave
        state: "{{ states('binary_sensor.light_control_logic') }}"
        delay_on: >-
          {% if is_state('binary_sensor.light_control_logic', 'off') %}10s{% endif %}
        delay_off: >-
          {% if is_state('binary_sensor.light_control_logic', 'on') %}10s{% endif %}

Untested - hopefully pushes you in the right direction if it’s wrong.

No that won’t work. Your templates produce a null result if the if statement resolves to false. You must return a time if you use the option.

But as I said all they need is the delay off. It will turn on instantly but take some time after the switch to turn off after the off request is made. This will prevent flapping of the state.

First of all, thanks to you both for your help! :smiley:

With your notes it was easy to fix the code in my very first post. Since then the sensor seems to work as intended without flaw. (I will continue testing it, but so far everything is looking right.)

As I’m still new to Home Assistant, I’m happy to learn about alternative implementations and improvements! Can you please point out anything that is wrong or questionable in my first (fixed) code?

Yes, but it doesn’t properly mirror: When the light has been off for a while, I want it to go immediately on. But when the light has been on for a while, I also want it to go immediately off: Whenever at least 10.0 seconds lapsed since the last state change, the next change, on or off, should be instant.
Is this really possible with delay_on and/or delay_off? I’ve long been thinking about it before I started with the above template binary sensor, but never found a solution that works both ways.

If your fixed version works, there’s no reason to change it :slight_smile: The other options, likely with some templating (mine needs an else statement apparently), would be alternatives - but there’s nothing “wrong” with your approach.

Glad you got it sorted out!

Yeah ok it won’t work in that case, esand’s template idea could work with a bit of adjustment but is no better than what you have.