Best Practices for ON/OFF within the same Automation

Hi all,

I’m trying to avoid automation sprawl and in simple cases, like switching a light on and off at predetermined times, I’m using a turn.on, delay X hours and turn.off within the same actions. This way we can avoid an automation for on and another for off.

I’m curious as to whether this would be not recommended and why, or if I’m on the right track.

Love to hear also your other best practices!

Thanks.

2 Likes

A delay would not survive Home Assistant restart/crash which would result in your lamp never getting turned off.

An example on how you can combine on/off using a single automation-

trigger:
  - platform: homeassistant
    event: start
  - platform: template
    value_template: "{{ states('sensor.time') == '07:00' }}"
  - platform: template
    value_template: "{{ states('sensor.time') == '17:45' }}"

condition: []

action:
  - service: >
      {% if '07:00' <= states('sensor.time') < '17:45' %}
        switch.turn_on
      {% else %}
        switch.turn_off
      {% endif %}
    entity_id:
      - light.living_room

I uses sensor.time, if you wish, you can add that sensor by adding this line in your configuration.yaml-

sensor:
  - platform: time_date
    display_options:
      - 'time'

OR this if you want without template-

trigger:
  - platform: homeassistant
    event: start
  - platform: time
    at: '07:00'
  - platform: time
    at: '17:45'

action:
  - choose:
      - conditions:
          - condition: time
            after: '07:00'
            before: '17:45'
        sequence:
          - service: light.turn_on
            target:
              entity_id: light.living_room
    default:
      - service: light.turn_off
        target:
          entity_id: light.living_room

Also, you may want to check out Taras’ example here-

4 Likes

While composing my response, ardysusilo beat me to the post. However, it’s all done so here it is anyway.


delay doesn’t survive a restart (the same is true for wait_template, for, timers, etc).

If Home Assistant is restarted while a delay is underway (or if automations are reloaded) the delay is cancelled (as is any automation that is in the process of being executed). That means all actions after the delay will not be executed.

If you know the two times, such as in your example where a device is turned on at one time and turned off at another, then use a Time Trigger.

Simple example:

alias: Scheduled Light
trigger:
- platform: time
  at:
  - 20:30:00
  - 23:15:00
action:
- service: "light.turn_{{ 'on' if now().hour == 20 else 'off' }}"
  target:
    entity_id: light.kitchen

There are also techniques for ensuring the actions are executed even if Home Assistant misses the trigger time. For example, if Home Assistant is restarted at 20:29 and takes more than a minute to start, it will miss the 20:30 time trigger and fail to turn on the light. (Refer to the link in the post above)


EDIT

I overlooked to mention that there are more complex situations where the on/off times are not explicitly specified in the Time Trigger (like in the simple example I posted) but referenced via input_datetime. This makes it more challenging to determine which of the two times triggered the automation. The simple solution is to use two separate Time Triggers, each with its own trigger id. The trigger id is used by a choose in the action to determine which Time Trigger occurred.

Another situation is where the start time is known but the stop time is a duration determined by an input_number. Yet again, that makes things a bit more complicated because the stop time must be computed.

1 Like

Thank you very much to both for your time, very informative!

I thought that would be a pitfall, but given the use-case and the relatively infrequent occurrence of a restart it was an acceptable downside.

However, and here I’m really glad I asked, the real news to me is the choose option which opens the door to many interesting consequences. Templating is yet not my forte so I’ll have to dig into that too.

Thank you also for the link, it did not appear in my search and discourse suggestions.

Also, for the sake of completeness, here the final automation:

alias: Light on/off based on time of day
description: Switch on at 6pm and off at 6am
trigger:
  - platform: time
    at: '18:00:00'
    id: time_on
  - platform: time
    at: '06:00:00'
    id: time_off
  - platform: homeassistant
    event: start
    id: ha_restart
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: time_on
          - condition: state
            entity_id: switch.xxx
            state: 'off'
        sequence:
          - service: switch.turn_on
            target:
              entity_id: switch.xxx
      - conditions:
          - condition: trigger
            id: time_off
          - condition: state
            entity_id: switch.xxx
            state: 'on'
        sequence:
          - service: switch.turn_off
            target:
              entity_id: switch.xxx
    default: []
mode: single

Of course we could define also a condition: trigger for id: ha_restart but then it gets complicated and we need to repeat the time conditions in the choose. I’m willing to accept that an HA restart at e.g. 17:59 will have an impact.

would the use of time pattern survive the reboot?

  trigger:
    - platform: time_pattern
      minutes: "/30"

If you want the lights to be ON from 18:00 to 06:00 (tomorrow’s morning), you could just do-

trigger:
  - platform: homeassistant
    event: start
  - platform: time
    at: '06:00'
  - platform: time
    at: '18:00'

action:
  - choose:
      - conditions:
          - condition: time
            after: '06:00'
            before: '18:00'
        sequence:
          - service: light.turn_off
            target:
              entity_id: light.living_room
    default:
      - service: light.turn_on
        target:
          entity_id: light.living_room

Take note of the condition, in which time after 06:00 and before 18:00, the call service used is light.turn_off. While outside of that condition, the automation will choose the default action which is light.turn_on.

2 Likes

What about when you reload automations?

When you create/modify an automation, you must reload automations to register the modification. That will terminate any automations that are in progress.

If you are using the Automation Editor, the moment you save an automation, it is checked and, if there are no errors, all automations are reloaded.

1 Like

I did not try, although I believe not since the automation gets canceled. I guess it would run a new automation after restart that checks every half hour.

Thanks @ardysusilo, that’s also a compact solution.

Hi Taras, that’s correct but I guess the impact is only if I reload them at 05:59 or 17:59 ?
Otherwise the triggers would work as intended.

Correct me if I’m wrong :slight_smile:

You’re correct; it only affects automations whose action is in progress like when a delay, for, or timer is counting down or a wait_template, or wait_for_trigger, is waiting for something to happen.

The point is that you might remember that one vulnerable time period now but will you recall it in 3 weeks? The more automations you have employing long delays, the more vulnerable time periods you will have in a day.

1 Like

I ended up here while looking if it’s better to keep automations for turning a light on and off or keep them together. After reading the answers in this post I think I got my answer.
To elaborate:
I want to turn on/off an entrance light if the sun has set triggered by a person detected from a camera or the entrance door opening
I think in both cases it’s better to have a trigger with a duration (e.g. person not detected for x amount of time) because the sensors will have this information recorded during a potential restart of the device. Even if it doesn’t a small delay won’t be an issue since the trigger will be restarted. For longer delays I think it’s better to set a specific time if one is required.
Anyway just adding my comment here in case someone ends up here looking for something similar. Thank you all for your replies

Again keeping these together, is there a reason this reports no error but does not turn on the switch? I am using it with a voice command to turn ON or OFF.
[EDIT] It actually turns off when on but not the other way. And yet Action is reported to have run successfully.

service: switch.turn_{{ 'on' if states('switch_plug_2') == 'off' else 'off' }}
metadata: {}
data: {}
target:
  entity_id: switch.plug_2

I tried formatting like this with the same result:

service: >
      {% if states('switch_plug_2') == 'off' %}
        switch.turn_on
      {% else %}
        switch.turn_off
      {% endif %}

How to fix this?

It should be switch.plug_2 not switch_plug_2.

1 Like