Bitfocus Telemetry Bridge
State-first integration hub for Bitfocus Companion and custom applications.
What It Is
The Telemetry Bridge is a software integration hub that provides bidirectional, state-first integration between:
- Bitfocus Companion (fully implemented, production-grade)
- Multiple custom applications (concurrent clients, first-class support)
- Bitfocus Buttons (framework and contracts only; full protocol is future work)
It maintains a canonical state store, publishes deltas, provides snapshot-on-connect, and enables external applications to both consume Companion telemetry and send commands.
What It Is Not
- Not a hardware device or control surface
- Not a replacement for Companion’s UI
- Not a generic message broker (it’s domain-specific)
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐│ BITFOCUS TELEMETRY BRIDGE │├─────────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────┐ ┌─────────────────────────────────────┐ ││ │ Companion │◄───►│ COMPANION ADAPTER │ ││ │ (External) │ │ • Satellite API (WebSocket) │ ││ └─────────────┘ │ • Variable subscriptions │ ││ │ • Action invocation │ ││ │ • Capability detection │ ││ └──────────────┬──────────────────────┘ ││ │ ││ ▼ ││ ┌──────────────────────────────────────────────────────────────────────┐ ││ │ CORE HUB │ ││ │ ┌────────────────┐ ┌─────────────────┐ ┌────────────────────────┐ │ ││ │ │ State Store │ │ Message Router │ │ Event Log (Append) │ │ ││ │ │ • Snapshots │◄─┤ • Topic-based ├─►│ • Replay support │ │ ││ │ │ • Deltas │ │ • Wildcards │ │ • Correlation IDs │ │ ││ │ │ • Ownership │ │ • Backpressure │ │ • Sequencing │ │ ││ │ └────────────────┘ └─────────────────┘ └────────────────────────┘ │ ││ └──────────────────────────────────────────────────────────────────────┘ ││ │ ││ ┌─────────────────────────┼─────────────────────────┐ ││ │ │ │ ││ ▼ ▼ ▼ ││ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ ││ │ CLIENT TRANSPORT│ │ CLIENT TRANSPORT│ │ BUTTONS ADAPTER │ ││ │ (WebSocket API) │ │ (WebSocket API) │ │ (PLACEHOLDER) │ ││ │ App Client 1 │ │ App Client N │ │ • Interface only │ ││ └─────────────────┘ └─────────────────┘ │ • Simulator │ ││ │ • Contract tests │ ││ └─────────────────────┘ │└─────────────────────────────────────────────────────────────────────────────┘Quick Start
Prerequisites
- Node.js 20 or later
- Bitfocus Companion running with Satellite API enabled
Installation
# Clone the repositorygit clone https://github.com/FiLORUX/bitfocus-telemetry-bridge.gitcd bitfocus-telemetry-bridge
# Install dependenciesnpm install
# Buildnpm run buildRunning in Development
# Start with default configurationnpm run dev
# Or with a custom config filenpm run dev -- -c ./my-config.jsonRunning in Production
# Build firstnpm run build
# Startnpm start
# Or use the CLI directlynpx telemetry-bridge -c /path/to/config.jsonConfiguration
Create a bridge.config.json file:
{ "name": "my-bridge", "environment": "production", "companion": { "enabled": true, "host": "localhost", "port": 16622 }, "clientTransport": { "port": 9000, "requireAuth": false }, "logging": { "level": "info" }}Companion Capabilities
| Feature | Supported | Notes |
|---|---|---|
| Variable subscription | ✅ | Real-time updates |
| Variable write | ✅ | Via commands |
| Button press/release | ✅ | Via Satellite API |
| Key state updates | ✅ | Images, text, colours |
| Brightness control | ✅ | Per-device |
| Encoder rotation | ⚠️ | If Companion supports |
| Auto-reconnect | ✅ | Configurable backoff |
| Capability detection | ✅ | Feature negotiation |
Bridge Protocol
The hub uses a vendor-agnostic protocol. Key concepts:
Message Types
- command: Request to perform an action
- event: Notification of something that happened
- state: State update (delta or snapshot)
- ack: Acknowledgement of command
- error: Error response
- subscribe/unsubscribe: Manage subscriptions
Message Envelope
interface BridgeMessage { id: string; // UUID v7 (time-ordered) type: string; // Message type source: string; // Sender namespace target?: string; // Recipient (for commands) path: string; // Hierarchical key payload: unknown; // Type-specific data timestamp: number; // Unix ms correlationId?: string; sequence: number; // Monotonic per source ttl?: number; // Command TTL idempotencyKey?: string;}State Paths
companion.variables.internal.tally_pgmcompanion.device.<id>.key.<index>companion.connection.statebuttons.device.<id>.key.<index>app.<name>.custom.<key>See Bridge Protocol Specification for full details.
Client Integration
Connecting
import WebSocket from 'ws';
const ws = new WebSocket('ws://localhost:9000');
// Send handshakews.on('open', () => { ws.send(JSON.stringify({ type: 'handshake', name: 'my-app', version: '1.0.0', }));});
// Handle responsews.on('message', (data) => { const msg = JSON.parse(data.toString());
if (msg.type === 'handshake_response' && msg.success) { console.log('Connected!', msg.namespace); }});Subscribing to State
ws.send(JSON.stringify({ id: 'sub-1', type: 'subscribe', source: 'app.myapp', path: 'hub.subscriptions', payload: { patterns: ['companion.variables.**', 'companion.device.*.key.*'], snapshot: true, }, timestamp: Date.now(), sequence: 1,}));Sending Commands
ws.send(JSON.stringify({ id: 'cmd-1', type: 'command', source: 'app.myapp', target: 'companion.satellite', path: 'companion.page.1.bank.0', payload: { action: 'press', params: { keyIndex: 5 }, }, timestamp: Date.now(), sequence: 2, idempotencyKey: 'press-5-' + Date.now(),}));See examples/client-app for a complete working example.
Buttons Placeholder
The Buttons adapter is scaffolded but not fully implemented. Current status:
| Component | Status |
|---|---|
| Type definitions | ✅ Complete |
| Adapter interface | ✅ Complete |
| Placeholder implementation | ✅ Complete |
| Simulator | ✅ Complete |
| Contract tests | ✅ Complete |
| Full protocol | ❌ Future work |
The placeholder can be used for development and testing while the full implementation is pending.
Observability
Metrics (Prometheus)
curl http://localhost:9090/metricsKey metrics:
telemetry_bridge_connections_current- Active connections by typetelemetry_bridge_messages_received_total- Messages receivedtelemetry_bridge_command_duration_seconds- Command latency histogramtelemetry_bridge_adapter_state- Adapter connection status
Health Endpoints
# Full health reportcurl http://localhost:9091/health
# Liveness (is process alive?)curl http://localhost:9091/health/live
# Readiness (can handle requests?)curl http://localhost:9091/health/readyLogging
Logs are structured JSON with correlation ID propagation:
{ "level": 30, "time": "2024-01-17T12:00:00.000Z", "service": "telemetry-bridge", "component": "companion", "correlationId": "abc-123", "msg": "Variable updated"}Testing
# Run all testsnpm test
# Run with coveragenpm run test:coverage
# Run specific test suitesnpm run test:contractnpm run test:integrationProject Structure
src/├── core/│ ├── protocol/ # Message types, schemas, factory│ ├── state/ # State store with ownership│ └── router/ # Message routing, subscriptions├── adapters/│ ├── companion/ # Full Companion integration│ └── buttons/ # Placeholder implementation├── transports/│ └── client/ # WebSocket server for apps├── observability/ # Logging, metrics, health├── config/ # Schema and loader├── bridge.ts # Main orchestrator└── cli.ts # CLI entry point
tests/├── unit/ # Unit tests├── contract/ # Adapter contract tests└── integration/ # Integration tests
docs/├── specs/ # Protocol specifications├── guides/ # Operational guides└── adr/ # Architecture decisionsDevelopment
Prerequisites for Development
# Install development dependenciesnpm install
# Run in watch modenpm run dev
# Type checknpm run typecheck
# Lintnpm run lintAdding a New Adapter
- Create adapter directory under
src/adapters/ - Define types implementing
XxxAdapterinterface - Create simulator for testing
- Write contract tests
- Register in router via
registerTarget()
Contributing
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass
- Submit a pull request
Licence
MIT