Passing variables to a script

I tried to create an automation: play music in the morning, music files was store in /local/morning. There are 4 songs, and I want to play all songs.


Here is automation code:

- id: '156823816799'
  alias: morning 
  description: New day 
  trigger:
  - at: 06:30:00
    platform: time
  condition:
  - condition: time
    weekday:
    - mon
    - tue
    - wed
    - thu
    - fri
    - sat
  action:
  - data:
      volume_level: 0.3
    entity_id: media_player.master_room
    service: media_player.volume_set
  - service: script.play_musics_in_list
    data:
      hass_url: "https://xxx.duckdns.org"
      file: >-
        {%- set files = states.sensor.morning.attributes.file_list-%}
        {%- set files = files|replace('/config/www/','')|replace('[','')|replace(']','')%}
        {%- set files = files.replace('\'','').replace(', ',',') %}
        {%- set files = files.split(',')-%}
        {{files}}
      number_of_files: "{{states.sensor.morning.attributes.number_of_files"

And here is script code:

play_musics_in_list:
  alias: Play music in list
  mode: restart
  sequence:
    - service: media_player.clear_playlist
      entity_id: media_player.master_room
    - repeat:
        while:
          - condition: template
            value_template: "{{repeat.index <= number_of_files}}"
        sequence:
          - wait_template: "{{ is_state('media_player.master_room', 'paused') }}"
          - delay: "00:00:02"
          - service: media_player.play_media
            entity_id: media_player.master_room
            data_template:
              entity_id: media_player.master_room
              media_content_id: "{{hass_url}}/local/{{file[repeat.index]}}"
              media_content_type: music

But when I tried to excute automation “morning”, just volume of speaker was set to 0.3.

Change data

  - service: script.play_musics_in_list
    data:

to data_template

  - service: script.play_musics_in_list
    data_template:

EDIT
I suspect there may be other issues as well. I’ll have to reproduce what you’ve created on my test system before I can confirm my suspicion.

