SMTP service - pass a dynamic target list

Hi,

I’ve several automation that uses SMTP notifications that lists multiple targets, and I’ve put all the emails in the secret file such as:

email_a: [email protected]
email_b: [email protected]
email_c: [email protected]

A typical automation would look something like following:

mode: single
trigger:
  - platform: sun
    event: sunrise
    offset: 0
condition: []
action:
  - service: notify.smtp
    data:
      message: Good morning!
      target: 
        - !secret email_a
        - !secret email_b
        - !secret email_c

Now I want to create a dashboard in the UI where all the users (a, b & c) will have option to check or uncheck their names for receiving notification. Please help me figure out how to parse a dynamic list of targets for the SMTP service.

Thanks.

You can’t directly combine templating and secrets, so it’s a multi-step process including trigger variables.

mode: single
trigger_variables:
  a: !secret email_a
  b: !secret email_b
  c: !secret email_c
trigger:
  - platform: sun
    event: sunrise
    offset: 0
condition: []
action:
  - variables:
      mapper:
        input_boolean.a: "{{ a }}"
        input_boolean.b: "{{ b }}"
        input_boolean.c: "{{ c }}"
      targets: |
        {% set ns = namespace( emails=[] ) %}
        {% set bools = ['input_boolean.a', 'input_boolean.b', 'input_boolean.c'] | select('is_state', 'on') | list %}
        {% for my_bool in bools %}
        {% set ns.emails = ns.emails + [ mapper.get(my_bool) ]%}
        {% endfor %}{{ ns.emails }}
  - service: notify.smtp
    data:
      message: Good morning!
      target: "{{ targets }}"
1 Like

Wow, you just took my knowledge of HA to a whole different level with your sample code :bowing_man:

Thanks for sharing, let me test this out and post updates.

PS: I already see lot of application for trigger attributes and variable mapper as you have used in the automation!

Just be aware that trigger variables only allow Limited templates, so they don’t have access to the state machine and some other common templating options.

I just tested the code and it works like a charm, thanks for sharing this solution!

Another question, I’ve around 20 automation that provides various notifications to our family. Do I need to add this code in each automation individually or is there a smarter way to reuse this code across multiple automations?

To make it easier to use in multiple automations you’d be better off the set it up as a script instead. One caveat, since scripts don’t have an analog to trigger_variables, to use secrets you’ll need to set the script up in YAML, not in the UI script editor and not in scripts.yaml.

alias: Email opted-in Users
mode: single
fields:
  message:
    name: Message
    description: The message to send
    example: "Good Morning"
    required: true
variables:
  a: !secret email_a
  b: !secret email_b
  c: !secret email_c
  mapper:
    input_boolean.a: "{{ a }}"
    input_boolean.b: "{{ b }}"
    input_boolean.c: "{{ c }}"
  targets: |
    {% set ns = namespace( emails=[] ) %}
    {% set bools = ['input_boolean.a', 'input_boolean.b', 'input_boolean.c'] | select('is_state', 'on') | list %}
    {% for my_bool in bools %}
    {% set ns.emails = ns.emails + [ mapper.get(my_bool) ]%}
    {% endfor %}{{ ns.emails }}
sequence:
  - service: notify.smtp
    data:
      message: "{{ message }}"
      target: "{{ targets }}"

Then your automation would then be:

mode: single
trigger:
  - platform: sun
    event: sunrise
    offset: 0
condition: []
action:
  - service: script.email_opted_in_users
    data:
      message: Good morning!

Thanks for suggestions, I am running into some problem while testing the above script.

I copied the code into scripts.yaml in the configuration folder and saved the file and reloaded the scripts. In the scripts view, I am not seeing a script named “Email opted_in users” but two different new items named “fields” and “variables” (see screenshot below).

These blocks would not open in normal script UI editor as you rightly mentioned in your note above, in Visual Studio Code the scripts.yaml shows the full pasted code without truncation and there are no errors to provide any insights into what might be happening here.

I’ve no experience of using code block you recommended, please see if you spot any possible error what is not allowing this script to be loaded like a normal service.
Thanks.

Delete it out of scripts.yaml. Then in configuration.yaml add:

script:
  email_opted_in_users:
    alias: Email opted-in Users
    mode: single
    fields:
      message:
        name: Message
        description: The message to send
        example: "Good Morning"
        required: true
    variables:
      a: !secret email_a
      b: !secret email_b
      c: !secret email_c
      mapper:
        input_boolean.a: "{{ a }}"
        input_boolean.b: "{{ b }}"
        input_boolean.c: "{{ c }}"
      targets: |
        {% set ns = namespace( emails=[] ) %}
        {% set bools = ['input_boolean.a', 'input_boolean.b', 'input_boolean.c'] | select('is_state', 'on') | list %}
        {% for my_bool in bools %}
        {% set ns.emails = ns.emails + [ mapper.get(my_bool) ]%}
        {% endfor %}{{ ns.emails }}
    sequence:
      - service: notify.smtp
        data:
          message: "{{ message }}"
          target: "{{ targets }}"

Restart HA and test.

1 Like

Hi Drew,

Yes putting it in configuration.yaml worked and now I’m little confused as I’ve been using code refactorization and have following additional lines in the configuration.yaml:

