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
150 lines (127 loc) • 4.17 kB
JavaScript
/**
* 亖米(Symi)协议 - 地暖设备处理
* 设备类型:0x05
* 支持操作类型:
* 0x01: 电源控制
* 0x02: 温度控制
*/
const DEVICE_TYPE = 0x05; // 地暖设备类型
// 操作类型定义
const OPERATION_TYPES = {
POWER: 0x01, // 电源控制
TEMPERATURE: 0x02 // 温度控制
};
/**
* 缓冲区数据转对象
* @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 && data.length >= 17) {
obj.type = 'query_response';
obj.deviceTypeName = 'climate';
obj.powerState = data[12] === 1 ? 'heat' : 'off';
obj.targetTemp = data[13];
obj.currentTemp = data[14];
obj.val = {
state: obj.powerState,
hvac_mode: obj.powerState,
temperature: obj.targetTemp,
current_temperature: obj.currentTemp
};
return obj;
}
// 只处理设置和上报类型的消息
if (obj.dataType !== 0x03 && obj.dataType !== 0x04) return null;
obj.type = obj.dataType === 0x03 ? 'set' : 'report';
obj.deviceTypeName = 'climate';
// 根据操作码解析具体操作
switch (obj.operationCode) {
case OPERATION_TYPES.POWER:
// 电源控制
obj.action = 'power';
obj.val = data[12] === 1 ? 'heat' : 'off';
break;
case OPERATION_TYPES.TEMPERATURE:
// 温度控制
obj.action = 'temperature';
obj.val = data[12]; // 温度值
break;
default:
return null;
}
return obj;
}
/**
* 对象转缓冲区
* @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 === 'heat' || obj.state === 'off') {
// 电源控制
frame.operationCode = OPERATION_TYPES.POWER;
frame.operationData[0] = obj.state === 'heat' ? 1 : 0;
} else if (obj.attributes && obj.attributes.temperature !== undefined) {
// 温度控制
frame.operationCode = OPERATION_TYPES.TEMPERATURE;
frame.operationData[0] = Math.round(obj.attributes.temperature);
}
return frame;
}
module.exports = {
buf2obj,
obj2buf,
DEVICE_TYPE,
OPERATION_TYPES
};