UNPKG

homebridge-homeconnect

Version:

A Homebridge plugin that connects Home Connect appliances to Apple HomeKit

110 lines 3.91 kB
// Homebridge plugin for Home Connect home appliances // Copyright © 2019-2025 Alexander Thoukydides import { setImmediate as setImmediateP } from 'timers/promises'; // Count of serialised operation objects let serialisedCounter = 0; // Coalesce and serialise operations that should run exclusively export class Serialised { log; operation; defaultValue; options; // Operation currently in progress activePromise; // Pending operation pendingValue; pendingPromise; // Count of operations performed counter = 1; // Initialise a serialised operation manager constructor(log, operation, defaultValue, options = {}) { this.log = log; this.operation = operation; this.defaultValue = defaultValue; this.options = options; // Ensure that the operation has a name ++serialisedCounter; this.options.name ??= serialisedCounter.toString(); // Set the initial value for the pending operation this.pendingValue = defaultValue; } // Trigger the operation trigger(value) { this.updatePendingValue(value); this.pendingPromise ??= this.startPending(); return this.pendingPromise; } // Wait for any active operation to complete and start a new one async startPending() { // Wait for any active operation to complete try { await this.activePromise; } catch { /* empty */ } await setImmediateP(); // Start the pending operation and reset ready for the next this.activePromise = this.startActive(this.pendingValue); this.resetPendingValue(); this.pendingPromise = undefined; // Wait for the active operation to complete try { return await this.activePromise; } finally { this.activePromise = undefined; } } // Start a new operation async startActive(value) { const logPrefix = this.logPrefix; ++this.counter; try { this.logVerbose(`${logPrefix} starting: ${JSON.stringify(value)}`); const result = await this.operation(value); this.logVerbose(`${logPrefix} successful`); return result; } catch (err) { const message = err instanceof Error ? err.message : String(err); this.logVerbose(`${logPrefix} failed: ${message}`); throw err; } } // Merge a new value with the pending value updatePendingValue(value) { let description; if (typeof value === 'object') { // Objects are merged, with new properties overwriting old ones const newValue = Object.assign({}, this.pendingValue, value); description = `${JSON.stringify(this.pendingValue)} + ${JSON.stringify(value)} = ${JSON.stringify(newValue)}`; this.pendingValue = newValue; } else if (value !== undefined) { // Other types just use the latest value // eslint-disable-next-line @typescript-eslint/restrict-template-expressions description = `${value} (was ${this.pendingValue})`; this.pendingValue = value; } else { // No value provided so keep the existing value description = '(no value)'; } this.logVerbose(`${this.logPrefix} coalescing: ${description}`); } // Reset the pending value resetPendingValue() { if (this.options.reset) { this.pendingValue = this.defaultValue; } } // Prefix for log messages get logPrefix() { return `Serialised request #${this.options.name}.${this.counter}`; } // Verbose logging logVerbose(message) { if (this.options.verbose) this.log.debug(message); } } //# sourceMappingURL=serialised.js.map