TTS with SSL on

Hello

I have enabled SSL for my home assistant (on raspberry pi 4). here is my tts in config file

image

But this way nothing plays on my google mini. I have used https instead of http, but still nothing.
Then i found out url that is generated once i send a command to my mini, is accesible if i use only https. I am talking about this url:

https://192.168.1.28:8123/api/tts_proxy/a110e6b9a361653a042e3f5dfbac4c6105693789_en_-_google_translate.mp3 -> plays in chrome

http://192.168.1.28:8123/api/tts_proxy/a110e6b9a361653a042e3f5dfbac4c6105693789_en_-_google_translate.mp3 -> doesnt plays in chrome

but i guess these local url cant be played by the mini since it cant accept the ssl certificate (or something like that)

Then i used external url as my base url and voila! it works!

So my question is, when i enable SSL on my HA, i have to use tts with external url? in the event of an internet outage, my google mini wont play anything! So what is the way out of here?

1 Like

I’ve come to this realization as well but then kind of said duh to myself, Google Home is cloud required.
As far as I know that’s the deal but I could be wrong.
Maybe the nest mini and other local devices will support local TTS whenever Google stops dragging their heels.

I’ve got the same problem. I’ve had some automations with TTS set up for some time and they stopped playing on my Google Home Mini somewhere between November 2019 and March 2020.

The google_translate TTS service seems to work fine and a mp3 is saved in the tts cache folder, however the file is not played back on the media player.

I’ve debugged the problem and found a solution.

TL;DR: There are three solutions below

My HA is configured with SSL certs from LetsEncrypt. Port 8123 is forwarded and I can access HA from outside my network using my.domain.tld over HTTPS. I use AdGuard Home with DNS rewrite to rewrite my.domain.tld to 192.168.1.100 so I can use my domain to access HA from everywhere.

Here are some tests I’ve run using the Services in Developer Tools.
I’ve run tests with my router configured to use AdGuard Home as DNS as well as using my ISP assigned DNS with the same results.

Normal setup with SSL enabled.

http:
  ssl_certificate: /ssl/fullchain.pem
  ssl_key: /ssl/privkey.pem

# Local HTTPS content with IP NAY
# It does NOT work via media_player.play_media
# It works via Chrome after manually proceeding past the Cert/Privacy error
entity_id: media_player.living_room_speaker
media_content_id: 'https://192.168.1.100:8123/local/audio/service-bell_daniel_simion.wav'
media_content_type: music

# Local HTTPS content with domain NAY
# It does NOT work via media_player.play_media
# It works via Chrome
entity_id: media_player.living_room_speaker
media_content_id: 'https://my.domain.tld:8123/local/audio/service-bell_daniel_simion.wav'
media_content_type: music

# External HTTPS content with domain YAY
# It works via media_player.play_media
# It works via Chrome
entity_id: media_player.living_room_speaker
media_content_id: 'https://www.external.tld/service-bell_daniel_simion.wav'
media_content_type: music

So I disabled SSL in configuration.yaml

http:
# ssl_certificate: /ssl/fullchain.pem
# ssl_key: /ssl/privkey.pem

# Local HTTP content with IP YAY
# It works! via media_player.play_media
# It works! via Chrome
entity_id: media_player.living_room_speaker
media_content_id: 'http://192.168.1.100:8123/local/audio/service-bell_daniel_simion.wav'
media_content_type: music

# Local HTTP content with domain NAY
# It does NOT work via media_player.play_media
# It works via Chrome
entity_id: media_player.living_room_speaker
media_content_id: 'http://my.domain.tld:8123/local/audio/service-bell_daniel_simion.wav'
media_content_type: music

# External HTTPS content with domain YAY
# It works via media_player.play_media
# It works via Chrome
entity_id: media_player.living_room_speaker
media_content_id: 'https://www.external.tld/service-bell_daniel_simion.wav'
media_content_type: music

Since test results with and without AdGuard Home are the same, I’m ruling it out as a cause.
External domains works (both HTTP and HTTPS) so it doesn’t seem to be the cause.

HTTP with IP works.
HTTPS with IP doesn’t work (probably because the SSL cert is for my.domain.tld so there’s a privacy/safety warning)

HTTP and HTTPS with domain does NOT work. Which leads me to think that the domain does not resolve. I use AdGuard Home as DNS to resolve my.domain.tld locally.

Conclusion

my.domain.tld is sent to the Google Home Mini.
The Home Mini, I’ve read, has Google DNS coded into it, so it doesn’t use AdGuard Home to resolve (and rewrite) my domain like all the other devices on my network, but instead resolves using Google DNS which will resolve to my public (router) IP.

Once it’s resolved, it tries to play the file, but instead of requesting it from my HA instance it now requests it from my public (router) IP and therefore 404s.

Solution 1 - An local subdomain

