#Is there a way to play a media file in a while loop but not wait for it to finish?

100 messages · Page 1 of 1 (latest)

uncut cosmos
#

I am working on a sound machine automation. This will play white noise, rain sounds, ocean, etc. It is tied in with Assist. I have an automation that when called will play a sound file and when the sound file ends it will loop again and replay it until a condtion is met.

Unfortunately it seems that it will play the entire sound file before moving to the condition check. This is not good as the user cannot cancel the playback until the whole sound file completes which could be 10-20 minutes depending on the sound file being played.

Here's the code I have now:

              - repeat:
                  sequence:
                    - data:
                        media_content_id: "{{ soundmachine_dict[trigger.slots.soundtype] }}"
                        media_content_type: MUSIC
                      target:
                        entity_id: "{{ target_mediaplayer_device }}"
                      action: media_extractor.play_media
                    - delay:
                        seconds: 2
                    - wait_template: "{{ states(target_mediaplayer_device) == 'idle'}}"
                      continue_on_timeout: true
                      enabled: true
                  while:
                    - condition: template
                      value_template: >-
                        {{ (state_attr(target_satellite_device,'soundmachine')
                        == 'playing') }}

Is there a better way to start the sound playing and stop the sound on demand?

Here is the entire automation:
https://github.com/dinki/View-Assist/blob/viewassist-soundmachine/View_Assist_custom_sentences/Sound_Machine/automation-soundmachine.yaml

Thanks!

GitHub

View Assist provides visual feedback for the Home Assistant Assist Voice Assistant - dinki/View-Assist

sterile juniper
#

@uncut cosmos break out the playing part into a script, you can turn off scripts

#

keep in mind, HA might not actually have this ability

#

You can certainly stop media

#

if you separate the playing into a separate script, you can use script.turn_on which will not block the automation

#

FYI this is the only way to have a non-blocking step in an automation

uncut cosmos
#

Okay, so to accomplish this I would replace the media play call I have now with a script turn_on. Would I just have the script loop and handle the script turn_off via the 'stop soundmachine' voice action?

sterile juniper
#

No

#

that was 2 separate thoughts

#

Use script.turn_on, make a generic play media script that accepts a media_player and the media as variables and plays the media with whatever service you want. Use that in your loop.

#

keep everything else the same

uncut cosmos
#

Excellent. Will give this a try. I appreciate your continued support.

uncut cosmos
#
alias: Play Sound on Media Player
description: Play a sound file on a specific media player entity
fields:
  media_player:
    description: The media player entity to play the sound on
    example: media_player.living_room_speaker
    required: true
    selector:
      entity:
        domain: media_player
  sound_file:
    description: The URL or file path of the sound to play
    example: /local/sounds/alert.mp3
    required: true
    selector:
      text:
        multiline: false
sequence:
  - data:
      entity_id: "{{ media_player }}"
      media_content_id: "{{ sound_file }}"
      media_content_type: music
    action: media_player.play_media
mode: single
icon: mdi:file-music-outline

So I have this script and I am using it to pass the media_player entity and the sound file. I tried it from dev tools and it worked fine BUT after the sound file ended it started playing another song. It's like it kicked off the last playlist I listened to using Music Assistant.

I then added it to the automation. I can launch the sound file using the script but I am unable to interact with assist while it is playing. Am I missing an important piece?

#

I've tried stopping the media via dev tools actions and setting the variable to idle to try and get out of the loop that I am presumably in but Assist is still not responsive.

I saw this with the previous version and the only thing I could think to do to clear it is a HA restart.

sterile juniper
uncut cosmos
#

Sorry. I fully understand now.

So I've put this in action and the sound does play as expected and will restart as expected. It will also stop playing after the current file finishes and the soundmachine attribute is set to idle.

The big problem is that when this is running from the automation I cannot use Assist during or after it starts. I think it may be executing that loop over and over again and the automation call never ends. I am unsure how to confirm this or really what the problem might be.

Can I bother you to take a look at the updated automation and script? I have not changed much from the original with the exception of the script call.

https://github.com/dinki/View-Assist/tree/viewassist-soundmachine/View_Assist_custom_sentences/Sound_Machine

GitHub

View Assist provides visual feedback for the Home Assistant Assist Voice Assistant - dinki/View-Assist

sterile juniper
#

