UNPKG

node-red-contrib-symi

Version:

Node-RED nodes for smart home device communication with Home Assistant integration, supporting HassKit, Clowire, Alive, LF and complete Symi protocols

196 lines (173 loc) 4.83 kB
"use strict"; var net = require("net"); const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const Lock = require('async-lock') /** * TCP连接端口类 * @param {string} ip - ip地址 * @param {number} port - 端口号 * @param {object} node - Node-RED节点实例 * @param {number} timeout - 超时时间 * @constructor */ class TcpBufferedPort { constructor(ip, port, node, timeout) { this.openFlag = false; this.callback = null; this.timeout = timeout this.heartbeatInterval = 0 this.heartbeatTime = 120000 //心跳间隔 this.heartbeatPack = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00]) //心跳包 this._client = null this._onData = [] this._onError = null this._onConnect = null this._onClose = null this.node = node this.reconnectTime = 0 this.lock = new Lock() this.connectOptions = { host: ip || "", port: port || 8666, }; } get isOpen() { return this.openFlag; } /** * 消息回调 */ set onData(cb) { this._onData.push(cb) } set onClose(cb) { this._onClose = cb } set onConnect(cb) { this._onConnect = cb } set onError(cb) { this._onError = cb } write_lock(data) { let self = this self.lock.acquire('key', function (done) { self.write(data, function(){ setTimeout(done, 25); }) }, function () { }) } _listener() { var self = this; this._client = new net.Socket(); var handleCallback = function (had_error) { if (self.callback) { self.callback(had_error); self.callback = null; } }; this._client.on('data', function (data) { for(let it of self._onData){ it[0].call(it[1], data) } }) if (this.timeout) this._client.setTimeout(this.timeout); this._client.on("connect", function () { self.openFlag = true; handleCallback(); if (self._onConnect) self._onConnect() }); this._client.on("close", function (had_error) { self.openFlag = false; handleCallback(had_error); if (self._onClose) self._onClose() }); this._client.on("error", function (had_error) { self.openFlag = false; handleCallback(had_error); if (self._onError) self._onError() }); this._client.on("timeout", function () { handleCallback(new Error("TCP连接超时")); }); } /** * TCP心跳方法 */ _heartbeat() { let self = this this.heartbeatInterval = setInterval(() => { self.write(self.heartbeatPack) }, this.heartbeatTime); } /** * 重连方法 */ async reconnect() { let self = this this.close() await sleep(1000) if (!self.isOpen) self.connect() } connect() { let self = this this._listener() this.open(function (err) { if (!err) { self.node.log('TCP连接成功') self._heartbeat()//心跳启动 } else { self.node.error("TCP连接错误") self.reconnectTime = setTimeout(() => { self.close() self.connect() }, 10000) } }) } /** * 打开端口连接 */ open(callback) { if (this.openFlag) { console.log("TCP端口已打开"); callback(); // 继续使用现有socket } else { this.callback = callback; this._client.connect(this.connectOptions); } } close(callback) { this.callback = callback; this._client.end(callback); if (this.heartbeatInterval) clearInterval(this.heartbeatInterval) if (this.reconnectTime) clearTimeout(this.reconnectTime) } destroy(callback) { this.callback = callback; if (!this._client.destroyed) { this._client.destroy(); } }; write(data, cb) { let self = this this._client.write(data, function (err) { if (err) { console.log(err) if (cb) cb(true) self.reconnect() } else { if (cb) cb() } }); }; } module.exports = TcpBufferedPort;