How to create a generic TTS notification script that accepts variables

I’m trying to build a generic TTS notification script that will play a message to one of my Google Nest Minis, Sonos speakers, or a pre-defined group of them. The script should take two parameters: the text of the message to convert to speech and the media player entity or entities for playing the message.

My plan is to use the generic notification TTS script with numerous other automations to create more complex notification messages based on templates combining sensor, weather, and other data. For example, I want to create a good morning message that’s customized to me with a weather forecast, traffic conditions, calendar information, etc. that will play on a Google Nest Mini when I first enter the kitchen in the morning.

I am a Home Assistant Coud subscriber and have confirmed that I can do basic TTS to any of my smart speakers using the tts.speak action.

I have searched and searched and have so far been unable to create what seems like it should be a pretty simple script. Here’s what I have to this point:

alias: Play Custom Audio Notification
sequence:
  - action: tts.speak
    metadata: {}
    data:
      message_text: "{{ message_text }}"
      message_target: "{{ message_target }}"
    target:
      entity_id: tts.home_assistant_cloud
description: >-
  Play a custom audio notification after accepting parameters for message and
  target.
icon: mdi:bell-ring-outline
fields:
  message_text:
    selector:
      text: null
    name: message_text
    description: Text of message to be spoken
    required: true
  message_target:
    selector:
      entity: {}
    name: message_target
    description: Entity of media player to play message
    required: true

I created a simple automation that is triggered from an input boolean helper:

alias: TTS test
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.dummy
    from: null
    to: "on"
condition: []
action:
  - action: script.play_custom_audio_notification
    data:
      message_text: This is a message from Home Assistant.
      message_target: media_player.sonos_roam
mode: single

Nothing happens when I trigger the automation, and I get the following in the log:

2024-09-25 18:08:45.342 ERROR (MainThread) [homeassistant.components.script.play_custom_audio_notification] Play Custom Audio Notification: Error executing script. Invalid data for call_service at pos 1: extra keys not allowed @ data['message_text']
2024-09-25 18:08:45.343 ERROR (MainThread) [homeassistant.components.automation.tts_test] TTS test: Error executing script. Invalid data for call_service at pos 1: extra keys not allowed @ data['message_text']
2024-09-25 18:08:45.344 ERROR (MainThread) [homeassistant.components.automation.tts_test] Error while executing automation automation.tts_test: extra keys not allowed @ data['message_text']

I’m sure this is not nearly as complicated as I’m trying to make it. :slight_smile: Can anyone offer some guidance on where to go from here?

I do that with a script it gets the message from an input text helper and sends it to the speaker selected in a dropdown list. Here is my code for that.

alias: Send message
sequence:
  - metadata: {}
    data:
      cache: false
      entity_id: >-
        {% if is_state('input_select.speaker_list', 'House') %}
        media_player.roosevelt_house {% else %} {% if
        is_state('input_select.speaker_list', 'Living Room') %}
        media_player.living_room_speaker {% else %} {% if
        is_state('input_select.speaker_list', 'Office') %}
        media_player.living_room_display {% else %} {% if
        is_state('input_select.speaker_list', 'Master Bedroom') %}
        media_player.master_bedroom {% else %} {% if
        is_state('input_select.speaker_list', 'Moonies Room') %}
        media_player.living_room_speaker {% else %} media_player.kitchen_speaker
        {% endif %} {% endif %} {% endif %} {% endif %} {% endif %}
      message: "{{ states( 'input_text.speak_text' ) }}"
    action: tts.cloud_say
mode: single
icon: mdi:speaker-wireless

I trigger it with a button on my dashboard

type: entities
entities:
  - entity: input_select.speaker_list
  - entity: input_text.speak_text
    name: Text to broadcast
footer:
  type: buttons
  entities:
    - entity: script.send_message
      name: Broadcast Now
      show_icon: true
      show_name: true
show_header_toggle: false
state_color: false
title: Cast a Message

I do something similar, but with choose:

alias: Willow TTS response
sequence:
  - alias: Speak response on appropriate speakers
    choose:
      - conditions:
          - condition: state
            entity_id: sensor.speakers_to_use
            state: Bedroom
        sequence:
          - target:
              entity_id: media_player.bedroom
            data:
              announce: true
              media_content_id: |
                media-source://tts/amazon_polly?message="{{ tts_sentence }}"
              media_content_type: music
              extra:
                volume: 45
            action: media_player.play_media
        alias: If most likely room is bedroom

There’s a similar choose option for each room, and sensor.speakers_to_use is updated by Bluetooth proxies as I move round the house, so (in theory) announcements should always come on speakers in the room where I am (they’re Sonos).

tts_sentence comes from intents, but you could pass parameters from an automation. There’s a section in the docs about it:

What the error messages are trying to convey is that you are using invalid keys in your tts.speak action.

The correct keys are message and media_player_entity_id:

  - action: tts.speak
    metadata: {}
    data:
      message: "{{ message_text }}"
      media_player_entity_id: "{{ message_target }}"
    target:
      entity_id: tts.home_assistant_cloud

Yes! That was the problem. Thanks, @Didgeridrew.

For the sake of completeness, here’s the final working script:

alias: Play Custom Audio Notification
sequence:
  - action: tts.speak
    metadata: {}
    data:
      message: "{{ message_text }}"
      media_player_entity_id: "{{ message_target }}"
    target:
      entity_id: tts.home_assistant_cloud
description: >-
  Play a custom audio notification after accepting parameters for message and
  target.
icon: mdi:bell-ring-outline
fields:
  message_text:
    selector:
      text: null
    name: message_text
    description: Text of message to be spoken
    required: true
  message_target:
    selector:
      entity: {}
    name: message_target
    description: Entity of media player to play message
    required: true

The TTS test automation I shared above now works perfectly.