In your loop, you need to add an if statement checking if the loop is the first one. IF it is, run the script, if it isn't, run the other stuff

#

or separate the script outside of the loop

uncut cosmos
# sterile juniper In your loop, you need to add an if statement checking if the loop is the first ...

Can you dumb this down for me. I'm not sure what's going on.

I'm not understanding how this will restart the playback when the sound file ends. Should I be looping that playback in the script and not the automation? If so, won't I still be in the situation where the automation won't end and thus I won't be able to call assist commands until it is no longer tied up with the orginal call to fire the option?

sterile juniper
#

you'd need 2 loops if you want it to restart. Or a second automation that performs the restart

uncut cosmos
#

Hmm.. okay.. Brainstorming. So these are the things I want it to do:

  • start a sound playing when the user calls for it (I do this with the script)
  • automation sets the attribute to playing while sound is playing
  • user can request automation to stop by voice or do a 'play for 10 minutes' thing and the event handling will cancel. The cancel is actually changing the attribute from 'playing' to 'idle'
  • the sound file should start playing again if it ends and the attribute is still set to playing.

Can I just do the play sound loop in the script?

I'm wondering if I should just do this:

User calls for soundmachine to start playing, the option to start sets the attribute to 'playing' and calls the play sound script.

The user calls for stopping the sound or the timer calls it. This sets the attribute to idle.

The problem is I don't have an easy way to trigger when that attribute changes without using a helper. All this typing is probably for nothing.

uncut cosmos
# sterile juniper you'd need 2 loops if you want it to restart. Or a second automation that perfo...

Me again. I'm trying to take this to the simplest level. I have the script from above that just takes the media_player and sound file and plays it.

From dev tools I am launching it like this:

action: script.turn_on
target:
  entity_id: script.play_sound_on_media_player
data: 
  variables:
    media_player: "media_player.viewassist_masterbedroom"
    sound_file: "https://cdn.pixabay.com/download/audio/2023/11/13/audio_133d124575.mp3?filename=rain-and-thunder-176105.mp3"

The sound plays as expected. I then try to stop it from dev tools using:

action: script.turn_off
data: {}
target:
  entity_id: script.play_sound_on_media_player

But it does not stop playing. What am I doing wrong?

#

Do I need to stop the media player and also stop the script?

sterile juniper
#

just like you'd stop any other media

#

you're still confusing 2 separate ways to do what I said

#

and we threw out the first one, yet you keep seeing that as an option

#

so, to recap:

  1. Run a script using script.turn_on
  2. All y our other stuff is monitoring and using the media_player like normal.
#

that's it

uncut cosmos
#

I don't think that's accurate. Perhaps I am still misunderstanding but what I was hoping to do is:

  • change the script to allow for a repeat variable and loop a sound if desired
  • write the sound machine automation so that when the user starts it it will call the script turn_on and the sound starts playing (this will allow the automation to continue)
  • add another trigger when the user asks to stop the sound machine that will call the script turn_off and the media playback stop/pause

Will that not work?

sterile juniper
#

No

#

that will not

#

if you want a script to run indefinitely until the user stops, you need to make a second script. You'd still need to stop the media AND stop the script

#

you can't just stop the script because the media will continue to play

uncut cosmos
#

Right. I can make a script that loops and plays a sound. I turn it on. When I want to stop it I turn it off and stop the media_player and it stops then right?

sterile juniper
#

your infinite loop script would need to watch for the media to stop playing, or you'd need to restart the media on a good transition

sterile juniper
#

because the script will continue to loop

#

or at least add a boolean that is checked, whatever you want to dream up

uncut cosmos
#

Sorry. I am missing what you are saying or don't understand what script.turn_off does. If I call it, it will not continue looping right? I understand that the media it is playing currently will continue but it will not play another instance when the playing one stops naturally right?

sterile juniper
#

ok, let me give you a scenario

#

you have a boom box

#

you tell your wife, "go press play every 3 minutes on the boom box, do not stop unless I tell you to stop"

#

you then come in and press the stop button

#

the music stops, but is your wife going to continue clicking the play button every 3 minutes?

#

you didn't tell her to stop

uncut cosmos
#

but if I tell her to stop and then the music doesn't play anymore after I manually stop it

#

and if she listens

sterile juniper
#

right

#

So 2 actions occured

#

you'd tell her to stop, and you'd click the stop button

