UNPKG

neramirez-broadlink-ts

Version:

A TypeScript-enhanced Node.JS fork of broadlinkjs, designed for interacting with RM devices in homebridge-broadlink-rm. Now includes a feature for handling multiple requests to the same device, with a specific focus on supporting homebridge-broadlink-wind

129 lines 6.77 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketHandler = void 0; const dgram_1 = __importDefault(require("dgram")); const crypto_1 = __importDefault(require("crypto")); const device_types_1 = require("./device.types"); const payload_handler_1 = require("./types/payload.handler"); class SocketHandler { constructor(logger, host, macAddress, deviceType, packetHandler) { this.handleMessage = (response) => { if (response.length < 0x39) return; const requestId = response.readUInt16LE(0x28); const encryptedPayload = Buffer.alloc(response.length - 0x38, 0); response.copy(encryptedPayload, 0, 0x38); const err = response[0x22] | (response[0x23] << 8); if (err != 0) return; const decipher = crypto_1.default.createDecipheriv("aes-128-cbc", this.packetHandler.getKey(), this.packetHandler.getIv()); decipher.setAutoPadding(false); let payload = decipher.update(encryptedPayload); const { resolve, reject, timeout, startTime, queueItem } = this.promises[requestId]; clearTimeout(timeout); delete this.promises[requestId]; // Calculate the time difference const endTime = process.hrtime.bigint(); const timeDiff = Number(endTime - startTime) / 1e6; // convert to milliseconds this.logger.info(`Response received: ${requestId}, Time taken: ${timeDiff} ms`); const p2 = decipher.final(); if (p2) payload = Buffer.concat([payload, p2]); if (!payload) { reject("No Payload"); queueItem === null || queueItem === void 0 ? void 0 : queueItem.promiseExecutor.reject("No Payload"); } this.logger.info(`Response received: ${requestId}`); const command = response[0x26]; if (command == 0xe9) { this.packetHandler.updateKey(Buffer.alloc(0x10, 0)); payload.copy(this.packetHandler.getKey(), 0, 0x04, 0x14); const id = Buffer.alloc(0x04, 0); payload.copy(id, 0, 0x00, 0x04); this.packetHandler.setId(id); resolve(payload); queueItem === null || queueItem === void 0 ? void 0 : queueItem.promiseExecutor.resolve(payload); //this.emit("deviceReady"); } else if (command == 0xee || command == 0xef) { const payloadHex = payload.toString("hex"); const requestHeaderHex = this.request_header.toString("hex"); const indexOfHeader = payloadHex.indexOf(requestHeaderHex); if (indexOfHeader > -1) { payload = payload.subarray(indexOfHeader + this.request_header.length, payload.length); } this.onPayloadReceived(err, payload); resolve(payload); queueItem === null || queueItem === void 0 ? void 0 : queueItem.promiseExecutor.resolve(payload); } else if (command == 0x72) { this.logger.info("Command Acknowledged"); } else { this.logger.info("Unhandled Command: ", command); } }; this.sendPacket = async (command, packet, requestId, queueItem) => { return await new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error(`Timeout: handleMessage for ${requestId} was not called within the specified time.`)); delete this.promises[requestId]; // remove the promise as it's no longer needed }, 5000); // 5000 milliseconds = 5 seconds const startTime = process.hrtime.bigint(); this.promises[requestId] = { startTime, resolve, reject, timeout, queueItem }; this.socket.send(packet, 0, packet.length, this.host.port, this.host.address, (err, _bytes) => { if (err) { this.logger.debug("send packet error", err); } else { this.logger.debug(`Packet sent to ${this.host.address}:${this.host.port} with command 0x${command.toString(16)}`); this.logger.debug(`MAC Address: ${this.macAddress.toString("hex")}`); this.logger.debug(`Payload: ${packet.toString("hex")}`); } }); }); }; this.setupSocket = () => { const socket = dgram_1.default.createSocket({ type: "udp4", reuseAddr: true }); socket.on("listening", () => { socket.setBroadcast(true); const address = socket.address(); this.logger.debug(`Socket is listening on ${address.address}:${address.port}`); }); socket.on("error", (err) => { this.logger.error(`Error in UDP socket: ${err}`); }); return socket.bind(); }; this.onPayloadReceived = (_err, payload) => { this.logger.debug(`(${this.macAddress.toString("hex")}) Payload received:${payload.toString("hex")}`); const param = payload[0]; const PayloadHandlerClass = payload_handler_1.payloadHandlers[param]; if (PayloadHandlerClass) { const handlerInstance = new PayloadHandlerClass(this.rm4Type); handlerInstance.handle(payload); } }; this.logger = logger; this.host = host; this.socket = this.setupSocket(); this.macAddress = macAddress; // this.requestCounter = 0; this.promises = {}; this.packetHandler = packetHandler; this.socket.on("message", this.handleMessage.bind(this)); this.rm4Type = (device_types_1.rm4DeviceTypes[(deviceType)] || device_types_1.rm4PlusDeviceTypes[deviceType]); this.request_header = this.rm4Type ? Buffer.from([0x04, 0x00]) : Buffer.from([]); this.code_sending_header = this.rm4Type ? Buffer.from([0xda, 0x00]) : Buffer.from([]); //except 5f36 and 6508 ¯\_(ツ)_/¯ if (deviceType === parseInt(`0x5f36`) || deviceType === parseInt(`0x6508`)) { this.code_sending_header = Buffer.from([0xd0, 0x00]); this.request_header = Buffer.from([0x04, 0x00]); } } } exports.SocketHandler = SocketHandler; //# sourceMappingURL=socket.handler.js.map