UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge.

321 lines • 12.1 kB
"use strict"; /** * This script is injected into a plugins custom settings ui by the Homebridge UI * You should not include it in your own code, however you can use it for type information if desired. * It provides the interface to interact with the Homebridge UI service. */ /* eslint-disable no-console */ let EventTargetConstructor = window.EventTarget; /** * Polyfill for older browsers that do not support EventTarget as a constructor. * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget */ if (!Object.prototype.hasOwnProperty.call(window.EventTarget, 'caller')) { EventTargetConstructor = function () { // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget this.listeners = {}; }; // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget EventTargetConstructor.prototype.listeners = null; EventTargetConstructor.prototype.addEventListener = function (type, callback) { // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget if (!(type in this.listeners)) { // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget this.listeners[type] = []; } // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget this.listeners[type].push(callback); }; EventTargetConstructor.prototype.removeEventListener = function (type, callback) { // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget if (!(type in this.listeners)) { return; } // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget const stack = this.listeners[type]; for (let i = 0, l = stack.length; i < l; i++) { if (stack[i] === callback) { stack.splice(i, 1); return; } } }; EventTargetConstructor.prototype.dispatchEvent = function (event) { // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget if (!(event.type in this.listeners)) { return true; } // @ts-expect-error - TS2339: Property listeners does not exist on type EventTarget const stack = this.listeners[event.type].slice(); for (let i = 0, l = stack.length; i < l; i++) { stack[i].call(this, event); } return !event.defaultPrevented; }; } class HomebridgePluginUi extends EventTargetConstructor { origin = ''; lastBodyHeight = 0; linkRequests = []; toast = new HomebridgeUiToastHelper(); // @ts-expect-error - TS2339: Property _homebridge does not exist on type Window & typeof globalThis plugin = window._homebridge.plugin; // @ts-expect-error - TS2339: Property _homebridge does not exist on type Window & typeof globalThis serverEnv = window._homebridge.serverEnv; constructor() { super(); window.addEventListener('message', this._handleIncomingMessage.bind(this), false); } async _handleIncomingMessage(e) { switch (e.data.action) { case 'ready': { await Promise.all(this.linkRequests); this.origin = e.origin; document.body.style.display = 'block'; this.dispatchEvent(new Event('ready')); this.fixScrollHeight(); this._monitorFrameHeight(); break; } case 'response': { this.dispatchEvent(new MessageEvent(e.data.requestId, { data: e.data, })); break; } case 'stream': { this.dispatchEvent(new MessageEvent(e.data.event, { data: e.data.data, })); break; } case 'body-class': { this._setBodyClass(e); break; } case 'inline-style': { this._setInlineStyle(e); break; } case 'link-element': { this._setLinkElement(e); break; } default: console.log(e.data); } } _postMessage(message) { window.parent.postMessage(message, this.origin || '*'); } _setBodyClass(e) { document.body.classList.add(e.data.class); } _setInlineStyle(e) { const styleElement = document.createElement('style'); styleElement.innerHTML = e.data.style; document.head.appendChild(styleElement); } _setLinkElement(e) { const request = new Promise((resolve) => { const linkElement = document.createElement('link'); linkElement.setAttribute('href', e.data.href); linkElement.setAttribute('rel', e.data.rel); linkElement.onload = resolve; linkElement.onerror = resolve; document.head.appendChild(linkElement); }); this.linkRequests.push(request); } _monitorFrameHeight() { if (window.ResizeObserver) { // use ResizeObserver if available const resizeObserver = new window.ResizeObserver(() => { this.fixScrollHeight(); }); resizeObserver.observe(document.body); } else { // fall back to polling setInterval(() => { if (document.body.scrollHeight !== this.lastBodyHeight) { this.lastBodyHeight = document.body.scrollHeight; this.fixScrollHeight(); } }, 250); } } async _requestResponse(payload) { // generate a random request id, so we can link the response const requestId = Math.random().toString(36).substring(2); payload.requestId = requestId; // post message to parent this._postMessage(payload); // wait for response return new Promise((resolve, reject) => { const responseHandler = (event) => { this.removeEventListener(requestId, responseHandler); if (event.data.success) { resolve(event.data.data); } else { reject(event.data.data); } }; this.addEventListener(requestId, responseHandler); }); } fixScrollHeight() { this._postMessage({ action: 'scrollHeight', scrollHeight: document.body.scrollHeight }); } closeSettings() { this._postMessage({ action: 'close' }); } showSpinner() { this._postMessage({ action: 'spinner.show' }); } hideSpinner() { this._postMessage({ action: 'spinner.hide' }); } disableSaveButton() { this._postMessage({ action: 'button.save.disabled' }); } enableSaveButton() { this._postMessage({ action: 'button.save.enabled' }); } showSchemaForm() { this._postMessage({ action: 'schema.show' }); } hideSchemaForm() { this._postMessage({ action: 'schema.hide' }); } createForm(schema, data, submitButton, cancelButton) { return new HomebridgeUiFormHelper(this, schema, data, submitButton, cancelButton); } endForm() { this._postMessage({ action: 'form.end' }); } async getPluginConfig() { return await this._requestResponse({ action: 'config.get' }); } async updatePluginConfig(pluginConfig) { return await this._requestResponse({ action: 'config.update', pluginConfig }); } async savePluginConfig() { return await this._requestResponse({ action: 'config.save' }); } async getPluginConfigSchema() { return await this._requestResponse({ action: 'config.schema' }); } async getCachedAccessories() { return await this._requestResponse({ action: 'cachedAccessories.get' }); } async request(path, body) { return await this._requestResponse({ action: 'request', path, body }); } async userCurrentLightingMode() { return await this._requestResponse({ action: 'user.lightingMode' }); } async i18nCurrentLang() { return await this._requestResponse({ action: 'i18n.lang' }); } async i18nGetTranslation() { return await this._requestResponse({ action: 'i18n.translations' }); } } class HomebridgeUiToastHelper { _postMessage(type, message, title) { window.parent.postMessage({ action: `toast.${type}`, message, title }, '*'); } success(message, title) { this._postMessage('success', message, title); } error(message, title) { this._postMessage('error', message, title); } warning(message, title) { this._postMessage('warning', message, title); } info(message, title) { this._postMessage('info', message, title); } } class HomebridgeUiFormHelper { parent; formId = Math.random().toString(36).substring(2); _changeHandle; _submitHandle; _cancelHandle; end; constructor(parent, schema, data, submitButton, cancelButton) { this.parent = parent; this.parent._postMessage({ action: 'form.create', formId: this.formId, schema, data, submitButton, cancelButton }); const handle = this._eventHandle.bind(this); this.parent.addEventListener(this.formId, handle); this.end = () => { this.parent.removeEventListener(this.formId, handle); this.parent._postMessage({ action: 'form.end', formId: this.formId, schema, data }); }; } _eventHandle(event) { switch (event.data.formEvent) { case 'change': { // general change events if (this._changeHandle && typeof this._changeHandle === 'function') { this._changeHandle(event.data.formData); } else { console.info('Homebridge Custom Plugin UI: Missing form onChange handler.'); } break; } case 'submit': { // submit form events if (this._submitHandle && typeof this._submitHandle === 'function') { this._submitHandle(event.data.formData); } else { console.info('Homebridge Custom Plugin UI: Missing form onSubmit handler.'); } break; } case 'cancel': { // cancel form events if (this._cancelHandle && typeof this._cancelHandle === 'function') { this._cancelHandle(event.data.formData); } else { console.info('Homebridge Custom Plugin UI: Missing form onCancel handler.'); } break; } default: { console.info('Unknown form event type:', event.data); } } } onChange(fn) { if (typeof fn !== 'function') { console.error('Homebridge Custom Plugin UI: Form onChange handler must be a function.'); return; } this._changeHandle = fn; } onSubmit(fn) { if (typeof fn !== 'function') { console.error('Homebridge Custom Plugin UI: Form onSubmit handler must be a function.'); return; } this._submitHandle = fn; } onCancel(fn) { if (typeof fn !== 'function') { console.error('Homebridge Custom Plugin UI: Form onCancel handler must be a function.'); return; } this._cancelHandle = fn; } } // @ts-expect-error - TS2339: Property _homebridge does not exist on type Window & typeof globalThis window.homebridge = new HomebridgePluginUi(); //# sourceMappingURL=ui.js.map