Choose random file in a service - automation

Hi

Is it possible that my automation, calling this service, selects a random mp3 from the chime path? There are some MP3 files there and I want an MP3 to be randomly selected and played.

service: chime_tts.say
data:
  chime_path: /media/sounds/test1.mp3
  tts_platform: tts.google_nl_com
  volume_level: 1
  message: test message
target:
  device_id: 66d5d1269b164cd50d3e6343354652410

Is the only possible way to use a template line like this?

  option: "{{['testmp1','testmp2', 'testmp3']|random}}"

but then I need to define all MP3 file names, what I would like to avoid. (there are a lot).
Thanks

Create a Folder Sensor using the Folder integration.

The sensor will have a file_list attribute containing a list of all files it found (as per how you configured the Folder Sensor).

Assuming the Folder Sensor is sensor.my_music, your template will look something like this:

chime_path: "{{ state_attr('sensor.my_music', 'file_list') | random }}"

NOTE

Ensure you follow the integration’s instructions. The usual problem is that the Folder Sensor doesn’t have permission to access the desired folder.

3 Likes

The disadvantage of the folder sensor is that it ignores sub folders.
If you have a folder structure like

➜  ~ tree /media/sounds                         
/media/sounds
├── file with space.mp3
├── file1.mp3
├── file2.mp3
└── subfolder
    ├── file with space.mp3
    ├── file1.mp3
    └── file2.mp3

the files in the subfolder are not visible.

path: /media/sounds/
filter: "*"
number_of_files: 4
bytes: 0
file_list:
  - /media/sounds/file1.mp3
  - /media/sounds/file with space.mp3
  - /media/sounds/subfolder <---
  - /media/sounds/file2.mp3
unit_of_measurement: MB
device_class: data_size
icon: mdi:folder
friendly_name: sounds

So my approach is:
Create a little bash script

#!/bin/bash

MP3DIR='/media/sounds'
MP3PATH=`find $MP3DIR -type f -name '*.mp3' | shuf -n 1`
MP3=`basename $MP3PATH`

echo "{\"mp3\": \"$MP3\", \"mp3path\": \"$MP3PATH\"}"

make it executable

chmod +x /path/to/script.sh

now a command_line sensor

command_line:
  - sensor:
      name: random_mp3
      command: "/path/to/script.sh"
      value_template: "{{ value_json.mp3 }}"
      json_attributes:
        - mp3path

Now you can use the mp3path attribute of the command_line sensor

chime_path: "{{ state_attr('sensor.random_mp3', 'mp3path') }}"
1 Like

Thanks for this addition for files in subfolders!

Hello, I am having a tough time with folder/media id paths here.

This is what my code looks like:

action: media_player.play_media
metadata: {}
data:
media_content_id: {{state_attr(‘sensor.wow’, ‘file_list’) | random }}
media_content_type: audio/mpeg
target:
entity_id: media_player.nesthubmax23e0

Add outer double-quotes to the template.

media_content_id: "{{state_attr('sensor.wow', 'file_list') | random }}"

I tried this and it didn’t seem to work.

When I try to play from the template, the content id path is:

action: media_player.play_media
metadata: {}
data:
  media_content_id: "/media/Wow/58_WeWow_on_the_Weekend_(9-28-24).mp3"
  media_content_type: audio/mpeg
target:
  entity_id: media_player.nesthubmax23e0

When I play media directly, content id path is: (This works fine btw)

data:
  media_content_id: >-
    media-source://media_source/local/Wow/04_WeWow_The_Great_Indoors_2024_-_Day_5:_Into_the_Outdoors_(12-20-24).mp3
  media_content_type: audio/mpeg
target:
  entity_id: media_player.nesthubmax23e0

My “wow” folder is in my media folder

Go to Developer Tools → Templates, erase anything that might already be there, then enter this template:

{{ state_attr('sensor.wow', 'file_list') | random }}

What does it report?

It reports

/media/Wow/06_WeWow_The_Great_Indoors_2024_-_Day_3:_Bored_Games_(12-18-24).mp3

It would appear that Home Assistant doesn’t have access to that folder path.

In sensor.wow’s file_list attribute, rename all entries that look like this:

/media/Wow/58_WeWow_on_the_Weekend_(9-28-24).mp3

To simply this:

58_WeWow_on_the_Weekend_(9-28-24).mp3

Then you can use this:

data:
  media_content_id: >-
    media-source://media_source/local/Wow/{{ state_attr('sensor.wow', 'file_list ') | random }}
  entity_id: media_player.nesthubmax23e0

Thank you.

I tried this in config.yaml

#cleaned wow list  
template:
  - sensor:
      - name: "Cleaned WOW File List"
        state: >
          {{ state_attr('sensor.wow', 'file_list') | map('replace', '/media/Wow/', '') | list }}

It works in Developer Tools → Templates but has no values under Developer Tools → States

I tried triggering the template by forcing sensor.wow to change and still nothing under attributes for sensor.cleaned_wow_file_list

Please let me know if you see anything I am doing wrong

Did you try what I had suggested? Because the latest thing you posted isn’t what I suggested.

Or is it simply impossible to manually rename the items in the file_list attribute, so that’s why you composed another Template Sensor?

It implies you either overlooked to Reload Template integration after composing the Template Sensor or you did reload it but there’s an error in the configuration.

For example, if the cleaned wow list exceeds 255 characters it will not be displayed (an entity’s state value is limited to 255 characters). In that case, you should store the cleaned wow list a new attribute (in the Template Sensor). Attributes allows for about 16K characters.

It works!

Thank you for your help!

I didn’t want to manually use find/replace for the sensor.wow attribute hence the new template sensor.

Final solution:
In config.yaml

#folder Integration
sensor:
  - platform: folder
    folder: /media/Wow

#cleaned wow list  
template:
  - sensor:
      - name: "Cleaned WOW File List"
        state: "Cleaned"
        attributes:
          cleaned_file_list: >
            {{ state_attr('sensor.wow', 'file_list') | map('replace', '/media/Wow/', '') | list }}

Action in automation:

action: media_player.play_media
metadata: {}
data:
  media_content_id: >-
    media-source://media_source/local/Wow/{{state_attr('sensor.cleaned_wow_file_list', 'cleaned_file_list') | random }}
  media_content_type: audio/mpeg
target:
  entity_id: media_player.googlehome4260

If you wish, you can avoid creating sensor.cleaned_wow_file_list and simply use its template within media_content_id.

data:
  media_content_id: >-
     media-source://media_source/local/Wow/{{ state_attr('sensor.wow', 'file_list') | map('replace', '/media/Wow/', '') | list | random }}

Even better and cleaner. Thank you.

1 Like