Skip to main content

Stream Deck Provider

Provider for integrating HTML widgets with Stream Deck: connects to Stream Deck devices, manages virtual device layout, and sends key events.

Note: Stream Deck Provider is available only on Windows.

Revision History​

  • Rev 1.1
    • Added documentation for Stream Deck signals: virtualDeviceCreated, buttonIconUpdated, streamdeckUnreachable, authenticationRequired, authenticationRejected.
    • Documented reconnectStreamDeck(widgetId) behavior, including when a new virtual device is created vs. reusing an existing one.
    • Added disconnectStreamDeck(widgetId) method description and QML example wiring for all error-related signals.
  • Rev 1.2
    • Updated connectStreamDeck behavior: it no longer creates a new virtual device when an empty deviceId is provided. To create a virtual device, use the stream-deck-virtual-device control manually.
    • When a widget is deleted, its virtual device is no longer deleted. To delete a virtual device, navigate to the Stream Deck application and delete it there.
    • Added documentation for Stream Deck signal streamdeckAuthenticated.

Overview​

  • Module name: widgetbuilder.streamdeck
  • Plugin name: StreamDeck
  • Version: 1.2

Manifest entry:

"required_plugins": [
  "widgetbuilder.streamdeck:StreamDeck:1.2"
]

Runtime objects:

  • Initialization flag: pluginStreamdeck_initialized
  • Initialization callback map: pluginStreamdeckEvents = { "onInitialized": onStreamdeckInitialized }
  • Plugin access: window.plugins.Streamdeck

All methods are synchronous from JavaScript perspective but operate on an asynchronous RPC connection to the Stream Deck provider. Device creation, icon updates, and error conditions are delivered via signals.

Methods​

connectStreamDeck(widgetId, deviceId, columns, rows)​

Connects the widget to an existing virtual Stream Deck device with the specified grid size.

ParameterTypeDescription
widgetIdstringUnique widget identifier provided by iCUE
deviceIdstringTarget Stream Deck device identifier
columnsintNumber of columns in the virtual key matrix
rowsintNumber of rows in the virtual key matrix

On success, virtualDeviceCreated(widgetId, deviceId) is emitted once the virtual device is ready.

On connection or authentication errors, one or more of the following signals may be emitted (see the Signals section below):

  • streamdeckAuthenticated(widgetId) – when authentication succeeds.
  • streamdeckUnreachable(widgetId) – when the Stream Deck provider cannot be reached or the connection drops.
  • authenticationRequired(widgetId) – when the user must authenticate before the widget can interact with Stream Deck.
  • authenticationRejected(widgetId) – when authentication fails or is rejected.

reconnectStreamDeck(widgetId)​

Requests reconnection for a previously connected widget. The method is asynchronous and restarts the underlying connection logic for the given widgetId, attempting to reuse the existing virtual device when possible. If the original virtual device no longer exists on the Stream Deck side, a new one is created during reconnection (see signal behavior below).

ParameterTypeDescription
widgetIdstringWidget identifier used during connect

Use this when the Stream Deck process was restarted or the connection was lost and re-established. Calling reconnectStreamDeck multiple times for the same widgetId is safe; as long as the original virtual device still exists, no extra devices are created.

Signal behavior:

  • If the existing virtual device for widgetId is still present on the Stream Deck side, virtualDeviceCreated(widgetId, deviceId) is not emitted again. The original deviceId remains valid. When the device is opened after reconnection, the plugin will re-emit buttonIconUpdated(widgetId, buttonIndex, iconDataUrl) for all known buttons.

  • If the stored virtual device no longer exists (for example, the Stream Deck app lost its state), a new virtual device is created and virtualDeviceCreated(widgetId, deviceId) is emitted again with the new deviceId.

  • In case of connection problems or authentication issues, no special "reconnect failed" signal is emitted; instead, the regular error signals are used:

    • streamdeckAuthenticated(widgetId) when the connection transitions to the Authenticated state;

    • streamdeckUnreachable(widgetId) when the connection transitions to the Disconnected state;

    • authenticationRequired(widgetId) when user authentication is needed;

    • authenticationRejected(widgetId) if authentication fails.


disconnectStreamDeck(widgetId)​

