@elgato/streamdeck
Version:
The official Node.js SDK for creating Stream Deck plugins.
239 lines (238 loc) • 10.3 kB
JavaScript
import { Lazy } from "@elgato/utils";
import { connection } from "../connection.js";
import { ActionEvent } from "../events/action-event.js";
import { getManifest } from "../manifest.js";
import { settings } from "../settings.js";
import { ui } from "../ui.js";
import { Action } from "./action.js";
import { settingsCache } from "./cache.js";
import { actionConfig } from "./config.js";
import { ActionContext } from "./context.js";
import { DialAction } from "./dial.js";
import { KeyAction } from "./key.js";
import { actionStore, ReadOnlyActionStore } from "./store.js";
const manifest = new Lazy(() => getManifest());
/**
* Provides functions, and information, for interacting with Stream Deck actions.
*/
class ActionService extends ReadOnlyActionStore {
/**
* Initializes a new instance of the {@link ActionService} class.
*/
constructor() {
super();
// Adds the action to the store.
connection.prependListener("willAppear", (ev) => {
const action = ev.payload.controller === "Encoder" ? new DialAction(ev) : new KeyAction(ev);
actionStore.set(action);
if (actionConfig.useExperimentalMessageIdentifiers) {
settingsCache.set(ev.context, ev.payload.settings);
}
});
// Update the settings cache when settings are received.
connection.prependListener("didReceiveSettings", (ev) => {
if (actionConfig.useExperimentalMessageIdentifiers) {
settingsCache.set(ev.context, ev.payload.settings);
}
});
// Remove the action from the store.
connection.prependListener("willDisappear", (ev) => {
actionStore.delete(ev.context);
settingsCache.delete(ev.context);
});
}
/**
* Occurs when the user presses a dial (Stream Deck +).
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onDialDown(listener) {
return connection.disposableOn("dialDown", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isDial()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user rotates a dial (Stream Deck +).
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onDialRotate(listener) {
return connection.disposableOn("dialRotate", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isDial()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user releases a pressed dial (Stream Deck +).
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onDialUp(listener) {
return connection.disposableOn("dialUp", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isDial()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the resources were updated within the property inspector.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onDidReceiveResources(listener) {
return connection.disposableOn("didReceiveResources", (ev) => {
// When the id is defined, the resources were requested, so we don't propagate the event.
if (ev.id !== undefined) {
return;
}
const action = actionStore.getActionById(ev.context);
if (action) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user presses a action down.
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onKeyDown(listener) {
return connection.disposableOn("keyDown", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isKey()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user releases a pressed action.
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onKeyUp(listener) {
return connection.disposableOn("keyUp", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isKey()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user updates an action's title settings in the Stream Deck application. See also {@link Action.setTitle}.
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onTitleParametersDidChange(listener) {
return connection.disposableOn("titleParametersDidChange", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when the user taps the touchscreen (Stream Deck +).
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onTouchTap(listener) {
return connection.disposableOn("touchTap", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action?.isDial()) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when an action appears on the Stream Deck due to the user navigating to another page, profile, folder, etc. This also occurs during startup if the action is on the "front
* page". An action refers to _all_ types of actions, e.g. keys, dials,
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onWillAppear(listener) {
return connection.disposableOn("willAppear", (ev) => {
const action = actionStore.getActionById(ev.context);
if (action) {
listener(new ActionEvent(action, ev));
}
});
}
/**
* Occurs when an action disappears from the Stream Deck due to the user navigating to another page, profile, folder, etc. An action refers to _all_ types of actions, e.g. keys,
* dials, touchscreens, pedals, etc.
* @template T The type of settings associated with the action.
* @param listener Function to be invoked when the event occurs.
* @returns A disposable that, when disposed, removes the listener.
*/
onWillDisappear(listener) {
return connection.disposableOn("willDisappear", (ev) => listener(new ActionEvent(new ActionContext(ev), ev)));
}
/**
* Registers the action with the Stream Deck, routing all events associated with the {@link SingletonAction.manifestId} to the specified {@link action}.
* @param action The action to register.
* @example
* @action({ UUID: "com.elgato.test.action" })
* class MyCustomAction extends SingletonAction {
* export function onKeyDown(ev: KeyDownEvent) {
* // Do some awesome thing.
* }
* }
*
* streamDeck.actions.registerAction(new MyCustomAction());
*/
registerAction(action) {
if (action.manifestId === undefined) {
throw new Error("The action's manifestId cannot be undefined.");
}
if (manifest.value !== null && !manifest.value.Actions.some((a) => a.UUID === action.manifestId)) {
throw new Error(`The action's manifestId was not found within the manifest: ${action.manifestId}`);
}
// Routes an event to the action, when the applicable listener is defined on the action.
const { manifestId } = action;
const route = (fn, listener) => {
const boundedListener = listener?.bind(action);
if (boundedListener === undefined) {
return;
}
fn.bind(action)(async (ev) => {
if (ev.action.manifestId == manifestId) {
await boundedListener(ev);
}
});
};
// Route each of the action events.
route(this.onDialDown, action.onDialDown);
route(this.onDialUp, action.onDialUp);
route(this.onDialRotate, action.onDialRotate);
route(ui.onSendToPlugin, action.onSendToPlugin);
route(this.onDidReceiveResources, action.onDidReceiveResources);
route(settings.onDidReceiveSettings, action.onDidReceiveSettings);
route(this.onKeyDown, action.onKeyDown);
route(this.onKeyUp, action.onKeyUp);
route(ui.onDidAppear, action.onPropertyInspectorDidAppear);
route(ui.onDidDisappear, action.onPropertyInspectorDidDisappear);
route(this.onTitleParametersDidChange, action.onTitleParametersDidChange);
route(this.onTouchTap, action.onTouchTap);
route(this.onWillAppear, action.onWillAppear);
route(this.onWillDisappear, action.onWillDisappear);
}
}
/**
* Service for interacting with Stream Deck actions.
*/
export const actionService = new ActionService();
export {};