Stream Deck SDK

repository·main·Indexed 42 days ago

https://github.com/elgatosf/streamdeck

A development framework for creating plugins for Stream Deck hardware. It includes the @elgato/cli (v1.9.0) for scaffolding, bundling, and managing the plugin lifecycle, as well as a TypeScript API for handling hardware events like button presses and dial rotations. The SDK provides tools for plugin validation, packaging into .streamDeckPlugin files, and a utility library (@elgato/utils) for resource management and event handling.

Tokens
27.1K
Snippets
93
Records
138
Agent score
53%

What's inside Stream Deck SDK

  1. Understand the Stream Deck plugin file structure

    main

    When you initialize a plugin, the following structure is generated:

    • *.sdPlugin/: The bundled plugin directory containing:
      • bin/: Binary files.
      • imgs/: Images/assets.
      • logs/: Log files.
      • ui/: HTML files for UI elements (e.g., increment-counter.html).
      • manifest.json: The plugin's metadata and configuration.
    • src/: The source code directory:
      • actions/: Contains action implementations (e.g., increment-counter.ts).
      • plugin.ts: The main plugin entry point.
    • package.json: Project dependencies and build scripts.
    • rollup.config.mjs: Rollup configuration for bundling.
    • tsconfig.json: TypeScript configuration.
  2. Manage resources with Disposables

    main

    The library provides utilities for explicit resource management, implementing patterns similar to TC39's Explicit Resource Management.

    • deferredDisposable(disposer): Creates a function that implements Symbol.dispose. When the function is used with the using keyword, the provided disposer is called automatically when the scope is exited.
    • DisposableStack: A stack of managed resources. You can register multiple disposers using .defer(disposer). When the stack itself is disposed, all registered disposers are called in reverse order.
    import { deferredDisposable, DisposableStack } from "@elgato/utils";
    
    // Using deferredDisposable
    {
        using deferredDisposable(() => console.log("Hello world"));
        // ...
    }
    
    // Using DisposableStack
    {
        using stack = new DisposableStack();
        stack.defer(() => console.log("Hello"));
        stack.defer(() => console.log("world"));
        // ...
    }
  3. Build and watch your Stream Deck plugin

    main

    The package.json file includes two primary scripts for managing the build lifecycle:

    • npm run build: Builds the plugin for production/distribution.
    • npm run watch: Continuously watches for source code changes and performs a hot-reload of the plugin after the build completes.
    npm run build
    npm run watch
  4. Install and use the Stream Deck CLI

    main

    The Stream Deck CLI is the recommended way to create, test, and bundle plugins.

    Prerequisites

    • Node.js 24 or higher
    • Stream Deck 7.1 or higher

    Installation Install the CLI globally using npm:

    npm install -g @elgato/cli@latest

    Initialization To start a new plugin project, run the creation wizard:

    streamdeck create
    npm install -g @elgato/cli@latest
    streamdeck create
  5. Define an EventMap for EventEmitter

    main

    To use EventEmitter with type safety, you must provide a type that follows the EventMap structure. An EventMap is an object where each property is a string (the event name) and the value is a tuple of arguments (e.g., [arg1: type, arg2: type]).

    Example structure:

    type MyEventMap = {
      eventA: [string, number];
      eventB: [boolean];
    }
  6. Understand ActionIdentifier and ActionEventMessage structures

    main

    All action-related events follow a structured format to identify which action and which specific device instance triggered the event.

    ActionIdentifier

    Every action event contains an ActionIdentifier to pinpoint the source:

    • action: The unique UUID of the action defined in your plugin's manifest.json (e.g., com.elgato.wavelink.mute).
    • context: The unique identifier for the specific instance (the specific key, dial, or pedal) that caused the event.
  7. Handle title changes: titleParametersDidChange

    main

    The titleParametersDidChange event is triggered when a user updates an action's title or its visual styling within the Stream Deck application. This allows your plugin to react to aesthetic changes like font size or color.

    The event provides a titleParameters object containing:

    • fontFamily: The font family used.
    • fontSize: The font size.
    • fontStyle: One of "", "Bold Italic", "Bold", "Italic", or "Regular".
    • fontUnderline: Boolean indicating if the text is underlined.
    • showTitle: Boolean indicating if the title is visible.
    • titleAlignment: One of "bottom", "middle", or "top".
    • titleColor: Hexadecimal color string.
  8. Handle action lifecycle events: willAppear and willDisappear

    main

    Plugins can respond to actions entering or leaving the user's view. This is useful for starting/stopping background processes or managing resource usage.

    • willAppear: Occurs when an action becomes visible (e.g., user navigates to the page, or during startup if the action is on the front page).
    • willDisappear: Occurs when an action is no longer visible (e.g., user navigates away).

    Note: For MultiActionPayload, these events require Stream Deck 6.5 or later.

  9. Use SingletonAction to handle plugin-wide events

    main

    The SingletonAction<T> class serves as the primary bridge between your plugin and the Stream Deck hardware. Instead of handling events on individual action instances, you can use a SingletonAction to listen for events across all actions that share the same manifestId. This is useful for managing global state or responding to hardware interactions (like dial rotations or key presses) regardless of which specific action instance triggered them.

    T represents the type of settings associated with the actions. You can implement various optional event handlers to respond to user interactions, property inspector changes, and lifecycle events.

    import { SingletonAction } from '@elgato/streamdeck';
    
    interface MySettings {
      theme: string;
      value: number;
    }
    
    const singleton = new SingletonAction<MySettings>();
    
    singleton.onKeyDown = (ev) => {
      console.log('A key was pressed:', ev.action.manifestId);
    };
    
    singleton.onDialRotate = (ev) => {
      console.log('Dial rotated:', ev.action.manifestId);
    };