UNPKG

homebridge-texecom-connect

Version:

A Homebridge plugin to integrate with Texecom Connect SmartCom.

284 lines 10.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TexecomConnectPlatform = void 0; const events_1 = require("events"); const net = __importStar(require("net")); const carbon_monoxide_sensor_accessory_1 = require("./accessory/carbon-monoxide-sensor-accessory"); const contact_sensor_accessory_1 = require("./accessory/contact-sensor-accessory"); const motion_sensor_accessory_1 = require("./accessory/motion-sensor-accessory"); const security_system_current_accessory_1 = require("./accessory/security-system-current-accessory"); const security_system_target_accessory_1 = require("./accessory/security-system-target-accessory"); const smoke_sensor_accessory_1 = require("./accessory/smoke-sensor-accessory"); const messages_1 = require("./interfaces/messages"); const settings_1 = require("./settings"); /** * Texecom Connect Platform. */ class TexecomConnectPlatform { accessories; accessoryEvent; api; characteristic; config; configAccessories; connection; log; service; lastSocketResponse; constructor(log, config, api) { this.api = api; this.config = config; this.log = log; this.accessories = []; this.accessoryEvent = new events_1.EventEmitter(); this.characteristic = this.api.hap.Characteristic; this.service = this.api.hap.Service; this.lastSocketResponse = 0; this.sanitiseConfig(); this.configAccessories = [...this.config.areas, ...this.config.zones] .filter((accessory) => typeof accessory.name === "string" && accessory.name.length > 1 && typeof accessory.number === "number" && accessory.number > 0); this.api .on("didFinishLaunching", this.onStartUp.bind(this)) .on("shutdown", this.onShutdown.bind(this)); } /** * Configure Accessory after Platform Init */ configureAccessory(accessory) { this.accessories.push(accessory); } getAccessoryId(configAccessory, prefix) { const accessoryLength = 3; const idNumber = Number(configAccessory.number) .toFixed() .padStart(accessoryLength, "0"); const idPrefix = prefix ?? (configAccessory.accessory === "security" ? messages_1.Messages.disarmUpdate : messages_1.Messages.zoneUpdate); return idPrefix + idNumber; } /** * Deprecate old Accessories which are invalid. */ deprecateAccessories() { this.accessories .filter((accessory) => !this.configAccessories.some((configAccessory) => configAccessory.accessory === accessory.context.config.accessory && configAccessory.name === accessory.context.config.name && configAccessory.number === accessory.context.config.number && accessory.UUID === this.api.hap.uuid.generate(this.getAccessoryId(configAccessory)))) .forEach((accessory) => { this.api.unregisterPlatformAccessories(settings_1.pluginName, settings_1.platformName, [accessory]); }); } /** * Discover new Accessories. */ discoverDevices() { this.configAccessories .forEach((configAccessory) => { const uuid = this.api.hap.uuid.generate(this.getAccessoryId(configAccessory)); let accessory = this.accessories.find((acc) => acc.UUID === uuid); if (accessory === undefined) { accessory = new this.api.platformAccessory(configAccessory.name, uuid); this.accessories.push(accessory); this.api.registerPlatformAccessories(settings_1.pluginName, settings_1.platformName, [accessory]); } this.initAccessory(accessory, configAccessory); this.api.updatePlatformAccessories([accessory]); }); } handleMessage(dataString) { let message; switch (dataString.length) { // eslint-disable-next-line @typescript-eslint/no-magic-numbers case 2: message = { event: dataString[0], state: dataString.endsWith("Y"), }; break; // eslint-disable-next-line @typescript-eslint/no-magic-numbers case 5: message = { event: dataString.slice(0, -1), state: dataString.endsWith("1"), }; break; default: message = { event: dataString, state: false, }; } this.accessoryEvent.emit(message.event, message.state); } initAccessory(accessory, config) { accessory.displayName = config.name; accessory.context.config = config; switch (config.accessory) { case "contact": new contact_sensor_accessory_1.ContactSensorAccessory(this, accessory); break; case "smoke": new smoke_sensor_accessory_1.SmokeSensorAccessory(this, accessory); break; case "carbon-monoxide": new carbon_monoxide_sensor_accessory_1.CarbonMonoxideSensorAccessory(this, accessory); break; case "security": new security_system_current_accessory_1.SecuritySystemAccessory(this, accessory); new security_system_target_accessory_1.SecuritySystemTargetAccessory(this, accessory); break; default: new motion_sensor_accessory_1.MotionSensorAccessory(this, accessory); } this.log.info("Accessory Loaded:", accessory.displayName); } /** * Shutdown */ onShutdown() { this.socketShutdown(); } /** * Start Up */ onStartUp() { this.deprecateAccessories(); this.discoverDevices(); this.socketStartUp(); this.socketVerify(); } parseData(data) { const events = this.sanitiseEventData(data); if (events === undefined || events.length === 0 || events[0] === undefined) { return; } if (!events[0].startsWith("\"")) { this.log.debug("Socket Data:", events[0]); if (this.connection?.destroyed === false) { this.connection.destroy(new RangeError("Invalid data from SmartCOM")); } return; } events.forEach((event) => { this.log.debug("Socket Data:", event); this.lastSocketResponse = Date.now(); this.handleMessage(event.substring(1)); }); } sanitiseConfig() { const maxPort = 65535; const defaultPort = 10001; this.config.port = typeof this.config.port === "number" && this.config.port >= 1 && this.config.port <= maxPort ? Math.floor(this.config.port) : defaultPort; this.config.host = typeof this.config.host === "string" ? this.config.host.trim() : "127.0.0.1"; if (!Array.isArray(this.config.areas)) { this.config.areas = []; } if (!Array.isArray(this.config.zones)) { this.config.zones = []; } } sanitiseEventData(data) { return typeof data === "string" || Buffer.isBuffer(data) ? data .toString() .trim() .split("\n") .map((value) => value.trim()) .filter((value) => value.length > 0) : undefined; } /** * Restarts the socket connection to SmartCom. */ socketRestart() { this.log.info("Reconnecting to SmartCom - %s:%s", this.config.host, this.config.port); this.socketShutdown(); const reconnectTimeout = 10000; global.setTimeout(this.socketStartUp.bind(this), reconnectTimeout); } /** * Shutdown the socket connection to SmartCom. */ socketShutdown() { if (this.connection?.destroyed === false) { this.connection.destroy(); } this.lastSocketResponse = 0; } /** * Start the socket connection to SmartCom. */ socketStartUp() { this.connection = net .createConnection(this.config.port, this.config.host) .setEncoding("utf8") .on("connect", () => { this.log.info("Connected to SmartCom - %s:%s", this.config.host, this.config.port); }) .on("error", (error) => { if (error.code === "ECONNREFUSED") { this.log.error("Unable to connect to %s:%s", this.config.host, this.config.port); } else { this.log.error(error.message); } }) .on("close", (hadError) => { if (hadError) { this.socketRestart(); } else { this.log.info("Disconnected from SmartCom - %s:%s", this.config.host, this.config.port); } }) .on("data", this.parseData.bind(this)); } socketVerify() { const verificationTimeout = 10000; global.setTimeout(() => { if (this.lastSocketResponse === 0 && this.connection?.destroyed === false) { this.connection.destroy(new Error("No response from SmartCom")); } }, verificationTimeout); } } exports.TexecomConnectPlatform = TexecomConnectPlatform; //# sourceMappingURL=texecom-connect-platform.js.map