UNPKG

streamdeck-typescript

Version:

This library will help you build elgato stream deck plugins in typescript

327 lines (289 loc) 8.92 kB
import { InitBase, PossibleEventsToReceive, PossibleEventsToSend, } from '../interfaces/interfaces'; import { EventManager } from '../manager/event.manager'; import { SettingsManager } from '../manager/settings.manager'; /** * This is the base class for all Stream Deck handlers * @author XeroxDev <help@xeroxdev.de> * @copyright 2021 */ export abstract class StreamDeckHandlerBase<GlobalSettings = any> { /** * @private * @internal */ protected _sd_events: Function[]; private _documentReady: boolean = false; private _connectionReady: boolean = false; private _globalSettingsReady: boolean = false; private _documentReadyInvoked: boolean = false; private _connectionReadyInvoked: boolean = false; private _globalSettingsInvoked: boolean = false; private _onReadyInvoked: boolean = false; private _debug: boolean = false; private _port: InitBase['port']; private _uuid: InitBase['uuid']; private _registerEvent: InitBase['registerEvent']; private _info: InitBase['info']; private _ws: WebSocket; private _eventManager: EventManager; private readonly _settingsManager: SettingsManager; private _cachedEvents: any[] = []; protected constructor() { this._settingsManager = new SettingsManager(this); this._eventManager = EventManager.INSTANCE; if (this._sd_events) for (let event of this._sd_events) event('*', this); (window as any).connectElgatoStreamDeckSocket = ( port: string, uuid: string, registerEvent: string, info: string, actionInfo?: string ) => { this._port = port; this._uuid = uuid; this._registerEvent = registerEvent; this._info = JSON.parse(info); if (actionInfo) { this._eventManager.callEvents('registerPi', '*', actionInfo); } this._connectElgatoStreamDeckSocket(); this._docReady(() => { this._documentReady = true; this._handleReadyState(); }); }; } /** * The port for the Stream Deck Application * @returns {InitBase["port"]} */ public get port(): InitBase['port'] { return this._port; } /** * @returns {InitBase["uuid"]} The UUID of the Plugin */ public get uuid(): InitBase['uuid'] { return this._uuid; } /** * The Event sent from Elgato, which is needed for the registration procedure * @returns {InitBase["registerEvent"]} */ public get registerEvent(): InitBase['registerEvent'] { return this._registerEvent; } /** * All the information send from Elgato * @returns {InitBase["info"]} */ public get info(): InitBase['info'] { return this._info; } /** * Through this object you can get, set and edit all available settings (global settings and context settings) * @see {@link SettingsManager} * @returns {SettingsManager} */ public get settingsManager(): SettingsManager { return this._settingsManager; } public get documentReady(): boolean { return this._documentReady; } public get connectionReady(): boolean { return this._connectionReady; } public get globalSettingsReady(): boolean { return this._globalSettingsReady; } /** * Sets settings for current context / action. * @param {Settings} settings * @param {string} context * @internal */ public setSettings<Settings = any>(settings: Settings, context: string) { this.send('setSettings', { context: context, payload: settings, }); } /** * Requests settings for current context / action * @param {string} context */ public requestSettings(context: string) { this.send('getSettings', { context: context, }); } /** * Sets global settings * @param {GlobalSettings} settings * @internal */ public setGlobalSettings<GlobalSettings = any>(settings: GlobalSettings) { this.send('setGlobalSettings', { context: this._uuid, payload: settings, }); } /** * Requests global settings */ public requestGlobalSettings() { this.send('getGlobalSettings', { context: this._uuid, }); } /** * Opens a url * @param {string} url */ public openUrl(url: string) { this.send('openUrl', { payload: { url }, }); } /** * Logs a message to the elgato log file * @param {string} message */ public logMessage(message: string) { this.send('logMessage', { payload: { message }, }); } /** * Sends custom socket events to the stream deck software * @param {PossibleEventsToSend} event * @param {any} data */ public send(event: PossibleEventsToSend, data: any) { const eventToSend = { event, ...data, }; if (this._debug) console.log(`SEND ${event}`, eventToSend, this._ws); if (this._ws) this._ws.send(JSON.stringify(eventToSend)); else { if (this._debug) console.error('COULD NOT SEND. CACHING FOR RESEND EVENT'); this._cachedEvents.push(JSON.stringify(eventToSend)); } } /** * Enables debug mode so you can see incoming and outgoing events. */ public enableDebug() { this._debug = true; } /** * Checks if dom ist ready * @param {() => void} fn * @private * @internal */ private _docReady(fn: () => void) { if ( document.readyState === 'complete' || document.readyState === 'interactive' ) { setTimeout(() => fn(), 1); } else { document.addEventListener('DOMContentLoaded', fn); } } /** * Handels the socket connection * @private * @internal */ private _connectElgatoStreamDeckSocket() { this._ws = new WebSocket('ws://127.0.0.1:' + this._port); this._ws.onopen = () => this._open(); this._ws.onclose = () => { this._eventManager.callEvents('connectionClosed'); }; this._ws.onmessage = (ev) => this._eventHandler(ev); } /** * Opens the connection * @private * @internal */ private _open() { this.send(this._registerEvent, { uuid: this._uuid }); if (this._cachedEvents.length >= 1) { if (this._debug) console.log('RESENDING CACHED EVENTS: ', this._cachedEvents); for (let cachedEvent of this._cachedEvents) { this._ws.send(cachedEvent); } } this._connectionReady = true; this._handleReadyState(); this.requestGlobalSettings(); } /** * Handels the ready state * @private * @internal */ private _handleReadyState() { if (this._connectionReady && !this._connectionReadyInvoked) { this._connectionReadyInvoked = true; this._eventManager.callEvents('connectionOpened'); } if (this._documentReady && !this._documentReadyInvoked) { this._documentReadyInvoked = true; this._eventManager.callEvents('documentLoaded'); } if (this._globalSettingsReady && !this._globalSettingsInvoked) { this._globalSettingsInvoked = true; this._eventManager.callEvents( 'globalSettingsAvailable', '*', this.settingsManager ); } if ( this._globalSettingsInvoked && this._documentReadyInvoked && this._connectionReadyInvoked && !this._onReadyInvoked ) { this._onReadyInvoked = true; this._eventManager.callEvents('setupReady'); } } /** * Handels all events * @param {MessageEvent} ev * @private * @internal */ protected _eventHandler(ev: MessageEvent) { const eventData = JSON.parse(ev.data); const event: PossibleEventsToReceive = eventData.event; if (this._debug) console.log(`RECEIVE ${event}`, eventData, ev); if (event === 'didReceiveGlobalSettings') { this.settingsManager.cacheGlobalSettings( eventData.payload.settings ); this._globalSettingsReady = true; this._handleReadyState(); } this._eventManager.callEvents( event, eventData.action ?? '*', eventData ); } }