Play a local mp3 file on Alexa Echo dot

Hello

I’m using the “alexa_media_player” Custom Component for Alexa tts.

I can’t find a way to play my local audio (mp3 file from my computer) to a Amazon Echo ?
I tried the following, unsuccessfully :

The abc.mp3 file located here:
config/www/audio

- service: media_player.play_media
        data:
          entity_id: media_player.alex_echo_dot
          media_content_type: music
          media_content_id: http://XXX.duckdns.org:8123/local/audio/abc.mp3
1 Like

I have tried. From what I’ve read in Amazon’s documentation, I do not believe it is possible to play hosted sounds that way.

However, @florian.ec in the topic “Actionable Notifications via Alexa Media Player” says he was able to host an mp3 on an Amazon S3 server and get it to play at the beginning of an announcement. Maybe there is a work-around there, where you play a sound, but don’t include any text?

as i know “alexa_media_player” can’t play mp3 files from an url.

For the Alexa Actions https://github.com/keatontaylor/alexa-actions in my post its working to only put the SSML audio tag in without text. But Alexa Actions are not intend to only do tts, alexa is waiting for an response afterwards.

Additional note to “local” files. I tried this too (https://myurl.de:8123/local/mp3/file.mp3) and it doesn’t work. Even i have a letsencrypt cert amazon don’t like my files from there. Without https it is not possible at all.

1 Like

I have to correct myself here. It’s possible to use the nabucasa url with the SSML audio tag.
So this will work:

<audio src='https://abcdefghijklmnupkxyz.ui.nabu.casa/local/audio/somefile.mp3'/>

And even more i was able to play this mp3 on my Echo with the new Triggering a Skill > https://github.com/custom-components/alexa_media_player/wiki#triggering-a-skill-versions--270 function for Alexa Media Player and an additional Alexa Custom Skill.

I created a new Custom and Alexa-Hosted (Phython) Skill and choose Fact Skill as a template for that.

The only thing i changed in that Skill was the lambda_function.py file. You find my code at the End.
I have no Idea what i am doing in Phython, so this was more a try and error process to change the sample code to the desired outcome.

I did some editing on the LaunchRequest Handler, here you put the URL:

        # INSERT YOUR URL
        # This is what Alexa says when the Skill is launched. For this purpose i only insert the SSML audio tag 
        # with the Link to the mp3 file but you can also put some text before or after
        speech = "<audio src='https://abcdefghijklmnupkxyz.ui.nabu.casa/local/audio/somefile.mp3'/>"

If you have an Echo Show you can edit and display Text here:

        handler_input.response_builder.speak(speech).set_card(
        # INSERT YOUR TEXT
        # This may be shown on an Echo Show, change "My Title" and "File is now played" to whatever you whant
        SimpleCard(title="My Title", content="File is now played"))

You should now be able to invoke the Skill in an Automation:
(You can find the Skill ID here https://developer.amazon.com/alexa/console/ask under your Skill Name
View Skill ID)

  action:
    - service: media_player.play_media
      data: 
        entity_id: <Your Echo Device>
        media_content_id: <Skill ID>
        media_content_type: skill 

A good way to get familiar with creating a Custom Skill for this is to set up Alexa Actions from @keatontaylor https://github.com/keatontaylor/alexa-actions

Here is my Code in the lambda_function.py file:

# -*- coding: utf-8 -*-
"""Play Audio File on Skill Launch based on Simple fact sample app."""

import random
import logging
import json
import prompts

from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import (
    AbstractRequestHandler, AbstractExceptionHandler,
    AbstractRequestInterceptor, AbstractResponseInterceptor)
from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_core.handler_input import HandlerInput

from ask_sdk_model.ui import SimpleCard
from ask_sdk_model import Response

sb = SkillBuilder()
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


# Built-in Intent Handlers
class PlayFileHandler(AbstractRequestHandler):
    """Handler for Skill Launch and GetNewFact Intent."""

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return (is_request_type("LaunchRequest")(handler_input)) 
                
    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In PlayFileHandler")

        # INSERT YOUR URL
        # This is what Alexa says when the Skill is launched. For this purpose i only insert the SSML audio tag 
        # with the Link to the mp3 file but you can also put some text before or after
        speech = "<audio src='https://abcdefghijklmnupkxyz.ui.nabu.casa/local/audio/somefile.mp3'/>"

        handler_input.response_builder.speak(speech).set_card(
        # INSERT YOUR TEXT
        # This may be shown on an Echo Show, change "My Title" and "File is now played" to whatever you whant
        SimpleCard(title="My Title", content="File is now played")) 
        return handler_input.response_builder.response


class HelpIntentHandler(AbstractRequestHandler):
    """Handler for Help Intent."""

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In HelpIntentHandler")

        # get localization data
        data = handler_input.attributes_manager.request_attributes["_"]

        speech = data[prompts.HELP_MESSAGE]
        reprompt = data[prompts.HELP_REPROMPT]
        handler_input.response_builder.speak(speech).ask(
            reprompt).set_card(SimpleCard(
                data[prompts.SKILL_NAME], speech))
        return handler_input.response_builder.response


class CancelOrStopIntentHandler(AbstractRequestHandler):
    """Single handler for Cancel and Stop Intent."""

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return (is_intent_name("AMAZON.CancelIntent")(handler_input) or
                is_intent_name("AMAZON.StopIntent")(handler_input))

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In CancelOrStopIntentHandler")

        # get localization data
        data = handler_input.attributes_manager.request_attributes["_"]

        speech = data[prompts.STOP_MESSAGE]
        handler_input.response_builder.speak(speech)
        return handler_input.response_builder.response


class FallbackIntentHandler(AbstractRequestHandler):
    """Handler for Fallback Intent.

    AMAZON.FallbackIntent is only available in en-US locale.
    This handler will not be triggered except in that locale,
    so it is safe to deploy on any locale.
    """

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_intent_name("AMAZON.FallbackIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In FallbackIntentHandler")

        # get localization data
        data = handler_input.attributes_manager.request_attributes["_"]

        speech = data[prompts.FALLBACK_MESSAGE]
        reprompt = data[prompts.FALLBACK_REPROMPT]
        handler_input.response_builder.speak(speech).ask(
            reprompt)
        return handler_input.response_builder.response


class LocalizationInterceptor(AbstractRequestInterceptor):
    """
    Add function to request attributes, that can load locale specific data.
    """

    def process(self, handler_input):
        locale = handler_input.request_envelope.request.locale
        logger.info("Locale is {}".format(locale))

        # localized strings stored in language_strings.json
        with open("language_strings.json") as language_prompts:
            language_data = json.load(language_prompts)
        # set default translation data to broader translation
        if locale[:2] in language_data:
            data = language_data[locale[:2]]
            # if a more specialized translation exists, then select it instead
            # example: "fr-CA" will pick "fr" translations first, but if "fr-CA" translation exists,
            # then pick that instead
            if locale in language_data:
                data.update(language_data[locale])
        else:
            data = language_data[locale]
        handler_input.attributes_manager.request_attributes["_"] = data


class SessionEndedRequestHandler(AbstractRequestHandler):
    """Handler for Session End."""

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In SessionEndedRequestHandler")

        logger.info("Session ended reason: {}".format(
            handler_input.request_envelope.request.reason))
        return handler_input.response_builder.response


# Exception Handler
class CatchAllExceptionHandler(AbstractExceptionHandler):
    """Catch all exception handler, log exception and
    respond with custom message.
    """

    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        return True

    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        logger.info("In CatchAllExceptionHandler")
        logger.error(exception, exc_info=True)

        handler_input.response_builder.speak(EXCEPTION_MESSAGE).ask(
            HELP_REPROMPT)

        return handler_input.response_builder.response


# Request and Response loggers
class RequestLogger(AbstractRequestInterceptor):
    """Log the alexa requests."""

    def process(self, handler_input):
        # type: (HandlerInput) -> None
        logger.debug("Alexa Request: {}".format(
            handler_input.request_envelope.request))


class ResponseLogger(AbstractResponseInterceptor):
    """Log the alexa responses."""

    def process(self, handler_input, response):
        # type: (HandlerInput, Response) -> None
        logger.debug("Alexa Response: {}".format(response))


# Register intent handlers
sb.add_request_handler(PlayFileHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelOrStopIntentHandler())
sb.add_request_handler(FallbackIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())

# Register exception handlers
sb.add_exception_handler(CatchAllExceptionHandler())

# Register request and response interceptors
sb.add_global_request_interceptor(LocalizationInterceptor())
sb.add_global_request_interceptor(RequestLogger())
sb.add_global_response_interceptor(ResponseLogger())

# Handler name that is used on AWS lambda
lambda_handler = sb.lambda_handler()

5 Likes

Maybe this will be useful (see end of the reply post):

I did the same process, I created the skill as you guided.

I played the skill but did not play the audio. I put a text before and the execution functioned. text.

Example:

speech = "Audio test <audio src='https://myhomeassistant.duckdns.org/local/audios/mp3/police.mp3' />"

Audio text - OK
Tag audio MP3 - NO

Got it. I needed to convert the MP3. I used the Audacity program and it worked.

https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html#audio

Thank you for this, by the way. Now my echo plays the AOL “You’ve Got Mail” clip when my mailbox is opened and plays a voice clip of my local team’s announcer when my team scores!

Is this still functioning? I’ve been pulling my hair out for the past 4 hours trying to get this to function :frowning:

@HA-Gecko

Got it working. Couple things to note, hope it may help.

Make sure you export the mp3 via amazons standards: https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html#h3_converting_mp3

Make sure the mp3 is LESS THAN 240 seconds per Amazons rules:

Make sure after you create the skill in your web browser than you open up your alexa app and enable it

Test the skill first with just speech to verify you got that much working, before trying to play mp3. Change the lambda.py to something like the following:

If hosting the mp3 on your homeassistant device, make sure you can access the URL in just a web browser. If it doesnt play in web browser, its not gonna play through your echo

1 Like

So I followed the set up instructions on the GitHub Wiki and have an automation working just fine that asks to re-arm my GE Concord 4 panel after it has been disarmed for 5 minutes (just set that up as an example). It works as expected.

But I can’t get MP3’s to work as I would like. I created a second skill as you mentioned in the other thread and cloned the script and changed the name.

I’m using Node Red to trigger it. I can get the MP3 to play, but I have a few different MP3’s I’m messing with. After I play the first MP3, the script seems to ignore the audio source “text” in subsequent triggers. I can only get it to play another MP3 if I restart HA.

I also have the same issue using Developer Tools/Services. Whatever MP3 I play first, keeps playing on subsequent calls of the script, regardless of what I’m trying to play.

Any ideas?

Please show your script code.

Sure thing. This is out of my scripts.yaml The first one is direct from the GitHub Wiki. The second and third are the ones I’m trying to use to play mp3’s. I though perhaps I needed a different script for each, so for testing I created the third one, but both refer to the same custom skill.

activate_alexa_actionable_notification:
  description: 'Activates an actionable notification on a specific echo device'
  fields:
    text:
      description: 'The text you would like alexa to speak.'
      example: 'What would you like the thermostat set to?'
    event_id:
      description: 'Correlation ID for event responses'
      example: 'ask_for_temperature'
    alexa_device: 
      description: 'Alexa device you want to trigger'
      example: 'media_player.bedroom_echo'
  sequence:
    - service: input_text.set_value
      data:
        entity_id: input_text.alexa_actionable_notification
        value: '{"text": "{{ text }}", "event": "{{ event_id }}"}'
    - service: media_player.play_media
      data:
        entity_id: "{{ alexa_device }}"
        media_content_type: skill
        media_content_id: amzn1.ask.skill.55914193-ef5f-4bed-91c8-23bf46391cba
activate_alexa_mp3_small_step:
  alias: Alexa MP3 Small Step Script
  description: 'Activates an actionable notification on a specific echo device'
  fields:
    text:
      description: 'The text you would like alexa to speak.'
      example: 'What would you like the thermostat set to?'
    event_id:
      description: 'Correlation ID for event responses'
      example: 'ask_for_temperature'
    alexa_device: 
      description: 'Alexa device you want to trigger'
      example: 'media_player.bedroom_echo'
  sequence:
    - service: input_text.set_value
      data:
        entity_id: input_text.alexa_actionable_notification
        value: '{"text": "{{ text }}", "event": "{{ event_id }}"}'
    - service: media_player.play_media
      data:
        entity_id: "{{ alexa_device }}"
        media_content_type: skill
        media_content_id: amzn1.ask.skill.8c3930db-5594-4b25-992b-fa7974394978
activate_alexa_mp3_dog_bark:
  alias: Alexa MP3 Dog Bark Script
  description: 'Activates an actionable notification on a specific echo device'
  fields:
    text:
      description: 'The text you would like alexa to speak.'
      example: 'What would you like the thermostat set to?'
    event_id:
      description: 'Correlation ID for event responses'
      example: 'ask_for_temperature'
    alexa_device: 
      description: 'Alexa device you want to trigger'
      example: 'media_player.bedroom_echo'
  sequence:
    - service: input_text.set_value
      data:
        entity_id: input_text.alexa_actionable_notification
        value: '{"text": "{{ text }}", "event": "{{ event_id }}"}'
    - service: media_player.play_media
      data:
        entity_id: "{{ alexa_device }}"
        media_content_type: skill
        media_content_id: amzn1.ask.skill.8c3930db-5594-4b25-992b-fa7974394978
1 Like

Are you trying to play your own mp3 or one from the Alexa soundlib?

I’m trying to play my own. I’m testing with two different. The One Small Step, is hosted by someone else on S3 storage and the Dog Barking MP3 is hosted on my own S3 storage.

Using Developer Tools/Services I can use either script.activate_alexa_mp3_small_step or script.activate_alexa_dog_bark

In the Service Data I put this to play the small step MP3

text: "<audio src='https://s3.amazonaws.com/cdn.dabblelab.com/audio/one-small-step-for-man.mp3'/>"
event_id: actionable_notification_small_step
alexa_device: media_player.office_echo

or this to play the dog barking mp3

text: "<audio src='https://dbs179.s3.amazonaws.com/audio/labrador-barking.mp3'/>"
event_id: actionable_notification_dog_barking
alexa_device: media_player.office_echo

All of that works fine, but once I play either of them, no matter which script.activate I choose, or what I put in the Service Data, my Echo still plays the first mp3 I played. A restart of Home Assistant clears it up, until I play either again. Also it seems that after a certain amount of time, maybe more than 15 minutes at a minimum I can play the other MP3 just fine, but then the cycle repeats.

Use data_template (otherwise you are submitting a constant value).
Also suggest “event”: “none” in your value.

I’m not super familiar with data_template, but I tried this.

data_template:
  text: "<audio src='https://dbs179.s3.amazonaws.com/audio/labrador-barking.mp3'/>"
  event: "none"
  alexa_device: media_player.office_echo

In that format I get an error “Alexa MP3 Dog Bark Script: Error executing script. Invalid data for call_service at pos 2: not a valid value for dictionary value @ data[‘entity_id’]”

If I put it in this format

data_template:
text: "<audio src='https://dbs179.s3.amazonaws.com/audio/labrador-barking.mp3'/>"
event: "none"
alexa_device: media_player.office_echo

I get the same behavior, first MP3 plays as expect, following tries play the first played. I’m sure it something I’m doing wrong, thanks for the help so far, I appreciate it!!

Working scripts examples. Put in your S3, your skill, your echoname, and your mp3 file.

alexa_play_mp3:
  sequence:
    - service: input_text.set_value
      data_template:
        entity_id: input_text.alexa_actionable_notification
        value: >
          {"text": "<audio src='https://yourfile.s3.amazonaws.com/{{mp3}}'/>", "event": "none"}
    - service: media_player.play_media
      data_template:
        entity_id: >
          {% if alexa_device %}
            {{ alexa_device }}
          {% else %}
            media_player.yourechodot
          {% endif %}
        media_content_type: skill
        media_content_id: amzn1.ask.skill.e4280a53-blahblahblah

alexa_knocking:
  sequence:
    - service: script.alexa_play_mp3
      data_template:
        mp3: knocking.mp3

alexa_think:
  sequence:
    - service: script.alexa_play_mp3
      data_template:
        mp3: think.mp3
1 Like

I get “There was a problem with the requested skills response”… :frowning: I edited the script names, but using your example script names, I should be able to just call the “alexa_knocking” or “alexa_think” scripts right from Services with no Service Data correct?

activate_alexa_actionable_notification:
  description: 'Activates an actionable notification on a specific echo device'
  fields:
    text:
      description: 'The text you would like alexa to speak.'
      example: 'What would you like the thermostat set to?'
    event_id:
      description: 'Correlation ID for event responses'
      example: 'ask_for_temperature'
    alexa_device: 
      description: 'Alexa device you want to trigger'
      example: 'media_player.bedroom_echo'
  sequence:
    - service: input_text.set_value
      data:
        entity_id: input_text.alexa_actionable_notification
        value: '{"text": "{{ text }}", "event": "{{ event_id }}"}'
    - service: media_player.play_media
      data:
        entity_id: "{{ alexa_device }}"
        media_content_type: skill
        media_content_id: amzn1.ask.skill...........


alexa_play_mp3:
  sequence:
    - service: input_text.set_value
      data_template:
        entity_id: input_text.alexa_actionable_notification
        value: >
          {"text": "<audio src='<audio src='https://dbs179.s3.amazonaws.com/audio/{{mp3}}'/>", "event": "none"}
    - service: media_player.play_media
      data_template:
        entity_id: >
          {% if alexa_device %}
            {{ alexa_device }}
          {% else %}
            media_player.office_echo
          {% endif %}
        media_content_type: skill
        media_content_id: amzn1.ask.skill..........


alexa_play_barking:
  sequence:
    - service: script.alexa_play_mp3
      data_template:
        mp3: labrador-barking.mp3


alexa_play_siren:
  sequence:
    - service: script.alexa_play_mp3
      data_template:
        mp3: siren_noise.mp3
1 Like

Your Alexa_play_mp3 points to your S3 site. If I understand your previous messages, the only file you have at that site is the dog barking file. I did not send you my actual S3 site url, so it is not possible for you to play knocking.mp3 or think.mp3.

If you are saying you have a script (x) that sets the mp3 parameter value and runs script.alexa_play_mp3, then yes. You should be able to run script X from Services without service data.