How do you trigger complex automations from UI?

There’s no “misunderstanding” here. I understand it very clearly. I’m not asking the community why it doesn’t work in a way I would expect, I’m asking how to overcome the limitation and maybe how others solve it.

The requirements are:

  1. Write automation/script code once, but scale it to multiple rooms/setups
  2. Configure each setup once
  3. Trigger specific parts of the project from lovelace dashboards / mobile app widgets, tiles, whatever easily

I’ve solved the problem using custom events. It’s not perfect, as I still have to create one script per project and hardcode event names there and I would like to get rid of those scripts

If you’re employing an Event Trigger then you haven’t “overcome” any alleged limitations because you are still working within the extent triggering framework. An Event Trigger listening for a custom event is simply just another trigger (that can be used within the action to determine the execution path).

Would it be fair to say that what you need is a service call to generate a custom event?

A service accepting event name as a parameter would be very helpful

Raise and consume custom events

The following automation example shows how to raise a custom event called event_light_state_changed with entity_id as the event data. The action part could be inside a script or an automation.

- alias: "Fire Event"
  trigger:
    - platform: state
      entity_id: switch.kitchen
      to: "on"
  action:
    - event: event_light_state_changed
      event_data:
        state: "on"

The part you want is the event call shown in the automation’s action.

Yeah, except that the event name isn’t “templatable”. The following doesn’t work:

alias: Emit event
fields:
  entity_id:
    description: entity_id
    example: sensor.name
  action:
    description: action
    example: left_switch
  name:
    description: name
    example: double_switch.action
sequence:
  - event: '{{ name }}'
    event_data:
      entity_id: '{{ entity_id }}'
      action: '{{ action }}'
mode: parallel
max: 10

Now that sounds like a good Feature Request.

Otherwise you might want to investigate creating a python_script to do it.

Why does the event name need to be templatable? Trigger data is amorphous. On top of that, scripts do exactly what you’re looking to do, regardless if you believe they do or not. You’re just trying to keep to this rigid data structure for some reason.

your automations…

trigger:
- platform: event
  event_type: my_event_stream
  event_data:
    my_custom_type: foo
condition: ...
action: ...
script:
  fire_event:
    mode: parallel
    fields:
      event:
        description: Event name
        example: foo
    variables:
      fired_event: >
        {{ event | default("no event") }}
    sequence:
    - event: my_event_stream
      event_data:
        my_custom_type: "{{ fired_event }}"

Now you can make any UI object you want and fire any event. Even pair it with an input_select and a button so you can fire everything from 2 controls.

Your service will be:

service: script.fire_event
data:
  event: xyz

Also, to clear up some of your miss conceptions…

You can blue print scripts as of 2021.11.

You can set the fields and have a custom service for your script with variables accessible from the ui. That’s the whole point of the fields area in scripts. It defines what the script is expecting so that you can select the items from the UI. Then you can then take those variables and push them anywhere in your script. And if you don’t stick to your ‘rigid I must use different event names’, then you can get exactly what you want (which I showed above).

If you make your script properly, you don’t have to specify things and you can use defaults. Above has an example of that.

So in regards to your example…

trigger:
- id: only_right
  ..
- id: only_left
  ..
- id: both
action:
- service: script.fire_event
  data:
    event: "{{ trigger.id }}"

I used to have your thought process, but all it does is create an extra headache. Make scripts with fields, document each field. Then call the scripts from your automations. Use variables like it’s your job. That way you can see the every condition play out before you hit conditions. Also, I avoid blueprints. They are good for beginners. However when you understand how to fire things in parallel and utilize templates, you can have 1 automation that handles everything. Instead of having 1 blueprint create 10 separate automations.

Here’s a script that allows for an input select from the ui:

script:
  fire_event:
    mode: parallel
    fields:
      event:
        description: Event name
        example: foo
        selector:
          select:
            options:
            - foo
            - bar
            - no event
    sequence:
    - event: my_event_stream
      event_data:
        my_custom_type: "{{ event }}"

Other examples

First of all, thanks a lot for the detailed answer!

Well, you’ve decided to use one event type for everything (this will definitely work), but I was planning to use one event type per blueprint and didn’t want to create multiple event emitter scripts for that.

