homebridge-homeconnect
Version:
A Homebridge plugin that connects Home Connect appliances to Apple HomeKit
150 lines • 5.25 kB
JavaScript
// Homebridge plugin for Home Connect home appliances
// Copyright © 2023-2025 Alexander Thoukydides
import { elementWithAbsolutePaths, cloneTemplate, getHTML } from './utils-dom.js';
// A configuration form handler
export class Form {
// Create a configuration form
constructor(log) {
this.log = log;
}
// Retrieve and display the form
async createForm() {
// Retrieve the form schema and data
const schema = await this.getSchema();
const data = await this.getData();
// Create the form (adding header and footer)
const patchedSchema = this.patchSchema(schema);
this.log.debug('createForm', patchedSchema, data);
this.form = window.homebridge.createForm(patchedSchema, data);
// Return any changes to the configuration
this.form.onChange(config => this.putData(config));
}
// Hide the form (implicitly called if a different form is created)
destroyForm() {
this.form?.end();
this.form = undefined;
}
// Add a header and footer to the schema form
patchSchema(schema) {
const form = [
...(schema.form ?? []),
...this.formFromTemplate('hc-form-footer')
];
return { ...schema, form };
}
// Create a form item from a template
formFromTemplate(...args) {
const template = cloneTemplate(...args);
const absTemplate = elementWithAbsolutePaths(template);
return this.formFromHTML(absTemplate);
}
// Create a form item from an HTML element
formFromHTML(element) {
return [{
type: 'help',
helpvalue: getHTML(element)
}];
}
}
// A global configuration form handler
export class GlobalForm extends Form {
constructor(log, ipc, config) {
super(log);
this.ipc = ipc;
this.config = config;
}
getSchema() { return this.ipc.request('/schema/global', null); }
getData() { return this.config.getGlobal(); }
putData(data) { return this.config.setGlobal(data); }
}
// An configuration form handler for a specific appliance
export class ApplianceForm extends Form {
constructor(log, ipc, config, haid) {
super(log);
this.ipc = ipc;
this.config = config;
this.haid = haid;
}
getSchema() { return this.ipc.request('/schema/appliance', this.haid); }
getData() { return this.config.getAppliance(this.haid); }
putData(data) { return this.config.setAppliance(this.haid, data); }
}
// A placeholder form handler
export class TemplateForm extends Form {
constructor(log, ...args) {
super(log);
this.elementAsForm = this.formFromTemplate(...args);
}
// Create a placeholder schema using the specified HTML element
async getSchema() {
return Promise.resolve({
schema: {
type: 'object',
properties: {}
},
form: this.elementAsForm
});
}
async getData() { return Promise.resolve({}); }
async putData() { }
}
// Identifiers for special forms
export var FormId;
(function (FormId) {
FormId["Global"] = "global";
FormId["Placeholder"] = "placeholder";
FormId["Unavailable"] = "unavailable";
})(FormId || (FormId = {}));
// Type guard to check whether a string is a special form identifier
function isFormId(value) {
return Object.values(FormId).includes(value);
}
// Manage the configuration forms
export class Forms {
// Create a new form manager
constructor(log, ipc, config) {
this.log = log;
this.ipc = ipc;
this.config = config;
this.showForm();
}
// Display the specified form (defaulting to the placeholder)
async showForm(formId) {
try {
// The forms are retrieved from the server, so display a spinner
window.homebridge.showSpinner();
// Display the requested form, with fallback to the error form
try {
await this.constructAndShowForm(formId ?? FormId.Placeholder);
}
catch (err) {
this.log.warn('Failed to display form', formId, err);
await this.constructAndShowForm(FormId.Unavailable);
}
}
finally {
// Ensure that the spinner is always hidden
window.homebridge.hideSpinner();
}
}
// Try to create the requested form, with fallback to the placeholder
async constructAndShowForm(formId) {
const form = this.constructForm(formId);
await form.createForm();
this.currentForm = form;
}
// Create a specific form
constructForm(formId) {
if (isFormId(formId)) {
switch (formId) {
case FormId.Placeholder: return new TemplateForm(this.log, 'hc-form-placeholder');
case FormId.Unavailable: return new TemplateForm(this.log, 'hc-form-unavailable');
case FormId.Global: return new GlobalForm(this.log, this.ipc, this.config);
}
}
else {
return new ApplianceForm(this.log, this.ipc, this.config, formId);
}
}
}
//# sourceMappingURL=forms.js.map