Skip to main content

Overview

OpenHome provides two methods for text-to-speech:
  • speak() - Uses the Agent’s default voice
  • text_to_speech() - Uses a specific voice ID for custom voices
Both methods stream audio to the user in real-time.
Keep TTS output to 1-2 sentences for natural conversation flow. This is voice, not text.

speak()

Converts text to speech using the Agent’s default voice. This is the most common method for voice output.

Signature

await self.capability_worker.speak(text: str) -> None
text
string
required
The text to convert to speech. Keep it concise (1-2 sentences).

Returns

None - Audio is streamed to the user

Examples

await self.capability_worker.speak("Hello! How can I help you today?")

When to Use

  • ✅ General Ability output
  • ✅ Quick responses and confirmations
  • ✅ Error messages
  • ✅ When you want consistent voice across the Agent

text_to_speech()

Converts text to speech using a specific voice ID. Use this when your Ability needs its own distinct voice personality.

Signature

await self.capability_worker.text_to_speech(text: str, voice_id: str) -> None
text
string
required
The text to convert to speech
voice_id
string
required
ElevenLabs voice ID. See Voice ID Reference below.

Returns

None - Audio is streamed to the user

Examples

# Use a deep male narrator voice
VOICE_ID = "pNInz6obpgDQGcFmaJgB"
await self.capability_worker.text_to_speech("Welcome aboard.", VOICE_ID)

When to Use

  • ✅ Character voices (storytelling, games)
  • ✅ Brand-specific voice personality
  • ✅ Different voice for different content types (e.g., news vs. meditation)
  • ✅ Non-English languages requiring specific accents

Voice ID Reference

Voice IDs are from ElevenLabs. Use these with text_to_speech().
Female
Voice IDGenderToneBest For
21m00Tcm4TlvDq8ikWAMFemaleCalmNarration
EXAVITQu4vr4xnSDxMaLFemaleSoftNews
XrExE9yKIg1WjnnlVkGXFemaleWarmAudiobooks
pMsXgVXv3BLzUgSXRplEFemalePleasantInteractive
Male
Voice IDGenderToneBest For
ErXwobaYiN019PkySvjVMaleWell-roundedNarration
GBv7mTt0atIp3Br8iCZEMaleCalmMeditation
TxGEqnHWrfWFTfGW9XjXMaleDeepNarration
pNInz6obpgDQGcFmaJgBMaleDeepNarration
Voice IDGenderToneBest For
ThT5KcBeYPX3keUQqHPhFemalePleasantChildren’s content
onwK4e9ZLuTAKqWW03F9MaleDeepNews
Voice IDAccentGenderToneBest For
D38z5RcWu1voky8WS1jaIrishMaleSailorGames/Characters
IKne3meq5aSn9XLyUdCDAustralianMaleCasualConversation
Test voices in the OpenHome Dashboard to hear samples before using them in your Ability.

Best Practices

Keep It Conversational

await self.capability_worker.speak("Got it. Let me check that.")

Break Long Output Into Chunks

await self.capability_worker.speak("Here are your reminders.")
for reminder in reminders[:3]:
    await self.capability_worker.speak(reminder)

Always Speak Errors

try:
    data = fetch_data()
except Exception as e:
    self.worker.editor_logging_handler.error(f"Fetch failed: {e}")
    await self.capability_worker.speak("Sorry, I couldn't get that information.")

Common Patterns

Confirmation Before Action

await self.capability_worker.speak("I found 3 unread emails.")
confirmed = await self.capability_worker.run_confirmation_loop(
    "Would you like me to read them?"
)
if confirmed:
    # read emails

Progress Updates

await self.capability_worker.speak("Searching...")
results = search_api(query)
await self.capability_worker.speak(f"Found {len(results)} results.")

Dynamic Voice Selection

VOICE_MAP = {
    "news": "onwK4e9ZLuTAKqWW03F9",  # Deep British
    "meditation": "GBv7mTt0atIp3Br8iCZE",  # Calm American
    "story": "21m00Tcm4TlvDq8ikWAM",  # Calm Female
}

async def speak_with_mode(self, text: str, mode: str):
    voice_id = VOICE_MAP.get(mode)
    if voice_id:
        await self.capability_worker.text_to_speech(text, voice_id)
    else:
        await self.capability_worker.speak(text)

Listening

Combine with user_response() for two-way conversation

Audio

Play pre-recorded audio files

Build docs developers (and LLMs) love