Generally speaking, I like your idea.

I see two approaches now:

  1. Automation from a blueprint (one per room) + single event emitter script + Lovelace entities emitting events through the script
  2. Script from a blueprint (one per room) + automation (from blueprint or manual) + Lovelace entities calling scripts

Is there some sort of inviolable design constraint demanding separate custom event names or just a preference?

Because if it’s a preferential constraint, if you forego it the Event call becomes usable for your application (because it supports templating in event_data as shown by petro). Just a change of preference may be the only thing needed to get what you want now (no Feature Request or python script required).

my only assumption is that he doesn’t like this:

trigger:
- platform: event
  event_type: my_event_stream
  event_data:
    my_custom_type: xyz

and he wants this

trigger:
- platform: event
  event_type: xyz

i.e. save 2 lines of editing? :man_shrugging:

No, it’s not a requirement. I was using different event names as namespaces, one event type per project, to simplify debug and for the readability

It’s not about what he likes and what he doesn’t :slight_smile:
What he doesn’t like - unmaintainable entities in his setup.

The original thread isn’t about emitting events at all, it’s really about how to manage complex automations (logic) and re-use or trigger parts of them from the Lovelace dashboards.

Using events for that - was my first idea. To my surprise, I discovered that Lovelace entities aren’t capable of emitting events natively, and I’ve started wondering was my idea good or not. That’s it.

My hass setup has ~100 devices already, ~30 more are coming. I have ~50 automations and I already don’t like it, as the configuration maintainability is important to me.

Then move away from blueprints and learn how to set up automations with parallel. I have

devices: 208
entities: 1182
automations: 31

If I were to add any device at this time in my automations, I simply have to add the entity_id to the trigger in the automation I wish and walk away.

The downside of blueprints is that you have to manage them. If you plan ahead and avoid them. You don’t have to manage anything outside of fixes.

Just for giggles, post a blueprint that you use alot of. Choose a simpler one and I’ll work through it with you to turn it into a parallel automation that doesn’t require a blueprint. This will purely just be an exercise.

Here we go: double_switch blueprint · GitHub

What ultimately would like to achieve: trigger those conditional scene transitions from the Lovelace dashboards without putting any logic into the dashboard configs

Did you purposely choose the most complicated one you have? I’m trying to show you the general idea that you can apply. Not reverse engineer your entire setup. Either way, we can continue with this but I’ll need to know all your settings for each automation you created with blueprint (i.e. each selectors entity / value you used). I can already tell that you can have 1 script as the sequence, with 1 automation paired with it to accomplish this without the blueprint or events. As well as have a script that can run the actions from the UI.

:smiley: I’m not solving simple tasks with blueprints. That’s why the topic title has a complex word in it.
I have 3 blueprints so far, and each of them is bigger than 3kb, mainly because of copy-paste.

The automation above isn’t complicated:

  1. Convert switch clicks to events
  2. Listen to those events
  3. Apply one of four scenes based on lights states ON/OFF

I need to know the states of sensor selector. Are they just action_left and action_right and action_both?

left, right, both. Please see the screenshot attached:

Sorry, work caught up with me. Anyways, here’s everything condensed and easily manageable.

automation

automation:
- alias: Double-switch automation
  mode: parallel
  trigger:
  - platform: state
    entity_id:
    - sensor.living_room_wall_switch_action
  action:
  - service: script.my_over_complicated_setup
    data:
      sensor: "{{ trigger.entity_id }}"

script

