How To Using Action Selectors in Blueprints

This is a solution for passing and executing actions dynamically in Home Assistant blueprints and scripts. The solution emerged through discussions with ChatGPT and was inspired by user nwithan8 and others in this thread.

Problem

The task was to execute actions defined during the configuration of a motion-activated blueprint. Passing an action as a runtime variable proved challenging considering that the action selector returns a list of actions.

Solution

The approach involves executing the action set during configuration by employing a choose condition in the action sequence since the choose condition will take a list of actions.

Here is a snippet of the action selector:

action_to_run:
  name: Action to run.
  description: Action to run when triggered.
  selector:
    action: {}

choose with action_to_run as default:

- alias: "Run the action"
  choose: []
  default: !input action_to_run

Here’s the solution in an example blueprint (which is a modification of the default motion_light.yaml):

blueprint:
  name: Motion-activated On/Off
  description: Run an action when motion is detected, run another action after waiting for a period of no motion.
  domain: automation
  input:
    motion_entity:
      name: Motion Sensor
      selector:
        entity:
          domain: binary_sensor
          device_class: motion
    on_action:
      name: On Action
      description: Action to run when motion is detected.
      selector:
        action: {}
    off_action:
      name: Off Action
      description: Action to run when motion is no longer detected.
      selector:
        action: {}
    no_motion_wait:
      name: Wait time
      description: Time to wait until off_action is called.
      default: 2
      selector:
        number:
          min: 0
          max: 60
          unit_of_measurement: minutes

variables:
  delay_var: !input no_motion_wait

# If motion is detected within the delay,
# we restart the script.
mode: restart
max_exceeded: silent

trigger:
  platform: state
  entity_id: !input motion_entity
  from: "off"
  to: "on"

action:
  - alias: "Run the on action"
    choose: []
    default: !input on_action

  - wait_for_trigger:
      platform: state
      entity_id: !input motion_entity
      to: "off"

  - delay: "{{ delay_var | multiply(60) }}"

  - alias: "Run the off action"
    choose: []
    default: !input off_action

Here, choose has an empty list of conditions and default executes the action passed as on_action and off_action.

This method adds flexibility, as it allows actions performed to be defined at the time of configuring the blueprint or script.

3 Likes

Thanks for this! This works well for Blueprints, but I was trying to use the Action Selector in a “normal” (non-Blueprint) script. This approach didn’t work because !input is only available for Blueprints. I see this thread is referenced in a few places, so I wanted to share my solution in case anybody else is in the same boat as me. Suggestions and improvements are welcome :).

I ended up with the following low-tech solve (where action is the field that uses the Action Selector). It’s a bit hacky because I needed to handle the permutations of the target and/or data being undefined.

- repeat:
    for_each: "{{ action }}"
    sequence:
      - choose:
        - conditions: "{{ repeat.item.target is defined and repeat.item.data is defined }}"
          sequence:
            - service: "{{ repeat.item.service }}"
              target: "{{ repeat.item.target }}"
              data: "{{ repeat.item.data }}"
        - conditions: "{{ repeat.item.target is defined }}"
          sequence:              
            - service: "{{ repeat.item.service }}"
              target: "{{ repeat.item.target }}"
        - conditions: "{{ repeat.item.data is defined }}"
          sequence:              
            - service: "{{ repeat.item.service }}"
              data: "{{ repeat.item.data }}"
        default:
            - service: "{{ repeat.item.service }}"

Since this is a pain to repeat in all of my scripts, I defined a helper script for doing this:

action:
  alias: _ Execute Action

  mode: parallel

  fields:
    action:
      name: Action
      required: true
      selector:
        action:

  sequence:
    # (loop from above goes here - omitted for brevity)

And then I can use the helper script like so:

- service: script.action
  data:
    action: "{{ foo }}"

Cheers!

2 Likes

@JacobSnyder
This works great, few limitation when we are actually not using service, like delay or other, but it the solution works great.
Probably adding another conditions for each case will work as well.
Thanks!