- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
<导读>本文围绕 ThingsBoard 集成(Integration)上行链路(Uplink)TBEL 解码器(Decoder)的返回值格式展开,以 label_json_output.md 中展示的“带设备标签、客户与分组”JSON 输出示例为核心骨架,系统讲解 decoder 必须返回的deviceName/deviceType(或assetName/assetType)对、可选的时间序列telemetry、服务端属性attributes,以及deviceLabel、customerName、groupName等实体关联字段的取值规则与平台侧的消费逻辑。读完本文,你将能在 TBEL 解码器中正确构造各类输出 JSON,并理解这些字段在集成运行时的真实处理链路,实现设备/资产的自动创建、客户归属和分组管理。</导读>
一、Decoded 输出的完整示例:带设备标签、客户与分组的 JSON
在 ThingsBoard 的集成(Integration)数据转换配置中,Uplink 解码器是一个用 TBEL(ThingsBoard Expression Language)编写的函数,其职责是把集成下发的原始报文解析、转换为平台统一的 JSON 结构。label_json_output.md给出的示例,是decoder_fn.md帮助文档中“输出带设备标签、客户和分组名称”一节的完整代码,也是解码器输出中字段最全的形态之一:
{ "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "customerName": "Company Name", "groupName": "Thermostats", "attributes": { "model": "Model A", "serialNumber": "SN-111", "integrationName": "Test integration" }, "telemetry": { "temperature": 42, "humidity": 80 } }对比同目录下不带标签的 simple_json_output.md 示例,可以发现该 JSON 在“deviceName/deviceType + attributes + telemetry”的基本骨架之外,额外增加了三层关联信息:
deviceLabel:非唯一、面向用户展示的设备标签(如 “Room A thermostat”);customerName:用于把设备自动归属到指定客户;groupName:用于把设备自动加入指定实体分组。
这三项是 decoder_fn.md 中“Returns”部分规定的可选字段,其语义与“仅在实体创建时生效”的限定条件密切相关,下文会逐项展开。
二、Decoder 函数签名与返回值的强约束
2.1 函数签名与参数
decoder_fn.md明确了 TBEL 解码器的标准函数签名:
function Decoder(payload, metadata): object | object[]payload(any):字节数组,包含对应 Integration 产生的上行报文。Integration 可能产生 JSON、TEXT 或 BINARY(Base64)三种内容类型;内容类型只是调试事件存储时的提示,不影响解码函数本身。解码时常用decodeToString和decodeToJson两个辅助函数将字节数组转换为 String 或 JSON 对象。- 大多数集成(如 SigFox、LORIOT、ChirpStack、The Things Stack)始终产生 JSON 载荷,并把设备二进制负载连同 RSSI、SNR 等元数据一起包装;
- HTTP 与 CoAP 集成根据请求头判断内容类型;
- MQTT 集成(MQTT 3.x 发布消息中无 content-type 概念)的载荷始终为 BINARY 类型。
metadata({[key: string]: string}):键值对形式的元数据映射,包含集成特定的字段,也可以在集成详情中为每个集成配置额外元数据。
2.2 返回值必须满足的条件
依据decoder_fn.md,解码函数必须返回一个合法的 JSON 文档,并满足以下要求:
| 字段 | 约束 | 说明 |
|---|---|---|
deviceName+deviceType或assetName+assetType | 必须(二选一) | 唯一标识设备或资产;平台据此在租户范围内查找已有实体,找不到且集成开启 “Allow to create devices or assets” 时自动创建新实体。DevEUI、MAC 地址等唯一标识常被用作设备名 |
attributes | 可选 | 分配给设备/资产的服务端属性对象 |
telemetry | 可选 | 设备/资产的时间序列(time-series)数据对象或数组 |
customerName | 可选 | 自动将设备分配给指定客户;客户不存在时自动创建 |
groupName | 可选 | 自动将设备加入指定实体分组;分组不存在时自动创建,默认创建在租户范围内,若同时存在customerName则创建在客户范围内 |
deviceLabel/assetLabel | 可选 | 非唯一的、友好的展示标签,可用于仪表盘,替代设备名展示 |
特别注意decoder_fn.md中反复强调的生效边界:customerName、groupName、deviceLabel/assetLabel的分配动作只发生在“当前集成创建设备或资产”的过程中——即只有当输出中的 deviceName/deviceType(或 assetName/assetType)对应的实体不存在、且平台允许创建时才生效;如果实体已经存在,这些参数会被忽略。
三、逐字段精解:以 label_json_output 示例为准
下面以第一节的完整 JSON 为标本,逐字段说明取值规范与实际含义。
3.1 实体标识:deviceName与deviceType
示例中:
"deviceName": "001B638446E7", "deviceType": "thermostat",deviceName使用 MAC 地址风格唯一标识001B638446E7,符合“用 DevEUI、MAC 或其它唯一标识作为设备名”的官方建议;deviceType标识设备类型thermostat,与同组其它示例(如simple-json示例输出中的"deviceType": "Thermostat")一致。
3.2 展示标签:deviceLabel
"deviceLabel": "Room A thermostat",deviceLabel用于存放非唯一、用户友好的展示名。它不参与实体唯一性判定(唯一性仍由deviceName保证),适合在仪表盘上直接展示,例如把晦涩的001B638446E7呈现为 “Room A thermostat”。在 simple-json/decoder_fn.md 中可以看到它在真实 TBEL 代码里的写法:
var result = { deviceName: json.serialNumber, deviceType: "Thermostat", deviceLabel: "Kitchen Thermostat", telemetry: { ts: timestamp, values: { temperature: json.t, humidity: json.h, } } }; return result;对应的输出见 simple-json/output.md。
3.3 客户归属:customerName
"customerName": "Company Name",平台会使用该名称自动把设备分配给客户:若同名的客户不存在,平台会先创建它。正如上文强调的,该分配仅发生于设备/资产由当前集成创建的过程中,实体已存在时会被忽略。这也解释了为什么 simple-metadata/metadata.md 中会把customerName作为集成元数据传入,由解码器读取后写入输出。
3.4 分组管理:groupName
"groupName": "Thermostats",平台会把设备自动加入名为 “Thermostats” 的实体分组;分组不存在时自动创建。分组默认建在租户范围内,如果输出中同时包含customerName,则分组会创建在客户范围内。与customerName一样,这一归属动作只在实体创建流程中生效。
3.5 服务端属性:attributes
"attributes": { "model": "Model A", "serialNumber": "SN-111", "integrationName": "Test integration" },该对象内的键值对会作为服务端属性写入设备/资产,例如设备型号、序列号、来源集成名称等静态信息。注意:属性是可选的整体对象,属性内部字段可根据业务自由组合。
3.6 时间序列:telemetry
"telemetry": { "temperature": 42, "humidity": 80 }该对象代表设备的时间序列数据。此处是“字段到值”的简化形态(键为遥测键,值为当前数据点);也可以扩展为带时间戳的结构化形态:
"telemetry": { "ts": 1527863043000, "values": { "temperature": 42, "humidity": 80 } }当省略ts时,平台使用服务端时间戳;若提供ts,则必须是Unix epoch 毫秒。完整的带时间戳输出示例见 simple_json_output_with_ts.md,其中"ts": 1527863043000即为毫秒级时间戳。
四、输出变体:数组输出与多数据点
除了单个对象,解码函数的返回类型允许是object[](对象数组)。如 json_array_output.md 所示,一个数组中可以包含多个设备/资产,每个元素又可能携带多个不同时间戳的时间序列数据点:
[ { "deviceName": "001B638446E7", "deviceType": "thermostat", "deviceLabel": "Room A thermostat", "attributes": { "model": "Model A" }, "telemetry": [ { "ts": 1527863043000, "values": { "battery": 3.99, "temperature": 27.05 } }, { "ts": 1527863044000, "values": { "battery": 3.98, "temperature": 27.06 } } ] }, { "assetName": "OF-123", "assetType": "office", "attributes": { "model": "Model A" }, "telemetry": { "ts": 1527863041000, "values": { "battery": 3.99, "temperature": 27.05 } } } ]注意第二个元素使用了assetName/assetType标识一个资产,且每个telemetry数据点都携带独立的毫秒时间戳ts。这印证了第一节输出结构中telemetry既可接受单对象、也可接受数组的弹性设计。
五、源码级验证:输出 JSON 如何在集成运行时被消费
上述 JSON 字段并非仅停留在文档层面,集成运行时会真正读取并消费它们。在 AbstractIntegration.java 的设备上行数据处理逻辑processDeviceUplinkData中可以看到:
DeviceUplinkDataProto.Builder builder = DeviceUplinkDataProto.newBuilder() .setDeviceName(entityName) .setDeviceType(data.getDeviceType()); if (StringUtils.isNotEmpty(data.getDeviceLabel())) { builder.setDeviceLabel(data.getDeviceLabel()); } if (StringUtils.isNotEmpty(data.getCustomerName())) { builder.setCustomerName(data.getCustomerName()); } if (StringUtils.isNotEmpty(data.getGroupName())) { builder.setGroupName(data.getGroupName()); } if (data.getTelemetry() != null) { builder.setPostTelemetryMsg(data.getTelemetry()); } if (data.getAttributesUpdate() != null) { builder.setPostAttributesMsg(data.getAttributesUpdate()); }从这段源码可以确认以下几点:
deviceName与deviceType无条件写入Proto 消息,是设备实体创建/查找的核心标识,与文档“必须包含”的约束一一对应;deviceLabel、customerName、groupName均为可选字段,且只有在StringUtils.isNotEmpty(...)(非空字符串)时才写入,空值会被静默忽略;telemetry与attributesUpdate也按非空判断写入,分别对应输出 JSON 中的telemetry与attributes对象。
资产侧的处理逻辑processAssetUplinkData(AbstractIntegration.java)与之对称:assetName/assetType必填,assetLabel、customerName、groupName可选。这从实现层面印证了decoder_fn.md中“deviceLabel 或 assetLabel”“customerName 仅在实体创建时生效”等规则的底层来源——实体归属、分组、标签的最终落库动作由集成处理链中的下游服务在创建实体时执行。
六、配套示例速查表
decoder_fn.md的 “Examples” 小节为每种输出形态给出了“输入 payload + 解码函数 + 期望输出”的完整对照,是验证本文所讲规则的实操素材:
| 名称 | 内容类型 | 说明 | 示例文件 |
|---|---|---|---|
| Simple JSON with date | JSON | 解析带字符串时间戳的特定 JSON 格式 | payload、decoder_fn、output |
| Simple CSV | TEXT | 解析 CSV 数据 | payload、decoder_fn、output |
| Simple binary data | BINARY | 解析含序列号、电池电量、温度与饱和度的二进制负载 | payload、decoder_fn、output |
| JSON with multiple hex encoded values | JSON | 转换多个带 hex “value” 字段与时间戳的 JSON 对象 | payload、decoder_fn、output |
| Use metadata fields | JSON | 用元数据字段确定设备类型、型号与客户名 | payload、metadata、decoder_fn、output |
综合本文与这些示例可以得出一个完整结论:TBEL 解码器输出 JSON 的骨架是“必填实体标识对 + 可选属性 + 可选遥测”,而deviceLabel/assetLabel、customerName、groupName是锦上添花的关联字段——它们让设备/资产在创建之初就具备可展示的友好标签、正确的客户归属与分组归属,从而实现“一次接入、自动归位”的集成体验。在编写解码器时,只要严格遵循“实体标识必填、其余按需携带、时间戳用毫秒 epoch”这三条规则,就能与平台的下游实体创建与数据处理链路无缝衔接。
- 物联网
- 后端
- 数据可视化
- 消息队列
【免费下载链接】thingsboard
All-in-one IoT Platform - Device management, data collection, processing and visualization.
相关推荐
ThingsBoard 集成解码器(Decoder)JSON 输出格式详解:deviceLabel、customerName 与 groupName 的完整应用
ThingsBoard 集成解码器(Decoder)JSON 输出格式详解:deviceLabel、customerName 与 groupName 的完整应用
物联网后端数据可视化消息队列ThingsBoard 集成下行链路 TBEL 编码器 JSON 输出格式详解:contentType、data 与 metadata 三字段实战指南
ThingsBoard 集成下行链路 TBEL 编码器 JSON 输出格式详解:contentType、data 与 metadata 三字段实战指南 导读 在
物联网后端数据可视化消息队列ThingsBoard TBEL 上联解码器输出格式详解:如何构造带时间戳的 JSON 输出
ThingsBoard TBEL 上联解码器输出格式详解:如何构造带时间戳的 JSON 输出 导读 本篇文章聚焦 ThingsBoard 数据集成中 TBEL(
物联网后端数据可视化消息队列
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考