script: !include scripts.yaml
script refactored:  !include_dir_merge_named ./refactored/scripts

The folder “./refactored/scripts” has few other scripts that are identified correctly. And my undestanding is that refactoring code in above manner is only for housekeeping as HA generates the same YAML syntax (expanded one) after collapsing all files and folders under “/refactored/scripts”.

Is there a reason why this code works in configuration.yaml but not in scripts.yaml?

PS: But thanks for helping solve this complex situation!!

If it works in configuration.yaml it should work as a file in you refactored folder. I am no expert when it comes to all the various ways the config can be split… as soon as I found out about Packages I stopped bothering with the other options.

This may not be completely accurate, but my understanding is that scripts.yaml and automations.yaml are subject to some sort of extra processing to make them work with the UI editors. That processing doesn’t play nice with YAML methods like nodes, anchors, and tags, so you should not use them in those two files. One exception to this is that the !secret tag can be used in trigger_variables, there may be others that I am unaware of.

Thanks and the explanation does make intuitive sense as the UI editor is optimized for user friendliness. From my early days, I do recall the frustration of typing YAML code blocks and all the mistakes I used to make around closing quotes and indentations!

Thanks again for walking me through this solution!

@Didgeridrew Drew, if I may ask for some more help to make this one little smarter.

Here are the things that I am doing or planning on doing:

  1. Created a UI where each user can select or deselect notifications specific to each event.
    Screenshot from 2023-10-24 21-41-07

  2. So there are three distinct set of lists in this context:
    a. Users
    b. Events or automatons that can broadcast notifications (for example, dishwasher cycle finished)
    c. Input boolean for each user (for each event) that can be toggled, so if there are 2 users and 3 events then there will be a grid of 2x3=6 input boolean as shown in the screenshot above.

  3. Trying to write a script that will have hard coded information on 2(a)(b) and 2(c) and there is a field in the script (“dynamic_opt_in-event”) which will pass the specific event type from an automation.

  4. The idea is that if there is an automation of notification related to dishwasher, the automation can pass “dynamic_opt_in-event=dishwasher” and the script should be able to parse the list of recipients who have opted to receive notifications related to dishwasher.

I started tweaking the code you shared earlier but clearly I’m not doing it correctly, please see if you can spot any obvious error in my approach:

dynamic_email_opt_in:
  alias: Dynamically opt in for notifications
  mode: single
  fields:
    dyanmic_opt_in-event:
      required: true
  variables:
    users:
      - !secret pankaj_cell
      - !secret pankaj_email
    notification events:
      dishwasher:
        - input_boolean.pankaj_email_dishwasher
        - input_boolean.pankaj_sms_dishwasher
      laundry_washer:
        - input_boolean.pankaj_email_laundry_washer
        - input_boolean.pankaj_sms_laundry_washer
      laundry_dryer:
        - input_boolean.pankaj_email_laundry_dryer
        - input_boolean.pankaj_sms_laundry_dryer
      dynamic_list: |
        {% set ns = namespace( dynamics=[] ) %}
        {% for notifcation_event in notification_events['dishwasher'] %}
          {% if states('notifcation_event') ='on'}
              {% set ns.dynamics = ns.dynamics + [ (my_bool) ]%}
        {% endfor %}{{ ns.dynamics }}
  sequence:
  - service: notify.smtp_gmail
    data:
      title: Dynamic opt-in list
      message: Some msg here       
      target: "{{ dynamic_list }}"

If you can think of a more efficient way to manage this dynamic opt-in list, please send me few pointers.
Thanks.

This script is working, I’ve hard coded the notification event to be dishwasher to test but can write a template to parse other appliances or events.

dynamic_email_opt_in:
  alias: Dynamically opt in for notifications
  mode: single
  fields:
    dyanmic_opt_in-event:
      required: true
  variables:
    a: !secret pankaj_email
    b: !secret pankaj_cell
    users:
      - !secret pankaj_cell
      - !secret pankaj_email
    dishwasher:
      - input_boolean.pankaj_email_dishwasher
      - input_boolean.pankaj_sms_dishwasher
    laundry_washer:
      - input_boolean.pankaj_email_laundry_washer
      - input_boolean.pankaj_sms_laundry_washer
    laundry_dryer:
      - input_boolean.pankaj_email_laundry_dryer
      - input_boolean.pankaj_sms_laundry_dryer
    mapper:
      input_boolean.pankaj_email_dishwasher: "{{ a }}"
      input_boolean.pankaj_email_laundry_dryer: "{{ a }}"
      input_boolean.pankaj_sms_dishwasher: "{{ b }}"
      input_boolean.pankaj_sms_laundry_washer: "{{ b }}"
      input_boolean.pankaj_sms_laundry_dryer: "{{ b }}"
    targets: |
      {% set ns = namespace( emails=[] ) %}
      {% set bools = dishwasher | select('is_state', 'on') | list %}
      {% for my_bool in bools %}
      {% set ns.emails = ns.emails + [ mapper.get(my_bool) ]%}
      {% endfor %}{{ ns.emails }}
  sequence:
  - service: notify.smtp_1_gmail
    data:
      title: Dynamic opt-in list
      message: "Test"
      target: "{{ targets }}"

I’ll share the final version once I’ve ironed out all the kinks and customizations.