做物联网这几年,ThingsBoard 算是我用得比较顺手的一套开源平台了。很多刚接触它的朋友,第一件事往往不是搭数据采集,也不是写规则引擎,而是先在仪表板上把设备状态摆出来——因为领导要看,客户也要看,没有一块像样的状态面板,项目连验收都过不了。今天就把 ThingsBoard 仪表板状态相关的整套做法捋一遍,从数据怎么来、状态怎么算,到组件怎么配、RPC 命令怎么下发,再到我踩过的那些坑,一次讲清楚。
这篇文章主要面向两类人。一类是刚上手 ThingsBoard、正在搭第一个设备监控面板的开发者,另一类是已经在用但被状态同步、RPC 下发、子设备联动折腾过的老用户。需要的基础知识不多,会用 MQTT 工具、能看懂 JSON 就够了,剩下的看完照着做就行。
1. 先搞清楚:仪表板上的"状态"到底指什么
很多人在仪表板上折腾半天,其实压根没想明白"状态"这个词包含几层意思。我见过最典型的需求是"我要看到设备状态",结果做出来只有一个绿灯红灯。实际上,状态至少可以拆成三层:设备在线状态、业务运行状态、告警状态。这三层数据来源不同、更新机制不同、展示方式也不同,分开处理才不容易乱。
1.1 设备在线状态:谁告诉你设备还活着
在线状态是 ThingsBoard 里最基础的一层。它不是一个你自己上报的字段,而是平台根据设备活动情况自动推断出来的。设备通过 MQTT、HTTP 或者 CoAP 接入后,只要上送了任意遥测、属性更新或者 RPC 响应,平台都会刷新这个设备的最后活动时间。如果你在 ThingsBoard 的设备列表里打开一个设备详情,能看到"活跃、最近连接时间"之类的信息,底层就是靠最后活动时间算出来的。
这里必须点破一个误区:设备在线,不等于设备一直连着 MQTT。平台不会因为你 TCP 没断开就永远认为你在线,它是按"超过多久没有活动就判定离线"的逻辑来的,这个时间在服务端配置里叫设备活动超时时间。默认情况下,一个设备如果只是挂着连接、从来不发数据,过一段时间就会被标记为离线。
所以在实际项目里,不要指望平台自动维护在线的准确性。设备侧必须做心跳,最简单的做法是每隔 30 到 60 秒上送一个心跳遥测,比如发送一个{"hb": 1},平台一收到就会刷新活跃时间。这样仪表板上的在线状态才可信。
1.2 业务状态:遥测数据如何变成"状态"
在线状态是基础,但业务上要看的远不止这个。比如一台空压机,客户要的不是在线/离线,而是"运行中、待机、故障、停机"这些业务状态,这就属于业务运行状态。
业务状态一般有两种生成方式。第一种是设备自己算,直接把结果上报,比如{"status": "running"},仪表板拿这个字段直接展示,简单直接,实时性最好。缺点是设备固件必须按你定义好的字段规范上报,如果设备是老协议改不动,这种方式就行不通。
第二种是平台侧算,通过规则链把原始遥测值转换成状态。比如温度传感器上报的是 5 分钟均值,但监控面板要的是"温度是否越限"这个状态,那就用规则链加一个脚本节点,判断temperature > 80就输出{"overheated": true},再保存到最新的遥测或者属性里,仪表板再绑定这个派生字段。
大多数项目两种方式都会用。设备侧负责上送开关状态、档位这些实时状态;平台侧负责兜底,把越限、离线这些异常从原始数据里挖出来。这样状态展示既有实时性又有可靠性,不会出现设备坏了自己上报一个"正常"的尴尬情况。
1.3 状态数据在 ThingsBoard 里的存放位置
这里必须先搞清楚三个概念:遥测(Telemetry)、属性(Attribute)和最新遥测(Latest Telemetry)。遇到过很多项目,状态数据一会儿放属性一会儿放遥测,结果不同组件读不到对不上,排查起来非常难受。
| 数据类别 | 特点 | 适合存放的状态 | 仪表板组件取值方式 |
|---|---|---|---|
| 遥测 Telemetry | 时序数据,带时间戳,可做历史查询 | 温度、转速、运行状态等随时间变化的值 | 最新遥测(Latest telemetry) |
| 客户端属性 | 设备上报的键值,不按时间序列存储 | 固件版本、安装位置、设备配置 | 客户端属性 |
| 共享属性 | 平台端配置,设备可读取 | 阈值、运行参数、平台下发的配置项 | 共享属性 |
| 服务器属性 | 主要由平台端维护 | 设备活跃状态、最后连接时间 | 服务器属性 |
判断标准就一条:这个状态值需不需要看历史变化?需要,就放遥测;只是看当前值,放属性反而更轻量。不过要注意,遥测存的是时间序列,数据量大了要规划保留策略,别拿它当属性存一份,又当遥测存一份,后面存储会很难受。
2. 仪表板状态设计的整体思路
实操之前,先讲设计思路。很多新手一上来就拖组件,做完发现东一块西一块,自己也看不懂。我现在的习惯是:在创建仪表板之前先画一版"草图",把要展示的状态分层、分组、定数据源,再动手配。
2.1 状态面板的分层设计
状态面板我一般分成三层,每层对应不同的组件类型,数据源也各不相关。
设备层:展示在线/离线、信号强度、最后通信时间。这一层负责回答"设备还在不在",通常用顶部的状态卡片队列或者实体表来展示。数据源优先绑定服务器属性里的 active、lastConnected 字段。
业务层:展示每个设备的核心指标和运行状态。比如空压机是运行中还是待机,门锁是开启还是关闭。这一层用状态指示器、卡片来展示,数据源绑定设备上送的业务状态字段,一般是遥测。
告警层:展示当前告警、未处理事件、需要人工介入的动作。这一层用告警组件、告警列表来展示。数据源是平台侧创建的告警实体,不是设备遥测。
这样分完之后,每一层的数据链路是独立的,排查的时候切分界面就知道问题出在设备侧还是平台规则链,还是仪表板配置上。
2.2 别名:状态组件的数据源基石
仪表板上的组件必须绑定数据源,而数据源最常用的方式就是实体别名(Entity Alias)。别名的作用是为仪表板上的组件建立一个"设备/资产集合"的引用,这样组件读取的是别名背后动态变化的数据,而不是写死的设备 ID。
别名有几种常用类型:单实体、实体列表、实体类型、由关系派生。单实体适合做单台设备的详情面板;实体类型适合做同一类设备的批量监控;关系派生适合从某个资产(比如一个站房)下面取设备,做有层级的设备组。
我强烈建议所有组件都走别名,哪怕你只有一个设备。因为后面设备换新、加设备、改设备类型,都只需要改别名对应的过滤条件,组件的绑定关系不会断。项目上线后最怕的就是设备 ID 变了,面板全部空白,别名机制就是为了避免这个灾难。
2.3 状态颜色与图标策略
状态展示最直观的还是颜色和图标。一个状态卡片如果是绿的,用户就知道正常;变红了,用户就知道要处理。但颜色的使用一定要统一规范,不然一个面板里黄绿红混着用,第二天客户就打电话来问你们哪里爆了。
我这边通常定这么一套规范:
- 正常/在线/运行中:绿色
- 待机/暂停/低风险:黄色
- 故障/离线/越限:红色
- 未知/未上报:灰色
这套规范不只用在状态指示器上,还用在整个平台的告警严重程度标记里,做到全局一致。在 ThingsBoard 的状态组件里,设置样式映射时按规范选颜色,不要临时起意换个浅红深红的,省得后面自己看都迷糊。
3. 实操:从数据上送到状态卡片落地
设计想清楚了,接下来就是动手。我从一个最简单的 MQTT 设备开始,把完整链路走一遍:设备上送数据 -> 创建仪表板 -> 配置别名 -> 绑定状态组件 -> 下发 RPC 命令 -> 状态联动刷新。
3.1 设备接入:状态字段怎么上送
设备接入 ThingsBoard 最常用的是 MQTT。设备在平台里创建之后,会拿到一个接入凭据,通常是一个 access token。设备用这个 token 连接 MQTT 服务的 1883 端口,主题路径就是v1/devices/me/telemetry。
用命令行工具模拟设备上送遥测,命令长这样:
mosquitto_pub -h 127.0.0.1 -p 1883 -t "v1/devices/me/telemetry" -u "ACCESS_TOKEN" -m '{"temperature": 36.5, "status": "running", "mode": 2}'这里上送了一个遥测,包含三个 key:temperature、status、mode。其中 status 这个字段就是仪表板状态卡片的直接数据来源。设备侧开发时,要保证 status 字段的值规范统一,比如running、stopped、fault三种取值,不要一会儿叫 run 一会儿叫 running,不然仪表板样式映射会失效。
如果只是想上送配置类属性(比如设备位置、固件版本),可以走属性主题:
mosquitto_pub -h 127.0.0.1 -p 1883 -t "v1/devices/me/attributes" -u "ACCESS_TOKEN" -m '{"position": "line-1", "fw_version": "1.0.3"}'这里面有个容易踩的坑:上送遥测和上送属性,虽然命令差不多,但数据归宿完全不同。遥测会进入时序数据库,占存储;属性存的是最新值,不按时间堆积。所以心跳这类高频且不需要历史的数据,尽量走属性主题,别和业务遥测混在一起。
3.2 仪表板与实体别名配置
在 ThingsBoard 左侧菜单找到 Dashboards,点加号创建新仪表板,起个名字比如"设备状态总览"。创建完点 Open Dashboard,进入仪表板后再点右下角铅笔图标进入编辑模式。
编辑模式下,先配实体别名。入口在 Dashboard 编辑界面的 Entity Aliases 菜单。点击 Add Alias,填写别名名称,比如"全部设备",然后在 Filter Type 里选择 Entity Type,Entity Type 选 Device,再按设备 Profile 过滤出你要的那批设备。
配置完保存之后,别名在组件的数据源里就可以被选中了。注意,别名是一个仪表板层面的引用,多个组件可以共用同一个别名。比如实体表和状态卡片都可以用"全部设备"这个别名,这样你只需要维护一个过滤条件。
3.3 常用状态组件及关键参数
ThingsBoard 自带组件库里,有几个组件做状态展示非常好用。我把它们和适用场景整理成了表,配置的时候直接参考。
| 组件类型 | 适合场景 | 数据源类型 | 关键配置项 |
|---|---|---|---|
| State Indicator | 单个设备状态指示 | Latest telemetry / Attribute | Value 字段、样式映射(颜色/图标) |
| Cards 卡片 | 设备关键指标总览 | Latest telemetry / Attribute | 字段映射、刷新周期 |
| Entity Table 实体表 | 设备列表状态总览 | Entity 类型别名 | 列配置、单元格样式函数 |
| Timeseries Graph 图表 | 状态/指标趋势分析 | Timeseries | 遥测 key、时间窗口、聚合方式 |
State Indicator 是最常用的状态卡片。拖入组件后,在编辑界面配置 Data source,选中设备别名,然后设置键值为status。接下来在样式设置里做映射,比如:
running对应绿色圆形图标stopped对应灰色方块图标fault对应红色警告图标
一个关键细节:如果状态字段不是设备直接上送的遥测,而是平台规则链算出来的、存放在共享属性里的值,那数据源类型就要选 Shared attribute,别选 Latest telemetry。这里选错了,卡片就永远读不到值。
还有一个特别容易忽略的点:刷新间隔。仪表板组件默认不是每秒钟都在拉数据,状态卡片可能停留在你上次打开页面的瞬间。在组件的高级设置里可以设置刷新周期,我一般状态卡片设 5 秒,图表设 30 秒。设得太频繁会给平台造成不必要的压力,设得太长状态又失去实时性。
3.4 RPC 命令下发与状态闭环
状态面板不能只"看",还要能"控"。这就涉及到热词里的"thingsboard 使用下发命令"和"thingsboard 下发 rpc 子设备下发"。
先讲单个设备怎么通过 RPC 下发命令。原理其实很清晰:
- 仪表板上的按钮触发 JavaScript 动作,调用
api.sendRpcRequest发送 RPC 请求 - 平台服务端收到后,把命令推给设备,设备通过订阅主题
v1/devices/me/rpc/request/+收到 - 设备处理完,把结果发到
v1/devices/me/rpc/response/<requestId> - 平台把响应返回给仪表板
设备端订阅 RPC 请求主题,命令行模拟是这样:
mosquitto_sub -h 127.0.0.1 -p 1883 -t "v1/devices/me/rpc/request/+" -u "ACCESS_TOKEN" -v收到请求后,设备执行操作,再发布响应:
mosquitto_pub -h 127.0.0.1 -p 1883 -t "v1/devices/me/rpc/response/1" -u "ACCESS_TOKEN" -m '{"success": true, "status": "running"}'这里的关键是:设备在响应 RPC 的同时,最好立刻把最新的状态以遥测上送,比如{"status": "running"}。这样仪表板上的状态卡片会随 RPC 命令下发后自动刷新,形成"下发命令 -> 设备执行 -> 状态反馈"的闭环。如果你只做了 RPC 响应但没有遥测上送,面板上的状态是不会变的。
仪表板上的按钮配置也不复杂。编辑仪表板,新建一个图片按钮或者 Switch 组件,在 Action 里选择 RPC 命令,填写方法名和参数。
{ "method": "setMode", "params": { "mode": "start" }, "timeout": 10000 }这里的 method 是自定义的,设备端解析 JSON 时按这个字段分发到对应处理逻辑。timeout 我建议一定设置,默认不设超时,设备离线时 RPC 会一直挂着,用户点按钮半天没反应,体验很差。设了 10 秒超时,前端至少能提示你"请求超时"。
实际项目中我还会做一个两段式反馈:RPC 响应返回 success 后,先提示"命令已送达",等最新遥测里的状态值真正变化后,再提示"状态已更新"。因为 RPC 响应只能证明命令送达并得到处理,不等于设备的物理状态已经变化,这两者之间通常有个执行延时。如果直接把 RPC 响应当成状态变更,很容易被客户投诉"我点了启动,界面显示成功了,但设备没转"。
4. 进阶场景:子设备状态与告警联动
设备数量少的时候,单个设备管理就够了。但工业项目里,一个网关底下挂几十个 Modbus 子设备是常态。这种场景下,子设备状态怎么上仪表板,是一个很讲究的工程问题。
4.1 网关场景下子设备状态怎么来
网关接入时,ThingsBoard 里的设备模型是"网关 + 子设备"两层。网关是一个真实的设备,子设备是逻辑设备,并不直接建立 MQTT 长连接。子设备的数据全部通过网关代理上送,使用网关专属主题:
- 上送子设备遥测:
v1/gateway/telemetry - 上送子设备属性:
v1/gateway/attributes - 接收下发到子设备的 RPC:
v1/gateway/rpc
子设备状态在仪表板上的展示,和普通设备有个本质区别:平台不会自动判断子设备在线与否,它只能看到网关的活动。如果网关不把子设备状态上报上来,仪表板对子设备就是一片空白。
所以项目里我一般让网关侧做两件事。第一,定期上报每个子设备的状态,比如:
{ "subDevice1": { "status": "online", "last_seen": 1720000000000 }, "subDevice2": { "status": "offline", "last_seen": 1719990000000 } }第二,网关自己维护一份子设备维护表,知道哪些子设备注册过、最近一次通信是什么时候,超过某个阈值就标记离线。这样仪表板上的子设备状态才有可信度。
在仪表板组件里,要对网关上报的数据做解析。比如用一个函数脚本,把这串 JSON 映射成每个子设备的状态字段,再绑定到实体表或者状态卡片上。如果直接用原始字段,组件只会显示一长串 JSON,客户看不懂。
4.2 告警状态与仪表板联动
状态面板不能只显示正常状态。我更愿意说,状态面板的核心价值是把"需要人处理的事情"暴露出来,这件事要交给告警。
ThingsBoard 的规则链可以配置告警逻辑。一条比较典型的告警规则链是这样:
- 从设备遥测消息里解析
temperature - 通过脚本节点判断
temperature > 80,返回 true - 调用 Create Alarm 节点,创建一条类型为"温度越限"的告警,设置严重程度为 CRITICAL
- 告警自动关联到当前设备
仪表板上加一个告警组件,绑定设备别名,就可以展示当前告警数量、最新告警详情。状态卡片也可以根据是否有未处理告警来换颜色:正常状态是绿色,有未处理告警就显示红色。
联动这块有个小技巧:规则链里更新状态字段时,把告警是否存在也考虑进去。比如脚本里判断"有未确认告警时,status 强制显示为 abnormal",这比前端组件自己猜要可靠得多。前端只负责展示,状态计算逻辑统一收口到规则链里,别散落在各个组件的 JS 函数里。
4.3 关于 JetLinks 的一点对比
热词里有 "jetlinks vs thingsboard" 的对比,这里简单聊几句我的看法。JetLinks 是国产物联网平台,在协议接入、设备管理、本地化支持方面做得不错,如果项目对国产化环境、本地二次开发、复杂协议适配有硬性要求,JetLinks 值得考虑。但单说仪表板生态和状态展示这块,ThingsBoard 的组件库、文档资源、社区案例都更丰富,做状态面板的周期明显更短。
选型不是非黑即白,核心还是看团队熟悉哪套、项目约束是什么。我自己在标准 IoT 监控场景里偏好 ThingsBoard,但遇到需要深度对接国内硬件私有协议的场景,也会认真评估 JetLinks。这里不展开说太多,一句话总结:仪表板能力只是选型维度之一,协议层和数据层的匹配度往往更重要。
5. 常见问题与排查技巧
这一节是重点,我把项目中实际遇到过的、社区里高频出现的问题做一次集中排查。状态面板出问题,大多数情况不是仪表板本身,而是数据链路某一段断了。
5.1 设备在线却显示离线的排查
这是被问得最多的一个问题:设备明明在跑,数据也在发,仪表板却显示离线。原因前面提过,平台判断在线依赖的是"最后活动时间",而设备可能根本没有持续上报数据。
排查步骤:
- 打开设备详情页,确认"最后活动时间"是否还在更新
- 如果停止更新,检查设备侧是否做了心跳上报
- 如果没有心跳,补上定时上报,频率建议 30 到 60 秒
- 确认服务端 activity timeout 配置是否过短
需要注意的是,心跳数据如果走遥测主题,会产生大量时序数据。推荐做法是走属性主题上报,或使用专门的轻量字段,别把心跳和业务遥测混在一起。
5.2 状态卡片不刷新
状态卡片有数据但一直不刷新,是最容易排查的一类。顺序如下:
- 确认最新遥测里有没有这个字段:在设备详情页的 Latest Telemetry 栏目里找
- 没有值,说明设备没上送对字段名,检查固件或脚本里的 key 是否和组件绑定的一致
- 有值但卡片不动,检查组件刷新间隔是否太长
- 刷新间隔没问题,退出仪表板编辑模式重新进入,排除编辑态缓存
还有一个常见原因:组件数据源类型选错了。比如字段存在遥测里,组件绑定了客户端属性,那当然读不到。排查的时候先看清字段类型,再核对组件的数据源配置。
5.3 别名与数据源配置错误
别名配置问题是新手重灾区。常见情况:
- 单实体别名选错了实体类型,绑定了资产,但目标是设备
- 实体列表过滤里设备 Profile 选错,导致列表为空
- 关系派生筛选方向反了,本来要向资产下面取设备,配置成了向设备上面找资产
排查方式很简单:在别名编辑界面右侧一般有实体预览功能,能实时看到当前别名查出来的实体数量。先确认这里能不能查出设备,再回仪表板判断组件是不是绑定错了别名。教训是,不要在多个组件里用 JSON 硬编码设备 ID,统一走别名管理,省很多事。
5.4 RPC 下发失败与状态不同步
RPC 下发失败,最常遇到的是设备端没有订阅正确的主题。普通设备订阅v1/devices/me/rpc/request/+;网关场景下,平台把命令发到v1/gateway/rpc,由网关负责转发给子设备,而不是直接发给子设备。
子设备 RPC 下发还有一个坑:平台里的子设备注册信息和网关内部的设备映射不匹配。比如平台知道子设备 A,但网关没维护 A 的注册信息,命令到网关就断了。遇到这种情况,优先检查网关的设备映射表。
状态不同步的问题,核心原因往往是设备执行完命令后没有上报新状态。记住前面说的那条铁律:RPC 响应只证明"命令已送达并处理",不等于"设备状态已变更"。状态是否变更,必须以最新遥测为准。所以设备固件里,一定要在动作执行完成后主动上送一次状态遥测,别等着平台去猜。
5.5 设备离线状态下命令怎么处理
还想多说一条:RPC 下发时设备恰好离线怎么办。ThingsBoard 的 RPC 有持久化机制,部分 RPC 可以让离线设备在重新上线后收到命令。但实际项目中我很少依赖这个特性,因为离线期间的状态变化很可能已经让这条命令失去意义。
更稳妥的做法是:前端在下发前先查询设备 active 状态,离线就直接提示"设备离线,无法执行命令";设备端也做状态同步,上线后拉取一次最新期望状态,而不是依赖平台补发命令。这样既省心又避免命令堆积。
6. 写在最后:仪表板背后是链路设计
做状态仪表板做久了,我最大的体会是:仪表板只是最上面那一层皮,真正决定它好不好用的,是数据从设备端到平台再到组件这条链路设计得是否清晰。设备侧字段规范、平台侧状态计算、仪表板组件映射,这三段缺一不可。分段排查的能力,比记住某一个组件的配置参数更重要。
我自己的习惯是:每个项目开工前,先定义一份"状态字段规范清单",把所有的状态字段名、取值、含义、数据存放位置写清楚,然后让设备端开发、规则链配置、仪表板设计共用这一份文档。这样做下来,后期维护会轻松很多,也不会出现设备端改了字段名、仪表板悄悄变灰的情况。
最后再分享一个小技巧:如果你第一次做 ThingsBoard 仪表板,不要追求一次做完。先搭一个最小可用的单设备状态卡片,把完整链路跑通,再慢慢丰富成多设备总览、告警联动、RPC 控制。链路通了,剩下的就是拼图;链路不通,组件拖得再多也是白搭。这套思路,我在多个项目里验证过,是真的能少走弯路。