UNPKG

@elgato/streamdeck

Version:

The official Node.js SDK for creating Stream Deck plugins.

137 lines (136 loc) 5.16 kB
import { withResolvers } from "@elgato/utils"; import { randomUUID } from "node:crypto"; import { connection } from "../connection.js"; import { logger } from "../logging/index.js"; import { requiresVersion } from "../validation.js"; import { settingsCache } from "./cache.js"; import { actionConfig } from "./config.js"; import { ActionContext } from "./context.js"; const REQUEST_TIMEOUT = 15 * 1000; // 15s /** * Provides a contextualized instance of an {@link Action}, allowing for direct communication with the Stream Deck. * @template T The type of settings associated with the action. */ export class Action extends ActionContext { /** * Gets the resources (files) associated with this action; these resources are embedded into the * action when it is exported, either individually, or as part of a profile. * * Available from Stream Deck 7.1. * @returns The resources. */ async getResources() { requiresVersion(7.1, connection.version, "getResources"); const res = await this.#fetch("getResources", "didReceiveResources"); return res.payload.resources; } /** * Gets the settings associated this action instance. * @template U The type of settings associated with the action.D * @returns Promise containing the action instance's settings. */ async getSettings() { if (actionConfig.useExperimentalMessageIdentifiers) { const cached = settingsCache.get(this.id); if (cached !== undefined) { logger.trace(JSON.stringify({ event: "getSettings", context: this.id, source: "cache", settings: cached, })); return cached; } } const res = await this.#fetch("getSettings", "didReceiveSettings"); return res.payload.settings; } /** * Determines whether this instance is a dial. * @returns `true` when this instance is a dial; otherwise `false`. */ isDial() { return this.controllerType === "Encoder"; } /** * Determines whether this instance is a key. * @returns `true` when this instance is a key; otherwise `false`. */ isKey() { return this.controllerType === "Keypad"; } /** * Sets the resources (files) associated with this action; these resources are embedded into the * action when it is exported, either individually, or as part of a profile. * * Available from Stream Deck 7.1. * @example * action.setResources({ * fileOne: "c:\\hello-world.txt", * anotherFile: "c:\\icon.png" * }); * @param resources The resources as a map of file paths. * @returns `Promise` resolved when the resources are saved to Stream Deck. */ setResources(resources) { requiresVersion(7.1, connection.version, "setResources"); return connection.send({ event: "setResources", context: this.id, payload: resources, }); } /** * Sets the settings associated with this action instance. Use in conjunction with {@link Action.getSettings}. * @param value Settings to persist. * @returns `Promise` resolved when the settings are sent to Stream Deck. */ setSettings(value) { settingsCache.delete(this.id); return connection.send({ event: "setSettings", context: this.id, payload: value, }); } /** * Temporarily shows an alert (i.e. warning), in the form of an exclamation mark in a yellow triangle, on this action instance. Used to provide visual feedback when an action failed. * @returns `Promise` resolved when the request to show an alert has been sent to Stream Deck. */ showAlert() { return connection.send({ event: "showAlert", context: this.id, }); } /** * Fetches information from Stream Deck by sending the command, and awaiting the event. * @param command Name of the event (command) to send. * @param event Name of the event to await. * @returns The payload from the received event. */ async #fetch(command, event) { const { resolve, reject, promise } = withResolvers(); // Set a timeout to prevent endless awaiting. const timeoutId = setTimeout(() => { listener.dispose(); reject("The request timed out"); }, REQUEST_TIMEOUT); // Listen for an event that can resolve the request. const listener = connection.disposableOn(event, (ev) => { // Make sure the received event is for this action. if (ev.context == this.id) { clearTimeout(timeoutId); listener.dispose(); resolve(ev); } }); // Send the request; specifying an id signifies its a request. await connection.send({ event: command, context: this.id, id: randomUUID(), }); return promise; } }