Help - expected dictionary for sequence

I’m trying to define a script to execute arbitrary (user-defined) actions, which will be passed as parameters on callback-time.

Here’s an excerpt with the relevant bits:

alias: my script
mode: single

fields:
  action_1:
    name: action(s) to perform
    required: true
    default: []
    selector:
      action:

sequence:
  - variables:
      actions_definition: '{{ action_1 }}'
      
  - choose:
    default: '{{ actions_definition }}'

Trying to save the script (YAML mode), I keep getting the following error:

Message malformed: expected dictionary @ data['sequence'][1]['default'][0]

Why can’t I pass the actions variable to the sequence? It’s like it’s forcing me to define actions in the code - which defeats the whole purpose of this script…

Any help is greatly appreciated :slight_smile:

should be aligned with

You can’t template an entire action. You can template a single service and a single item inside data.

You should make a python script instead, that’ll allow you to do what you want.

1 Like

Hmm, that’s intriguing :thinking:
TBH, I have zero experience with writing HA python scripts as services. Will have to do some digging. Any useful links will be appreciated (already found official docs).

Can you explain how this differs from automations?
I’ve seen automations which allow passing entire actions as their arguments; meaning something like:

input:
  action_1:
    name: action(s) to perform
    required: true
    default: []
    selector:
      action:

# ...some more automation code

action:
  - choose:
    default: !input action_1

Why does that work, but variables in a script do not? Obviously the implementation in automations is not a straightforward template (using !input etc.), but I don’t understand how it differs under the hood…

Thank you for the prompt reply :+1:

That’s a blueprint, not an automation. You can’t pass full actions in automations. Only blueprints.

Again, blueprint.

Blueprints create automations based on what you select for the blueprint. There’s a dedicated blueprint UI that does all of this for you. In the end, you end up with an automation. That automation will not contain !input and will not have variable actions.

Apologies, I did mean blueprints. And I see your point.
I think maybe the confusion comes from the fact the front-end usage looks very (very!) similar in both use-cases: user is given form-fields to provide data, which can be used in the configuration as variables.

  • in blueprints, it’s inputs that define form-fields for data to be used in the action section;
  • in scripts it’s fields that define similar form-fields for data to be used in the sequence section.

Follow-up question: assuming I’ve created a python script service (challenge accepted :muscle: ), how do you suggest implementing a script to handle arbitrary actions?

Reading the docs, I see how I can use variables passed to the script, to call services for example. But arbitrary actions can come in lots of different forms, (service, variables, while, delay, wait_for_trigger, choose etc.); is there an execute_action()- type callback, or would I need to handle each action type separately?

all service calls are the same in the home assistant api, the data passed is different. The data is a dictionary, so just add the appropriate keys when needed. You can treat it as a pass through.

Why are you even doing this? What’s the end goal.

Fair question :laughing:

I’m looking for a better solution to creating actionable notifications then what I’ve found so far.
The best I’ve found was @vorion’s implementation with a blueprint; this simplifies the action(s)-definition and event handling, but means you’d create multiple automation definitions, one for each notification scenario. That seems redundant IMHO.

I’d like to be able to create these actionable notifications on-the-fly.
Things I need to code (I think):

  • create a script with fields similar to the inputs in the blueprint above
  • the script will take care of all the notification-creation logic- calling the service, passing title/message/action title(s) etc., waiting/receiving for the notification event…
  • … at that point it will pass the received user-choice and possible actions to python script for execution

End goal- to be able to simply call script.actionable_notifications, pass title/message and actions to it, and let it dispatch the notification.

try this

script:
  blah:
    fields:
      services:
        description: A list of services
        example: []
    sequence:
    - repeat:
        count: "{{ services | length }}"
        sequence: "{{ services[repeat.index - 1] }}"

then to call:

service: script.blah
data:
  services:
  - service: x.y
    data:
      title: 1
      message: 2
  - service: x.y
    data:
      title: 1
      message: 2

if that doesn’t work, then this script will with the same call

script:
  blah:
    fields:
      services:
        description: A list of services
        example: []
    sequence:
    - repeat:
        count: "{{ services | length }}"
        sequence:
        - service: "{{ services[repeat.index - 1].service }}"
          data:
            title: "{{ services[repeat.index - 1].title }}"
            message: "{{ services[repeat.index - 1].message }}"

The downside of method 2 is that you’ll have to always provide title and message. You could feed defaults but the services could error.

Thank you for these suggestions.

Suggestion #1: tried it as is, and got:

value should be a string for dictionary value @ data['fields']['services']['example']

changed to example: "", then got:

expected dictionary @ data['sequence'][0]['repeat']['sequence'][0]

so seems like the error is the same; can’t have a template as a sequence.

Then tried your suggestion #2, was able to save it as a script.
However, can’t use this in the GUI; only way to pass a list of services would be via YAML code, as far as I understand.

Correct me if I’m wrong?

Actually, I now realise the 2nd suggestion wouldn’t work either.

- service: "{{ services[repeat.index - 1].service }}"
  data:
    title: "{{ services[repeat.index - 1].title }}"
    message: "{{ services[repeat.index - 1].message }}"

This means that the only parameters you can pass to a service are title and message, which is useless since services may need to pass different data.

I tried with:

- repeat:
  count: "{{ action_3_action | length }}"
  sequence:
    - service: "{{ action_3_action[repeat.index - 1].service }}"
      data: "{{ action_3_action[repeat.index - 1].data }}"

But that returns the same (original) error:

expected dict for dictionary value @ ....['repeat']['sequence'][0]['data']

@petro
The solution I found was indeed using a python script. It turned out the most flexible.
It can handle service calls which use either target or data as their parameter(s).
Thanks for the suggestion.

/config/scripts.yaml

my_script:
  alias: Send (an unknown number of) various actions to be executed
  mode: single

  fields:
    actions:
      name: 'Service call(s)'
      required: true
      default: []
      selector:
        action:

  sequence:
  - alias: Send the actions to python script
    service: python_script.actions_execute
    data:
      actions: "{{ actions }}"

/config/python_scripts/services.yaml

actions_execute:
  name: Execute actions
  description: Execute the chosen actions
  fields:
    actions:
      description: The actions to execute

/config/python_scripts/actions_execute.py

def get_param(service):
  if "data" in service:
    return service["data"]
  if "target" in service:
    return service["target"]
  return null

actions = data.get("actions")
for service in actions:
  service_name = service.pop("service")
  domain, name = service_name.split(".")
  param = get_param(service)
  hass.services.call(domain, name, param)