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
278 lines (247 loc) • 7.48 kB
JavaScript
/**
* 亖米(Symi)协议 - 空调设备处理
* 设备类型:0x02
* 支持操作类型:
* 0x01: 电源控制
* 0x02: 模式控制
* 0x03: 风速控制
* 0x04: 温度控制
*/
const DEVICE_TYPE = 0x02; // 空调设备类型
// 操作类型定义
const OPERATION_TYPES = {
POWER: 0x01, // 电源控制
MODE: 0x02, // 模式控制
FAN_SPEED: 0x03, // 风速控制
TEMPERATURE: 0x04 // 温度控制
};
// 空调模式定义
const HVAC_MODES = {
AUTO: 0x00, // 自动
COOL: 0x01, // 制冷
DRY: 0x02, // 抽湿
FAN_ONLY: 0x03, // 送风
HEAT: 0x04 // 制热
};
// 风速定义
const FAN_SPEEDS = {
AUTO: 0x00, // 自动
LOW: 0x01, // 低风速
MEDIUM: 0x02, // 中风速
HIGH: 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 && data.length >= 19) {
obj.type = 'query_response';
obj.deviceTypeName = 'climate';
obj.powerState = data[12] === 1 ? 'on' : 'off';
obj.mode = _getModeFromByte(data[13]);
obj.fanSpeed = _getFanSpeedFromByte(data[14]);
obj.targetTemp = data[15];
obj.currentTemp = data[16];
obj.val = {
state: obj.powerState,
hvac_mode: obj.mode,
fan_mode: obj.fanSpeed,
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 ? 'on' : 'off';
break;
case OPERATION_TYPES.MODE:
// 模式控制
obj.action = 'mode';
obj.val = _getModeFromByte(data[12]);
break;
case OPERATION_TYPES.FAN_SPEED:
// 风速控制
obj.action = 'fan_speed';
obj.val = _getFanSpeedFromByte(data[12]);
break;
case OPERATION_TYPES.TEMPERATURE:
// 温度控制
obj.action = 'temperature';
obj.val = data[12]; // 温度值
break;
default:
return null;
}
return obj;
}
/**
* 根据字节值获取模式
* @param {number} byte
*/
function _getModeFromByte(byte) {
switch (byte) {
case HVAC_MODES.AUTO:
return 'auto';
case HVAC_MODES.COOL:
return 'cool';
case HVAC_MODES.DRY:
return 'dry';
case HVAC_MODES.FAN_ONLY:
return 'fan_only';
case HVAC_MODES.HEAT:
return 'heat';
default:
return 'auto';
}
}
/**
* 根据模式获取字节值
* @param {string} mode
*/
function _getByteFromMode(mode) {
switch (mode) {
case 'auto':
return HVAC_MODES.AUTO;
case 'cool':
return HVAC_MODES.COOL;
case 'dry':
return HVAC_MODES.DRY;
case 'fan_only':
return HVAC_MODES.FAN_ONLY;
case 'heat':
return HVAC_MODES.HEAT;
default:
return HVAC_MODES.AUTO;
}
}
/**
* 根据字节值获取风速
* @param {number} byte
*/
function _getFanSpeedFromByte(byte) {
switch (byte) {
case FAN_SPEEDS.AUTO:
return 'auto';
case FAN_SPEEDS.LOW:
return 'low';
case FAN_SPEEDS.MEDIUM:
return 'medium';
case FAN_SPEEDS.HIGH:
return 'high';
default:
return 'auto';
}
}
/**
* 根据风速获取字节值
* @param {string} fanSpeed
*/
function _getByteFromFanSpeed(fanSpeed) {
switch (fanSpeed) {
case 'auto':
return FAN_SPEEDS.AUTO;
case 'low':
return FAN_SPEEDS.LOW;
case 'medium':
return FAN_SPEEDS.MEDIUM;
case 'high':
return FAN_SPEEDS.HIGH;
default:
return FAN_SPEEDS.AUTO;
}
}
/**
* 对象转缓冲区
* @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) {
if (obj.attributes.hvac_mode !== undefined) {
// 模式控制
frame.operationCode = OPERATION_TYPES.MODE;
frame.operationData[0] = _getByteFromMode(obj.attributes.hvac_mode);
} else if (obj.attributes.fan_mode !== undefined) {
// 风速控制
frame.operationCode = OPERATION_TYPES.FAN_SPEED;
frame.operationData[0] = _getByteFromFanSpeed(obj.attributes.fan_mode);
} else if (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,
HVAC_MODES,
FAN_SPEEDS
};