Control4 DriverWorks References / Voice Input Proxy: Device Contract / Markdown

Control4 Subsystem Reference

Voice Input Proxy

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 nameBacked byAttached
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

input device coordinator voiced voice target Selection CurrentRoom (variable 1010) the device says where it is SET_VOICE_TARGETS_DATA SET_VOICE_INPUT_CONFIG c4vc URI, keyed by service name SET_CURRENT_TARGET follows the room's selected watch device URI change status: c4vc_uris changed SET_VOICE_INPUT_CONFIG only when the value differs from the last send Push to talk audio to the stored c4vc URI PCM stream direct sockets; the proxy and the coordinator carry no audio
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_CONFIG Config push

Stores one service's connection URI on the device. This is the load-bearing command of the whole interface.

Parameters
NameTypeNotes
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_CONFIG Config 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_TARGET Selection

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_TARGET Selection

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_ENABLED Selection

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_TARGETS Room 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_DATA Room 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_STATE Session 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_DATA Session 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
IdVariableTypeHow the coordinator uses it
1000IsMicEnabledbool Read as status.
1001RegistrationDatastring Read back; written only by the dormant registration flow.
1002IsRegisteredbool Read as status.
1003IsTargetEnabledbool Ignored at this version; the coordinator treats every device as target-enabled.
1004VoiceInputConfigstring Read back to inspect what the device holds.
1005ResponseDatastring Read as status.
1006FeedbackStatestring Read as status.
1007VoiceInputModestring Read as status.
1008IsConnectedbool The command gate. Nothing is sent while false.
1009CurrentTargetstring Read as status.
1010CurrentRoomroom id Acted on. A change moves the device between rooms, which can retarget it and trigger a fresh round of pushes.
1011CurrentRoomVoiceTargetsstring Read as status.
1012TargetViablebool Read as status.
1013DefaultTargetstring Read as status.
1014UserNamestring Unused at this version.
1015SystemVoiceTargetsstring Unused at this version.
1016TargetNamestring 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:

<capabilities>
  <has_navigator>true</has_navigator>
  <has_concierge>false</has_concierge>
</capabilities>

Behavior notes

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.