UNPKG

iobroker.gree-hvac

Version:
253 lines (252 loc) 8.99 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var connection_exports = {}; __export(connection_exports, { Connection: () => Connection }); module.exports = __toCommonJS(connection_exports); var dgram = __toESM(require("node:dgram")); var import_node_events = require("node:events"); var import_encryptor = require("./encryptor"); const commandsMap = { bind: "bindok", status: "dat", cmd: "res" }; class Connection extends import_node_events.EventEmitter { requestTimeoutMs; logger; socket; socketScan; devices = {}; deviceEncVer = {}; /** * */ constructor(address, logger, requestTimeoutMs = 1e3) { super(); this.logger = logger; this.requestTimeoutMs = requestTimeoutMs; this.socket = dgram.createSocket("udp4"); this.socketScan = dgram.createSocket("udp4"); this.socketScan.on("message", this.handleResponse.bind(this)); this.socketScan.on("listening", () => { const socketAddress = this.socketScan.address(); this.logger.info(`Socket server is listening on ${socketAddress.address}:${socketAddress.port}`); this.scan(address); }); this.socket.on("error", (error) => { this.logger.error(error.message); }); this.socketScan.on("error", (error) => { this.logger.error(error.message); }); this.socket.bind(); this.socketScan.bind(); } /** * */ registerKey(deviceId, key) { this.logger.debug(`Registering key: ${deviceId} - ${key}`); this.devices[deviceId] = key; } /** * */ getEncryptionKey(deviceId) { return this.devices[deviceId] || import_encryptor.defaultKey; } /** * */ registerEncVersion(deviceId, encVer) { this.logger.debug(`Registering encVer: ${deviceId} - ${encVer}`); this.deviceEncVer[deviceId] = encVer; } /** * */ getEncVersion(deviceId) { return this.deviceEncVer[deviceId] || 1; } /** * */ scan(ipAddresses) { const message = Buffer.from(JSON.stringify({ cid: "app", t: "scan", i: 1, uid: 0 })); ipAddresses.split(";").forEach((deviceAddress) => { this.logger.debug(`Test address ${deviceAddress} for available device with message: ${message}`); this.socketScan.send(message, 0, message.length, 7e3, deviceAddress); }); } /** * */ async sendRequest(address, port, payload, encVersion) { return new Promise((resolve, reject) => { var _a; let requestTimeout; try { const key = this.getEncryptionKey(address); const effectiveEncVersion = encVersion != null ? encVersion : this.getEncVersion(address); let pack; let tag; if (effectiveEncVersion === 1) { pack = (0, import_encryptor.encryptV1)(payload, key); } else { const { encPack, encTag } = (0, import_encryptor.encryptV2)(payload, key); pack = encPack; tag = encTag; } this.logger.debug(`Payload: ${JSON.stringify(payload)}`); this.logger.debug(`key: ${key}`); this.logger.debug(`pack: ${pack}`); this.logger.debug(`tag: ${tag}`); this.logger.debug(`encVersion: ${effectiveEncVersion}`); const request = { cid: "app", i: payload.t === "bind" ? 1 : key === import_encryptor.defaultKey ? 1 : 0, t: "pack", uid: 0, tcid: payload.mac, pack, tag }; const messageHandler = (msg, rinfo) => { var _a2; const message = JSON.parse(msg.toString()); this.logger.debug( `Received message from ${message.cid} (${rinfo.address}:${rinfo.port}) ${msg.toString()} - ${JSON.stringify(rinfo)}` ); if (rinfo.address !== address || rinfo.port !== port) { return; } const decKey = this.getEncryptionKey(rinfo.address); const decTag = message.tag; let resolvedEncVersion = decTag != void 0 ? 2 : 1; if (resolvedEncVersion === 2) { this.registerEncVersion(rinfo.address, resolvedEncVersion); } else { resolvedEncVersion = this.getEncVersion(rinfo.address); } this.logger.debug(`decKey: ${decKey}`); this.logger.debug(`decTag: ${decTag}`); this.logger.debug(`encVersion: ${resolvedEncVersion}`); let response; try { if (resolvedEncVersion === 1) { response = (0, import_encryptor.decryptV1)(message.pack, decKey); } else { response = (0, import_encryptor.decryptV2)(message.pack, decKey, decTag); } this.logger.debug(`sendRequest - Response data: ${JSON.stringify(response)}`); } catch (e) { this.logger.error( `Can not decrypt message from ${message.cid} (${rinfo.address}:${rinfo.port}) with key ${decKey} and tag ${decTag}` ); this.logger.error((_a2 = e.stack) != null ? _a2 : String(e)); return; } if (response.t !== commandsMap[payload.t]) { this.logger.debug(`No matching command: ${response.t}`); return; } if (response.mac !== payload.mac) { this.logger.debug(`No matching mac ${response.mac} - ${payload.mac}`); return; } this.socket.off("message", messageHandler); clearTimeout(requestTimeout); resolve(response); }; this.logger.debug(`Sending request to ${address}:${port}: ${JSON.stringify(request)}`); this.socket.on("message", messageHandler); const toSend = Buffer.from(JSON.stringify(request)); this.socket.send(toSend, 0, toSend.length, port, address); requestTimeout = setTimeout(() => { this.socket.off("message", messageHandler); clearTimeout(requestTimeout); reject(new Error(`Request to ${address}:${port} timed out`)); }, this.requestTimeoutMs); } catch (e) { this.logger.error((_a = e.stack) != null ? _a : String(e)); reject(e instanceof Error ? e : new Error(String(e))); } }); } /** * */ close() { this.socket.close(); this.socketScan.close(); } /** * */ handleResponse(msg, rinfo) { var _a; let message; try { message = JSON.parse(msg.toString()); } catch { this.logger.error(`Device ${rinfo.address}:${rinfo.port} sent invalid JSON that can not be parsed`); this.logger.debug(String(msg)); return; } const tag = message.tag; const encVersion = tag != void 0 ? 2 : 1; const key = encVersion === 1 ? import_encryptor.defaultKey : import_encryptor.defaultKeyGCM; this.logger.silly(`key: ${key}`); this.logger.silly(`tag: ${tag}`); this.logger.silly(`encVersion: ${encVersion}`); let response; try { if (encVersion === 1) { response = (0, import_encryptor.decryptV1)(message.pack, key); } else { response = (0, import_encryptor.decryptV2)(message.pack, key, tag); } this.logger.debug(`handleResponse - Response data: ${JSON.stringify(response)}`); } catch (e) { this.logger.error( `handleResponse - Can not decrypt message from ${message.cid} (${rinfo.address}:${rinfo.port}) with key ${key} and tag ${tag}` ); this.logger.error((_a = e.stack) != null ? _a : String(e)); return; } this.emit(response.t, response, rinfo, encVersion); } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Connection }); //# sourceMappingURL=connection.js.map