For example, the output of a template is always a string. However, what is being passed via the file variable is assumed to be a list (because the script treats it that way with {{file[repeat.index]}}.

Thank Taras.
But in this guidline: https://www.home-assistant.io/integrations/script/, for how to passing variables to script, we just use “data” in automation.
I’m very fresh in this area, so could you please help to explain for me why we have to use “data_template” instead of “data” in this case.

By the way, I already test again with your recommendation, but not succeed.

You have to use data_template because the variables contain templates. Here are several other examples where the solution is to use data_template:

Because, as I mentioned, I suspect there are other problems and I’m currently trying to reproduce your scripts on my system to identify them.

Ah, I got your point:

the output of a template is always a string

This is my problem.
The output of file is string, but in script I’m using it as a list.
Am I right?

As suspected, these two variables:

file
number_of_files

contain strings and that causes script.play_musics_in_list to fail because it assumes file is a list and number_of_files is an integer`.

Within the script, you must convert them from string to the desired type.

Here’s what I suggest for the automation. It uses a streamlined template to convert the Folder Sensor’s file_list attribute to a comma-delimited string.

- id: '156823816799'
  alias: morning 
  description: New day 
  trigger:
  - at: 06:30:00
    platform: time
  condition:
  - condition: time
    weekday:
    - mon
    - tue
    - wed
    - thu
    - fri
    - sat
  action:
  - data:
      volume_level: 0.3
    entity_id: media_player.master_room
    service: media_player.volume_set
  - service: script.play_musics_in_list
    data:
      hass_url: "https://xxx.duckdns.org"
      file: >-
        {{ state_attr('sensor.morning', 'file_list')
          | map('replace', '/config/www/', '') | list | join(',') }}
      number_of_files: "{{ state_attr('sensor.morning', 'number_of_files') }}"

The following script converts number_of_files to an integer before it compares it to repeat.index. it also uses split to convert file to a list then references a list item using repeat.index.

Please be advised that I have set the condition template to check if repeat.index is less than number_of_files (as opposed to less than or equal to). A list’s items are indexed starting with zero. So if you have 4 songs in the list, they are referenced 0, 1, 2, and 3.

play_musics_in_list:
  alias: Play music in list
  mode: restart
  sequence:
    - service: media_player.clear_playlist
      entity_id: media_player.master_room
    - repeat:
        while:
          - condition: template
            value_template: "{{ repeat.index < number_of_files | int }}"
        sequence:
          - wait_template: "{{ is_state('media_player.master_room', 'paused') }}"
          - delay: "00:00:02"
          - service: media_player.play_media
            entity_id: media_player.master_room
            data_template:
              entity_id: media_player.master_room
              media_content_id: "{{hass_url}}/local/{{file.split(',')[repeat.index]}}"
              media_content_type: music

I haven’t tested it yet so it may require additional adjustments. For example, I’m making an assumption that repeat.index is 0 during the first iteration of the while loop. If I’m wrong about that, then we will have to subtract 1 from repeat.index when referencing the file list.

Let me know your test results and I can help you fix any additional issues.

NOTE
FWIW, passing number_of_files is optional because it can be calculated from the file variable.

{{ file.split(',') | count }}

Thank Taras very much.
I already test it and it run already.
But I face another issue. The speaker just play the third song in few seconds, and jump to play the fourth song (the last one) until finish.

Yes, I just got the same result in my tests which means my assumption was incorrect. The initial value of repeat.index is 1 (not 0). Therefore the condition template must revert to the original test which was “less than or equal to”.

        while:
          - condition: template
            value_template: "{{ repeat.index <= number_of_files | int }}"

In addition, we must subtract 1 from repeat.index when referencing the file list.

              media_content_id: "{{hass_url}}/local/{{file.split(',')[repeat.index-1]}}"

Here is a fresh copy of the script with the modifications:

play_musics_in_list:
  alias: Play music in list
  mode: restart
  sequence:
    - service: media_player.clear_playlist
      entity_id: media_player.master_room
    - repeat:
        while:
          - condition: template
            value_template: "{{ repeat.index <= number_of_files | int }}"
        sequence:
          - wait_template: "{{ is_state('media_player.master_room', 'paused') }}"
          - delay: "00:00:02"
          - service: media_player.play_media
            entity_id: media_player.master_room
            data_template:
              entity_id: media_player.master_room
              media_content_id: "{{hass_url}}/local/{{file.split(',')[repeat.index-1]}}"
              media_content_type: music
1 Like

It’s absolutely perfect now. just one minor issue, but I already fix it.
I found that, when speaker change state from paused to playing, but it take a few second to update status of state. This make the first song just is played in few seconds.
Solution is quite simple, just change the order of delay service and wait_template service like below:

        sequence:
           -  delay: "00:00:02"
           -  wait_template: "{{ is_state('media_player.master_room', 'paused') }}"
           -  service: media_player.play_media

Thanks Taras for your strongly support.
I gain a lots knowledge from your help.

1 Like

Glad to hear it’s working.

For the benefit of other users, pleas mark my previous post with the Solution tag, as shown in the following image:
Screenshot from 2020-08-13 14-19-54

By doing this, it will automatically place a check-mark next to the topic’s title which signals to other users that this topic has an accepted solution. In addition, it will place a link below your first post that leads to the Solution post. All of this helps other users to find answers to similar questions about passing variable to scripts.

1 Like

Since I banged around for far too long on this, I thought I’d share my final code. I wanted a wrapper script to play local sound files to my Google Home Nest Hub (whatever it’s called now).

alias: Play Passed In Sound File
sequence:
  - service: media_player.play_media
    data:
      media_content_type: '{{ media_content_type }}'
      media_content_id: '{{ media_content_id }}'
    target:
      entity_id: media_player.nesthub
mode: single

Calling Script

alias: Play Dog Bark
sequence:
  - service: script.1658966781043
    data:
      media_content_type: audio/mp3
      media_content_id: /local/media/dog_bark/bark.mp3
mode: single

Note that the mp3 file is actually stored at:
/config/www/media/dog_bark/bark.mp3

(HassOS, Raspberry Pi)

1 Like