Safety timer that isn't thrown off by brief periods of device becoming unavailable

Use a timer helper.

If that’s your conclusion then you have misunderstood the purpose of unavailable. When your switch loses connection with Home Assistant, no matter how briefly, it’s true state is no longer a known quantity so it is reported as unavailable. To assume its state remains unchanged during the loss of communication would be just that, an assumption, so that’s why it’s reported as unavailable.

If you’re using a State Trigger with the for option set to 6 hours, that’s a risky choice. Any restart of Home Assistant during the 6-hour period will reset the countdown.

Longer periods are best handled using other strategies such as with a timer entity (it can survive a restart) or, ideal for your application, what hawksj suggested (History Stats sensor).

That’s actually a good idea! I already use history stats for a lot of things but didn’t consider it for this case. Clever! :+1:

Well I get that it’s there, unavailable, as it’s certainly relevant information. But the way it’s implemented make things overly complicated and abstract to the user.

That could work too. Guess I was too focused on making this an automation.

As I continued looking at a solution before reading these replies, I came up with this which for all I can tell should work fine as well, but I can’t say it’s more elegant other than the fact that it’s a single-component solution:

alias: "Mattress: turn off if on for 6 hours (safety timer)"
description: ""
trigger:
  - platform: state
    entity_id:
      - switch.bedroom_mattress
    for:
      hours: 6
      minutes: 0
      seconds: 0
    from: "off"
    not_to:
      - unavailable
      - unknown
condition: []
action:
  - alias: If unavailable, wait up to 5m
    if:
      - condition: state
        entity_id: switch.bedroom_mattress
        state: unavailable
    then:
      - wait_for_trigger:
          - platform: state
            entity_id:
              - switch.bedroom_mattress
            to: "on"
        continue_on_timeout: true
        timeout:
          hours: 0
          minutes: 5
          seconds: 0
          milliseconds: 0
  - service: switch.turn_off
    target:
      entity_id: switch.bedroom_mattress
    data: {}
mode: single

Yeah. That won’t work. Your if will never be true.

A trigger saying not to unavailable will never pass an if statement looking for unavailable. Your trigger is basically from off to on.

After the switch goes on, once the switch goes unavailable or unknown, the automation will end because the trigger is no longer partially satisfied waiting for six hours to pass. Then, this won’t trigger again until the switch has been off.

I get what you’re saying, but I did a test using a dummy dropdown with options on, off and unavailable. When I switched that dummy from off to on, the FOR time starts waiting for the target time as expected. When switching the dropdown from on to unavailable back to on, it didn’t impact the wait for the FOR time to be achieved. It triggered after the set time regardless of the temporary switch to unavailable. It seems the “not_to” does not drop the FOR waiting unless I go back to off. When using from and to, what you are saying was 100% true in my tests, but not when using not_to.

Although it is a bit of a hack and in fact, I ended up doing this with a timer instead which is much more solid.

1 Like

What way would you implement it so it wouldn’t be “overly complicated and abstract to the user”?

If you have the time, post your automation so others can benefit from it.

I haven’t spent a lot of time considering that, but I have spent quite some time in various home automation solutions and HA is a bit of a king when it comes to abstract solutions that requires a lot of research to use properly (although things are getting better, the update to automations is very good for new users)

Maybe I’d offer the user an option to, for example:

  • Assume the last status while being unavailable (at least if duration < x mins).
  • Offer a separate event or signal chain for unavailable status that makes more sense, or just:
  • Offer the user a simple action on what should happen if the entity becomes unavailable, allowing advanced scripting with unavailable status included for the people wanting this and just ignoring unavailable all together for basic users.

If you have the time, post your automation so others can benefit from it.
[/quote]

This is what I came up with (requires a timer helper set to the desired timeout value):

alias: Mattress safety timer handler
description: >-
  This is the HA safety timer which turns off the mattress if it's been on for 6
  hours. It handles occasional switches to unavailable and uses a timer