Disconnects the widget from its Stream Deck session. If this is the last subscriber for the given widgetId, the underlying client session and WebSocket connection are closed.

ParameterTypeDescription
widgetIdstringWidget identifier used during connect

Typical usage:

  • Call disconnectStreamDeck(widgetId) when the widget is removed or its QML/HTML component is destroyed.
  • When multiple views share the same widgetId (for example, dashboard + mimic), the Stream Deck session remains active until the last view disconnects.

updateVirtualDeviceSize(widgetId, columns, rows)​

Updates the size of the virtual device grid without reconnecting.

ParameterTypeDescription
widgetIdstringWidget identifier
columnsintNew number of columns
rowsintNew number of rows

The provider keeps existing connection and only resizes the virtual device.


sendKeyPress(widgetId, buttonIndex, pressed)​

Sends a key press or release event for a virtual button.

ParameterTypeDescription
widgetIdstringWidget identifier
buttonIndexintZero-based button index in the virtual key grid
pressedbooltrue for press, false for release

The grid is traversed in row-major order:

  • Rows are numbered from top to bottom starting at 0.

  • Columns are numbered from left to right starting at 0.

  • The index is computed as:

    buttonIndex = row * columns + column

For example, for a 5 × 3 grid (columns = 5, rows = 3):

  • buttonIndex = 0 → row 0, column 0 (top-left)
  • buttonIndex = 4 → row 0, column 4 (top-right)
  • buttonIndex = 5 → row 1, column 0 (first button of the second row)
  • buttonIndex = 14 → row 2, column 4 (bottom-right)

Use this from an interactive widget when the user taps or releases a virtual key.

Properties​

connectionStatus​

Current state of the connection to the Stream Deck application, reported as a string. The value belongs to the widget passed to connectStreamDeck(widgetId, ...), statuses of other widgets are ignored. The value is updated automatically and the connectionStatusChanged() notify signal is emitted on every change. The signal carries no arguments, read the property to get the current value.

ValueDescription
"Disconnected"No connection to the Stream Deck application
"Connecting"Connection is being established
"AuthRequested"Waiting for the user to approve the iCUE connection
"Authenticated"Connection is approved
"DeviceOpened"Virtual device is opened and ready
"AuthenticationRejected"The iCUE connection was not approved
const plugin = window.plugins.Streamdeck;
plugin.connectionStatusChanged.connect(function () {
	// Update the widget UI according to plugin.connectionStatus
});

Signals​

virtualDeviceCreated(widgetId, deviceId)​

Emitted when a virtual Stream Deck device has been created and is ready.

ParameterTypeDescription
widgetIdstringWidget identifier
deviceIdstringConnected Stream Deck device id

buttonIconUpdated(widgetId, buttonIndex, iconDataUrl)​

Emitted when an icon for a specific virtual button changes.

ParameterTypeDescription
widgetIdstringWidget identifier
buttonIndexintButton index whose icon was updated
iconDataUrlstringIcon image as a data: URL (PNG, etc)

streamdeckAuthenticated(widgetId)​

Emitted when a connection with the Stream Deck application is established.

ParameterTypeDescription
widgetIdstringWidget identifier

streamdeckUnreachable(widgetId)​

Emitted when the Stream Deck provider cannot be reached or the connection is lost.

ParameterTypeDescription
widgetIdstringWidget identifier

authenticationRequired(widgetId)​

Emitted when authentication is required before the widget can interact with Stream Deck.

ParameterTypeDescription
widgetIdstringWidget identifier

authenticationRejected(widgetId)​

Emitted when authentication fails or is rejected.

ParameterTypeDescription
widgetIdstringWidget identifier

Example​

manifest.json

"required_plugins": [
	  "widgetbuilder.streamdeck:StreamDeck:1.2"
	]

index.html

Add the stream-deck-virtual-device control to the HTML <head> so the user can select a virtual device:

<meta
    name="x-icue-property"
    content="virtualDevice"
    data-label="tr('Virtual Device')"
    data-type="stream-deck-virtual-device"
>

Then use the selected virtualDevice value in your script:

// Initialize plugin events for async signals (recommended)
pluginStreamdeckEvents = {
	"onInitialized": function onStreamdeckInitialized() {
		if (!window.plugins || !window.plugins.Streamdeck) {
			return;
		}

		const plugin = window.plugins.Streamdeck;

		// Handle successful virtual device creation
		plugin.virtualDeviceCreated.connect(function (widgetId, deviceId) {
			// Example: hide an error overlay and cache deviceId if needed
		});

		// Handle connection loss
		plugin.streamdeckUnreachable.connect(function (widgetId) {
			// Example: show an error overlay in the widget
		});

		// Plugin is ready; you can safely call methods here
		connectStreamDeckPlugin();
	}
};

function connectStreamDeckPlugin() {
	if (window.plugins && window.plugins.Streamdeck && pluginStreamdeck_initialized) {
		const widgetId = iCUE.widgetId; // Provided by iCUE global object
		const deviceId = virtualDevice; // Provided by the stream-deck-virtual-device control
		const columns = 5;
		const rows = 3;

		window.plugins.Streamdeck.connectStreamDeck(widgetId, deviceId, columns, rows);
	}
}

// Called by your UI when a virtual button is pressed or released
function onVirtualButtonPressed(buttonIndex, pressed) {
	if (window.plugins && window.plugins.Streamdeck && pluginStreamdeck_initialized) {
		window.plugins.Streamdeck.sendKeyPress(iCUE.widgetId, buttonIndex, pressed);
	}
}

// Example: bind UI events to virtual button presses
const button = document.getElementById("streamdeck-button-0");
if (button) {
	button.addEventListener("pointerdown", () => onVirtualButtonPressed(0, true));
	button.addEventListener("pointerup", () => onVirtualButtonPressed(0, false));
}

Note: The iCUE widget runtime provides iCUE.widgetId. The selected device ID comes from the virtualDevice property defined by the stream-deck-virtual-device control, not from the iCUE global object.

StreamDeck QML Wrapper​

The StreamDeck QML wrapper provides a thin, declarative layer over the HTML plugin API so that QML components can control Stream Deck devices without writing JavaScript glue code.

The wrapper is implemented in the widgetbuilder.streamdeck module as StreamDeck.qml and internally forwards all calls to the same backend that powers the HTML plugin.

Initialization​

To use the QML wrapper, import the module and keep a single StreamDeck object in the QML scope that needs to interact with Stream Deck. The wrapper exposes the same methods and signals as the HTML plugin:

  • connectStreamDeck(widgetId, deviceId, columns, rows)
  • reconnectStreamDeck(widgetId)
  • disconnectStreamDeck(widgetId)
  • updateVirtualDeviceSize(widgetId, columns, rows)
  • sendKeyPress(widgetId, buttonIndex, pressed)

Properties:

  • connectionStatus

Signals:

  • virtualDeviceCreated(widgetId, deviceId)
  • buttonIconUpdated(widgetId, buttonIndex, iconDataUrl)
  • streamdeckAuthenticated(widgetId)
  • streamdeckUnreachable(widgetId)
  • authenticationRequired(widgetId)
  • authenticationRejected(widgetId)

QML Example​

import QtQuick
import widgetbuilder.streamdeck

	Item {
		id: root

		// These values are typically provided by the host QML environment,
		// for example via context properties or a view model.
		property string widgetId: "{widget-id-from-environment}"
		property string deviceId: "{streamdeck-device-id-from-environment}"

		StreamDeck {
			id: streamDeck

			onVirtualDeviceCreated: function(widgetId, deviceId) {
				// Handle initial virtual device creation
			}

			onButtonIconUpdated: function(widgetId, buttonIndex, iconDataUrl) {
				// Update button visuals in QML if needed
			}

			onStreamdeckAuthenticated: function(widgetId) {
				// Handle Stream Deck authentication
			}

			onStreamdeckUnreachable: function(widgetId) {
				// Handle connection loss, e.g. show an error overlay in QML
			}

			onAuthenticationRequired: function(widgetId) {
				// Prompt the user to authenticate before using Stream Deck
			}

			onAuthenticationRejected: function(widgetId) {
				// Inform the user that authentication failed
			}
		}

		Component.onCompleted: {
			streamDeck.connectStreamDeck(widgetId, deviceId, 5, 3);
		}

	}