How to template the entire data field in a script?

I am trying to create a script to send notifications to anyone currently home. I would like to leverage another of my custom scripts that unifies notification service calls. (example call to this script below)

service: script.send_notification
data:
  destination:
    - Me
    - Wife
  message: Motion Detected in Garage
  title: Security Alert
  tag: security_alert
  url: /dashboard-mobile/security

My new script would automatically set the destination field and fill the fields in based on the script input fields. How do I create the entire “data:” field using a template? Below is what I’ve got so far but I’m getting this error: Error rendering data template: Result is not a Dictionary. How do I create a dictionary?

sequence:
  - variables:
      message_data: >
        {% set at_home = (states.person|selectattr('state','eq','home')|map(attribute='name')|list) %}
        {% set ns = namespace(data=[]) -%}
        {%- set ns.data = ns.data + [{"destination": at_home}] + [{"message": message_input }] -%}
        {%- set ns.data = (ns.data + [{"url": url_input }]) if url_input is defined else ns.data -%}
        {%- set ns.data = (ns.data + [{"tag": tag_input }]) if tag_input is defined else ns.data -%}
        {%- set ns.data = (ns.data + [{"title": title_input }]) if title_input is defined else ns.data -%}
        {{ ns.data }}
  - if:
      - condition: state
        entity_id: binary_sensor.combined_presence
        state: "on"
    then:
      - service: script.send_notification
        data: >
          {{ message_data }}

Thanks!

While you can template the value for data in a service call, you have gone a little overboard… and lost the thread. There’s no need to invoke a namespace since you aren’t using a loop. And, as indicated by the error message, you template does not output a dictionary which is what is required. What it is outputting is a list of dictionaries because you explicitly set the data type of ns.data to a list in the set statement: {% set ns = namespace(data=[]) -%}.

The proper configuration is a lot more basic:

sequence:
  - variables:
      message_data: >
        {% set at_home = (states.person|selectattr('state','eq','home')|map(attribute='name')|list) %}
        {{ {"destination": at_home, "message": message_input, "title": title_input, "tag": tag_input, "url": url_input} }}        
  - if:
      - condition: state
        entity_id: binary_sensor.combined_presence
        state: "on"
    then:
      - service: script.send_notification
        data: '{{ message_data }}'

Appreciate your help on this, and the clarification on namespace usage for this code noob.

The main issue I was running into (that I should have noted upfront) was that I only want the message variable to be a required field for the script. If I try to run the approach you shared, I run into “undefined” tags, urls, and titles that cause some errors with my send_notification script – which obviously needs to be fixed using the same approach.

So I’ve done more experimenting and returned back at the namespace (hopefully using it more appropriately this time :joy:).

The following is my updated, and hopefully cleanest, approach that seems to work as expected.

  - variables:
      message_data: >
        {%- set athome = (states.person|selectattr('state','eq','home')|map(attribute='name')|list) %}
        {%- set ns = namespace(data={}) %}
        {%- set ns.data = {"destination": athome, "message": message_input } -%}
        {%- set opt_data = {'tag': tag_input, 'title': title_input, 'url': link_input } %}
        {%- for item in opt_data %}
          {%- if opt_data[item] is defined %}
            {%- set dict2 = { item : opt_data[item] } -%}
            {%- set ns.data = dict(ns.data, **dict2) -%}
          {%- endif -%}
        {%- endfor -%}
        {{ ns.data }}
  - if:
      - condition: state
        entity_id: binary_sensor.combined_presence
        state: "on"
    then:
      - service: script.send_notification
        metadata: {}
        data: "{{ message_data }}"

Obviously, I’ve spent way more time on writing this marginally helpful script than it will ever be worth (compared to the alternative of a bunch of basic conditional statements or choose blocks) but I am finding this to be a fun challenge and hopefully good practice for future advanced automations.