trigger:
  - platform: state
    entity_id:
      - switch.bedroom_mattress
    to:
      - "on"
      - "off"
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.bedroom_mattress_safety_timer
condition: []
action:
  - if:
      - alias: Check if on/off trigger
        condition: template
        value_template: "{{ trigger.idx == \"0\" }}"
    then:
      - alias: Handle on/off events
        if:
          - alias: Check on/off
            condition: template
            value_template: "{{ trigger.to_state.state == \"on\" }}"
        then:
          - service: timer.start
            metadata: {}
            data: {}
            target:
              entity_id: timer.bedroom_mattress_safety_timer
        else:
          - service: timer.cancel
            metadata: {}
            data: {}
            target:
              entity_id: timer.bedroom_mattress_safety_timer
    else:
      - if:
          - condition: state
            entity_id: switch.bedroom_mattress
            state: unavailable
        then:
          - wait_for_trigger:
              - platform: state
                entity_id:
                  - switch.bedroom_mattress
                to: "on"
            timeout:
              hours: 0
              minutes: 5
              seconds: 0
              milliseconds: 0
        alias: If unavailable, wait up to 5m
      - service: switch.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: switch.bedroom_mattress
      - service: script.sendnotification
        data:
          title: 🔔 Mattress safety timer triggered
          message: Mattress has been on for 6 hours, turning off for safety
    enabled: true
mode: queued
max: 10

Thanks for sharing. You may have avoided them for a reason, but in case you weren’t aware of Trigger ID’s, you can use them to identify which trigger started the automation in place of the value templates

Not that there’s anything wrong with templating but just offering an alternative. Personally I find them much easier to use :slight_smile:

EDIT: Your other thread just appeared in the suggested threads at the bottom and I see you’ve just responded to another comment about trigger IDs, my bad. As you were :smiley:

This isn’t quantum mechanics. An entity’s state is either unavailable or it’s not. It can’t be two states at the same time.

Offer a separate event or signal chain for unavailable status that makes more sense

You’re back to making assumptions about an entity’s state while Home Assistant has lost connection with the device and its ability to confirm the device’s actual state. The device’s state value is literally unavailable during loss of communication and anything other than unavailable would be an assumption.

Offer the user a simple action on what should happen if the entity becomes unavailable, so it can be allowing advanced scripting with unavailable status included for the people wanting this and just ignoring unavailable all together for basic users.

Effectively you are asking to selectively disable the processing of a given state value. As you have discovered, a State Trigger’s options, if used selectively, allow you to control the precise state-change(s) that will serve to trigger the State Trigger.

Beyond that, temporarily suspending the meaning of unavailable, is unlikely to be anything the Core development team would ever agree to implement and for all the reasons I have already outlined (and probably several more).

Trigger ID’s are good, I’m just a lazy person :wink:

Valid points. I’m not sure there’s a better way, I’m just saying that I have archived the same things with less abstraction and scratching my head in other environments. At the flip side, HA is probably the most flexible and advanced.

Your automation is designed to use the switch’s state to control the timer’s operation.

  • When the switch’s state changes from anything to on it starts the timer.
  • When the switch’s state changes from anything to off it stops the timer.

That means the timer starts when the switch changes from off to on. It also means that if the switch changes from on to unavailable, the subsequent change from unavailable to on will serve to restart the timer. Is that a foreseen and acceptable operation for your application? If not, use the not_from option in the State Trigger.

Example

alias: Mattress safety timer handler
trigger:
  - platform: state
    entity_id:
      - switch.bedroom_mattress
    to:
      - "on"
      - "off"
    not_from:
      - unavailable
      - unknown
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.bedroom_mattress_safety_timer
condition: []
action:
  - if: "{{ trigger.platform == 'state'}}"
    then:
      - service: "timer.{{ iif(trigger.to_state.state == 'on', 'start', 'cancel') }}"
        target:
          entity_id: timer.bedroom_mattress_safety_timer
    else:
      ... etc ...

Your automation employs two State Triggers that monitor the same entity. The first State Trigger is in the trigger section and the second one is in the action section within a wait_for_trigger. However the automation’s mode is set to queued. Why?

Your automation’s wait_for_trigger employs a timeout value. However it doesn’t change continue_on_timeout from its default (true) to false. That means regardless if the wait_for_trigger is triggered immediately or is never triggered and times out, the outcome is the same (switch is turned off and notification is sent). In other words, regardless of the switch’s state, the final two service calls are always executed.

If that’s not what you want then you should either abort after a timeout (by setting continue_on_timeout to false) or determine how to proceed based on the value of the wait variable.

That’s an old version that got a bit messed up. This is my current version:

alias: Mattress safety timer handler
description: >-
  This is the HA safety timer which turns off the mattress if it's been on for 6
  hours. It handles occasional switches to unavailable and uses a timer