I configured a subdomain local.my.domain.tld pointing to my HA local IP (192.168.1.100) and volla, my tests pass. Note that I have a wildcard certificate for my.domain.tld, so I can have subdomains using the same certificate.

This allowed me to get TTS working by setting the tts base_url to this internal sub-domain.

# Text to speech
tts:
  - platform: google_translate
    base_url: !secret tts_base_url # local.my.domain.tld
    service_name: google_translate_say

I hope this helps someone.

Why this setup?

Now I asked myself why I have this problem? The answer lies in my setup. When I started using HA, I wanted to access HA in my phone browser using the same my.domain.tld everywhere I go. It’s works fine when requesting from outside my LAN, but on the LAN the domain resolves and points to my router. To get around this I used DNS rewriting to point the domain to my HA instance when resolving it internally.

HA now has an app and the app can be configured with a URL and Internal Connection URL which sort of takes care of things. It automatically switches between the (local) IP and (public) domain, I guess, based on whether I’m connected to my Home Network WiFi SSIDs or not. This works great when you’re just using the app, but, if you want to use your phone’s browser or your laptop, you still have to go to either https://192.168.1.100 (local) or https://my.domain.tld (public), which I find somewhat annoying.

Alternative Solutions

Solution 2 - Use a reverse proxy

There are alternative solutions to the problem. If you don’t use SSL or if you make use of a reverse proxy for SSL, you can set your !secret tts_base_url to just use your IP (http://192.168.1.100:8123) over HTTP. The Home Mini doesn’t need to resolve this, so it plays fine. In my case, since I have configured my HA http to use SSL and not a reverse proxy, I cannot reach https://192.168.1.100:8123 without getting an SSL warning, because I don’t have a certificate for the IP.

Solution 3 - Change the DNS on your Google Home

I searched around and found that one can change the DNS on the Google Home. See this Google support page on changing the DNS of the speaker. Updating the DNS to your router IP should make it use the same DNS as the other devices on your network (AdGuard Home in my case). Update: I can’t get the Google WiFi app to detect my speaker, maybe it only works if you also have Google WiFi.

Solution 4 - Block Google DNS

Some have suggested blocking 8.8.8.8 and 8.8.4.4 as a solution. I haven’t tested this, but it could be a viable solution for some.

Final note

Recently v0.110 HA added Internal URL and External to Configuration > General. Setting the internal URL to local.my.domain.tld should solve lots of problems.

2 Likes

Thanks for your detailed information.

I dont understand one thing. In solution 1, when you set a subdomain, google mini will not try to resolve it by google dns? then how is it working in this case?

With Solution 1 I have 2 domains configured…

my.domain.tld # My public IP
local.my.domain.tld # My internal HA IP e.g. 192.168.1.100

local.my.domain.tld is currently only used for the tts.base_url, but it could be used in other places too.

The Mini still resolves the domain using Google DNS, but instead of resolving to my public IP, it resolves to my internal HA IP.

How did you create the certificate with wildcard? i used duck dns addon to create them, is it enough?

Not sure how it will work with DuckDNS. The LetsEncrypt addon allows you to do *.domain.tld when using the dns challenge.

You can try using an alias as described here

Thanks. I will check this surely.
At the moment i settled with another workaround. Since chromecast only accepting links without ssl with ip address (local), i will use my synology for this.
That means when i need to play some local media, i will use synology http link. When i need to say some text, i will use HA’s https external link. It will require internet connection, but to convert text to speech i need internet connection anyway.

You solution is also a very good workaround. But i dont want to use adguard home at my router at the moment. I appreciate your effort to describe your findings here. I hope others will benefit from this surely.

Hi,

Another solution (which I’ve applied with success), is to redirect all external DNS queries to your internal DNS.

Google Home mini will ask the Google DNS (8.8.8.8 or 8.8.4.4 or any other external address) but the request is catched by your firewall and redirected to your internal DNS resolver which respond with the internal IP.

how to add local subdomain. Thanks!. I run home assitant on docker(docker run on openwrt router x86). I have problem with google home mini not play google tts: “Error on init TTS: No TTS from google_translate for…”

EDIT: slolution is 4: Block Google DNS work fine. Only block google dns for google home mini.

I just don’t get it to work. I have:

  • Configuration as follows:
tts:
  - platform: google_translate
    language: nl
  • Set both internal & external url to my public url:
homeassistant:
  internal_url: https://<FQDN>:8123
  external_url: https://<FQDN>:8123
  • I have a static route in my Unify controller to route all traffic to 8.8.8.8/32 & 8.8.4.4/32 to a “black hole”
  • TTS mp3s end up in the tts folder
  • Nothing to be seen in the logs

But still the nest mini only beeps and won’t play the speech :frowning:

Is your base URL configured in the web interface? It’s done there now.

1 Like

I can’t configure it in the GUI, since I have the settings (incl https) in the yaml config. But somehow it works now, when adding the (public) baseurl to the tts entry too.

1 Like