更新时间:2024-06-20 04:20:43下载pdf
本文档提供 TuyaLink设备接入中数据解析的示例。
阅读此篇文档前,建议先查看 数据解析。
结合实际应用需求,本文档为开发者提供一份数据解析脚本参考。您可以根据以下示例学习应用。
请参考 自定义透传协议。
脚本模板是平台默认提供的基本函数代码块,包含上行、下行的函数定义。
/**
*
* 当设备上报数据到云端时本函数将设备的自定义协议数据转换为tylink协议json格式。
* @param {array} rawData 设备原始数据的字节数组,示例:[0x10,0xFF,0x01,0x02]。
* @returns {object} 返回转换之后的结果对象,该对象应包含如下属性:
* msgType:转换之后的tylink协议消息类型;
* payload:转换之后的tylink协议消息主体;
* 返回结果示例:
* {
* "msgType":"thing.property.report",
* "payload":{
* "msgId":"100001",
* "time":1636625851737,
* "data":{
* "color":{
* "value":"RED",
* "time":1636625851737
* }
* }
* }
* }
*/
function rawDataToTyLink(rawData) {
}
/**
*
* 当云端下发数据到设备时本函数将tylink协议json格式转换为设备自定义格式。
* @param {object} tylinkData 云端下发的tylink协议数据对象,该对象应包含如下属性:
* msgType:当前下发的tylink协议消息类型;
* payload:当前下发的tylink协议消息主体;
* 示例:
* {
* "msgType":"thing.property.set",
* "payload":{
* "msgId":"1000324923",
* "time":1636625851737,
* "data":{"color":"BLUE"}
* }
* }
* @returns {array} 返回转换之后的设备协议数据字节数组,实例:[0x12,0xEF,0x03,0x12]。
*/
function tyLinkToRawData(tylinkData) {
}
创建产品。
在 产品创建页,选择 光源 产品品类,来创建 智能彩灯 产品,智能化方式选择 设备生态接入,产品信息选择数据协议为 自定义透传。创建详情见下图。关于创建产品,详情可查看 创建产品。
功能定义。
在 功能定义 页签中,对产品进行功能定义,完成基本设备模型设计。
关于功能定义详情,可查看 功能定义。
脚本编写。
可参考下方功能代码,拷贝至 脚本调试 框中。
/**
*
* 以下是一个智能彩灯设备的自定义协议解析示例:
*
* ## 彩灯设备模型定义
* {
* "code":"0asdlk234234",
* "name":"智能彩灯",
* "services":[
* {
* "properties":[
* {"abilityId":1, "code":"color", "name":"颜色", "typeSpec":{"type":"enum", "range":[ "RED", "GREEN", "BLUE" ] }},
* {"abilityId":2, "code":"brightness", "name":"亮度", "typeSpec":{"type":"value", "min":0, "max":100, "step":1, "scale":0}},
* {"abilityId":3, "code":"switch", "name":"开关", "typeSpec":{"type":"bool"}}
* ]
* }
* ]
* }
*
* ## 彩灯设备协议格式定义
* +-----+---+---+---+---+---+---+---+---+---+---+---+---+-----+---+---+---+
* | \ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
* +-----+---+---+---+---+---+---+---+---+---+---+---+---+-----+---+---+---+
* | 0 | version | cmd | code |
* +-----+---------------+---------------+---------------+-----+---+---+---+
* | 2 | property id |
* +-----+-----------------------------------------------------------------+
* | 4 | property value |
* +-----+-----------------------------------------------------------------+
*
**/
//~~~~~~~~~~~~~~~~~~~~~~~~~tylink协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
var MSG_PROPERTY_SET = "thing.property.set";
var MSG_PROPERTY_REPORT = "thing.property.report";
//~~~~~~~~~~~~~~~~~~~~~~~~~tylink协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~自定义协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
var VERSION = 0x1; // 协议版本
var CMD_TYPE_SET = 0x1; // 属性下发
var CMD_TYPE_REPORT = 0x2; // 属性上报
var PROPERTY_COLOR = 0x1; // 颜色属性标识
var PROPERTY_BRIGHTNESS = 0x2; // 亮度属性标识
var PROPERTY_SWITCH = 0x3; // 开关属性标识
var CODE_SUCCESS = 0;
var CODE_ERROR = 1;
var COLOR_TO_CODE = {"RED":0, "GREEN":1, "BLUE":2};
var CODE_TO_COLOR = {0:"RED", 1:"GREEN", 2:"BLUE"};
//~~~~~~~~~~~~~~~~~~~~~~~~~自定义协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
/**
* 示例1:下发颜色属性设置消息
* 传入参数:
* {"msgType":"thing.property.set", "payload":{"msgId":"15", "data":{"color":"BLUE"}}}
* 输出结果:
* [0x11,0x00,0x00,0x01,0x00,0x02]
*/
function tyLinkToRawData(json) {
// 根据消息类型分别处理编码逻辑
if(json.msgType == MSG_PROPERTY_SET) {
return encodePropertySet(json.payload);
}
}
/**
* 示例1:上报亮度属性数据
* 传入参数:
* [0x12,0x00,0x00,0x02,0x00,0x32]
* 输出结果:
* {"msgType":"thing.property.report","payload":{"data":{"brightness":{"value":50}}}}
*
*/
function rawDataToTyLink(bytes) {
var packet = decode(bytes);
// 根据命令类型分别处理解码逻辑
if(packet.cmd == CMD_TYPE_REPORT) {
return decodePropertyReport(packet);
}
}
/** 编码属性设置消息 */
function encodePropertySet(json) {
var data = json.data;
if(data.color !== undefined) {
return encode(CMD_TYPE_SET, 0, PROPERTY_COLOR, COLOR_TO_CODE[data.color]);
} else if (data.brightness !== undefined) {
return encode(CMD_TYPE_SET, 0, PROPERTY_BRIGHTNESS, data.brightness);
} else if (data.switch !== undefined) {
return encode(CMD_TYPE_SET, 0, PROPERTY_SWITCH, data.switch ? 1 : 0);
}
}
/** 编码自定义数据包 */
function encode(cmd, code, proId, proVal) {
var data = [];
// 4位版本+4位命令类型
data = data.concat(encodeUint8(((VERSION << 4) & 0xF0) | (cmd & 0xF)));
// 8位code
data = data.concat(encodeUint8(code));
if(proId != null) {
// 16位属性标识
data = data.concat(encodeInt16(proId));
// 16位属性值
data = data.concat(encodeInt16(proVal));
}
return data;
}
/** 解码属性上报消息 */
function decodePropertyReport(packet) {
var proId = packet.proId;
var proVal = packet.proVal;
var data = {"msgType": MSG_PROPERTY_REPORT,
"payload":{"data":{}}}
var payload = data.payload;
if(proId == PROPERTY_COLOR) {
payload.data.color = {"value": CODE_TO_COLOR[proVal]};
} else if(proId == PROPERTY_BRIGHTNESS) {
payload.data.brightness = {"value": proVal};
} else if(proId == PROPERTY_SWITCH) {
payload.data.switch = {"value": proVal == 1};
}
return data;
}
/** 解码自定义数据包 */
function decode(bytes) {
var uint8RawData = new Uint8Array(bytes);
var dataView = new DataView(uint8RawData.buffer, 0);
var packet = {};
var header = dataView.getInt16(0);
packet.version = (header & 0xF000) >> 12;
packet.cmd = (header & 0x0F00) >> 8;
packet.code = (header & 0x00FF);
if(bytes.length > 2) {
packet.proId = dataView.getInt16(2);
}
if(bytes.length > 4) {
packet.proVal = dataView.getInt16(4);
}
return packet;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~实用工具方法~~~~~~~~~~~~~~~~~~~~~~~~~//
function encodeUint8(value) {
var uint8Array = new Uint8Array(1);
var dv = new DataView(uint8Array.buffer, 0);
dv.setUint8(0, value);
return [].slice.call(uint8Array);
}
function encodeInt16(value) {
var uint8Array = new Uint8Array(2);
var dv = new DataView(uint8Array.buffer, 0);
dv.setInt16(0, value);
return [].slice.call(uint8Array);
}
function encodeInt32(value) {
var uint8Array = new Uint8Array(4);
var dv = new DataView(uint8Array.buffer, 0);
dv.setInt32(0, value);
return [].slice.call(uint8Array);
}
调试脚本。
发布为正式版本。
填写发布说明,版本发布成功后,当前产品所有在线设备会通过数据解析进行数据上报和处理。
创建产品。
在 产品创建页 选择“低功耗摄像机”产品品类来创建“智能摄像头“产品,选择数据协议为自定义透传。创建详情见下图。
关于创建产品,详情可查看创建产品。
功能定义。
在 功能定义 页签中,对产品进行功能定义,完成基本设备模型设计。
关于功能定义,详情可查看 功能定义。
脚本编写。
可参考下方功能代码,拷贝至 脚本调试 框中。
/**
*
* 以下提供一个包含属性、事件、动作的智能摄像头示例:
* ## 智能摄像头设备模型
* {
* "code":"0asdlk234234",
* "name":"智能摄像头",
* "services":[
* {
* "properties":[
* {"abilityId":1, "code":"nightvision","name":"红外夜视", "accessMode":"rw", "typeSpec":{"type":"enum", "range":[ "OFF", "ON", "AUTO" ] }}
* ],
* "events":[
* {"abilityId":2, "code":"fault", "name":"故障事件", "outputParams":[{"code":"faultNumber", "name":"故障编号", "typeSpec":{"type":"value", "min":0, "max":20, "step":1, "scale":0}} }]}
* ],
* "actions":[
* {"abilityId":3, "code":"ptzcontrol", "name":"云台转动", "inputParams":[{"code":"direction", "typeSpec":{"type":"enum", "range":[ "UP", "DOWN", "LEFT","RIGHT" ]}}]
* ]
* }
* ]
* }
*
* ## 智能摄像头协议以utf8字符串方式编码,每个协议字段之间使用","分割,其格式如下:
* <version>,<cmd>,<sub cmd>,<code>,<ack>,<msgId>,<timestamp>,<cmd args>
**/
//~~~~~~~~~~~~~~~~~~~~~~~~~tylink协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
var MSG_PROPERTY_SET = "thing.property.set";
var MSG_PROPERTY_SET_RES = "thing.property.set.response";
var MSG_PROPERTY_REPORT = "thing.property.report";
var MSG_PROPERTY_REPORT_RES = "thing.property.report.response";
var MSG_ACTION_EXECUTE = "thing.action.execute";
var MSG_ACTION_EXECUTE_RES = "thing.action.execute.response";
var MSG_EVENT_TRIGGER = "thing.event.trigger";
var MSG_EVENT_TRIGGER_RES = "thing.event.trigger.response";
//~~~~~~~~~~~~~~~~~~~~~~~~~tylink协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~自定义协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
var VERSION = 0x1; // 协议版本
var CMD_PROPERTY_SET = 0x1; // 属性设置
var CMD_PROPERTY_SET_RES = 0x2; // 属性设置响应
var CMD_PROPERTY_REPORT = 0x3; // 属性上报
var CMD_PROPERTY_REPORT_RES = 0x4; // 属性上报响应
var CMD_ACTION_EXECUTE = 0x5; // 动作执行
var CMD_ACTION_EXECUTE_RES = 0x6; // 动作执行响应
var CMD_EVENT_TRIGGER = 0x7; // 事件触发
var CMD_EVENT_TRIGGER_RES = 0x8; // 事件触发响应
var SUB_CMD_NIGHT_VISION = 0x1; // 红外夜视功能
var SUB_CMD_FAULT = 0x2; // 故障上报功能
var SUB_CMD_PTZ_CONRTOLR = 0x3; // 云台转动功能
var SEPARATOR = ','; // 字段分割符
//~~~~~~~~~~~~~~~~~~~~~~~~~自定义协议常量~~~~~~~~~~~~~~~~~~~~~~~~~//
/**
* 示例1:下发红外夜视设置消息
* 传入参数:
* {"msgType":"thing.property.set", "payload":{"msgId":"1001", "sys":{"ack":1}, "time":1637114892287, "data":{"nightvision":"ON"}}}
* 输出结果:
* [0x31,0x2c,0x31,0x2c,0x31,0x2c,0x30,0x2c,0x31,0x2c,0x31,0x30,0x30,0x31,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37,0x2c,0x4f,0x4e]
*
* 示例2:下发属性上报响应消息
* 传入参数:
* {"msgType":"thing.property.report.response", "payload":{"msgId":"1994", "time":1637114892287, "code":1000}}
* 输出结果:
* [0x31,0x2c,0x34,0x2c,0x30,0x2c,0x31,0x30,0x30,0x30,0x2c,0x30,0x2c,0x31,0x39,0x39,0x34,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37]
*
* 示例3:下发动作执行消息
* 传入参数:
* {"msgType":"thing.action.execute", "payload":{"msgId":"2011", "time":1637114892287, "data":{"actionCode":"ptzcontrol", "inputParams":{"direction":"UP"}}}}
* 输出结果:
* [0x31,0x2c,0x35,0x2c,0x33,0x2c,0x30,0x2c,0x31,0x2c,0x32,0x30,0x31,0x31,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37,0x2c,0x55,0x50]
*
* 示例4:下发事件上报响应消息
* 传入参数:
* {"msgType":"thing.event.trigger.response", "payload":{"msgId":"2028","time":1637114892289, "code":0}}
* 输出结果:
* [0x31,0x2c,0x38,0x2c,0x30,0x2c,0x30,0x2c,0x30,0x2c,0x32,0x30,0x32,0x38,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x39]
*/
function tyLinkToRawData(json) {
if(json.msgType == MSG_PROPERTY_SET) {
// 编码属性设置消息
return encodePropertySet(json.payload);
} else if(json.msgType == MSG_PROPERTY_REPORT_RES) {
// 编码属性上报响应消息
return encodeResponse(CMD_PROPERTY_REPORT_RES, json.payload);
} else if(json.msgType == MSG_ACTION_EXECUTE) {
// 编码动作执行消息
return encodeActionExecute(json.payload);
} else if(json.msgType == MSG_EVENT_TRIGGER_RES) {
// 编码事件触发响应消息
return encodeResponse(CMD_EVENT_TRIGGER_RES, json.payload);
}
}
/**
* 示例1:上报红外夜视属性数据
* 传入参数:
[0x31,0x2c,0x33,0x2c,0x31,0x2c,0x30,0x2c,0x31,0x2c,0x31,0x34,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37,0x2c,0x4f,0x4e]
* 输出结果:
* {"msgType":"thing.property.report","payload":{"msgId":"14", "time":1637114892287, "sys":{"ack":1}, "data":{"nightvision":{"value":"ON","time": 1637114892287}}}}
*
* 示例2:上报属性设置响应消息
* 传入参数:
* [0x31,0x2c,0x32,0x2c,0x30,0x2c,0x30,0x2c,0x30,0x2c,0x31,0x35,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37]
* 输出结果:
* {"msgType":"thing.property.set.response", "payload":{"msgId":"15", "time":1637114892287, "code":0}}
*
* 示例3:上报设备故障事件
* 传入参数:
* [0x31,0x2c,0x37,0x2c,0x32,0x2c,0x30,0x2c,0x31,0x2c,0x31,0x36,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37,0x2c,0x35]
* 输出结果:
* {"msgType":"thing.event.trigger", "payload":{"msgId":"16", "time":1637114892287,"sys":{"ack":1}, "data":{"eventCode":"fault", "outputParams":{"faultNumber":5}}}}
*
* 示例4:上报设备动作结果
* 传入参数:
* [0x31,0x2c,0x36,0x2c,0x30,0x2c,0x30,0x2c,0x30,0x2c,0x31,0x37,0x2c,0x31,0x36,0x33,0x37,0x31,0x31,0x34,0x38,0x39,0x32,0x32,0x38,0x37]
* 输出结果:
* {"msgType":"thing.action.execute.response", "payload":{"msgId":"17", "time":1637114892287, "code":0}}
*
*/
function rawDataToTyLink(bytes) {
var packet = decode(bytes);
if(packet.cmd == CMD_PROPERTY_REPORT) {
// 解码属性上报数据
return decodePropertyReport(packet);
} else if(packet.cmd == CMD_PROPERTY_SET_RES){
// 解码属性设置响应数据
return decodePropertySetResponse(packet);
} else if(packet.cmd == CMD_EVENT_TRIGGER){
// 解码事件触发数据
return decodeEventTrigger(packet);
} else if(packet.cmd == CMD_ACTION_EXECUTE_RES){
// 解码动作执行数据
return decodeActionExecuteRes(packet);
}
}
/** 编码属性设置消息 */
function encodePropertySet(json) {
var data = json.data;
var sys = json.sys;
if(data.nightvision !== undefined) {
return encode(CMD_PROPERTY_SET, SUB_CMD_NIGHT_VISION, 0, (sys && sys.ack) ? 1 : 0, json.msgId, json.time, data.nightvision);
}
}
/** 编码属性上报响应消息 */
function encodeResponse(cmdType, json) {
return encode(cmdType, 0, json.code, 0, json.msgId, json.time);
}
/** 编码动作执行消息 */
function encodeActionExecute(json) {
var data = json.data;
if(data.actionCode == "ptzcontrol") {
return encode(CMD_ACTION_EXECUTE, SUB_CMD_PTZ_CONRTOLR, 0, 1, json.msgId, json.time, data.inputParams.direction);
}
}
/** 编码自定义数据包 */
function encode(cmd, subCmd, code, ack, msgId, timestamp, cmdArgs) {
var line = [VERSION].concat([].slice.call(arguments)).join(SEPARATOR);
return encodeUtf8(line);
}
/** 解码属性上报消息 */
function decodePropertyReport(packet) {
var msgId = packet.msgId;
var subCmd = packet.subCmd;
var args = packet.args;
var time = packet.timestamp;
var result = {"msgType": MSG_PROPERTY_REPORT,
"payload":{"msgId": msgId,
"time":time,
"data":{},
"sys":{"ack":packet.ack}}}
var params = result.payload.data;
if(subCmd == SUB_CMD_NIGHT_VISION) {
params.nightvision = {"value": args, "time": time};
}
return result;
}
/** 解码属性设置响应消息 */
function decodePropertySetResponse(packet) {
return {
"msgType": MSG_PROPERTY_SET_RES,
"payload":{"msgId": packet.msgId,
"time": packet.timestamp,
"code": packet.code}
};
}
/** 解码事件触发 */
function decodeEventTrigger(packet) {
var msgId = packet.msgId;
var subCmd = packet.subCmd;
var args = packet.args;
var time = packet.timestamp;
var result = {"msgType": MSG_EVENT_TRIGGER,
"payload":{"msgId": msgId,
"time":time,
"data":{},
"sys":{"ack":packet.ack}}}
var params = result.payload.data;
params.eventTime = time;
if(subCmd == SUB_CMD_FAULT) {
params.eventCode = "fault";
params.outputParams = {"faultNumber": parseInt(args)};
}
return result;
}
/** 解码动作执行响应消息 */
function decodeActionExecuteRes(packet) {
return {
"msgType": MSG_ACTION_EXECUTE_RES,
"payload":{"msgId": packet.msgId.toString(),
"time":packet.timestamp,
"code": packet.code}
};
}
/** 解码自定义数据包 */
function decode(bytes) {
var line = decodeUtf8(bytes);
var fields = line.split(SEPARATOR);
return {
"version" :parseInt(fields[0]),
"cmd" :parseInt(fields[1]),
"subCmd" :parseInt(fields[2]),
"code" :parseInt(fields[3]),
"ack" :fields[4] == "1" ? 1 : 0,
"msgId" :fields[5],
"timestamp" :parseInt(fields[6]),
"args" :fields[7]
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~实用工具方法~~~~~~~~~~~~~~~~~~~~~~~~~//
function encodeUtf8(text) {
const code = encodeURIComponent(text);
const bytes = [];
for (var i = 0; i < code.length; i++) {
const c = code.charAt(i);
if (c === '%') {
const hex = code.charAt(i + 1) + code.charAt(i + 2);
const hexVal = parseInt(hex, 16);
bytes.push(hexVal);
i += 2;
} else bytes.push(c.charCodeAt(0));
}
return bytes;
}
function decodeUtf8(bytes) {
var encoded = "";
for (var i = 0; i < bytes.length; i++) {
encoded += '%' + bytes[i].toString(16);
}
return decodeURIComponent(encoded);
}
脚本调试。
发布为正式版本。
填写发布说明。版本发布成功后,当前产品所有在线设备会通过数据解析进行数据上报和处理。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