UNPKG

homebridge-smartsystem

Version:

SmartServer (Proxy Websockets to TCP sockets, Smappee MQTT, Duotecno IP Nodes, Homekit interface)

298 lines (249 loc) 9.88 kB
import { PlatformConfig, Sanitizers, UnitExtendedType } from "../duotecno/types"; import { log, setLogFunction, LogLevel, logSettings } from "../duotecno/logger"; import { API, Logger } from "homebridge"; import { System } from "../duotecno/system"; import { Smappee } from "./smappee"; import { Power, SmartApp } from "./smartapp"; import { Dimmer } from "../accessories/dimmer"; import { Unit } from "../duotecno/protocol"; import { Switch } from "../accessories/switch"; import { Bulb } from "../accessories/bulb"; import { WindowCovering } from "../accessories/windowcovering"; import { GarageDoor } from "../accessories/garagedoor"; import { Mood } from "../accessories/mood"; import { Temperature } from "../accessories/temperature"; import { Base } from "./base"; import { Accessory } from "../accessories/accessory"; import { SocApp } from "./socapp"; import { Door } from "../accessories/door"; import { Lock } from "../accessories/lock"; import { P1 } from "./p1"; import { Shelly } from "./shelly"; import { kConfigFiles, setConfig } from "../duotecno/config"; import { readFileSync } from "fs"; import { cleanStart, setProxyConfig } from "./proxy"; export class Platform extends Base { homebridgeAPI: API; config: PlatformConfig; system: System; power: Power; smartapp: SmartApp; smartsoc: SocApp; accessoryList: Array<Accessory> = []; ready = false; startWaiting = 0; constructor(logger: Logger, pConfig: PlatformConfig, api: API) { super("homebridge"); if (logger.info) setLogFunction(logger.info); this.config = {... pConfig}; this.config.debug = (pConfig && ("debug" in pConfig)) ? pConfig.debug : false; if (this.config.debug) logSettings["homebridge"] = LogLevel.debug; if (typeof this.config.system.cmasters === "undefined") { log("homebridge", "try doing upgrade from pre v6.2 versions"); this.doUpgradeV62(); } log("homebridge", "running in " + ((this.config.debug) ? "debug" : "prod") + " mode in directory: " + process.cwd()); log("homebridge", "with config:" + JSON.stringify(this.config, null, 2)); // save config for other components setConfig(this.config); // remember api for later use this.homebridgeAPI = api; if (this.config.system) { try { this.system = new System(); this.system.openMasters(true); this.system.emitter.on('ready', this.systemReady.bind(this)); this.system.emitter.on('update', this.updateState.bind(this)); } catch(err) { log("homebridge", err); if (!this.system) { log("homebridge", "platform - Can't run without a System."); return; } } } else { log("homebridge", "platform - No System configured -> can't run !!!"); return; } // start power managers this.power = {}; this.startPower("smappee", Smappee); this.startPower("p1", P1); this.startPower("shelly", Shelly); // startup a smartApp if configured if (this.config.smartapp) { try { this.smartapp = new SmartApp(this.system, this.power, this); this.smartapp.serve(); } catch(err) { log("homebridge", err); if (!this.smartapp) { log("homebridge", "platform - No SmartApp started."); } } } else { log("homebridge", "platform - No SmartApp configured."); } // startup a SocApp if configured -> for proxy (not needed for normal operations) if (this.config.socapp) { try { // reuse the config file from the smartapp this.smartsoc = new SocApp(this.system, 'socapp'); this.smartsoc.serve(); } catch(err) { log("homebridge", err); if (!this.smartsoc) { log("homebridge", "platform - No SocApp started."); } } } else { log("homebridge", "platform - No SocApp configured."); } if (this.config.proxy && this.config.proxy?.uniqueId) { this.config.proxy.kind = "gw"; setProxyConfig(this.config.proxy); cleanStart(true); log("homebridge", "platform - Proxy configured: " + JSON.stringify(this.config.proxy, null, 2)); } else { log("homebridge", "platform - No Proxy configured."); } } startPower(type: string, PowerMgr: any) { // startup a power handler, if configured if (this.config[type]) { try { this.power[type] = new PowerMgr(this.system); } catch(err) { log("homebridge", err); if (!this.power[type]) { log("homebridge", "platform - No " + type + " started."); } } } else { log("homebridge", "platform - No " + type + " configured."); } } updateState(unit: Unit) { const accessory = this.accessoryList.find((acc: Accessory) => unit.isUnit(acc.unit)); if (accessory) { accessory.updateState(); } } systemReady() { const inConfig = this.system.activeUnitsConfig().length; const inSystem = this.system.allActiveUnits().length; log("homebridge", "=== SYSTEM READY received update -> addMasters: " + inSystem + " of " + inConfig + " ==="); this.ready = (inSystem == inConfig); // trigger status request of all active units in 2 seconds. setTimeout( async() => { let units = this.system.allActiveUnits(); for (let u of units) { await u.node.master.requestUnitStatus(u) } }, 2000); } accessories(callback) { // waiting until the database is complete or give up after 5 minutes const kMaxWaiting = 5 * 60; const inConfig = this.system.activeUnitsConfig().length; const inSystem = this.system.allActiveUnits().length; this.ready = (inSystem == inConfig); if (this.ready) { log("homebridge", "=== running after timeout of " + this.startWaiting + " secs --> " + inSystem + " == " + inConfig + " units -> " + (inConfig == inSystem) + ", will start in 10 seconds ==="); setTimeout( () => { this.doAccessories(callback) }, 10 * 1000); } else if (this.startWaiting > kMaxWaiting) { // give up, retry connection to the masters, a manager like "forever" or "pm2" will restart us. log("homebridge", "=== giving up after " + kMaxWaiting + " secs -> auto restart system ==="); process.exit(-1) } else { // wait another 5 seconds. log("homebridge", "=== waiting >> found " + inSystem + " units out of " + inConfig + " selected after " + this.startWaiting + " sec ==="); this.startWaiting += 5; setTimeout( () => { this.accessories(callback) }, 5000); } } doAccessories(callback) { log("homebridge", "platform - accessories() called by Homebridge - System is ready"); this.addAccessories() .then( list => { log("homebridge", "platform - addAccessories returned: " + list.length + " accessories.") callback(list); }) .catch( reason => { log("homebridge", "platform - addAccessories errored: " + reason); callback([]); }); } async addAccessories() { if (!this.system) { log("homebridge", "platform - No System -> No accessories"); this.accessoryList = []; return; } // clear our list this.accessoryList = []; this.system.allActiveUnits().forEach(unit => { log("homebridge", "adding accessory: " + unit.getDescription()); switch (unit.getType()) { case UnitExtendedType.kDimmer: this.accessoryList.push( new Dimmer(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kSwitch: this.accessoryList.push( new Switch(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kLightbulb: this.accessoryList.push( new Bulb(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kSwitchingMotor: this.accessoryList.push( new WindowCovering(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kGarageDoor: this.accessoryList.push( new GarageDoor(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kDoor: this.accessoryList.push( new Door(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kLock: case UnitExtendedType.kUnlocker: this.accessoryList.push( new Lock(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kMood: case UnitExtendedType.kCondition: case UnitExtendedType.kInput: this.accessoryList.push( new Mood(this.homebridgeAPI, unit) ); break; case UnitExtendedType.kTemperature: this.accessoryList.push( new Temperature(this.homebridgeAPI, unit) ); break; default: log("homebridge", "platform - addAccessories: accessory type not yet supported: " + unit.typeName() + " ("+ unit.getName() + ")") break; } }); return this.accessoryList; } doUpgradeV62() { function readFile(type: string): object | boolean { const fn = kConfigFiles + "/config." + type + ".json"; try { const configBuf = readFileSync(fn); const configStr = configBuf.toString(); return (Sanitizers[type])(JSON.parse(configStr)); } catch(err) { log("system", "Couldn't read my config file (" + fn + ") -- setting to 'false'"); return false; } } this.config.system = readFile("system"); this.config.smartapp = readFile("smartapp"); this.config.scenes = readFile("scenes"); this.config.settings = readFile("settings"); this.config.smappee = readFile("smappee"); this.config.p1 = readFile("p1"); this.config.shelly = readFile("shelly"); setConfig(this.config); log("homebridge", "Upgraded the config file -> NOW: log in and save something..."); } }