UNPKG

homebridge-homeconnect

Version:

A Homebridge plugin that connects Home Connect appliances to Apple HomeKit

141 lines 5.05 kB
// Homebridge plugin for Home Connect home appliances // Copyright © 2023-2025 Alexander Thoukydides import { setImmediate as setImmediateP } from 'timers/promises'; import { logError } from '../log-error.js'; // Appliance data required for the configuration schema generator export class ConfigSchemaData { // Create a new schema generator constructor(log, persist) { this.log = log; this.persist = persist; // Details of known appliances, indexed by haId this.appliances = new Map(); } // Update the active plugin configuration async setConfig(config) { await this.applyUpdate(() => { this.config = config; }); } // Update the list of accessories async setAppliances(newAppliances) { await this.applyUpdate(() => { const appliances = new Map(); for (const ha of newAppliances) { const appliance = { programs: [], features: [], ...this.appliances.get(ha.haId), ...ha }; appliances.set(ha.haId, appliance); } this.appliances = appliances; }); } // Set whether the Control scope has been authorised for an appliance async setHasControl(haId, control) { await this.applyUpdate(() => { const appliance = this.appliances.get(haId); if (appliance) appliance.hasControl = control; }); } // Add the list of optional features for an appliance to the schema async setOptionalFeatures(haId, features) { await this.applyUpdate(() => { const appliance = this.appliances.get(haId); if (appliance) appliance.features = features; }); } // Add the list of programs for an appliance to the schema async setPrograms(haId, newPrograms) { await this.applyUpdate(() => { const appliance = this.appliances.get(haId); if (!appliance) return; const findProgram = (key) => appliance.programs.find(p => p.key === key); appliance.programs = newPrograms.map(program => ({ ...findProgram(program.key), ...program })); }); } // Add the options for an appliance program to the schema async setProgramOptions(haId, programKey, options) { await this.applyUpdate(() => { const appliance = this.appliances.get(haId); const program = appliance?.programs.find(p => p.key === programKey); if (program) program.options = options; }); } // Apply an update to the schema data async applyUpdate(update) { // Load the old schema data await this.load(); // Perform the required update update(); // Save the updated data const save = async () => { // Coalesce updates from the same event loop await setImmediateP(); // Perform the write this.exclusive(async () => { delete this.savePromise; this.log.debug('Saving configuration schema data'); await this.trySet(); }); }; this.savePromise ?? (this.savePromise = save()); await this.savePromise; } // Read any previously saved data async load(reload = false) { if (reload || !this.loadPromise) this.loadPromise = this.exclusive(() => this.tryGet()); await this.loadPromise; } // Perform an operation that must be exclusive async exclusive(operation) { // Wait for any previous operation to complete while (this.busyPromise) await this.busyPromise; // Perform the requested operation const busyOperation = async () => { try { await operation(); } finally { delete this.busyPromise; } }; this.busyPromise = busyOperation(); await this.busyPromise; } // Attempt to read previously saved data async tryGet() { try { const persist = await this.persist.getItem('config.schema.json'); if (persist) { this.config = persist.config; this.appliances = new Map(Object.entries(persist.appliances ?? {})); } } catch (err) { logError(this.log, 'Failed to load configuration schema data', err); } } // Attempt to write new data async trySet() { try { const persist = { config: this.config, appliances: Object.fromEntries(this.appliances) }; await this.persist.setItem('config.schema.json', persist); } catch (err) { logError(this.log, 'Failed to save configuration schema data', err); } } } //# sourceMappingURL=schema-data.js.map