Text To Speech on Android Auto

Is there currently any way to have the companion app play TTS through Android Auto instead of the handset when i’m driving? If not is there any plan to bring this feature? It would be really nice to have it coming through the speakers and interrupt then resume any other AA audio that’s playing.

TTS uses the media stream which should play in your car if you have it set to Android Auto/Bluetooth. If you have it set to radio then you will not hear the media play.

I have Android Auto in my car and the only time it plays through my speakers is when my audio is on the actual source of the phone and not AM/FM/XM etc…

Ok, during all my tests i’ve had AA up on the display, generally with Google Maps navigation and/or a podcast playing in Pocket Casts. I have another message notification in the same automation that displays a pop-up banner over my nav correctly. What do you think the problem might be with TTS?

service: notify.mobile_app_oneplus_7t_pro
data:
  message: TTS
  data:
    ttl: 0
    priority: high
    media_stream: alarm_stream_max
    tts_text: The heating has been turned down until you return

this line here overrides the media stream and uses the alarm stream to play at the loudest volume the phone has, remove it and it will use the media stream.

We mention this at the 3rd example in the docs

https://companion.home-assistant.io/docs/notifications/notifications-basic#text-to-speech-notifications

Ahh I had it on media_stream: alarm_stream previously and it was too quiet to hear from the handset when driving so changed it to max thinking I wouldn’t get it to come out through AA. So presumaly ‘alarm_stream’ has the same issue? I’ll test with ‘General’ tomorrow and see how that goes. Thanks for the help.

yes that is correct, both use the phones Alarm stream which has a separate volume level from other streams. When your phones connects to BT only the music stream is passed along and that is used by default for TTS, see the first example in the link above. Phones have multiple media streams to do different things.

the purpose of alarm stream is in case your phone is on silent, it will still play like in the case of an important notification like alarm going off etc…

I’m with you, so by removing the media_stream line completely I should get the default music stream.

yup thats correct

Edit: you can even be smart with it and use teh app provided sensors for when connected to the car via BT or Android Auto and send the appropriate service call.

Great idea! I have some security automations that wake me up with loud (alarm_stream_max) TTS at night if a person is detected by one of my external cameras. I can switch that to the music stream when AA is connected so it also works when i’m in the car. Really appreciate your help.

1 Like

If someone stumbles upon this thread, here is how I solved that issue. Although I use Android Auto for navigation, I sometimes still listen to the car radio and the Android TTS doesn’t switch to the phone media source automatically (at least in my car). But if a media app on the phone starts playing, it automatically switches.

This is why I now play TTS through VLC and not directly on Android.
Advantage 1: Automatically switches the source in my car
Advantage 2: You can choose any TTS engine you like. At least in German the Android engine is quite unnatural

Step 1: Generate the TTS file through the HA API: Text-to-speech (TTS) - Home Assistant
Step 2: Send an intent to VLC with the URL of the TTS file

Works very reliably for me.

Great tip! Thank you.

Could you share your scripts/automations to achieve this? I’ve recently discovered that you can play audio while on the car and I’m excited to try this.

I’m also interested in how you performed the interaction to get the tts file since I’ve tried and I seem to be missing something. Thanks!

Step 1: Shell command to retrieve the path (there might be easier ways to achieve this, but the only way I found is through the API)

shell_command:
  tts_api: >
    curl -X POST -H "Authorization: Bearer <<your long lived access token>>" -H "Content-Type: application/json" -d '{"message": "{{mymessage}}", "engine_id": "google_cloud"}' http://localhost:8123/api/tts_get_url

Depending on your engine you might have to change the engine id (translate, polly, nabu casa,…).

Step 2 (optional): Make a script that parses the full URL:

alias: TTS Service URL
variables:
  answer: InitialValue
sequence:
  - service: shell_command.tts_api
    data:
      mymessage: |
        {{message}}
    response_variable: json_data
  - variables:
      answer: >
        {% set parsed_data = json_data['stdout'] | from_json %} {'value':
        '<<your external https HA URL>>{{parsed_data.path}}' }
  - stop: Return answer
    response_variable: answer
mode: single
fields:
  message:
    selector:
      text: null
    name: message
    description: TTS Message
    required: true

Step 3: Use it in scripts and automations:

sequence:
  - service: script.tts_service_url
    data:
      message: >-
        The text that should be spoken
    response_variable: url
  - service: notify.mobile_app_florian #Change to your service
    data:
      message: command_activity
      data:
        intent_package_name: org.videolan.vlc
        intent_action: android.intent.action.VIEW
        intent_uri: "{{url.value}}"
