UNPKG

homebridge-poolcontroller-clocklear

Version:

A (semi) private Homebridge plugin for my custom Pool Controller app

149 lines 6.16 kB
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'; import { PoolControllerAccessory } from './platformAccessory.js'; import { PoolControllerClient } from './poolControllerClient.js'; /** * Homebridge platform: one Pool Controller accessory with multiple services. */ export class PoolControllerHomebridgePlatform { log; api; Service; Characteristic; accessories = []; client; config; controllerAccessory; pollTimer; constructor(log, config, api) { this.log = log; this.api = api; this.Service = api.hap.Service; this.Characteristic = api.hap.Characteristic; this.config = config; if (!config.url) { throw new Error('Missing required config parameter: url'); } if (!config.apiKey) { throw new Error('Missing required config parameter: apiKey'); } if (!config.relays || config.relays.length === 0) { throw new Error('Missing required config parameter: relays (select at least one)'); } this.client = new PoolControllerClient(config.url, config.apiKey); this.log.debug('Finished initializing platform:', this.config.name || 'Pool Controller'); this.api.on('didFinishLaunching', () => { this.discoverDevices() .then(() => this.startPoller()) .catch((error) => this.log.error('Failed to discover pool controller devices:', error)); }); this.api.on('shutdown', () => { if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = undefined; } }); } configureAccessory(accessory) { this.log.info('Loading accessory from cache:', accessory.displayName); this.accessories.push(accessory); } controllerUUID() { return this.api.hap.uuid.generate(`${this.config.url}::poolcontroller`); } legacyRelayUUID(relay) { return this.api.hap.uuid.generate(`${this.config.url}::${relay}`); } async discoverDevices() { const relays = await this.client.getRelays(); let schedulerEnabled = true; if (this.config.exposeScheduler !== false) { try { const schedules = await this.client.getSchedules(); schedulerEnabled = schedules.schedulerEnabled; } catch (error) { this.log.warn('Could not load scheduler state; defaulting to enabled:', error); } } const relayIds = this.config.relays .map((r) => parseInt(r, 10)) .filter((id) => { const found = relays.some((relay) => relay.relay === id); if (!found) { this.log.warn(`Relay ${id} not found in pool controller, skipping`); } return found; }); if (relayIds.length === 0) { throw new Error('No configured relays were found on the pool controller'); } this.unregisterLegacyAccessories(); const snapshot = { relays, schedulerEnabled }; const uuid = this.controllerUUID(); const displayName = this.config.name || 'Pool Controller'; const existing = this.accessories.find((accessory) => accessory.UUID === uuid); if (existing) { this.log.info('Restoring existing accessory from cache:', existing.displayName); existing.displayName = displayName; this.api.updatePlatformAccessories([existing]); this.controllerAccessory = new PoolControllerAccessory(this, existing, snapshot, relayIds); } else { this.log.info('Adding new accessory:', displayName); const accessory = new this.api.platformAccessory(displayName, uuid); accessory.context.isPoolController = true; this.controllerAccessory = new PoolControllerAccessory(this, accessory, snapshot, relayIds); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); this.accessories.push(accessory); } } unregisterLegacyAccessories() { const controllerUUID = this.controllerUUID(); const toRemove = this.accessories.filter((accessory) => accessory.UUID !== controllerUUID && this.config.relays.some((relay) => accessory.UUID === this.legacyRelayUUID(relay))); if (toRemove.length === 0) { return; } for (const accessory of toRemove) { this.log.info('Removing legacy per-relay accessory from cache:', accessory.displayName); } this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, toRemove); for (const accessory of toRemove) { const idx = this.accessories.indexOf(accessory); if (idx >= 0) { this.accessories.splice(idx, 1); } } } pollIntervalMs() { const seconds = this.config.pollIntervalSeconds ?? 30; return Math.max(5, Math.min(300, seconds)) * 1000; } startPoller() { const interval = this.pollIntervalMs(); this.log.info(`Starting poolcontroller poller every ${interval / 1000}s`); const tick = async () => { if (!this.controllerAccessory) { return; } try { const relays = await this.client.getRelays(); let schedulerEnabled = true; if (this.config.exposeScheduler !== false) { const schedules = await this.client.getSchedules(); schedulerEnabled = schedules.schedulerEnabled; } this.controllerAccessory.applySnapshot({ relays, schedulerEnabled }); } catch (error) { this.log.error('Poll failed; keeping last-known state:', error); } }; // Immediate refresh after launch, then interval. void tick(); this.pollTimer = setInterval(() => { void tick(); }, interval); } } //# sourceMappingURL=platform.js.map