streamdeck_client 0.4.0 copy "streamdeck_client: ^0.4.0" to clipboard
streamdeck_client: ^0.4.0 copied to clipboard

Pure Dart client for the Elgato Stream Deck WebSocket protocol. Handles connection, reconnection, and event parsing.

streamdeck_client #

Pure Dart client for the Elgato Stream Deck WebSocket protocol. Handles connection, reconnection, event parsing, and raw action/device state. No Flutter dependency. (Manifest models and device layout live in streamdeck_flutter, since they're rendering concerns.)

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 DialDownEvent():
        print('dialDown');

      case DialUpEvent():
        print('dialUp');

      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.
  • Scoped clientclient.scoped('context-id') returns a lightweight wrapper that auto-fills the action context on all send methods.
0
likes
130
points
96
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Pure Dart client for the Elgato Stream Deck WebSocket protocol. Handles connection, reconnection, and event parsing.

Repository (GitHub)
View/report issues

Topics

#streamdeck #elgato #websocket

License

BSD-3-Clause (license)

Dependencies

freezed_annotation, json_annotation, logging, rxdart

More

Packages that depend on streamdeck_client