UNPKG

@elgato/streamdeck

Version:

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

150 lines (149 loc) 5.89 kB
import { EventEmitter, withResolvers } from "@elgato/utils"; import WebSocket from "ws"; import { RegistrationParameter } from "../api/index.js"; import { Version } from "./common/version.js"; import { logger } from "./logging/index.js"; /** * Provides a connection between the plugin and the Stream Deck allowing for messages to be sent and received. */ class Connection extends EventEmitter { /** * Private backing field for {@link Connection.registrationParameters}. */ _registrationParameters; /** * Private backing field for {@link Connection.version}. */ _version; /** * Used to ensure {@link Connection.connect} is invoked as a singleton; `false` when a connection is occurring or established. */ canConnect = true; /** * Underlying web socket connection. */ connection = withResolvers(); /** * Logger scoped to the connection. */ logger = logger.createScope("Connection"); /** * Underlying connection information provided to the plugin to establish a connection with Stream Deck. * @returns The registration parameters. */ get registrationParameters() { return (this._registrationParameters ??= this.getRegistrationParameters()); } /** * Version of Stream Deck this instance is connected to. * @returns The version. */ get version() { return (this._version ??= new Version(this.registrationParameters.info.application.version)); } /** * Establishes a connection with the Stream Deck, allowing for the plugin to send and receive messages. * @returns A promise that is resolved when a connection has been established. */ async connect() { // Ensure we only establish a single connection. if (this.canConnect) { this.canConnect = false; const webSocket = new WebSocket(`ws://127.0.0.1:${this.registrationParameters.port}`); webSocket.onmessage = (ev) => this.tryEmit(ev); webSocket.onopen = () => { webSocket.send(JSON.stringify({ event: this.registrationParameters.registerEvent, uuid: this.registrationParameters.pluginUUID, })); // Web socket established a connection with the Stream Deck and the plugin was registered. this.connection.resolve(webSocket); this.emit("connected", this.registrationParameters.info); }; } await this.connection.promise; } /** * Sends the commands to the Stream Deck, once the connection has been established and registered. * @param command Command being sent. * @returns `Promise` resolved when the command is sent to Stream Deck. */ async send(command) { const connection = await this.connection.promise; const message = JSON.stringify(command); this.logger.trace(message); connection.send(message); } /** * Gets the registration parameters, provided by Stream Deck, that provide information to the plugin, including how to establish a connection. * @returns Parsed registration parameters. */ getRegistrationParameters() { const params = { port: undefined, info: undefined, pluginUUID: undefined, registerEvent: undefined, }; const scopedLogger = logger.createScope("RegistrationParameters"); for (let i = 0; i < process.argv.length - 1; i++) { const param = process.argv[i]; const value = process.argv[++i]; switch (param) { case RegistrationParameter.Port: scopedLogger.debug(`port=${value}`); params.port = value; break; case RegistrationParameter.PluginUUID: scopedLogger.debug(`pluginUUID=${value}`); params.pluginUUID = value; break; case RegistrationParameter.RegisterEvent: scopedLogger.debug(`registerEvent=${value}`); params.registerEvent = value; break; case RegistrationParameter.Info: scopedLogger.debug(`info=${value}`); params.info = JSON.parse(value); break; default: i--; break; } } const invalidArgs = []; const validate = (name, value) => { if (value === undefined) { invalidArgs.push(name); } }; validate(RegistrationParameter.Port, params.port); validate(RegistrationParameter.PluginUUID, params.pluginUUID); validate(RegistrationParameter.RegisterEvent, params.registerEvent); validate(RegistrationParameter.Info, params.info); if (invalidArgs.length > 0) { throw new Error(`Unable to establish a connection with Stream Deck, missing command line arguments: ${invalidArgs.join(", ")}`); } return params; } /** * Attempts to emit the {@link ev} that was received from the {@link Connection.connection}. * @param ev Event message data received from Stream Deck. */ tryEmit(ev) { try { const message = JSON.parse(ev.data.toString()); if (message.event) { this.logger.trace(ev.data.toString()); this.emit(message.event, message); } else { this.logger.warn(`Received unknown message: ${ev.data}`); } } catch (err) { this.logger.error(`Failed to parse message: ${ev.data}`, err); } } } export const connection = new Connection();