voice · git:20260910.44ed5b7 · 2026-09-10 · sha256 c8a213c114cb62e3
voice git:20260910.44ed5b7B
Immutable. This exact content is served forever at /api/v1/blob/c8a213c114cb62e3.
---
name: voice
description: TTS speech + mic/speaker mute for privacy. MUST trigger on meetings, calls, privacy, silence requests. "meeting"/"call"/"private" = mic+speaker mute. "be quiet"/"silent" = speaker mute only. Always call HW markers — never just text.
---
# Voice — Speak Through Speaker
## Quick Start
Choose the relevant path first:
- **Mic/speaker mute, unmute, or privacy request:** apply **Ambient Audio Guard**, then the applicable mute/unmute or Meeting Mode section below. Emit its HW markers directly in your reply; `/voice/status` and `/voice/speak` are not prerequisites for these controls.
- **Normal conversational reply:** automatic TTS handles the reply on spoken channels. No explicit speech call is needed.
- **Additional or separate speech:** follow the workflow below only when speech must happen during tool work or differ from your normal reply.
## Workflow — explicit additional or separate speech
1. Determine if you need explicit speech beyond your normal reply:
- Normal conversational reply -> do NOT call this skill, TTS is automatic
- Need to speak while also performing tool calls -> use `POST /voice/speak`
- Need to speak different text than your chat reply -> use `POST /voice/speak`
- Reacting to a sensing event before reply is finalized -> use `POST /voice/speak`
2. Optionally check if TTS is busy: `GET /voice/status`
3. If `tts_speaking` is true, wait or skip
4. Call `POST /voice/speak` with plain text
## Examples
Input: Normal conversational reply
Output: Do NOT call this skill. Just reply normally — your text is automatically spoken.
Input: You need to greet the user while also activating a scene
Output: Call `POST /voice/speak` with `{"text": "Good morning!"}` in parallel with the Scene API call.
Input: You want to say something different from your chat reply
Output: Call `POST /voice/speak` with the spoken text. Then provide your chat reply separately.
Input: User says "say something" / "tell me a joke"
Output: Do NOT call this skill. Just reply normally with the joke — automatic TTS handles it.
## Tools
Use `Bash` with `curl` to call the HTTP API at `http://127.0.0.1:5001`.
### Speak text
```bash
curl -s -X POST http://127.0.0.1:5001/voice/speak \
-H "Content-Type: application/json" \
-d '{"text": "Hello, this is a test."}'
```
Text max 2000 characters. Returns immediately; audio plays in background.
### Check voice status
```bash
curl -s http://127.0.0.1:5001/voice/status
```
Response:
```json
{
"voice_available": true,
"voice_listening": false,
"tts_available": true,
"tts_speaking": false
}
```
## Error Handling
- If `tts_speaking` is true, the speaker is busy. Wait briefly or skip the explicit speech.
- If `voice_available` or `tts_available` is false, inform the user: "Voice output is currently unavailable."
- If the API is unreachable, fall back to chat-only reply. Speech is non-critical.
## Rules
- **Normal replies = automatic TTS.** Do NOT call `/voice/speak` for every response.
- Use `/voice/speak` explicitly only when:
- You need to say something while ALSO performing tool calls (speech in parallel)
- You want to speak a different text than your chat reply
- You are reacting to a sensing event and want to speak before your reply is finalized
- **Keep spoken text plain and short** — 1-3 sentences. No markdown, no emoji, no formatting. Plain natural speech only.
- **Exception: reading a draft back for approval.** When another skill has you read back something the user is about to send or delete (e.g. `connectors` before sending, sharing, or deleting), speak it in full. The user is approving that exact payload, so compressing it to fit 1-3 sentences defeats the confirmation.
- **Match the user's language** — if they speak Vietnamese, speak Vietnamese.
- Text max 2000 characters.
- For volume control, use the **Audio** skill, not this skill.
## Ambient Audio Guard (read FIRST)
If the user's message contains the literal token `[ambient]` (typically alongside a `[user]` priority marker, e.g. `[user] [ambient] ...`), it is overheard passive audio — NOT directed at the device. Do NOT trigger mute markers from a single bare word like "call", "meeting", "private", or a clipped fragment.
Mute markers `[HW:/voice/mute:{}]` and `[HW:/speaker/mute:{}]` may fire on ambient audio ONLY when the transcript contains a clear, complete intent:
- "I'm on a call" / "I have a meeting" / "I need privacy" / "stop listening"
- Or directly addresses it by name with a mute request
When ambient is ambiguous, reply naturally or stay quiet — DO NOT mute. Voice commands (no `[ambient]` token in the message) follow the normal trigger tables below.
## Mic Mute/Unmute (Privacy)
Users can mute the mic for privacy (meetings, calls). Use HW markers — no curl needed.
### Mute mic
```
[HW:/voice/mute:{}]
```
Stops all listening — STT, wake word, sound detection. The device becomes fully deaf. Unmute via physical button, web toggle, or Telegram command.
### Trigger phrases (MANDATORY — must call HW marker, not just reply with text)
Any phrase about **privacy, meetings, calls, not wanting to be heard, or asking the device to stop listening** MUST trigger `[HW:/voice/mute:{}]`. Do NOT just acknowledge — you MUST include the HW marker.
| User says | Action |
|-----------|--------|
| "don't listen" / "stop listening" / "mute" / "mute mic" | `[HW:/voice/mute:{}]` — MUST call |
| "I'm in a meeting" / "I have a meeting" / "I need a private meeting" / "meeting" | `[HW:/voice/mute:{}]` — MUST call |
| "I'm on a call" / "I have a call" / "phone call" | `[HW:/voice/mute:{}]` — MUST call |
| "privacy" / "private" / "give me privacy" / "need privacy" | `[HW:/voice/mute:{}]` — MUST call |
| "don't hear me" / "in a meeting" / "mute mic" / "stop hearing" | `[HW:/voice/mute:{}]` — MUST call |
### Examples
**Input:** "I have a meeting now"
**Output:** `[HW:/voice/mute:{}]` OK, I'll stop listening. Press the button when you need me.
**Input:** "Stop listening"
**Output:** `[HW:/voice/mute:{}]` Got it, mic off. Press my button to unmute.
**Input:** "I need a private meeting"
**Output:** `[HW:/voice/mute:{}]` Got it, going silent. Press the button when you're done.
**Input:** "I'm on a call"
**Output:** `[HW:/voice/mute:{}]` Muting now. Press the button to unmute when you're done.
### Unmute mic
```
[HW:/voice/unmute:{}]
```
Use when a **Telegram or web chat** user asks to unmute remotely. Voice unmute is not possible (the device is deaf when muted). Physical button also unmutes.
| User says (via Telegram/web) | Action |
|-----------|--------|
| "unmute" / "start listening" / "listen again" / "mic on" | `[HW:/voice/unmute:{}]` — only works from Telegram/web, not voice |
## Speaker Mute/Unmute (Silent Mode)
Suppress all audio output — TTS, music, backchannel. The device stays silent but still listens.
> Ambient guard above also applies here — bare fragments like "quiet" or "silence" in `[ambient]` audio do NOT trigger speaker mute.
### Mute speaker
```
[HW:/speaker/mute:{}]
```
### Unmute speaker
```
[HW:/speaker/unmute:{}]
```
Mic still works when speaker is muted — user can unmute via voice command.
**CRITICAL: "unmute" ≠ "mute". Read the EXACT word. Do NOT call mute when user says unmute.**
| User says | Action |
|-----------|--------|
| "be quiet" / "silent mode" / "don't talk" / "hush" / "silence" | `[HW:/speaker/mute:{}]` — MUST call |
| "**unmute**" / "you can talk" / "unmute speaker" / "speak again" / "talk again" | `[HW:/speaker/unmute:{}]` — MUST call (UN-mute, not mute!) |
### Examples
**Input:** "Be quiet"
**Output:** `[HW:/speaker/mute:{}]` Going silent. Just say "you can talk" when you want me back.
**Input:** "You can talk now"
**Output:** `[HW:/speaker/unmute:{}]` I'm back!
## Meeting Mode (mic + speaker mute)
When user mentions a meeting or call and wants **full silence** (not just speaker), mute BOTH mic and speaker. Ambient guard above applies — explicit intent required, not bare fragments.
**Input:** "I'm in a meeting"
**Output:** `[HW:/voice/mute:{}][HW:/speaker/mute:{}]` Meeting mode — fully silent. Press the button when you're done.
## Rules
- **Mic mute is the last thing the device hears via voice** — after mic mute, only physical button, web toggle, or Telegram can unmute
- Voice unmute for mic is impossible (the device is deaf) — tell user to press the button
- **Speaker mute**: user can still voice-unmute (mic still on)
- TTS still works when only mic is muted — the device can speak but not hear
- Always confirm mute with how to unmute