streamdeck_client 0.1.1
streamdeck_client: ^0.1.1 copied to clipboard
Pure Dart client for the Elgato Stream Deck WebSocket protocol. Handles connection, reconnection, event parsing, and manifest models.
streamdeck_client #
Pure Dart client for the Elgato Stream Deck WebSocket protocol. Handles connection, reconnection, event parsing, manifest models, and device layout constants.
Usage #
The Stream Deck app launches your plugin with CLI args: -port, -pluginUUID, -registerEvent, -info. Parse them with ConnectionInfo.fromArgs, connect, and react to events.
import 'dart:convert';
import 'dart:io';
import 'package:streamdeck_client/streamdeck_client.dart';
void main(List<String> args) async {
// Parse CLI args from the Stream Deck app.
final connection = ConnectionInfo.fromArgs(args);
final client = Client(connection);
await client.connect();
// Track which actions are visible on the device.
client.actions.listen((actions) {
for (final action in actions.values) {
print('Active: ${action.action} at '
'(${action.payload.coordinates.column}, '
'${action.payload.coordinates.row})');
}
});
// Listen to all events globally.
client.events.listen((event) {
switch (event) {
case ActionInfo():
// An action appeared on the device. ActionInfo is the
// willAppear event — it carries the action UUID, context,
// device, coordinates, settings, and controller type.
final scoped = client.scoped(event.context);
scoped.setTitle(title: 'Ready');
print('willAppear: ${event.action} '
'controller=${event.payload.controller}');
case WillDisappearEvent():
print('willDisappear: ${event.context}');
case KeyDownEvent():
client.showAlert(event.context);
print('keyDown: ${event.context}');
case KeyUpEvent(:final payload):
print('keyUp: state=${payload.state}');
case DialRotateEvent(:final payload):
print('dialRotate: ${payload.ticks} ticks');
case DialPressEvent(:final payload):
print('dialPress: pressed=${payload.pressed}');
case TouchTapEvent(:final payload):
print('touchTap: pos=${payload.tapPos}');
case DidReceiveSettingsEvent(:final payload):
print('settings: ${jsonEncode(payload.settings)}');
default:
break;
}
});
// Or listen to events for a specific action context.
// This is more efficient — events are filtered and cached.
client.eventsFor('some-context-id').listen((event) {
switch (event) {
case KeyDownEvent():
final scoped = client.scoped(event.context);
scoped.setSettings(settings: {'count': 42});
scoped.showOk();
default:
break;
}
});
// Keep the process alive.
await ProcessSignal.sigint.watch().first;
await client.dispose();
}
Connection Lifecycle #
connect() → Connection.connecting → Connection.connected
↓ (socket drops)
Connection.disconnected
↓ (auto)
Connection.reconnecting → connected
↓ (max attempts)
Connection.failed
Monitor connection state:
client.connection.listen((state) {
print('Connection: $state');
});
// Or read synchronously:
if (client.connectionValue == Connection.connected) { ... }
Scoped Client #
When working with a specific action, use scoped() to avoid passing the context on every call:
client.events.listen((event) {
if (event case ActionInfo()) {
final action = client.scoped(event.context);
action.setTitle(title: 'Hello');
action.setSettings(settings: {'key': 'value'});
action.showAlert();
// All calls auto-fill context from the scoped client.
}
});
Features #
- WebSocket client with automatic reconnection (exponential backoff, configurable max attempts).
- Full protocol coverage: all received events (
keyDown,dialRotate,touchTap,willAppear, etc.) and sent commands (setImage,setFeedback,setSettings, etc.). - Typed event models using freezed —
toString,==,hashCode,copyWith,fromJson/toJson. - Manifest models for generating
manifest.jsonfrom Dart code. - Device layouts with key sizes, gaps, margins, and encoder dimensions for all Stream Deck models.
- Scoped client —
client.scoped('context-id')returns a lightweight wrapper that auto-fills the action context on all send methods.