How to capture initial state of non-triggering entity for all actions to use?

I’m trying to make a rapid light flashing automation. The behavior is intended to be 2 quick flashes and remain at the initial state when done. I got it mostly working with discrete switch.turn_on and switch.turn_off actions with wait_template between the actions to ensure the state is reached before the next action, however it only works properly if the light is initially off. If the initial state is on, then it only flashes once and leaves the light off when done.

I believe switch.toggle would better account for the initial and final states with the correct number of flashes, except I can’t figure out how the wait_template can know the initial state for comparison.

Note 1:
The entity being flashed is not the triggering entity for the automation, so I can’t use the trigger variable in the templating.

Note 2:
Using switch.toggle with delay: 0:00:01 works without having to know the initial state, but it’s far too slow for my purpose.

My first thought was to capture the initial state as a variable that all the wait_template actions would be able to compare to, but I have been searching all day how to create that variable in the automation without any luck.

My second thought was to use a script to do the toggle/wait and pass the desired entity and its starting state to the script. That basically reverses the problem because now I have the initial state value, but I haven’t figured out how to successfully pass an entity from the automation to the script and then use it in the toggle and wait actions. Again, searching for hours and not finding any examples that pass an entity to a script.

Examples of my attempts below. Does anyone know how to get the desired behavior?

Incidentally, if anyone knows how to do a loop for n times, that would be handy to know. I couldn’t find anything on that either.


Example 1: my first working automation (incorrect behavior if initially on)

# This automation works great if the light is initially off, but only flashes 
# once and leaves the light in the wrong final state if initially on.

- id: example1_automation
  trigger:
    platform: state
    entity_id: input_boolean.var1
  action:
    - service: switch.turn_on
      entity_id: switch.office_neon
    - wait_template: "{{ is_state('switch.office_neon', 'on') }}"
    - service: switch.turn_off
      entity_id: switch.office_neon
    - wait_template: "{{ is_state('switch.office_neon', 'off') }}"
    - service: switch.turn_on
      entity_id: switch.office_neon
    - wait_template: "{{ is_state('switch.office_neon', 'on') }}"
    - service: switch.turn_off
      entity_id: switch.office_neon
    - wait_template: "{{ is_state('switch.office_neon', 'off') }}"    

If I change all services to switch.toggle, the wait_template needs to be conditional based on the initial state of the switch. I have not been able to figure out how to capture the initial state of the switch for each action to compare to.


Example 2: passing entity and initial state to script (broken)

- id: example1_automation
  trigger:
    platform: state
    entity_id: input_boolean.var1
  action:
    - service: script.turn_on
      entity_id: script.flash_entity
      data_template:
        variables:
          enttiy_id: "{{ switch.office_neon }}"
          start_state: "{{ states('switch.office_neon') }}"

# The script has this error in the logs:
# Error rendering data template: UndefinedError: 'switch' is undefined
script:
  flash_entity:
    alias: Flash Entity
    description: 'Turns an entity on and off repeatedly'
    fields:
      entity_id:
        description: "The entity ID"
        example: 'switch.office_neon'
      start_state:
        description: "The initial state of the entity so we can toggle appropriately"
        example: 'on'
    sequence:
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) != start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) == start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) != start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) == start_state }}"

I can’t seem to handle passing the entity correctly. I have tried a few things, such as passing {{ switch.office_neon.unique_id }}, but that gets the same error: “switch is undefined”.

You’ve misspelled ‘entity_id’ with the T and I the wrong way round in the calling automation, meaning that you’re not passing the entity id to the correct parameter, meaning that you are calling switch toggle on nothing.

Correct the spelling and it should work fine.

1 Like

Wow, good catch! I can’t believe I missed that! I confirmed that solves my script approach.

Any thoughts on whether the variable scenario is achievable? It would be nice to know if that was also an option.

Sorry, don’t know if it’s me being tired but I don’t know what you’re asking me :smile:

I just mean my paragraph about trying to capture the entity state in a variable right in the automation so the automation actions can use the variable for comparison directly instead of calling a script. I couldn’t find a way to do it.

Also looking for ways to repeat a series of actions a specified number of times. For example, telling my script to repeat the flash a specific number of times by looping the same toggle-wait-toggle-wait.until the count is reached.

Yeah, it should capture the state fine. Have you tried it since you corrected the mistake?

Automation actions are just scripts so effectively it can be done, but it’s actually easier to do it with passing variables to the script like you have because of having to set the variable somewhere, as it were.

As for the loop, unfortunately it’s about as verbose as you’ve done it, there’s a feature request for a loop key in scripts but it’s never been actioned (I think because there is too many what-ifs)

The script is capturing the state perfectly, and working exactly the way I need since fixing the typo. Capturing a variable directly in the automation without having to call a script would be nice to know how to do, if it’s possible, just for the knowledge of it.

I’m realizing that the script approach, while nice to encapsulate the behavior for re-use, has a limitation in that it cannot be called in parallel. I tried calling the script twice in the same automation and the second call silently did nothing. I don’t know yet if that limitation is just when called from the same automation, or if separate automations calling the script at the same time would also silently fail to operate. Any thoughts on how to solve that?

You’d need to duplicate the script so that you have one for each time you want to use it. You can cut down the amount of code to do that using yaml anchors…

script:
  flash_entity: &flash_script
    sequence:
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) != start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) == start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) != start_state }}"
      - service: switch.toggle
        data_template:
          entity_id: "{{ entity_id }}"
      - wait_template: "{{ states( entity_id ) == start_state }}"

  flash_entity_2: *flash_script

  flash_entity_3: *flash_script 

Etc

1 Like

That’s quite handy! Thanks!

1 Like