system.mqttmessage MQTT 消息编解码
提供 MQTT 业务消息(
MqttMessage)的格式校验与 Protocol Buffers 编解码能力:在 JS 对象与 protobuf 二进制(Uint8Array)之间互相转换,供 MQTT 收发消息前后使用。社区资料(洛汐文档库):用于小米内部 MQTT 消息结构的 protobuf 校验、编码与解码,不负责建立 MQTT 连接。
| 项目 | 说明 |
|---|---|
| 模块名 | system.mqttmessage |
| Feature 类型 | 标准 Feature |
| 所需权限 | 无 |
接口声明
{ "name": "system.mqttmessage" }导入方式
// 快应用标准写法
import mqttmessage from '@system.mqttmessage'
// 本仓库运行时/测试中使用的写法
const mqttmessage = require('system.mqttmessage')功能概述
system.mqttmessage 是一个纯数据处理工具模块,本身不发起任何网络连接或 MQTT 订阅(不建立连接、不选择 broker、不发布消息):
encode(obj):把符合MqttMessage结构的 JS 对象序列化为 protobuf 二进制(Uint8Array),可直接作为 MQTT 消息 payload 发布。decode(buffer):把收到的 protobuf 二进制(Uint8Array)反序列化回 JS 对象。verify(obj):校验一个 JS 对象是否包含全部必需字段且类型正确(只检查对象及字段类型,不检查业务含义)。
底层使用 protobuf-c 库(mqtt_message__pack / mqtt_message__unpack),消息格式由 mqtt_message.proto 定义。三个方法均为同步调用、直接返回结果,没有回调和 Promise。
消息结构(MqttMessage)
消息格式定义(mqtt_message.proto,proto3):
syntax = "proto3";
message MqttMessage {
int64 ts = 1; // 时间戳
string id = 2; // 消息 id
string carId = 3; // 车辆 id
string packageName = 4;// 包名
string action = 5; // 动作/消息类型
bytes data = 6; // 二进制负载
}JS 侧对象字段:
| 字段 | JS 类型 | protobuf 类型/编号 | 必填(encode/verify 时) | 说明 |
|---|---|---|---|---|
| ts | number | int64(编号 1) | 是 | 时间戳(如 Date.now)。社区资料(洛汐文档库):ts 在 protobuf 中是整数语义,但 JS 运行时只有 Number;若协议要求毫秒时间戳,当前日期仍处于安全整数范围,不要传 BigInt,固件类型校验不接受它 |
| id | string | string(编号 2) | 是 | 消息唯一标识。社区资料:业务消息 ID,模块不会替你生成或去重 |
| carId | string | string(编号 3) | 是 | 车辆 id。社区资料:协议保留的车辆标识,不属于设备蓝牙 ID |
| packageName | string | string(编号 4) | 是 | 业务包名(如 "com.mi.car.digitalKey")。社区资料:消息来源/目标业务包名,不会自动从当前应用填充,模块也不验证它是否等于当前应用 |
| action | string | string(编号 5) | 是 | 动作名(如 "messageAck")。社区资料:业务动作名,允许值由上层协议决定 |
| data | Uint8Array | bytes(编号 6) | 是 | 二进制消息负载,必须是 TypedArray(Uint8Array),不能是普通字符串。社区资料:原始二进制负载,不会自动 JSON 编解码(社区文档类型栏写作 Uint8Array/ArrayBuffer,openvela 实证要求 Uint8Array/TypedArray) |
API
三个方法均为同步方法、参数为位置参数,直接返回结果,无回调/Promise。
| 方法 | 返回值 | 说明 |
|---|---|---|
verify(object) | Boolean | 检查对象及字段类型,不检查业务含义 |
encode(object) | Uint8Array | 校验并编码 protobuf;对象为空或校验失败时失败(返回 undefined) |
decode(buffer) | Object | 解码 typed buffer;空参数、普通数组或损坏数据会失败(返回 undefined) |
mqttmessage.verify(obj)
校验一个 JS 对象是否为合法的 MqttMessage(必须是对象类型,且 6 个字段全部存在、类型正确)。
参数
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| obj | object | 是 | - | 待校验的消息对象 |
返回值
| 字段 | 类型 | 说明 |
|---|---|---|
| (返回值) | boolean | true 表示格式合法;false 表示参数为空、不是对象,或任一字段缺失/类型不符(Native 侧同时打印错误日志)。社区资料:verify 通过只能说明类型可编码,不代表业务字段合法 |
示例
const mqttmessage = require('system.mqttmessage')
let msg = {
ts: Date.now,
id: '123',
carId: 'car-001',
packageName: 'com.aiot.test',
action: 'test',
data: new Uint8Array([1, 2, 3]),
}
if (mqttmessage.verify(msg)) {
console.log('message ok')
} else {
console.log('message invalid')
}
// 类型错误示例:action 传了数字、data 传了字符串,都会返回 false
let bad = { ts: Date.now, id: '123', carId: '123', packageName: 'com.aiot.test', action: 5, data: 'abc' }
mqttmessage.verify(bad) // falsemqttmessage.decode(buffer)
将 protobuf 二进制消息反序列化为 JS 对象。
参数
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| buffer | object(Uint8Array / TypedArray) | 是 | - | protobuf 编码后的二进制数据 |
返回值
| 字段 | 类型 | 说明 |
|---|---|---|
| (返回值) | object / undefined | 成功返回 MqttMessage 对象(字段见上表,data 为 Uint8Array);失败(参数为空、不是 TypedArray、protobuf 解包失败)返回 undefined 并打印错误日志 |
说明
- 解码出的对象字段名与 JS 侧约定一致:
carId、packageName(protobuf-c 的 C 结构中为carid、packagename,JS 侧已做映射)。 - 按 proto3 语义,二进制中缺失的字段取默认值:字符串为
""、ts为0、data为空Uint8Array。
示例
const mqttmessage = require('system.mqttmessage')
// payload 为从 MQTT 收到的 Uint8Array 二进制
let obj = mqttmessage.decode(payload)
if (obj) {
console.log('ts:', obj.ts)
console.log('id:', obj.id)
console.log('carId:', obj.carId)
console.log('packageName:', obj.packageName)
console.log('action:', obj.action)
console.log('data length:', obj.data.length)
} else {
console.log('decode failed')
}mqttmessage.encode(obj)
将合法的 MqttMessage JS 对象序列化为 protobuf 二进制。内部会先执行与 verify 相同的校验。
参数
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| obj | object | 是 | - | 待编码的消息对象,字段要求见"消息结构"一节 |
返回值
| 字段 | 类型 | 说明 |
|---|---|---|
| (返回值) | object(Uint8Array)/ undefined | 成功返回 protobuf 编码后的 Uint8Array,可作为 MQTT 消息 payload 发布;校验失败(字段缺失或类型错误)返回 undefined 并打印错误日志 |
示例
const mqttmessage = require('system.mqttmessage')
let msg = {
ts: Date.now,
id: '3730756743974617089',
carId: '111',
packageName: 'com.mi.car.digitalKey',
action: 'messageAck',
data: new Uint8Array([1, 2, 3, 4, 5]),
}
let bin = mqttmessage.encode(msg)
if (bin instanceof Uint8Array) {
console.log('encoded bytes:', bin.length)
// 将 bin 作为 MQTT 消息体发布...
} else {
console.log('encode failed: message invalid')
}
// encode → decode 往返一致性
let decoded = mqttmessage.decode(mqttmessage.encode(msg))
// decoded 与 msg 各字段一致社区示例(verify + encode/decode 基本用法):
const msg = {
ts: Date.now,
id: '1',
carId: '',
packageName: 'com.example.demo',
action: 'ping',
data: new Uint8Array([1, 2, 3])
}
if (mqttmessage.verify(msg)) {
const wire = mqttmessage.encode(msg)
const decoded = mqttmessage.decode(wire)
console.log(decoded.action, decoded.data)
}安全封装建议(社区资料:洛汐文档库)
encode/decode 失败时返回 undefined 而不抛异常,业务层可封装为带类型检查的函数:
function encodeMqttMessage(message) {
if (!message || !mqttmessage.verify(message)) {
throw new TypeError('invalid mqtt message')
}
const result = mqttmessage.encode(message)
if (!result) throw new Error('mqtt message encode failed')
return result
}
function decodeMqttMessage(buffer) {
if (!(buffer instanceof Uint8Array) && !(buffer instanceof ArrayBuffer)) {
throw new TypeError('typed buffer required')
}
const result = mqttmessage.decode(buffer)
if (!result) throw new Error('mqtt message decode failed')
return result
}注意事项
- 本模块只做编解码,不负责 MQTT 连接:MQTT 的连接、订阅、发布、收消息需配合系统其他 MQTT 能力;收到二进制 payload 后用
decode,发布前用encode。模块不建立连接、不选择 broker、不发布消息,也不验证packageName是否等于当前应用(社区资料:洛汐文档库)。 - encode 前对象必须 6 个字段齐全且类型正确:
ts必须是 number(int64,不要传 BigInt),id/carId/packageName/action必须是 string,data必须是Uint8Array(TypedArray);普通字符串、普通对象、空对象、null、原始类型等都会导致verify返回false、encode返回undefined。 - 失败不抛异常:
decode/encode失败时返回undefined并在 Native 日志中打印原因(如"message unpack failed"、"arg is not typed buffer"、"message encode failed, verify failed"),使用时应判断返回值。 - 字段语义(社区资料:洛汐文档库):
id为业务消息 ID,模块不生成也不去重;carId是协议保留的车辆标识而非设备蓝牙 ID;packageName不会自动填充;action允许值由上层协议决定;data是原始二进制负载,不会自动 JSON 编解码。 - 字段名大小写:JS 侧使用驼峰
carId、packageName,与.proto定义一致。 - 消息结构为固定格式:字段集合在 Native 实现中硬编码(
ts/id/carId/packageName/action/data),不能用于任意 protobuf 消息的通用编解码。 - 参考用例见
openvela/frameworks/runtimes/feature/modules/mqtt_msg_test.js(包含 verify、encode/decode 往返一致性、压力测试用法)。
机型支持明细
| 设备产品 | 说明 |
|---|---|
| 小米 S1 Pro 运动健康手表 | 不支持 |
| 小米手环 8 Pro | 不支持 |
| 小米手环 9 / 9 Pro | 不支持 |
| Xiaomi Watch S3 | 不支持 |
| Redmi Watch 4 | 不支持 |
| 小米腕部心电血压记录仪 | 不支持 |
| 小米手环 10 | 不支持 |
| 小米手环 10 Pro | 不支持 |
| Xiaomi Watch S4 | 部分支持 |
| REDMI Watch 5 | 不支持 |
| REDMI Watch 6 | 不支持 |
| Xiaomi Watch S5 | 支持 |
源码
openvela 快应用代码仓库:openvela/frameworks/runtimes/feature/(JIDL 接口与 Native 实现)、openvela/packages/(使用示例);引擎内置 Feature 预编译于 libquickappfeatures。
参考来源
- Vela官方快应用开发文档
- 洛汐文档库
- openvela快应用代码仓库:
openvela/frameworks/、openvela/packages/