script:
  my_over_complicated_setup:
    mode: parallel
    fields:
      sensor:
        description: sensor being used
        example: sensor.living_room_wall_switch_action
    variables:
      config:
        sensor.living_room_wall_switch_action: 
          left_light: light.living_room_tv_light
          right_light: light.living_room_table_light
          left: scene.living_room_only_tv_light
          right: scene.living_room_only_dining_light
          both: scene.living_room_only_full_light
          no_switches: scene.living_room_no_lights       
      continue: >
        {{ config.get(sensor) is not None }}
      left_light: >
        {{ is_state(config.left_light, 'on') if continue else False }}
      right_light: >
        {{ is_state(config.right_light, 'on') if continue else False }}
      target_scene: >
        {{ config.get(sensor, {}).get(states(sensor)) }}
      scene:
        {% if continue %}
          {% if left_light and right_light %}
            {{ config.both }}
          {% elif left_light and not right_light %}
            {{ config.left }}
          {% elif not left_light and right_light %}
            {{ config.right %}
          {% else %}
            {{ config.no_switches }}
        {% endif %}
      expected_scene: >
        {{ target_scene == scene }}
    sequence:
    - condition: template
      value_template: "{{ continue and expected_scene }}"
    - service: scene.turn_on
      target:
        entity_id: "{{ scene }}"

Then for each new sensor/light combos, just add to the config variables. For example, I want to add some_other_room…

trigger would change to:

  - platform: state
    entity_id:
    - sensor.living_room_wall_switch_action
    - sensor.some_other_room_switch_action

config in the script would change to:

      config:
        sensor.living_room_wall_switch_action: 
          left_light: light.living_room_tv_light
          right_light: light.living_room_table_light
          left: scene.living_room_only_tv_light
          right: scene.living_room_only_dining_light
          both: scene.living_room_only_full_light
          no_switches: scene.living_room_no_lights
        
        # Added my other room.
        sensor.some_other_room_switch_action: 
          left_light: light.some_other_room_tv_light
          right_light: light.some_other_room_table_light
          left: scene.some_other_room_only_tv_light
          right: scene.some_other_room_dining_light
          both: scene.some_other_room_only_full_light
          no_switches: scene.some_other_room_no_lights 

Lastly, your single button UI check…

tap_action:
  action: call-service
  service: script.my_over_complicated_setup
  service_data:
    sensor: sensor.living_room_wall_switch_action

Much more managable.


To be honest, you have redundancy in it. I’m not sure why you check the lights to ensure you’re calling the correct scene. I’d just use the scene from the sensor. This is what that setup would look like

automation:

automation:
- alias: Double-switch automation
  mode: parallel
  trigger:
  - platform: state
    entity_id:
    - sensor.living_room_wall_switch_action
  action:
  - service: script.my_over_complicated_setup
    data:
      sensor: "{{ trigger.entity_id }}"
      expected: "{{ trigger.to_state.state }}"

script

script:
  my_over_complicated_setup:
    mode: parallel
    fields:
      sensor:
        description: sensor being used
        example: sensor.living_room_wall_switch_action
      expected:
        description: expected state
        example: left
    variables:
      config:
        sensor.living_room_wall_switch_action: 
          left_light: light.living_room_tv_light
          right_light: light.living_room_table_light
          left: scene.living_room_only_tv_light
          right: scene.living_room_only_dining_light
          both: scene.living_room_only_full_light
          no_switches: scene.living_room_no_lights
       
      continue: >
        {{ config.get(sensor) is not None }}
      target_scene: >
        {{ config.get(sensor, {}).get(expected) }}
    sequence:
    - condition: template
      value_template: "{{ continue }}"
    - service: scene.turn_on
      target:
        entity_id: "{{ target_scene }}"

then the UI buttons

tap_action:
  action: call-service
  service: script.my_over_complicated_setup
  service_data:
    sensor: sensor.living_room_wall_switch_action
    expected: left

FYI there may be is typos (EDIT: I Think i fixed them all), I pushed this out pretty quick but it should get the idea across.

@petro first of all, thanks a lot for your efforts! I appreciate it.
The approach is very smart and gives me another perspective.

I understand you keep automation(s) lightweight and move all logic to the scripts. With the price of not having conditions and triggers there, and I don’t feel I will like the code above.

I think the code above won’t trigger the desired scene change, as states(sensor) will give you an empty state

The switch itself doesn’t have any state. It just generates actions (clicks). Lights are being used as state indicators. What I’m achieving here is the following sequence:
Both lights OFF → Left switch click → TV light ON → Right switch click → Both lights ON → Right switch click → Dining light OFF, TV light ON → Left switch click → Both lights OFF.
Like both switches control dedicated lights, but light states depend on each other’s states. Actually TV light consists of one smart bulb + WLED strip.