trigger:
  - platform: state
    entity_id:
      - switch.bedroom_mattress
    to:
      - "on"
      - "off"
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.bedroom_mattress_safety_timer
condition: []
action:
  - if:
      - alias: Check if on/off trigger
        condition: template
        value_template: "{{ trigger.idx == \"0\" }}"
    then:
      - alias: Handle on/off events
        if:
          - alias: Check on/off
            condition: template
            value_template: "{{ trigger.to_state.state == \"on\" }}"
        then:
          - service: timer.start
            metadata: {}
            data: {}
            target:
              entity_id: timer.bedroom_mattress_safety_timer
        else:
          - service: timer.cancel
            metadata: {}
            data: {}
            target:
              entity_id: timer.bedroom_mattress_safety_timer
    else:
      - if:
          - condition: state
            entity_id: switch.bedroom_mattress
            state: unavailable
        then:
          - wait_for_trigger:
              - platform: state
                entity_id:
                  - switch.bedroom_mattress
                to: "on"
            timeout:
              hours: 0
              minutes: 5
              seconds: 0
              milliseconds: 0
        alias: If unavailable, wait up to 5m
      - service: switch.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: switch.bedroom_mattress
      - service: script.sendnotification
        data:
          title: 🔔 Mattress safety timer triggered
          message: Mattress has been on for 6 hours, turning off for safety
    enabled: true
mode: queued
max: 10

I used VS Code to compare your “current version” with the version you posted here and there’s no difference.

Therefore all the observations, suggestions, and questions I posted earlier apply to your “current version”.

My bad, I read your suggested edits and got confused apparently.

Excellent observations. It’s as if I always miss something in automations.

  • Corrected.
  • I was considering the risk that I’d get an off while waiting which would not be processed. I changed to single and instead set the wait to wait for on or off. It’s still being redundant in turning off if it’s already off but I like the idea of getting a notification in this unlikely situation to manually confirm what’s going on.

My need was to wait out a temporary unavailable state and not care about anything else. I’ve changed to stopping the script and sending a notification if 5m of waiting doesn’t help.

ps: I stayed with my trigger.idx rather than trigger.platform as it feels safer for future edits when I forget about using trigger.platform

Full edited script below (I prolly screwed something up :joy:)

alias: Mattress safety timer handler
description: >-
  This is the HA safety timer which turns off the mattress if it's been on for 6
  hours. It handles occasional switches to unavailable and uses a timer
trigger:
  - platform: state
    entity_id:
      - switch.bedroom_mattress
    to:
      - "on"
      - "off"
    not_from:
      - unavailable
      - unknown
  - platform: event
    event_type: timer.finished
    event_data:
      entity_id: timer.bedroom_mattress_safety_timer
condition: []
action:
  - if:
      - alias: Check if on/off trigger
        condition: template
        value_template: "{{ trigger.idx == \"0\" }}"
    then:
      - service: timer.{{ iif(trigger.to_state.state == 'on', 'start', 'cancel') }}
        target:
          entity_id: timer.bedroom_mattress_safety_timer
        alias: Start or stop timer
    else:
      - alias: If unavailable, wait up to 5m
        if:
          - condition: state
            entity_id: switch.bedroom_mattress
            state: unavailable
        then:
          - wait_for_trigger:
              - platform: state
                entity_id:
                  - switch.bedroom_mattress
                to:
                  - "on"
                  - "off"
            timeout:
              hours: 0
              minutes: 5
              seconds: 0
              milliseconds: 0
          - alias: Warn user and stop if still unavailable
            if:
              - condition: template
                value_template: "{{ wait.trigger == none }}"
                alias: Timed out
            then:
              - service: script.sendnotification
                data:
                  title: 🔔 Mattress safety timer FAILED
                  message: Mattress could not be turned off because it's unavailable
              - stop: Cannot turn mattress off, it's unavailable
                error: true
      - service: switch.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: switch.bedroom_mattress
      - service: script.sendnotification
        data:
          title: 🔔 Mattress safety timer triggered
          message: Mattress has been on for 6 hours, turning off for safety
    enabled: true
mode: single

Then I suggest you add an id to each trigger and use trigger.id to identify the trigger. Why? Because trigger.idx is position dependent and if you’re forgetful you might add the new trigger before the existing one.

BTW, at this point it’s a complete farce to label the second post as the Solution; I suggest you untag it to avoid misleading others. You’re not using a History Stats sensor for your application. Plus it wouldn’t care if your switch was unavailable for a moment or for several hours. It just reports the cumulative number of hours the switch is on. That’s wildly different from how a State Trigger’s for option behaves or your timer based automation.

It’d probably be good housekeeping to swish to id instead of idx. I’ll consider doing that. Not doing was mere lazyness.

Changed solution marker. I’ve often found great FAST solutions by reading the solution so it’s quite relevant to keep up to date. Thanks for reminding me.

1 Like