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
163 lines (144 loc) • 4.72 kB
JavaScript
/**
* 串口通信类
* 支持RS485/RS232串口通信
*/
const { SerialPort } = require('serialport');
const { ReadlineParser } = require('@serialport/parser-readline');
const EventEmitter = require('events');
class SerialConnection extends EventEmitter {
constructor(options = {}) {
super();
this.options = {
path: options.path || '/dev/ttyUSB0',
baudRate: options.baudRate || 9600,
dataBits: options.dataBits || 8,
stopBits: options.stopBits || 1,
parity: options.parity || 'none',
autoOpen: false,
...options
};
this.port = null;
this.parser = null;
this.isConnected = false;
this.reconnectTimer = null;
this.reconnectInterval = 5000; // 5秒重连间隔
}
/**
* 连接串口
*/
connect() {
return new Promise((resolve, reject) => {
try {
this.port = new SerialPort(this.options);
// 设置解析器(如果需要按行解析)
if (this.options.useLineParser) {
this.parser = this.port.pipe(new ReadlineParser({ delimiter: '\r\n' }));
this.parser.on('data', (data) => {
this.emit('data', Buffer.from(data));
});
} else {
this.port.on('data', (data) => {
this.emit('data', data);
});
}
this.port.on('open', () => {
this.isConnected = true;
this.emit('connect');
console.log(`串口已连接: ${this.options.path}`);
resolve();
});
this.port.on('close', () => {
this.isConnected = false;
this.emit('disconnect');
console.log(`串口已断开: ${this.options.path}`);
this.scheduleReconnect();
});
this.port.on('error', (err) => {
this.isConnected = false;
this.emit('error', err);
console.error(`串口错误: ${err.message}`);
this.scheduleReconnect();
});
// 打开串口
this.port.open();
} catch (error) {
reject(error);
}
});
}
/**
* 断开串口连接
*/
disconnect() {
return new Promise((resolve) => {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.port && this.port.isOpen) {
this.port.close(() => {
this.isConnected = false;
resolve();
});
} else {
this.isConnected = false;
resolve();
}
});
}
/**
* 发送数据
* @param {Buffer|string} data 要发送的数据
*/
write(data) {
return new Promise((resolve, reject) => {
if (!this.isConnected || !this.port || !this.port.isOpen) {
reject(new Error('串口未连接'));
return;
}
this.port.write(data, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
/**
* 计划重连
*/
scheduleReconnect() {
if (this.reconnectTimer) {
return;
}
this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null;
console.log(`尝试重连串口: ${this.options.path}`);
this.connect().catch(() => {
// 重连失败,继续计划下次重连
});
}, this.reconnectInterval);
}
/**
* 获取可用串口列表
*/
static async listPorts() {
try {
const ports = await SerialPort.list();
return ports.map(port => ({
path: port.path,
manufacturer: port.manufacturer,
serialNumber: port.serialNumber,
pnpId: port.pnpId,
locationId: port.locationId,
productId: port.productId,
vendorId: port.vendorId
}));
} catch (error) {
console.error('获取串口列表失败:', error);
return [];
}
}
}
module.exports = SerialConnection;