Voice Speaker — voice_speaker
A wake-word endpoint. It listens, and when it hears its word it opens a voice path to whatever the house has been told to route it to.
Not a media_player. A media player originates content and is a source in the signal graph; this
originates speech, and the thing on the other end is a conversation rather than an audio stream. A
room's listen should never resolve to one of these.
Everything that makes one of these work is configuration the house owns, not settings somebody should be visiting a web page to change. Which word wakes it, where its voice goes, whether its microphone is live at all — a house with six of them has to be able to set those centrally, and a speaker that can only be configured on itself is a speaker nobody reconfigures.
Deliberately says nothing about how voice is carried. The route is a name the controller resolves; whether that ends up as a socket, a LiveKit room or something not invented yet is the switching layer's business, and a contract that named a transport would have to change every time that did.
Capabilities
Declared in the driver manifest under [[proxy]] capabilities. Anything not declared takes the default below.
| Capability | Type | Default | Meaning |
|---|---|---|---|
has_adjustable_wake_word | bool | false | The armed word can be changed — see set_wake_word |
has_direction | bool | false | |
has_hardware_mute | bool | false | |
has_indicator | bool | false | |
has_monitor | bool | false | A copy of what it hears can be listened to |
has_mute | bool | false | The microphone can be turned off |
has_route | bool | false | Where its voice goes can be set — see set_route |
has_speaker | bool | true | There is a speaker in it; it can play what it is sent |
has_volume | bool | false | It speaks replies and the level can be set |
indicator_max | u32 | 100 | |
volume_max | u32 | 100 | |
wake_word_needs_restart | bool | false | |
wake_words | string | "" | Comma-separated model names the device can arm |
Commands
alarm
Play one of Juno's offline alarm sounds through this endpoint's current room voice route. task
makes delivery idempotent: Core retries a ringing alarm after a dropped request or restart, and the
switch must continue the existing playback rather than stack another copy.
| Parameter | Type | Notes |
|---|---|---|
duration_seconds | u32 1–600 | |
sound | one of classic · bell · digital · gentle · ascending | |
task | u32 | |
volume | u32 0–100 |
dismiss_alarm
Stop alarm playback for the named task without stopping unrelated room media.
| Parameter | Type | Notes |
|---|---|---|
task | u32 |
listen
Only present when
has_monitoris declared true.
Open a session to listen to what this speaker is hearing, and say how long it may stay open.
The controller opens the session; whoever wants to hear it fetches the audio for themselves. That split is the whole design. A live microphone is not something to relay through the controller — it would put a continuous audio stream through the one process the house cannot afford to have busy, for a feature nobody uses twice a month — and it is not something any browser on the network should be able to switch on either. So: asking is a command here, and listening is a fetch somewhere else.
Time-bounded rather than a toggle, because the failure that matters is a session nobody closed. A microphone left open by a tab somebody shut is exactly the thing this contract should make hard, and an expiry is what makes the default outcome "it stops" instead of "it stays on".
Calling it again while a session is open extends it — that is how a listener that is still listening says so.
| Parameter | Type | Notes |
|---|---|---|
minutes | u32 1–60 |
mute
Only present when
has_muteis declared true.
Stop listening. A muted speaker does not wake, however clearly the word is said.
| Parameter | Type | Notes |
|---|---|---|
muted | bool |
restart
Restart the speaker.
Not cosmetic on this class of device: several settings — the wake word among them — are read once at boot, so a house that can set them but not restart can only half-configure a speaker.
No parameters.
set_indicator
Only present when
has_indicatoris declared true.
How bright the listening indicator is. Zero is off.
Worth having reachable rather than fixed: the same brightness that reads as "on" across a kitchen is what somebody unplugs the thing over in a bedroom.
| Parameter | Type | Notes |
|---|---|---|
level | u32 ≥0 |
set_route
Only present when
has_routeis declared true.
Where this speaker's voice goes. A name the controller resolves, not an address.
The indirection is the point. A speaker pointed at a hostname breaks when that machine moves, and every speaker in the house has to be revisited; a speaker pointed at a name keeps working because the thing that resolves it is the thing that knows. It is also what lets the same speaker be routed somewhere else — a different assistant, an intercom, nothing at all — without reflashing it.
Empty unroutes it: it will still wake, and go nowhere.
| Parameter | Type | Notes |
|---|---|---|
route | string |
set_volume
Only present when
has_volumeis declared true.
| Parameter | Type | Notes |
|---|---|---|
level | u32 ≥0 |
set_wake_word
Only present when
has_adjustable_wake_wordis declared true.
Which word arms this speaker.
Named from the device's own model list rather than a free string: the words a speaker can hear are whatever is flashed into it, and asking for one that is not there fails silently at the point where somebody is stood in front of it saying the word.
Whether this is live immediately depends on wake_word_needs_restart. Where it does, the caller is
expected to follow with restart — this command does not do it implicitly, because restarting a
speaker in the middle of somebody using it is not a decision a setter should make.
| Parameter | Type | Notes |
|---|---|---|
model | string |
stop_listening
Only present when
has_monitoris declared true.
Close the session now rather than waiting for it to expire.
No parameters.
Notifications
activity_changed
Where this endpoint is in a conversation, from woke to going quiet again.
woke says a conversation started; this says what it is doing now, and the difference is what a
screen needs. A television in the room should show that the house is listening while somebody is
still talking and take it down when the reply has finished, and neither edge is derivable from a
wake event and a guess at how long a sentence takes.
Sent by whatever is running the conversation rather than by the endpoint: the device knows it was woken and nothing more — whether a reply is still playing is a fact only the switching layer holds.
| Parameter | Type | Notes |
|---|---|---|
reply | string (optional) | What Juno is saying in the current turn, for a room display |
state | one of idle · listening · thinking · speaking · complete | |
transcript | string (optional) | What the person said in the current turn, for a room display |
direction_changed
Only present when
has_directionis declared true.
Roughly where the last speech came from, in degrees clockwise from the device's front.
| Parameter | Type | Notes |
|---|---|---|
degrees | u32 0–359 |
listening_changed
Only present when
has_monitoris declared true.
Whether a session is open, and where the audio can be fetched from.
The address comes back on the notification rather than being derived by whoever wants to listen: the thing serving the audio is the only party that knows where it is reachable, and a client that guessed would be wrong the first time a house ran two switches.
| Parameter | Type | Notes |
|---|---|---|
minutes | u32 (optional) | |
open | bool | |
url | string (optional) |
muted_changed
Only present when
has_muteis declared true.
| Parameter | Type | Notes |
|---|---|---|
muted | bool |
online_changed
| Parameter | Type | Notes |
|---|---|---|
online | bool |
route_changed
Only present when
has_routeis declared true.
| Parameter | Type | Notes |
|---|---|---|
route | string |
wake_word_changed
Only present when
has_adjustable_wake_wordis declared true.
What the device is armed with now. Sent after a successful set, and on discovery.
| Parameter | Type | Notes |
|---|---|---|
model | string |
woke
The word was heard. Sent on every wake, whether or not the voice path went anywhere.
Reported even when unrouted on purpose: "it hears me and nothing happens" and "it does not hear me" are the same complaint from the outside and completely different repairs, and this is what tells them apart.
| Parameter | Type | Notes |
|---|---|---|
model | string |
State
Last-known values core keeps for a binding of this proxy.
| Key | Type | Meaning |
|---|---|---|
activity | string | |
direction | u32 | |
in_conversation | bool | |
indicator | u32 | |
muted | bool | |
online | bool | |
reply | string | |
route | string | |
transcript | string | |
volume | u32 | |
wake_word | string |