| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Text-to-speech for Home Assistant from OpenAI, Mistral, Groq, Lemonfox, Kokoro, Chatterbox or any server that implements the OpenAI speech API.
OpenAI TTS is a text-to-speech integration for Home Assistant that works with any service implementing the OpenAI speech API, in the cloud or self-hosted. Presets for the common providers fill in the endpoint, the models and the voices.
Each integration entry starts from a preset. The preset fills in the endpoint, offers the models the provider is known to serve and decides where the voice list comes from. It hides the speed slider on Mistral, which rejects it, and on Groq, which ignores it. It shows the extra payload field only on the Custom and Chatterbox presets, and it limits the audio formats on Groq, Chatterbox and OpenRouter. The model field also accepts a typed model name on every preset.
| Provider | Where it runs | Voices in the picker | API key |
|---|---|---|---|
| OpenAI | Cloud | OpenAI's voices, filtered by model | Required |
| Mistral Voxtral | Cloud | The voices on your account, read live, up to a hundred | Required |
| Groq (Orpheus) | Cloud | Six English Orpheus voices from a fixed list, or a typed name such as an Arabic voice | Required |
| Lemonfox.ai (Kokoro) | Cloud | The English voices Lemonfox documents, from a fixed list, or a typed Kokoro voice name | Required |
| OpenRouter | Cloud | The voices each speech model accepts, read live | Required |
| Kokoro-FastAPI | Self-hosted | The voice packs installed on the server, read live | Optional |
| Chatterbox | Self-hosted | The voices on the server, read live | Optional |
| Custom | Cloud or self-hosted | Read live when the server lists its voices, typed otherwise | Optional |
The names are shortened from the labels in the provider list.
The Custom preset covers every other server that implements the OpenAI speech endpoint, such as LocalAI, pocket-tts or TTS Web UI. When the server publishes no voice list, the voice field is a text field that takes any name the backend understands. When it does publish one, the picker shows that list, and it also takes a typed name when the list holds plain names. The audio format selector helps with a backend that rejects mp3, and the extra payload field sends backend-specific JSON parameters with each request.
Speech is always requested through the OpenAI speech API. Voice and model lists are read from a provider's own listing endpoint where one exists. A provider that offers speech only through a different API of its own is not supported.
On OpenAI the models are tts-1, tts-1-hd and gpt-4o-mini-tts, and gpt-4o-mini-tts also takes speaking-style instructions. The voices are alloy, ash, coral, echo, fable, nova, onyx, sage and shimmer, and gpt-4o-mini-tts adds ballad, cedar, marin and verse.
OpenAI is retiring all three models. OpenAI announced on 1 October 2026 that tts-1, tts-1-hd and every snapshot of gpt-4o-mini-tts stop working on 6 January 2027 (OpenAI deprecations). The replacement OpenAI recommends, gpt-realtime-2.1-mini, runs on its Realtime API, which this integration does not use, and OpenAI has not announced a replacement for its speech API. Unless it does, OpenAI profiles stop speaking on that date, and moving them to another provider is the way to keep them working. Every profile that calls OpenAI with one of these models raises a warning in Settings > Repairs two weeks before the shutdown, from 23 December 2026. Profiles on other providers are not affected, including self-hosted servers that accept tts-1 as a model name.
Version 3.10 needs Home Assistant 2026.9.1 or later.
Version 3.9 is mostly about backends other than OpenAI, and about what a speaker does while an announcement is playing.
WHATSNEW.md lists every change, including the fixes.
Version 3.10 needs Home Assistant 2026.9.1 or later. Version 3.9.2 needs Home Assistant 2025.7 or later.
Each integration entry stores the provider, the endpoint and the API key. Add one entry per provider or per account. Each sub-entry (TTS agent) stores the per-profile settings:
A chime turns streaming off for every request it is added to, because a chime has to be attached to finished audio. A chime switched on in the profile therefore turns streaming off for all of that profile's announcements and voice assistant replies, unless the call sets chime: false. Loudness normalisation does not turn streaming off, and neither does the gain: both run on the stream for mp3, opus, aac and pcm.
Every TTS agent is a regular Home Assistant text-to-speech entity. To use one for spoken replies, open Settings → Voice assistants, pick the assistant and choose the agent under Text-to-speech. The assistant's own Voice field is sent with every reply and takes precedence over the voice set in the profile.
With Sentence streaming switched on in the profile, synthesis starts on the first finished sentence. This happens only when the conversation agent streams its reply and the reply is longer than about 60 characters. The profile must also use MP3 or PCM, have Stream the audio switched on, and add no chime.
The entities also work with Home Assistant's own tts.speak action. That action sends no volume level. Most speakers play the announcement at their current volume, while Music Assistant uses its own announcement volume setting and Sonos its own default announcement level. On a speaker without an announcement feature, such as Cast, the music it replaces does not come back. openai_tts.say adds a volume level for the announcement, and on speakers without an announcement feature it pauses the music and resumes it afterwards where the player allows that.
action: tts.speak
target:
entity_id: tts.openai_tts_living_room
data:
media_player_entity_id: media_player.kitchen
message: "The washing machine has finished"Targets media players directly, with per-call overrides for voice, speed, instructions, extra payload, chime, chime sound, normalisation, volume and announcement behaviour. Only tts_entity and message are required fields, but the call must target at least one available media player or it fails. Voice, speed, instructions, extra payload, chime, chime sound, normalisation and announcement mode fall back to the profile when they are left out. When volume is left out, the speaker volume is not changed.
pause_playback is still accepted as an older name for announce so existing automations keep working, but new ones should use announce.
action: openai_tts.say
target:
entity_id: media_player.living_room_speaker
# area_id: living_room
# device_id: 12345abcde
# floor_id: ground_floor
# label_id: announcements
data:
tts_entity: tts.openai_tts_living_room
message: "Dinner is ready"
volume: 0.6 # this announcement only, see Features for how each speaker applies it
announce: true # default; pause and resume speakers that cannot announce
chime: true # play a chime first; turns streaming off
chime_sound: threetone.mp3
normalize_audio: true # loudness normalisation, on by default
voice: nova
speed: 1.0
language: en # checked by Home Assistant, not sent to the provider
instructions: "Say it warmly" # gpt-4o-mini-tts only
extra_payload: '{"temperature": 0.8}' # Custom and Chatterbox; merged into the request bodyWith response_variable, an error that happens while the announcement runs is returned instead of raised. The response is success: true, or success: false together with an error message, so an automation can send a notification when an announcement did not play. A call that fails validation, for example one with speed out of range, still raises an error. Unavailable targets are skipped, and the call reports success when at least one speaker played.
Some speakers appear in Home Assistant more than once. A speaker that Music Assistant plays to usually has a Music Assistant entity and a second entity from its own integration, such as ESPHome or Cast. Target the Music Assistant entity. The integration pauses a Cast entity before every announcement, and an ESPHome entity when a volume is given, and Music Assistant only hears about those pauses through the speaker itself. On a speaker in a Music Assistant group, this can stop the whole group. On a Cast speaker, the announcement can also replace Music Assistant's playback on the device, and the speaker then stays silent afterwards while Music Assistant still shows it playing.
When openai_tts.say announces on such an entity, the integration raises a warning in Settings > Repairs that names the Music Assistant entity of the same speaker. Announcements made through tts.speak or a voice assistant are not checked. The warning goes away after a restart once the entity is no longer used for announcements.
With a volume override on a Music Assistant speaker, the integration hands the level to Music Assistant instead of stopping the speaker and resuming it afterwards, because Music Assistant passes a stop on a grouped speaker on to the whole group. A speaker that plays announcements natively plays it over the group's music. Otherwise Music Assistant takes the speaker out of its group for the announcement and adds it back afterwards, while the rest of the group keeps playing. If the speaker leads the group, or the group cannot change its members, the group stops for the announcement.
Replaces the stored API key on an entry, so an automation can rotate a short lived token without anyone opening the settings. It needs an administrator, and it targets either config_entry_id or tts_entity, not both. Automations that run without a user are allowed.
Before storing the key, the action checks it against the endpoint. If the endpoint refuses the key, or the check cannot complete for another reason, the old key stays in place and the action raises an error. A key that equals the stored one is neither checked nor written. The check sends a speech request with an empty text, which every provider refuses only after it has looked at the key, so the check works on every provider, produces no audio and costs nothing. Add validate: false to store the key without checking it.
When the entry is loaded and Home Assistant is running, the entry reloads with the new key, so no restart is required. Otherwise the key takes effect the next time the entry loads.
action: openai_tts.set_api_key
data:
config_entry_id: 01ABCDEF...
api_key: "{{ token.content.access_token }}"
# validate: falseWith response_variable the call reports what it did. success is true, and changed is false when the key was already the one stored, with nothing else reported in that case. When the key changed, reloading is true if the entry is being reloaded with it now, and false if the key will be used at the next load. A failed check raises an error instead of returning a response.
Automation traces keep the data an action was called with, so the key passed to this action is visible in the trace of the automation that called it.
Turn on debug logging for the integration to see each request to the provider, which speakers are paused and how the volume is set and restored:
logger:
logs:
custom_components.openai_tts: debugCheck the entry's API status sensor. It is a diagnostic entity named Status on the entry's provider device, for example "OpenAI TTS API". Its state is ok or the kind of the most recent failure, such as auth_failed or quota_exceeded, and it returns to ok after the next successful request. Its attributes hold a description, the last error message, and the times of the last success and the last error. It starts as ok after a restart.
After auth_failed or quota_exceeded, the entry's TTS entities report unavailable, and every announcement is refused for ten minutes without contacting the provider. After that, one request is let through to check again. A rejected key also starts Home Assistant's re-authentication prompt on the entry, and entering a new key there, or with openai_tts.set_api_key, reloads the entry and clears the status. An HTTP 403 counts as a rejected key, whatever the provider meant by it.
When you open an issue, attach the diagnostics file from Settings → Devices & services → OpenAI TTS → ⋮ → Download diagnostics, for the entry that has the problem. The API key is removed from that file. The endpoint URL, the entry title, a short hash of the API key and the profile settings, including instructions and extra payload, are kept, so remove anything secret from them before attaching the file.
Bug reports, backend reports and pull requests are all welcome. Pull requests target the dev branch. For anything larger than a small fix, open an issue or a discussion first, so that the approach can be agreed before you write the code. CONTRIBUTING.md has the details.
If you use a backend that behaves differently from the others, saying so in an issue is useful on its own, even without a patch.
Cloud providers need an API key, and on paid providers the account needs available balance or credits. OpenAI's pricing is at https://platform.openai.com/docs/pricing. The entry's API status sensor shows quota_exceeded when the provider reports that the balance has run out.
| Back | FazBrowse Home | New Git URL |