LoRaWAN 项目里有一类需求很常见:数据已经在 ThinkLink 上跑起来了,但用户想看的界面不是 ThinkLink 的------他想看 Home Assistant 的仪表盘,或者想在 Node-RED 里画一条自己的流程线。
这个需求合理。Home Assistant 的 Dashboard 和自动化规则确实好用,Node-RED 的可视化编排在做跨系统联动时也确实顺手。但要把数据送过去,得先搞清楚一件事:这两个平台都不认识 LoRaWAN。
Home Assistant 官方支持的通信方式是 MQTT、Zigbee、Z-Wave、HTTP 这些,没有 LoRaWAN。它不能直连网关,也不解析 LoRaWAN 帧。Node-RED 同理------它只是一个流程引擎,数据从哪来完全取决于你给它接什么输入节点。
所以中间必须有一个网络服务器(NS)先把 LoRaWAN 帧解成结构化数据。ThinkLink 干的正是这一步:网关收到射频帧 → NS 解密 → 物模型解析脚本把二进制变成有名字的字段 → 这时数据才变成"温度 24.6 度"这种能被消费的东西。
剩下的问题只有一个:怎么把解析后的数据送到 HA / Node-RED 手里。
一、先说一个版本变化:内置 HA 集成已经没有了
这一条要写在最前面,因为它决定了整篇文章的做法。
如果翻旧版本的手册,能看到"物模型里配置 Home Assistant 字段""服务器配置里填 HA 地址"这类说明。但官方文档在 2.00.066 的说明里写得很明确:本轮源码移除了内置 ThingsBoard / Home Assistant 集成及相关字段 ,下文旧版的 HA / ThingsBoard 配置说明"仅适用于旧版本,不可作为 2.00.066 的操作步骤"。同一段里还特别注明:BACnet / Modbus 保留。
也就是说,从 2.00.066 起,"在 ThinkLink 里填一个 HA 地址就能自动推实体"这条路不存在了。取而代之的是通用能力:**MQTT 转发器(Broker + Forwarder)**。
这个变化不是退化。内置集成的问题是它把对接方式写死了------平台只能按自己定义的格式推,对方系统必须配合。改成 MQTT 转发器之后,Topic 怎么改、Payload 怎么重组、要不要过滤,全在一个 JavaScript 脚本里,对接任何系统都用同一套机制。HA、Node-RED、ThingsBoard、自研应用,在 ThinkLink 这一侧的差别只剩脚本内容不同。
还有一处口径需要注意:产品对比资料里提到 ThinkLink 边缘形态(TKG,网关内置)预置了 Home Assistant OS 与 ThingsBoard CE 组件。那是同一台硬件上另外跑一个 HA 实例的部署方式,不是"ThinkLink 把数据推进 HA"。两件事不要混为一谈------本文讲的是后者,即把数据从 ThinkLink 送到一个已有的 HA / Node-RED 实例。
二、三条可用路径,先选对再动手
对接之前有个选择题。官方文档里其实出现过三种推送方式,可用性和推荐度差别很大。
| 路径 | 机制 | 状态 | 适用 |
|---|---|---|---|
| 设备侧"第三方转发"开关 + 服务器配置 ThirdParty | 老式直推 | 旧版配置,已停用 | 不建议新项目使用 |
| 服务器配置 → 第三方 MQTT(ThirdParty) | 平台侧配一个 Broker 地址 | 文档口径存在冲突,见下 | 需要与实际版本确认 |
| 高级功能 → Broker + 转发器 | 订阅内部 Topic,转发到目标 Broker | 当前版本的正式做法 | 推荐 |
这里有一个必须如实说明的文档冲突:MQTT 转发器文档 明确写着"无需在设备侧开启任何'第三方转发'开关,也无需在'服务器配置'中配置任何第三方平台。旧版本中的这两处配置已经停用";而服务器配置文档的 1.2 节仍然保留着 ThirdParty 配置区(Protocol / Host / Port / Telemetry Topic / Username / Password)与"设备详情页启用 ThirdParty 开关"的操作说明。
两份文档说的不是同一件事,或者至少不是同一个版本。处理办法很简单:以实际平台版本为准,先在界面上看有没有"高级功能 → Broker / 转发器"入口。 有,就走转发器;那是新版本的主路径,也是本文下面讲的做法。
三、转发器:两步配置,五个字段
数据流是这样的:
ThinkLink 内部 Broker(AS 或 ThinkLink) ↓ Source Broker / Subscription Topic 转发器规则(可选 JavaScript 转换) ↓ Target Broker 第三方 MQTT Broker(Customize)
第一步:建两个 Broker 连接
进入高级功能 → Broker。一条转发规则需要一个来源连接和一个目标连接。
来源 Broker 有两种内部类型,选哪个决定了你拿到的数据形态:
ThinkLink:经过物模型解析后的遥测数据。字段已经有了名字和单位。AS:LoRaWAN 解密后的上行原始数据,还没过物模型。
绝大多数场景选 ThinkLink。选 AS 的理由只有一个:你想在外部系统里自己做解析,或者要拿 RSSI / SNR 这类射频侧信息做网络质量分析。如果目的只是"让 HA 显示一个温度",选 AS 等于把已经做完的解析工作重做一遍。
内部 Broker 的地址和租户凭据由平台自动填写,不需要去服务器配置页找。
目标 Broker 新增一个 Customize 类型,填对方的信息:
名称和启用状态 MQTT URL,例如 mqtt://192.168.1.50:1883 或 mqtts://host:8883 用户名和密码(对方要求时) CA 证书、客户端证书等 TLS 信息(对方要求时)
如果 HA 和 Node-RED 都装在同一台机器上(常见情况),目标就是这台机器上跑的 Mosquitto,或者 HA 自带的 MQTT 插件(HA Add-on: Mosquitto broker)。
保存启用后,先看 Broker 列表的运行状态:正常是 RUNNING,出现 ERROR 就查 ErrMsg,逐项核对 URL、端口、凭据、证书、网络连通性。
第二步:建转发器规则
进入高级功能 → 转发器 → 新增:
| 字段 | 说明 |
|---|---|
| 名称 | 转发规则名称 |
| 启用 | 开启后规则才会订阅和转发 |
| Source Broker | 要订阅的来源 Broker |
| Target Broker | 要发布到的目标 Broker |
| Subscription Topic | 来源 Broker 上的订阅 Topic,支持 + 和 # 通配符 |
| Custom Script | 可选;转换 Topic、消息内容或 retain 选项 |
| 备注 / 提交说明 | 可选;用于说明规则和版本变更 |
一条硬约束:Source Broker 和 Target Broker 不允许同时选 AS / ThinkLink 内部类型,避免内部循环。第三方转发的正常组合是:
Source Broker = AS 或 ThinkLink Target Broker = Customize
订阅 Topic 怎么填
这是最容易填错的一项,因为没有默认值,占位符必须替换。
| Source Broker | 数据类型 | Subscription Topic |
|---|---|---|
ThinkLink |
物模型解析后的遥测数据 | /v32/{tenant}/tkl/up/telemetry/# |
AS |
LoRaWAN 解密后的上行原始数据 | /v32/{tenant}/as/up/data/# |
AS |
多网关收集完成后的完整上行消息 | /v32/{tenant}/as/up/dataAll/# |
{tenant} 要替换成实际组织账号,在个人中心可以查看,不要原样保留占位符 。# 表示该组织下所有设备;只想转发一台设备时,把 # 换成那台设备的 DevEUI。
举一个具体的例子:组织账号是 mtfac,要转发全部设备的解析后遥测,订阅 Topic 就是 /v32/mtfac/tkl/up/telemetry/#。
四、转换脚本:把 ThinkLink 的话翻译成 HA 的话
不配脚本时,转发器原样保留 Topic 和消息内容------这其实是一个很好的起点:先让消息直通,在 HA / Node-RED 那边抓一条真实消息,看清字段结构,再决定要不要写脚本。 凭想象写的脚本往往第一次跑不通。
需要改格式时,入口函数只有一个:
function forwardScript({ topic, msg, org_params }) { if (!msg?.telemetry_data) return null
return { topic:
${org_params.target_topic_prefix}/${msg.device_id}, option: { retain: false }, msg: { temperature: msg.telemetry_data.T, humidity: msg.telemetry_data.H } } }
三个输入参数:topic 是收到消息时的原始 Topic,msg 是解析后的消息内容,org_params 是只读的组织参数。
返回对象支持三个字段:topic(发到目标 Broker 的 Topic)、msg(发布的消息内容)、option(MQTT 发布选项,比如 { retain: true })。返回 null 或不返回内容,这条消息就被丢弃------这是做过滤的正路,比如只转发某一类设备、只在数值超阈值时才推。
官方示例里出现的字段是 msg.device_id 和 msg.telemetry_data,telemetry_data 下面的键(示例里是 T 和 H)由物模型里的字段名决定。不要猜字段名,抓一条真实消息确认。 第 21 期讲过物模型解析脚本,那里定义的字段键就是这里能取到的键。
org_params 值得单独说一句。它是组织级别的"环境变量",在系统管理 → 服务器配置 → 组织参数 按键值对维护,一次维护全租户共享,平台执行脚本时自动注入。设计目的是把对接外部系统用的地址、前缀、业务标识从脚本里剥离出来------换环境、切前缀只改一处,不用逐个改脚本。而且它无需重启服务即刻生效,只有组织管理员能看见。
五、Home Assistant 侧:两种接法
说明:以下 Home Assistant 的配置方式属于 Home Assistant 通用知识,不是 ThinkLink 官方文档内容。HA 版本迭代较快,字段名以你所用版本为准。
数据到了 HA 用的 Broker 之后,HA 要把它变成实体(entity)。有两条路。
第一条:手写 MQTT sensor。 在 configuration.yaml 里声明传感器,用 state_topic 指向转发器发布过来的 Topic,用 value_template 从 JSON 里取值。这条路最稳,字段完全可控,代价是每台设备每个测点要写一段配置。设备数量少、点位固定时非常适合。
第二条:自己造 MQTT 自动发现消息。 HA 的 MQTT 集成支持 discovery 机制:往约定的 discovery Topic(默认前缀 homeassistant/)发一条带设备元数据的消息,HA 就会自动创建对应实体。ThinkLink 不会原生产出这种格式,但可以在转发脚本里造出来 ------forwardScript 的返回值里 topic 和 msg 都是你说了算,完全可以让第一条上行消息触发一个 discovery 消息。
这条路的优势是加设备不用改 HA 配置,代价是脚本复杂度和一次性的调试成本。设备数量多、经常增减时值得做。
一个实操提醒:ThinkLink 的遥测消息是一台设备一条消息、里面多个字段 ,而 HA 的实体是一个实体一个值 。这个一对多的转换要么在 HA 侧用 value_template 拆,要么在转发脚本里把一条消息拆成多条按字段发布。想清楚在哪一层拆,比后面反复改要省事。
还有一点必须提醒:HA 的 Dashboard 上显示"未知"或"不可用",很多时候不是 HA 的问题,而是数据压根没到 Broker。先在 Broker 上用命令行订阅确认消息真的到了,再去查 HA 配置。
六、Node-RED:三个节点就能跑起来
Node-RED 接 MQTT 比 HA 更直接,因为它不要求实体模型,拿到什么处理什么。
最小流程是三个节点:mqtt in (订阅 Broker 上的 Topic)→ json (把字符串解析成对象)→ debug 或 dashboard 节点。
mqtt in 里填的就是转发器发到目标 Broker 的 Topic,也就是 forwardScript 返回的那个 topic。如果走了直通没写脚本,那它就是原始 Topic /v32/mtfac/tkl/up/telemetry/{eui}。
Node-RED 比 HA 好用的地方在中间那层:你可以在流程里插 function 节点做任意逻辑------单位换算、阈值判断、跨设备关联、写入数据库、调第三方 API。做"温湿度超限时先查同一区域另一个传感器,两个都超才告警"这类联动,Node-RED 比在平台侧写触发脚本更直观,也比在 HA 自动化里写更灵活。
反向控制也是一条 mqtt out 节点的事,但下一节的警告一定要看。
七、写方向:从 HA / Node-RED 回到设备,别只看"发送成功"
这是整个对接里最容易产生误判的地方,和第 24 期讲 BACnet 写入时是同一个道理。
从 HA 或 Node-RED 发一条控制指令,到设备真的执行,中间至少三件事:
- 指令被平台接受------这一步是即时的。
- RPC 下发到设备 ------如果设备是 Class A 的 LoRaWAN 终端,下行只能在每次上行之后的接收窗口发出(RX1 约 1 秒、RX2 约 2 秒)。设备十分钟上报一次,这条指令最坏要等十分钟才落到设备。
- 设备回传新值------执行结果要等下一次上行才能看到。
所以 HA 上点了一个按钮、界面上没反应,不等于指令失败了。验收时要写成"下发后在 N 个上报周期内回读确认",而不是"点击后立刻看结果"。
给集成方的一条实用建议:如果这个项目里控制指令的时效性要求高(比如要在几秒内响应),Class A 终端本身就不是合适的选择,应该考虑 Class C。这个判断要在设备选型阶段做,不是对接阶段能补救的。
八、在线状态:一个必须单独开的开关
HA 和 Node-RED 都需要知道某个传感器还活着吗。
2.00.084 起,转发器在 ThinkLink 来源规则里可以订阅设备状态主题:
/v32/{tenant}/tkl/device/status/{eui}
或者在授权组织范围内用 /v32/{tenant}/tkl/device/status/+。
有两个坑必须提前说:
第一,状态消息的产生要求设备 notify_offline="TRUE",这个开关默认是关闭的。 不开就没有状态消息,转发器再怎么配也收不到。注意转发器本身不要求设备侧任何"第三方转发"开关,状态消息的产生只取决于 notify_offline。
第二,这条消息没有 telemetry_data。 上面那段示例脚本开头的 if (!msg?.telemetry_data) return null 会把它直接丢掉。要转发状态,要么走原消息直通,要么在脚本里按状态格式单独处理。
另外,平台上的"在线"有三个口径,彼此不能互换(第 22 期讲过):设备列表的活动时间、server_attrs.online、MQTT 通知里的布尔值。对接外部系统时用哪个,要在交付文档里写清楚,别让两边对同一个"在线"有不同理解。
九、2.00.070 之后:别只盯着 RUNNING
转发器的连接与订阅行为在 2.00.070 有一轮改动,直接影响排障判断。
登记成功不等于订阅成功。 规则启用时,即使来源 Broker 离线,也会先登记订阅需求,连接恢复后自动发起订阅。但只有收到 SUBACK 才确认订阅真的成了。连接期间失败的订阅每 3 秒自动重试。
目标不可用时不缓冲、不补发。 规则启动时会在原连接超时上限内等待自己启用的目标 Broker 就绪,超时后仍登记源订阅;目标不可用时沿用跳过转发策略,不新增消息缓冲或离线补发保证。这意味着转发器不是可靠消息队列------断线期间的数据不会事后补。
不提供 exactly-once。 MQTT 重投递或发送端重复发送都可能产生重复消息,需要业务去重就自己在消息里带标识。
一句话总结:不能只依据"规则已启用"或"Broker 已连接"判断转发成功,必须看真实消息流。
另外,同一组织的转发 Broker 并行加载,某个连接较慢不会让其他 Broker 排队等待。多个脚本共用一个主题时,移除其中一个不影响其他脚本;最后一个回调移除后才退订。
十、排障:按这个顺序查
官方文档给的五步,实测很管用:
- 确认来源和目标 Broker 都已启用,目标 Broker 状态为
RUNNING。 - 检查
ErrMsg、主机、端口、账号密码和 TLS 证书。 - 确认转发器本身已启用(不是只保存了)。
- 确认
Subscription Topic与实际消息 Topic 匹配------尤其检查{tenant}有没有替换、有没有多写或少写一层。 - 暂时关掉自定义脚本,确认原消息能否直通;能通再排查脚本返回值。
第 5 步是最容易被跳过、也最常查出问题的一步。新手最常见的脚本错误就是返回了 null(比如 msg.telemetry_data 不存在)却不自知,以为"数据没转发过来",实际上是自己把消息丢了。
补充两条实操经验:
- 每次保存转发器脚本都会产生版本快照,可以在编辑器里查看详情或还原。还原会生成一条新的版本记录,不删除原有历史------所以脚本改坏了不要慌,还原即可。
- 系统平台可以维护公共转发器 :公共规则在租户侧可见但只读;选择具体组织时,规则可以引用该组织的 Broker 和
org_params。多组织复用同一套转发逻辑时用这个。
十一、验收清单
| # | 项目 | 通过标准 |
|---|---|---|
| 1 | 路径选择 | 使用"高级功能 → Broker + 转发器",未依赖已停用的旧版第三方转发开关 |
| 2 | 来源 Broker | 按需求选定 ThinkLink(已解析)或 AS(原始),未混淆 |
| 3 | 目标 Broker | 类型为 Customize,保存后状态为 RUNNING,ErrMsg 为空 |
| 4 | 订阅 Topic | {tenant} 已替换为实际组织账号;层级与官方口径一致 |
| 5 | 规则启用 | 转发器"启用"已打开(不是只保存);已收到 SUBACK 确认 |
| 6 | 真实消息流 | 在目标 Broker 上实测订阅到消息,不只依据状态显示 |
| 7 | 脚本行为 | 直通先行验证;启用脚本后确认未误返回 null 丢弃消息 |
| 8 | 字段口径 | telemetry_data 下的字段名已用真实消息确认,未凭想象命名 |
| 9 | HA / Node-RED 侧 | 实体或流程读到值,且与平台侧显示一致 |
| 10 | 在线状态 | 设备 notify_offline="TRUE" 已开;状态主题已订阅且未被脚本丢弃 |
| 11 | 写控制 | 下发后按上报周期回读确认,未以"发送成功"作为验收依据 |
| 12 | 断线行为 | 已确认断线期间消息不补发;业务侧有去重或补偿设计 |
| 13 | 版本留档 | 转发器脚本版本、平台版本、目标 Broker 配置已记录 |
十二、结语
从 2.00.066 起,ThinkLink 把"对接某个具体平台"换成了"对接任意 MQTT 端点"。对 Home Assistant 和 Node-RED 来说,这不是坏消息------内置集成能给的只是一个固定格式,而转发器给的是一个可以自己写转换的地方。
代价是这段脚本要你自己写、自己调。但它的边界很清晰:Topic 怎么填有固定口径,脚本入口只有一个函数,返回值只有三个字段,排障顺序官方给了五步。真正容易出错的都是细节------{tenant} 没替换、脚本返回了 null、状态消息没有 telemetry_data、目标 Broker 断线期间的消息不会补发。
把这些细节写进验收清单,比反复试错省时间。
相关阅读
- 第 21 期:ThinkLink 物模型解码脚本实战------把 15 个字节变成一张能看的表
- 第 22 期:ThinkLink 触发模型与告警规则实战------不迟到、不刷屏、找对人
- 第 24 期:BACnet/IP 与 Modbus TCP 在 ThinkLink 上的对接实战------从字段映射到 YABE 调试
本文基于 ThinkLink 官方文档撰写,配置路径与参数以实际平台版本为准。官方手册:www.think-link.net/docs/zh/Adv...www.think-link.net/docs/zh/Qui...www.think-link.net/docs/zh/Pro...