UNPKG

homebridge-homeconnect

Version:

A Homebridge plugin that connects Home Connect appliances to Apple HomeKit

129 lines 6.02 kB
// Homebridge plugin for Home Connect home appliances // Copyright © 2023 Alexander Thoukydides import { setTimeout as setTimeoutP } from 'timers/promises'; import { MockAppliance } from './mock-appliance.js'; import { MockDishwasher } from './mock-dishwasher.js'; import { MockHob } from './mock-hob.js'; import { MockHood } from './mock-hood.js'; import { MockOven } from './mock-oven.js'; import { MockFridgeFreezer } from './mock-fridgefreezer.js'; import { MockCoffeeMaker } from './mock-coffeemaker.js'; import { MockDryer } from './mock-dryer.js'; import { MockWasher } from './mock-washer.js'; // Random delay before completing API requests const MOCK_MIN_DELAY = 1; // (milliseconds) const MOCK_MAX_DELAY = 20; // (milliseconds) // Low-level access to the Home Connect API with mocked appliances export class MockAPI { // Create a new API object constructor(log, config, persist) { this.log = log; this.config = config; this.persist = persist; // The mock appliances this.appliances = new Map(); // Create the mock appliances this.addMock(MockCoffeeMaker); this.addMock(MockDishwasher); this.addMock(MockDryer); this.addMock(MockFridgeFreezer); this.addMock(MockHob); this.addMock(MockHood); this.addMock(MockOven); this.addMock(MockWasher); } // Instantiate a mock appliance addMock(constructor) { const mockAppliance = new constructor(this.log); this.appliances.set(mockAppliance.haid, mockAppliance); } // Delay requests and events async delay() { await setTimeoutP(MOCK_MIN_DELAY + Math.random() * (MOCK_MAX_DELAY - MOCK_MIN_DELAY)); } // Wrap an API request async request(method, haid, ...args) { await this.delay(); const mockAppliance = this.appliances.get(haid); if (!mockAppliance) throw MockAppliance.statusCodeError(404, `Unknown appliance "${haid}"`); const fn = mockAppliance[method]; try { const result = fn.bind(mockAppliance)(...args); await this.delay(); return result; } catch (err) { await this.delay(); throw err; } } // Check whether a particular scope has been authorised hasScope(_scope) { return true; } // Get authorisation status updates async getAuthorisationStatus() { return Promise.resolve({ state: 'success' }); } // Trigger a retry of Device Flow authorisation retryAuthorisation() { } // Get a list of paired home appliances async getAppliances() { return Promise.resolve([...this.appliances.values()].map(appliance => appliance.getAppliance())); } // Forward most methods to the appropriate mock appliance /* eslint-disable max-len */ getAppliance(...args) { return this.request('getAppliance', ...args); } getPrograms(...args) { return this.request('getPrograms', ...args); } getAvailablePrograms(...args) { return this.request('getAvailablePrograms', ...args); } getAvailableProgram(...args) { return this.request('getAvailableProgram', ...args); } getActiveProgram(...args) { return this.request('getActiveProgram', ...args); } setActiveProgram(...args) { return this.request('setActiveProgram', ...args); } stopActiveProgram(...args) { return this.request('stopActiveProgram', ...args); } getActiveProgramOptions(...args) { return this.request('getActiveProgramOptions', ...args); } setActiveProgramOptions(...args) { return this.request('setActiveProgramOptions', ...args); } getActiveProgramOption(...args) { return this.request('getActiveProgramOption', ...args); } setActiveProgramOption(...args) { return this.request('setActiveProgramOption', ...args); } getSelectedProgram(...args) { return this.request('getSelectedProgram', ...args); } setSelectedProgram(...args) { return this.request('setSelectedProgram', ...args); } getSelectedProgramOptions(...args) { return this.request('getSelectedProgramOptions', ...args); } setSelectedProgramOptions(...args) { return this.request('setSelectedProgramOptions', ...args); } getSelectedProgramOption(...args) { return this.request('getSelectedProgramOption', ...args); } setSelectedProgramOption(...args) { return this.request('setSelectedProgramOption', ...args); } getStatus(...args) { return this.request('getStatus', ...args); } getStatusSpecific(...args) { return this.request('getStatusSpecific', ...args); } getSettings(...args) { return this.request('getSettings', ...args); } getSetting(...args) { return this.request('getSetting', ...args); } setSetting(...args) { return this.request('setSetting', ...args); } getCommands(...args) { return this.request('getCommands', ...args); } setCommand(...args) { return this.request('setCommand', ...args); } /* eslint-enable max-len */ // Get events for a single appliance or all appliances async *getEvents(haid) { // Select the appliances const appliances = haid ? [this.appliances.get(haid)] : this.appliances.values(); // Receive the event streams from all of the selected appliances let eventPromise; let eventResolve; const receiveEvents = async (events) => { for await (const event of events) { await this.delay(); eventResolve(event); } }; for (const mockAppliance of appliances) { if (mockAppliance) receiveEvents(mockAppliance.getEvents()); } // Merge the event streams yield { event: 'START' }; eventPromise = new Promise(resolve => eventResolve = resolve); for (;;) { const event = await eventPromise; eventPromise = new Promise(resolve => eventResolve = resolve); yield event; } } } //# sourceMappingURL=index.js.map