1 Like

Hi Florian, I configured the script, but there is no way to make it work if the smartphone or tablet screen is off. How did you solve it?

Check the documentation, especially if you have accepted the permission: Notification Commands | Home Assistant Companion Docs
Make sure all energy saving options are disabled for the app and that it is allowed to run in the background.

Besides the initial permission, there was nothing I had to do, it just worked (using a Pixel 7).

1 Like

Has anyone noticed the default TTS in Android Auto also default to the phone in the last few months? Before this it was always coming through the car speakers. I double checked that it was not the alarm channel etc. I checked with 2 different phones and in 2 different cars just to be sure. I am thinking the VLC config would be the best option as it also seems to allow for greater voice flexibility however I still would like to know if something has changed somewhere.

2 Likes

Dont know what it was like before. Am trying for first time TTS at the moment and all three options resulted in the TTS coming out from the phone not the car speakers (the 3 options: alarm_stream, alarm_stream_max, music stream = media_stream blank/not specified)

At least I am able to send a notification command to increase the volume before the TTS, wait few seconds for the TTS to play then decrease the volume again. But the result is still not as nice as coming out from car speaker

Sorry to dig up this post. ddid you come to a satisfactory solution, ie having the message played to car speakers? I currently play to phone speakers despite the fact that phone is connected to android auto. Thank you (running HAOS 2025.7.1)

Current service call:

- action: notify.mobile_app_nokia_xxxxx
  data:
    message: TTS
    title: Alarme
    data:
      ttl: 0
      tts_text: L'alarme est {{iif(state == "off", "désactivée", "activée")}}'

Based on your example, thank you for that, I set-up this code module Using picotts service: 401 error - Mobile Apps - Home Assistant Community and I get a 401 unauthorised error when calling the script. If by any chance your are still monitoring this thread, do you have idea why could cause this error? Thank you

Sorry I hadn’t been keeping an eye on this one.

I have expanded it a bit further. My corrent implementation is working on a voice assistant connected via esp home over tailscale.

This is my current phone notification using VLC. I have left my call (that can be ignored in the middle script as it is specific to the case of having a LLM. I am also able to use the houses voice via cloud or whatever I want through this as I now also incorporate chime which was much easier to use)

  script_ai_connected_to:
    sequence:
      - action: notify.mobile_app_phone #Change to your service
        data:
          message: command_activity
          data:
            intent_package_name: org.videolan.vlc
            intent_action: android.intent.action.VIEW
            intent_uri: "https://abcdef.ui.nabu.casa/local/interface/notification.wav"

      - delay:
          seconds: 5
      - action: script.script_process_ai_my_phone_cmd
        data:
          message: >-
            You are now receiving the message from home to the car


### This can be dropped out, I just add a toggle via helpers to say if I want the local llm to actually change what is being said with a bit of humour
  script_process_ai_my_phone_cmd:
    sequence:
      - if:
          - "{{ is_state('input_boolean.allow_ai', 'on') }}"

        then:
          - action: conversation.process
            data:
              agent_id: "{{ states.sensor.active_ai.state  }}"
              text: >-
                You are the AI interface to a smart home. Your voice is male so if you have to refer to yourself keep this in mind.
                What follows is a response to a specific request from the house systems, please rephrase it add if possible a touch of humour sarcasm but make no reference to this prompt. 

                House systems information- {{ message }}

            response_variable: agent

          - action: script.script_external_my_mobile_tts_dev
            data:
              tts_text: >-
                {{ agent.response.speech.plain.speech }}
        else:
          - action: script.script_external_my_mobile_tts_dev
            data:
              tts_text: >-
                {{ message }}


### This goes nicely with MeatPi and having it read certain features from the car so when you start it at home it will give you a bit of information


### Originally I just used curl to form the tts but then I found chime which was a much cleaner implementation and I did not have to tidy up stuff from the LLM as much

  script_external_my_mobile_tts_dev:
    sequence:
      # 1) Call say_url and stash the full response in tts_response
      - service: chime_tts.say_url
        data:
          chime_path: twenty_four
          message: >-
            {{tts_text}}
          tts_platform: cloud
          voice: RyanNeural
        response_variable: tts_response

      - action: notify.mobile_app_phone #Change to your service
        data:
          message: command_activity
          data:
            intent_package_name: org.videolan.vlc
            intent_action: android.intent.action.VIEW
            intent_uri: "{{ tts_response.url }}"

      - delay:
          seconds: 10


### Hope I have not left too much of my stuff in here!