#

i.e. Stop the script that's looping, stop the media

#

2 actions

#

telling your wife to stop pressing the play button will not stop the music from playing

uncut cosmos
#

Right. I think we are saying the same thing:

automation:

Option 1 Start playback

  • Call script turn_on

Option 2 stop playback

  • Call script turn_off
  • Call media stop/pause
sterile juniper
#

right

uncut cosmos
sterile juniper
#

I think I missed that reply

uncut cosmos
#

It is not worded the best either as I can see where one might think I was still trying to do within the script.

I think I got it now. Thanks again.

sterile juniper
#

Np

#

Your script that loops could simply be a while loop with a wait for trigger

#

wait for the media player to go from playing to stopped

#

or you can monitor the media end param

#

there's alot of things you can try

#

I'm not sure how you're going to get a seamless experience

uncut cosmos
#

It doesn't have to be seamless for my purposes. What I was trying before was the wait for mp to go to stopped.

Excellent. I think I'll be able to knock this one out with this information. Thanks

uncut cosmos
#
sequence:
  - repeat:
      sequence:
        - data:
            entity_id: "{{ media_player }}"
            media_content_id: "{{ sound_file }}"
            media_content_type: music
          action: media_player.play_media
      while:
        - condition: template
          value_template: ""{{ states(media_player) == 'idle'}}"
#

I can't figure out how to make this darn script loop. Tried a few different conditions and not working

#

Just noticed the double double quotes. Trying without one of them.

#

yeah that doesn't seem to matter. It plays once and only once where I'd like it to play indefinitely and be controlled using the turn_off method discussed above

uncut cosmos
#

I reread what you wrote above. I am having a hard time finding where to insert:

          - wait_for_trigger:
              - trigger: state
                entity_id: "{{ media_player }}"
                from: playing
                to: idle   
sterile juniper
#

a while repeat loop. just use {{ True }}

#

then your second action in the sequence is wait for trigger

#

You'll likely need to use a template trigger as you can't template other triggers

uncut cosmos
#
sequence:
  - repeat:
      sequence:
        - data:
            entity_id: "{{ media_player }}"
            media_content_id: "{{ sound_file }}"
            media_content_type: music
          action: media_player.play_media
      while:
        - condition: template
          value_template: "{{ true }}"

I've tried this. It plays once and then does not repeat.

sterile juniper
#

You don’t have the wait for trigger…

#

That action will block

#

Feel like we are going in circles here

uncut cosmos
#

I'm sorry. While I have a lot more experience writing these automations/scripts I still consider myself a novice especially when doing things I am unfamiliar with.

I see now that I glossed over the 'wait for trigger' and I guess am generally confused.

Am I doing what you suggested here:

a while repeat loop. just use {{ True }}

with what I have above but I am not doing this part:

then your second action in the sequence is wait for trigger

#

hold on. So second action will be wait for trigger which would be a template that would be true when the media player goes from playing to idle? It would wait there for that to happen and then it would repeat?

#

or even just equals idle?

sterile juniper
#
sequence:
- repeat:
    while: "{{ True }}"
    sequence:
    - action: script.turn_on
      target:
        entity_id: script.that_turns_on_media_player
      data:
        variables:
          media_player: "{{ media_player }}"
          media_content_id: "{{ sound_file }}"
          media_content_type: music
    - wait_for_trigger:
      - trigger: template
        value_template: "{{ is_state(media_player, 'idle') }}"
#

again, 2 scripts

#

1 script that loops

#

the other script that turns on the media player and plays the music

#

the script that plays the music must be called with script.turn_on

uncut cosmos
#

ohhh.. I am sure you told me all of this but I thought this could be done with one script. Thank you.

uncut cosmos
# sterile juniper again, 2 scripts

Back again to try your patience. I have this script that plays a sound. It works fine:

alias: Play Sound on Media Player
description: Play a sound file on a specific media player entity
fields:
  media_player:
    description: The media player entity to play the sound on
    example: media_player.living_room_speaker
    required: true
    selector:
      entity:
        domain: media_player
  sound_file:
    description: The URL or file path of the sound to play
    example: /local/sounds/alert.mp3
    required: true
    selector:
      text:
        multiline: false
