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
194 lines (168 loc) • 5.05 kB
JavaScript
/**
* 亖米(Symi)协议 - 新风设备处理
* 设备类型:0x06
* 支持操作类型:
* 0x01: 电源控制
* 0x02: 风速控制
*/
const DEVICE_TYPE = 0x06; // 新风设备类型
// 操作类型定义
const OPERATION_TYPES = {
POWER: 0x01, // 电源控制
FAN_SPEED: 0x02 // 风速控制
};
// 风速定义
const FAN_SPEEDS = {
LOW: 0x00, // 低风速
MEDIUM: 0x01, // 中风速
HIGH: 0x02, // 高风速
AUTO: 0x03 // 自动
};
/**
* 缓冲区数据转对象
* @param {Buffer} data 命令缓冲
* @param {object} obj 节点对象
*/
function buf2obj(data, obj) {
const protocolData = _protocol(data);
if (!protocolData) return null;
protocolData.uid = obj.entity_id;
// 检查设备匹配
if (obj.uid === protocolData.localAddress &&
obj.address === protocolData.deviceAddress &&
obj.channel === protocolData.deviceChannel) {
return protocolData;
} else {
return null;
}
}
/**
* 协议解析
* @param {Buffer} data 数据帧
*/
function _protocol(data) {
if (!data || data.length < 15) return null;
let obj = {};
// 解析数据帧结构
obj.localAddress = data[1]; // 本地地址
obj.dataType = data[2]; // 数据类型
obj.dataLength = data[3]; // 数据长度
obj.deviceType = data[4]; // 设备类型
obj.brandId = data[5]; // 品牌ID
obj.deviceAddress = data[6]; // 设备地址
obj.deviceChannel = data[7]; // 设备通道
obj.roomNumber = data[8]; // 房间号
obj.roomType = data[9]; // 房间类型
obj.roomId = data[10]; // 房间ID
obj.operationCode = data[11]; // 操作码
// 检查设备类型
if (obj.deviceType !== DEVICE_TYPE) return null;
// 处理查询响应(包含完整状态信息)
if (obj.dataType === 0x01) {
obj.type = 'query_response';
obj.deviceTypeName = 'fan';
obj.powerState = data[12] === 1 ? 'on' : 'off';
obj.fanSpeed = _getFanSpeedFromByte(data[13]);
obj.val = {
state: obj.powerState,
speed: obj.fanSpeed
};
return obj;
}
// 只处理设置和上报类型的消息
if (obj.dataType !== 0x03 && obj.dataType !== 0x04) return null;
obj.type = obj.dataType === 0x03 ? 'set' : 'report';
obj.deviceTypeName = 'fan';
// 根据操作码解析具体操作
switch (obj.operationCode) {
case OPERATION_TYPES.POWER:
// 电源控制
obj.action = 'power';
obj.val = data[12] === 1 ? 'on' : 'off';
break;
case OPERATION_TYPES.FAN_SPEED:
// 风速控制
obj.action = 'fan_speed';
obj.val = _getFanSpeedFromByte(data[12]);
break;
default:
return null;
}
return obj;
}
/**
* 根据字节值获取风速
* @param {number} byte
*/
function _getFanSpeedFromByte(byte) {
switch (byte) {
case FAN_SPEEDS.LOW:
return 'low';
case FAN_SPEEDS.MEDIUM:
return 'medium';
case FAN_SPEEDS.HIGH:
return 'high';
case FAN_SPEEDS.AUTO:
return 'auto';
default:
return 'low';
}
}
/**
* 根据风速获取字节值
* @param {string} fanSpeed
*/
function _getByteFromFanSpeed(fanSpeed) {
switch (fanSpeed) {
case 'low':
return FAN_SPEEDS.LOW;
case 'medium':
return FAN_SPEEDS.MEDIUM;
case 'high':
return FAN_SPEEDS.HIGH;
case 'auto':
return FAN_SPEEDS.AUTO;
default:
return FAN_SPEEDS.LOW;
}
}
/**
* 对象转缓冲区
* @param {object} obj HA状态对象
* @param {object} obj1 节点对象
*/
function obj2buf(obj, obj1) {
if (obj1.entity_id !== obj.entity_id) return null;
// 构建基础帧结构
let frame = {
localAddress: obj1.uid,
dataType: 0x04, // 上报
deviceType: DEVICE_TYPE,
brandId: 0x00,
deviceAddress: obj1.address,
deviceChannel: obj1.channel || 0x00,
roomNumber: 0x00,
roomType: 0x00,
roomId: 0x00,
operationCode: OPERATION_TYPES.POWER,
operationData: Buffer.alloc(1)
};
// 根据HA状态设置操作信息
if (obj.state === 'on' || obj.state === 'off') {
// 电源控制
frame.operationCode = OPERATION_TYPES.POWER;
frame.operationData[0] = obj.state === 'on' ? 1 : 0;
} else if (obj.attributes && obj.attributes.speed !== undefined) {
// 风速控制
frame.operationCode = OPERATION_TYPES.FAN_SPEED;
frame.operationData[0] = _getByteFromFanSpeed(obj.attributes.speed);
}
return frame;
}
module.exports = {
buf2obj,
obj2buf,
DEVICE_TYPE,
OPERATION_TYPES,
FAN_SPEEDS
};