UNPKG

homebridge-blaq

Version:

Control and view your garage door(s) remotely with real-time updates using Konnected's BlaQ hardware

233 lines 10.5 kB
import { AutoReconnectingEventSource } from './utils/eventsource.js'; import stripAnsi from 'strip-ansi'; import { createConnection } from 'net'; import { BlaQGarageDoorAccessory } from './accessory/garage-door.js'; import { BlaQGarageLightAccessory } from './accessory/garage-light.js'; import { BlaQGarageLockAccessory } from './accessory/garage-lock.js'; import { BlaQGarageMotionSensorAccessory } from './accessory/garage-motion-sensor.js'; import { BlaQGaragePreCloseWarningAccessory } from './accessory/garage-pre-close-warning.js'; import { BlaQGarageLearnModeAccessory } from './accessory/garage-learn-mode.js'; import { BlaQGarageObstructionSensorAccessory } from './accessory/garage-obstruction-sensor.js'; import { formatMAC } from './utils/formatters.js'; export class BlaQHub { pluginConfig; configDevice; accessories = []; eventSource; host; initialized = false; friendlyName; deviceMac; port; user; pass; eventsBeforeAccessoryInit = []; initAccessoryCallback; logger; constructor(pluginConfig, configDevice, initAccessoryCallback, logger) { this.pluginConfig = pluginConfig; this.configDevice = configDevice; logger.debug('Initializing BlaQHub...'); this.host = configDevice.host; this.port = configDevice.port; this.user = configDevice.username; this.pass = configDevice.password; this.initAccessoryCallback = initAccessoryCallback; this.logger = logger; this.reinitializeEventSource(); if (pluginConfig.enableNativeAPIHeartbeat) { setInterval(() => { this.performNativeAPIHeartbeat(); }, 5 * 60 * 1000); } logger.debug('Initialized BlaQHub!'); } getAPIBaseURL() { return `http://${this.host}:${this.port}`; } reinitializeEventSource() { if (this.eventSource) { this.eventSource.close(); } this.accessories.forEach(accessory => accessory.resetSyncState()); this.eventSource = new AutoReconnectingEventSource({ host: this.host, port: this.port, user: this.user, pass: this.pass, logger: this.logger, onStateUpdate: (stateEvent) => this.handleStateUpdate(stateEvent), onLog: (logEvent) => this.handleLogUpdate(logEvent), onPing: (pingEvent) => this.handlePingUpdate(pingEvent), }); } updateHostPort(host, port) { const isChanged = host !== this.host || port !== this.port; this.host = host; this.port = port; if (isChanged) { this.reinitializeEventSource(); this.accessories.forEach(accessory => accessory.setAPIBaseURL(this.getAPIBaseURL())); } } performNativeAPIHeartbeat() { const socket = createConnection({ host: this.host, port: 6053 }, (() => setTimeout(() => { socket.destroy(); }, 5 * 1000))); } possiblyFinalizeInit() { if (!this.initialized && this.friendlyName && this.deviceMac) { this.logger.info(`[${this.configDevice.displayName}] [init] Publishing accessories with device model:`, this.friendlyName); this.initAccessories({ friendlyName: this.friendlyName, serialNumber: this.deviceMac, }); this.initialized = true; this.eventsBeforeAccessoryInit.forEach(oldEvent => { const getFuncToCall = { 'ping': (accessory) => accessory.handlePingEvent?.bind(accessory), 'log': (accessory) => accessory.handleLogEvent?.bind(accessory), 'state': (accessory) => accessory.handleStateEvent?.bind(accessory), }[oldEvent.type]; this.accessories.forEach(accessory => { const funcToCall = getFuncToCall(accessory); if (funcToCall) { funcToCall(oldEvent.event); } }); }); this.eventsBeforeAccessoryInit = []; this.logger.debug(`[${this.configDevice.displayName}] [init] Accessories initialized!`); } } handleStateUpdate(msg) { if (!this.initialized) { this.eventsBeforeAccessoryInit.push({ type: 'state', event: msg }); } if (!this.initialized && msg.data !== '') { try { const b = JSON.parse(msg.data); if (['text_sensor-device_id'].includes(b.id)) { this.deviceMac = formatMAC(b.value); } this.possiblyFinalizeInit(); } catch (e) { this.logger.debug(`[${this.configDevice.displayName}] [init] Got event:`, msg); this.logger.debug(`[${this.configDevice.displayName}] [init] Got event data:`, msg.data); this.logger.error(`[${this.configDevice.displayName}] [init] Cannot parse BlaQTextSensorEvent`, e); } } this.logger.debug(`[${this.configDevice.displayName}] Processing state event:`, msg.data); this.accessories.forEach(accessory => { if (accessory.handleStateEvent) { accessory.handleStateEvent(msg); } }); } handleLogUpdate(msg) { if (!this.initialized) { this.eventsBeforeAccessoryInit.push({ type: 'log', event: msg }); } this.logger.debug(`[${this.configDevice.displayName}] GDO blaQ log:`, stripAnsi(msg.data)); this.accessories.forEach(accessory => { if (accessory.handleLogEvent) { accessory.handleLogEvent(msg); } }); } initGarageDoorAccessory({ platform, accessory, friendlyName, serialNumber }) { this.accessories.push(new BlaQGarageDoorAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), type: this.pluginConfig.garageDoorType, apiUser: this.user, apiPass: this.pass, })); } initGarageLightAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enableLight ?? true) { this.accessories.push(new BlaQGarageLightAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initGarageLockAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enableLockRemotes ?? true) { this.accessories.push(new BlaQGarageLockAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initGarageMotionSensorAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enableMotionSensor ?? true) { this.accessories.push(new BlaQGarageMotionSensorAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initGaragePreCloseWarningAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enablePreCloseWarning ?? true) { this.accessories.push(new BlaQGaragePreCloseWarningAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initGarageLearnModeAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enableLearnMode ?? true) { this.accessories.push(new BlaQGarageLearnModeAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initGarageObstructionSensorAccessory({ platform, accessory, friendlyName, serialNumber }) { if (this.pluginConfig.enableSeparateObstructionSensor ?? true) { this.accessories.push(new BlaQGarageObstructionSensorAccessory({ platform, accessory, friendlyName, serialNumber, apiBaseURL: this.getAPIBaseURL(), apiUser: this.user, apiPass: this.pass, })); } } initAccessories({ friendlyName, serialNumber }) { const { platform, accessory } = this.initAccessoryCallback(this.configDevice, friendlyName, serialNumber); this.initGarageDoorAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGarageLightAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGarageLockAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGarageMotionSensorAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGaragePreCloseWarningAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGarageLearnModeAccessory({ platform, accessory, friendlyName, serialNumber }); this.initGarageObstructionSensorAccessory({ platform, accessory, friendlyName, serialNumber }); } handlePingUpdate(msg) { if (!this.initialized) { this.eventsBeforeAccessoryInit.push({ type: 'ping', event: msg }); } if (!this.initialized && msg.data !== '') { try { const b = JSON.parse(msg.data); this.friendlyName = b.title; this.possiblyFinalizeInit(); } catch (e) { this.logger.debug(`[${this.configDevice.displayName}] [init] Got event:`, msg); this.logger.debug(`[${this.configDevice.displayName}] [init] Got event data:`, msg.data); this.logger.error(`[${this.configDevice.displayName}] [init] Cannot parse BlaQPingEvent`, e); } } this.accessories.forEach(accessory => { if (accessory.handleLogEvent) { accessory.handleLogEvent(msg); } }); } } //# sourceMappingURL=hub.js.map