sequence:
  - data:
      entity_id: "{{ media_player }}"
      media_content_id: "{{ sound_file }}"
      media_content_type: music
    action: media_player.play_media
  - action: media_player.clear_playlist
    target:
      entity_id: "{{ media_player }}"
mode: single
icon: mdi:file-music-outline

I can call it using this from dev tools:

action: script.turn_on
target:
  entity_id: script.play_sound_on_media_player
data:
  variables:
    media_player: media_player.viewassist_masterbedroom
    sound_file: https://cdn.pixabay.com/download/audio/2023/11/13/audio_133d124575.mp3
#

I then have this script that is supposed to loop:

alias: Play Sound on Media Player repeat
sequence:
  - repeat:
      while:
        - condition: template
          value_template: "{{ True }}"
      sequence:
        - action: script.turn_on
          target:
            entity_id: script.play_sound_on_media_player
          data:
            variables:
              media_player: "{{ media_player }}"
              media_content_id: "{{ sound_file }}"
              media_content_type: music
        - wait_for_trigger:
            - trigger: template
              value_template: "{{ is_state(media_player, 'idle') }}"
description: Play a sound file on a specific media player entity
fields:
  media_player:
    description: The media player entity to play the sound on
    example: media_player.living_room_speaker
    required: true
    selector:
      entity:
        domain: media_player
  sound_file:
    description: The URL or file path of the sound to play
    example: /local/sounds/alert.mp3
    required: true
    selector:
      text:
        multiline: false
mode: single
icon: mdi:file-music-outline

I call it using a very similar call:

action: script.turn_on
target:
  entity_id: script.play_sound_on_media_player_repeat
data:
  variables:
    media_player: media_player.viewassist_masterbedroom
    sound_file: https://cdn.pixabay.com/download/audio/2023/11/13/audio_133d124575.mp3

This produces no sound output.

#

I am not sure how to troubleshoot. Here is one of the traces. It looks like it is calling the play sound script fine but no sound is played:

sterile juniper
#

start looking at the play script script

#

that's the only area it would be broken because the loop is running it. Looks like it's waiting too

#

so the loop script is doing what it's meant to do

uncut cosmos
#

hmmm.. I am showing a service call via dev tools that works for the play_sound_on_mediaplayer script. That works. The loop one is passing the exact same values to that script and I THINK the service call shown in the screenshot is providing the same values that I am passing to the play_sound script but no audio starts. So weird.

sterile juniper
#

Look at the trace when it's ran from the loop script

uncut cosmos
#

Ah ha. Something goofy happening in the repeat script when calling the play one. It is not getting valid content so failing. Investigating

#

Can't say I'm familiar with that error though

#

I got it. Variable name was wrong in the repeat script. A bunch of chasing for nothing.

#

Yep confirming that this is working great. Here are the updated scripts in case someone else may stumble on this and want to use them:

alias: Play Sound on Media Player
description: Play a sound file on a specific media player entity
fields:
  media_player:
    description: The media player entity to play the sound on
    example: media_player.living_room_speaker
    required: true
    selector:
      entity:
        domain: media_player
  sound_file:
    description: The URL or file path of the sound to play
    example: /local/sounds/alert.mp3
    required: true
    selector:
      text:
        multiline: false
sequence:
  - data:
      entity_id: "{{ media_player }}"
      media_content_id: "{{ sound_file }}"
      media_content_type: music
    action: media_player.play_media
  - action: media_player.clear_playlist
    target:
      entity_id: "{{ media_player }}"
    enabled: true
mode: single
icon: mdi:file-music-outline
alias: Play Sound on Media Player repeat
sequence:
  - repeat:
      while:
        - condition: template
          value_template: "{{ True }}"
      sequence:
        - action: script.turn_on
          target:
            entity_id: script.play_sound_on_media_player
          data:
            variables:
              media_player: "{{ media_player }}"
              media_content_id: "{{ sound_file }}"
        - wait_for_trigger:
            - trigger: template
              value_template: "{{ is_state(media_player, 'idle') }}"
description: Play a sound file on a specific media player entity
fields:
  media_player:
    description: The media player entity to play the sound on
    example: media_player.living_room_speaker
    required: true
    selector:
      entity:
        domain: media_player
  sound_file:
    description: The URL or file path of the sound to play
    example: /local/sounds/alert.mp3
    required: true
    selector:
      text:
        multiline: false
mode: single
icon: mdi:file-music-outline