vindral-composer-connection
v0.2.0
Published
TypeScript Node.js library for connecting to Vindral Composer via WebSocket
Keywords
Readme
vindral-composer-connection
TypeScript/Node.js library for connecting to a Vindral Composer instance via its WebSocket and HTTP Web APIs.
Designed to be used as the foundation for a TSR (Timeline State Resolver) device integration and potentially useful for others in future.
Features
- WebSocket connection to Vindral Composer's WS API
- HTTP Web API integration for controlling Composer (connectors, scenes, media, script engine)
- Automatic reconnection with exponential backoff;
connectedis only emitted once both the WebSocket and HTTP API are healthy - HTTP keepalive polling (
/api/getstatistics) to detect connectivity loss and trigger reconnection - Typed state model mirroring Composer's
AudioMixerStrips EventEmitter-based API, similar tosofie-atem-connection- Manual subscribe/unsubscribe to Composer channels (AudioMixer, etc.)
Installation
npm install vindral-composer-connection
# or
yarn add vindral-composer-connectionUsage
import { VindralComposer } from 'vindral-composer-connection'
const composer = new VindralComposer({
host: '192.168.1.100',
wsPort: 8081,
httpPort: 44433,
})
composer.on('connected', () => {
// Both WebSocket and HTTP API are healthy
console.log('Connected to Vindral Composer')
void composer.subscribeAudioMixer()
})
composer.on('disconnected', () => {
console.log('Disconnected from Vindral Composer')
})
composer.on('stateChanged', (state, changedPaths) => {
console.log('State updated:', changedPaths)
console.log('Audio strips:', state.audioMixer?.strips)
})
composer.on('error', (err) => {
console.error('Error:', err)
})
await composer.connect()
// WebSocket — set a property on an audio strip by its object ID
await composer.setPropertyValue('strip-abc123', 'Volume', 0.8)
// HTTP API — trigger a Connector by name
await composer.triggerConnector('MyConnector', { scene: 'intro' })
// HTTP API — trigger a Connector by value
await composer.triggerConnectorByValue('42')
// HTTP API — execute a Script Engine function
await composer.executeScriptFunction('MyFunction', { playerId: 'u123' })
// HTTP API — list all Connectors
const connectors = await composer.listConnectors()
// HTTP API — change a scene layer's source
await composer.setLayerSource('MyScene', 'MyLayer', 'MyInput')
// HTTP API — load and play a video file
await composer.playVideoFileInput('MyVideoInput', '/media/clip.mp4')
// Unsubscribe and disconnect when done
await composer.unsubscribeAudioMixer()
composer.disconnect()API
VindralComposer
Constructor options
| Option | Type | Default | Description |
| ------------------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| host | string | 'localhost' | Hostname or IP of the Composer instance |
| wsPort | number | 8081 | WebSocket port |
| httpPort | number | 44433 | HTTP Web API port |
| autoReconnect | boolean | true | Whether to automatically reconnect on disconnect |
| reconnectInterval | number | 1000 | Initial reconnect delay in ms (doubles on each attempt, capped at 30s) |
| pollInterval | number | 10000 | Interval in ms between HTTP keepalive polls to /api/getstatistics |
| pollTimeout | number | 5000 | Timeout in ms for each keepalive poll request |
| requestTimeout | number | 10000 | Timeout in ms for HTTP API method calls |
| apiKey | string | '' | API key sent as the apikey header on every HTTP request. Required when apikeys.json on the Composer host defines keys. |
WebSocket methods
connect(): Promise<void>— Open the WebSocket connectiondisconnect(): void— Close the connection and stop reconnectingsubscribeAudioMixer(): Promise<void>— Subscribe to the AudioMixer channelunsubscribeAudioMixer(): Promise<void>— Unsubscribe from the AudioMixer channelsetPropertyValue(objectId: string, propertyName: string, value: string | number | boolean): Promise<void>— Set a writable property on a Composer object
Note:
subscribeAudioMixer(),unsubscribeAudioMixer(), andsetPropertyValue()are fire-and-forget — the returned promise resolves immediately after sending the message, before the server has processed it. There is no acknowledgement from Composer's WebSocket API.
HTTP API methods
All HTTP methods require the composer to be in the CONNECTED state and throw if it is not. They also throw on non-2xx HTTP responses.
triggerConnector(name: string, params?: Record<string, string>): Promise<void>— Trigger a Connector by name. Optionalparamsare appended to the request URL and accessible via@@paramnamein Connector commands.triggerConnectorByValue(value: string, params?: Record<string, string>): Promise<void>— Trigger a Connector by value. Optionalparamsare appended as above.executeScriptFunction(functionName: string, parameter?: Record<string, unknown>, signal?: AbortSignal): Promise<void>— Execute a Script Engine function.parameteris JSON-serialised and passed as theparameterquery argument.listConnectors(signal?: AbortSignal): Promise<VindralConnector[]>— Return all Connectors configured in Composer.setLayerSource(scene: string, layer: string, source: string, signal?: AbortSignal): Promise<void>— Change the input source of a scene layer. The source must already exist in the Composer Inputs list.playVideoFileInput(inputName: string, sourceUri: string, signal?: AbortSignal): Promise<void>— Load and play a video file in a named Video File Input. Playback starts automatically once the file is parsed.
Events
| Event | Payload | Description |
| -------------- | ------------------------ | -------------------------------------------------------------------------------------------------- |
| connected | — | WebSocket connected, Welcome received, and HTTP API is reachable |
| disconnected | — | Connection closed; state is reset to initial values (consumers should treat cached state as stale) |
| error | string | Error message |
| stateChanged | VindralState, string[] | State updated; second arg is list of changed paths |
| info | string | Informational message |
| debug | string | Debug message |
State (VindralState)
interface VindralState {
info: {
composerVersion: string
projectName: string
composerOS: string
}
audioMixer:
| {
strips: AudioMixerStrip[]
}
| undefined
}Note: The
connectedevent is deferred until the first HTTP keepalive poll to/api/getstatisticssucceeds. If the HTTP API becomes unreachable after connection, the socket is reconnected anddisconnectedis emitted.
Types
VindralConnector — shape of each entry returned by listConnectors():
interface VindralConnector {
Id: string
Name: string
IsActive: boolean
InvokeCount: number
Description: string
}API setup notes
WebSocket API
Composer's WebSocket API defaults to port 8081. You must enable it in Composer settings:
- Desktop: Settings → Web API → Enable WebSockets
- Runtime: Edit
settings.xml:
"EnableWebSockets": true,
"WebSocketsHostName": "localhost",
"WebSocketsPort": 8081Note: For remote connections, set
WebSocketsHostNameto the machine's IP address (notlocalhost).
HTTP Web API
Composer's HTTP Web API defaults to port 44433. You must enable it in Composer settings. Refer to the HTTP Web API docs for the exact configuration keys.
Note: For remote connections, configure the API to bind to the machine's IP address rather than
localhost.
Message protocol
All WebSocket messages are JSON with the shape:
{ "Type": "MessageType", "Content": "string or JSON string" }Key message types:
Subscribe/Unsubscribe— Subscribe to a named channel (AudioMixer,LogFile)SetPropertyValueByObjectId— Write a property value on a Composer objectAudioMixerSummary(incoming) — Full AudioMixer state (sent after subscribe and on changes)Welcome(incoming) — Sent on connect with Composer version/project info
Development
yarn install
yarn build # compile TypeScript
yarn test # lint + unit tests
yarn unit # unit tests only
yarn watch # run tests in watch mode