The contract between the Voice Coordinator and voice input devices: the
push-to-talk remotes, touchscreens, and apps that carry the microphone.
It covers the commands the coordinator pushes, the proxy variables the
device reports, and how a microphone press finds its way to a voice
target.
Proxy
voiceinput.c4i
Device side
platform firmware
Coordinator side
control4_agent_voicecoordinator.c4z
Documented at
coordinator version 139
The model
The proxy carries configuration, never audio. The coordinator pushes
each input device a connection URI per service; when the microphone
button is held, the device streams straight to the URI of its current
target. If the pushed state is stale, the microphone lights up and the
audio goes nowhere.
Both ends of this interface ship with the platform. The device side is
implemented in the firmware of devices that bind the
voiceinput.c4i proxy; no packaged driver implements it,
and a target driver never touches it directly. The coordinator side is
the Voice Coordinator agent. The interface is still worth knowing
precisely, because it is the half of the voice pipeline that explains
most field failures: a target can be healthy while the input device
holds a blank or stale config. This page pairs with the
Voice
Coordinator reference, which documents the target side.
The unit of configuration is the service name. An
input device holds one stored configuration per service, and the
coordinator addresses every push with a TARGET_ID
parameter carrying that name. Which services an input device owns
depends on the project:
Services on an input device
Service name
Backed by
Attached
comcast
Every transcoder-path voice target, third-party ones
included.
Per room, following the room's selected watch device.
appletv
An apple-path voice target.
Per room, following the room's selected watch device.
siri
A per-device instance that piggybacks on an apple target's
configuration.
Per device, when the system companion driver is in the
project.
concierge
The concierge agent, through the daemon.
Per device, only on devices without a navigator or with the
has_concierge capability.
avs
The far-field service class. Disabled at this version: its
support check returns false for every device.
Never, currently.
""
A null placeholder every device owns, used when no real
service is available.
Always.
Call flow
Configuration flows down, audio flows around.
Ochre arrows are coordinator-initiated. The device's only
upstream channel is its proxy variables.
Conventions
Two channels, one direction each
Commands flow from the coordinator to the device, sent with
C4:SendToDevice to the voiceinput proxy's device id.
State flows back from the device as proxy variable changes: the
coordinator registers a listener on every variable in the table below
and reads current values on demand. There is no request and reply
pair anywhere on this interface.
The connection gate
The coordinator sends a device nothing while the
IsConnected variable is false. Sends attempted during
that window are dropped, not queued; a device that comes online later
receives current state on the next synchronization pass, not a replay.
Capabilities are a UI request
Device capabilities on this interface come from a
GET_CAPABILITIES UI request to the proxy device, answered
with an XML capabilities block and cached for the life of the
coordinator's device object. This is where the
has_navigator and has_concierge flags that
gate the concierge service come from.
Declared surface versus exercised surface
The proxy vocabulary is larger than what the current coordinator
actually sends. The commands documented individually below are
exercised at this version. A second group is declared and dormant:
SET_REGISTRATION_DATA, CLEAR_REGISTRATION,
SET_VOICE_INPUT_MODE, SET_TARGET_VIABLE,
SET_TARGET_NAME, SET_WAKE_WORD,
SET_VOICE_TARGET_DEVICE_CONFIG, and
SET_WHOLEHOME_SERVICE_CONFIG exist in the coordinator's
vocabulary but have no live caller, mostly because the far-field
service that used them is disabled. Do not build expectations on
them.
Commands to the device
All sent by the coordinator, all parameterized by service name in
TARGET_ID unless noted. The device is expected to store
per-service state and act on the current target.
SET_VOICE_INPUT_CONFIGConfig push
Stores one service's connection URI on the device. This is the
load-bearing command of the whole interface.
Parameters
Name
Type
Notes
CONFIG
string req
The service's c4vc:// URI, or empty. Empty
after a service has been configured means the service
stopped; the device should drop the endpoint.
TARGET_ID
string req
Service name the config belongs to.
Send discipline. The coordinator keeps a per-device,
per-service cache of the last value sent and suppresses duplicates.
It also defers pushing an empty config for a service that has never
had a valid one, so slow startups do not erase state a device
already holds.
CLEAR_VOICE_INPUT_CONFIGConfig push
Removes one service's stored configuration entirely, as opposed to
storing an empty value. The coordinator uses it when two services
would collide on the same URI port on one device: when a service
becomes current, any sibling sharing its port is cleared rather than
blanked.
SET_CURRENT_TARGETSelection
Names the service the microphone should feed now.
TARGET_ID is the service name. Sent when a room's
selected watch device maps to a different target, when a config for
the current service changes, and when a device joins a room that has
an active target.
SET_DEFAULT_TARGETSelection
Names the fallback service used when no room target is selected.
The coordinator assigns the default as services are attached, so the
last attached service wins; when a room has no eligible target, the
device is steered back to this default.
SET_TARGET_ENABLEDSelection
Marks a service enabled on the device. The current coordinator sends
it with IS_ENABLED = "true" as each service is
attached; the disable direction is not exercised.
SET_CURRENT_ROOM_VOICE_TARGETSRoom data
The room's eligible voice targets as a comma-separated list of proxy
device ids in TARGET_IDS. The older of the two formats;
kept for compatibility.
SET_VOICE_TARGETS_DATARoom data
The same information with structure: TARGETS_DATA is a
JSON array covering both the room's target devices and the
project-wide services. This is the preferred format, and it is where
a device learns the service type it will see for each target. Note
that every transcoder-path target reports the type
comcast here.
SET_FEEDBACK_STATESession state
Pushes a service's dialog state string in STATE. At
this version it is exercised only as cleanup: when the current
service changes, the outgoing service's state and response data are
blanked.
SET_RESPONSE_DATASession state
Pushes a service's response payload in DATA. Same
situation as SET_FEEDBACK_STATE: currently sent only to
blank state on a target switch.
What the device reports
The device speaks by setting proxy variables. The coordinator listens
to all of them, acts on one, and reads the rest as state.
Voiceinput proxy variables
Id
Variable
Type
How the coordinator uses it
1000
IsMicEnabled
bool
Read as status.
1001
RegistrationData
string
Read back; written only by the dormant registration flow.
1002
IsRegistered
bool
Read as status.
1003
IsTargetEnabled
bool
Ignored at this version; the coordinator treats every
device as target-enabled.
1004
VoiceInputConfig
string
Read back to inspect what the device holds.
1005
ResponseData
string
Read as status.
1006
FeedbackState
string
Read as status.
1007
VoiceInputMode
string
Read as status.
1008
IsConnected
bool
The command gate. Nothing is sent while
false.
1009
CurrentTarget
string
Read as status.
1010
CurrentRoom
room id
Acted on. A change moves the device
between rooms, which can retarget it and trigger a fresh
round of pushes.
1011
CurrentRoomVoiceTargets
string
Read as status.
1012
TargetViable
bool
Read as status.
1013
DefaultTarget
string
Read as status.
1014
UserName
string
Unused at this version.
1015
SystemVoiceTargets
string
Unused at this version.
1016
TargetName
string
Unused at this version.
Beyond variables, two messages travel from drivers toward the
coordinator on adjacent paths. A voice input driver stack can send the
agent REQUEST_RESYNC_VOICE_INPUT_CONFIG with the
voiceinput proxy device id to force a full re-push past the dedup
cache. And the coordinator's own proxy accepts
VOICE_TARGET_CONFIG_CHANGED, which at this version is
logged and otherwise ignored. Both are documented on the
coordinator
page.
Data shapes
The config URI
What SET_VOICE_INPUT_CONFIG carries is the first entry of
the service's c4vc_uris from voiced, shaped
c4vc://<id>@<ip>:<port>/. The device
connects to it when the microphone is held. The coordinator treats it
as opaque except for one detail: the port, which it extracts with the
pattern "@[^:]*:(%d+)" to detect same-port collisions
between services.
TARGETS_DATA
[
{
"DeviceId": 321, -- the target's proxy device id
"ServiceType": "comcast", -- as rewritten, not as the driver reported
"Category": "target"
},
{
"DeviceId": 13, -- the agent's own device id
"ServiceType": "concierge",
"Category": "service",
"DisplayName": "Concierge"
}
]
Targets carry the proxy device id of the target
device, not its protocol id. Service entries exist only while voiced
is connected and managed, and only for services the user has enabled.
The table is assembled from an unordered map, so entry order is not
meaningful.
GET_CAPABILITIES reply
An XML block of flag elements. The two the coordinator reads today:
The device follows its room, and the room picks the target
Retargeting is driven by the room's selected watch device, not by
anything the input device chooses. When the selection maps to a
voice target with a valid config, every input device in the room
is switched to that target's service. A target with no config yet
is skipped, so a half-started driver cannot displace a working
target.
The dedup cache is unconfirmed
The coordinator's per-service cache records what it sent, not
what the device accepted. A device that missed a push, or lost
state on its own, stays out of sync until a value actually
changes or a resync is forced with
REQUEST_RESYNC_VOICE_INPUT_CONFIG. The symptom is a
coordinator holding correct URIs while the device holds
nothing.
One stored endpoint per URI port on some firmware
Some device firmware keys its stored voice endpoint by the c4vc
port rather than by service name, so two services sharing a port
overwrite each other no matter what is pushed. The coordinator
works around it by clearing same-port siblings when a service
becomes current, which preserves cross-port caching for fast
target switching.
Empty is deferred, then meaningful
A service that has never produced a valid URI is not pushed at
all. After the first valid URI, an empty
SET_VOICE_INPUT_CONFIG is a deliberate statement
that the service stopped or was disabled.
Room membership follows the variable, not Composer
The device's Composer room is only the starting point. The
CurrentRoom variable wins, which is how a portable
remote carries its voice targeting from room to room.
Joining a room re-asserts the room's target
A device entering a room with an active voice target is
immediately attached to that service and made current. Entering
a room without one falls back to the device's default service.
Re-entry is not repeated on every synchronization pass, so a
user's explicit service choice on the device survives
resyncs.
Registration is designed in, switched off
The variables and commands for service registration (codes,
registered flags, wake words) belong to the far-field service
flow, which is disabled at this version. Expect them to stay
inert until that class returns.
Observing the exchange
Nothing on this interface has a UI, so debugging is reading state from
both ends and comparing.
On the coordinator, raise Log Level to
3 - Info: every SET_VOICE_INPUT_CONFIG send
is logged with the device, the value, and the service, and an
explicit warning marks every push of an empty config. The
Query Statuses action prints each service instance and
the URI it currently holds, which is the coordinator's side of the
truth.
On the device, read the proxy variables: VoiceInputConfig
and CurrentTarget say what the device actually holds and
where the microphone will stream. When the two ends disagree, the
dedup cache note above is usually the story, and a
REQUEST_RESYNC_VOICE_INPUT_CONFIG for that device is the
fix.