I need to make an automation that turns off a device after it’s been on for 6 hours. Easy enough, device state to on, for 6 hours, right? No, sometimes my rfxcom sets my switches to unavailable briefly and if this happens to happen at least once every 6 hours while the device is on, it will stay on forever.
So I’m considering from: off for 6 hours instead, but this automation will instead also trigger if the device becomes unavailable for a long time. Guess I could set a condition to handle that.
I also considered from:off not_to: unavailable for 6 hours, but would this keep counting up the 6 hours if the device went from off → on → unavailable → on?
The unavailable state in HA is just not thought out very well when it comes to writing bulletproof automation. What’s the correct way of doing this?
If you take a look at the first example, it shows how you can create a sensor that will monitor the amount of TIME a device has been on for. You could then use this sensor (if sensor > 6) as a trigger for the automation.
In this example, the sensor will report the amount of time entity_id has been in state “on” in the last 7 hours (to give some room for the “unavailable” state). If you would rather tighten the window and count unavailable state too, you can do so like this (according to docs, I’ve not tested):
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).
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.
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.
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
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
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).
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.
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.
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 )
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