快应用 FeatureFeature API

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 时)说明
tsnumberint64(编号 1)是时间戳(如 Date.now)。社区资料(洛汐文档库):ts 在 protobuf 中是整数语义,但 JS 运行时只有 Number;若协议要求毫秒时间戳,当前日期仍处于安全整数范围,不要传 BigInt,固件类型校验不接受它
idstringstring(编号 2)是消息唯一标识。社区资料:业务消息 ID,模块不会替你生成或去重
carIdstringstring(编号 3)是车辆 id。社区资料:协议保留的车辆标识,不属于设备蓝牙 ID
packageNamestringstring(编号 4)是业务包名(如 "com.mi.car.digitalKey")。社区资料:消息来源/目标业务包名,不会自动从当前应用填充,模块也不验证它是否等于当前应用
actionstringstring(编号 5)是动作名(如 "messageAck")。社区资料:业务动作名,允许值由上层协议决定
dataUint8Arraybytes(编号 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 个字段全部存在、类型正确)。

参数

字段类型必填默认值说明
objobject是-待校验的消息对象

返回值

字段类型说明
(返回值)booleantrue 表示格式合法;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) // false

mqttmessage.decode(buffer)

将 protobuf 二进制消息反序列化为 JS 对象。

参数

字段类型必填默认值说明
bufferobject(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 相同的校验。

参数

字段类型必填默认值说明
objobject是-待编码的消息对象,字段要求见"消息结构"一节

返回值

字段类型说明
(返回值)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。

参考来源

大纲