当前位置: 首页 > news >正文

12 极物科技 JetLinks MQTT直连设备事件上报实战(继电器场景)

JetLinks MQTT直连设备事件上报实战(继电器场景)

1. 前言

JetLinks作为国产开源物联网平台,MQTT协议直连设备是最常用的接入方式之一。但很多开发者在实操中会遇到两个核心问题:一是分不清“事件上报”和“属性上报”的使用场景,二是JetLinks 2.10社区版存在“事件上报后无法直接在页面查看”的坑,导致测试时无从验证,本人在验证的时候折腾了半天,最后通过事件触发告警(需要编写规则引擎),告警记录可查看来验证事件是否上报成功,太难了,坑爹,也有可能是本人某些地方设置有问题,但是使用当前文档的方法测试是完全没问题的。

本文以继电器开关状态事件上报为例,从协议规范、物模型配置、代码实现到测试验证,完整拆解JetLinks MQTT直连设备的事件上报全流程,重点解决“社区版怎么验证事件上报成功”的核心痛点,内容可直接落地。

突然想到可能和存储有关系,开启TimescaleDB-单列模式,果然看到事件日志了,不过还是不影响本人的证明过程,问题奀,可防可控,看第7小结,如何设置即可愉快使用了

2. 事件上报规范(主题+报文)

2.1 上报主题

JetLinks对MQTT直连设备的事件上报主题做了固定约定,格式如下:

/{productId}/{deviceId}/event/{eventId}
  • 传输方向:上行(设备 → 平台)
  • 字段说明:
    • productId:平台创建产品时生成的产品唯一标识
    • deviceId:产品下创建设备时生成的设备唯一标识
    • eventId:物模型中定义的事件ID(需和配置完全一致)

2.2 报文格式

事件上报报文无需冗余字段,仅需传输核心业务数据即可,平台会自动补全时间戳、messageID等元数据:

{"data":{"switch2":"on"}}

2.3 核心疑问解答

(1)设备需要获取上报返回值吗?

不需要。MQTT协议层面可通过QoS(本文用QoS0,追求可靠性可改QoS1)保证消息传输,应用层无需平台返回“上报成功/失败”,设备只需按规范发送即可。

(2)data字段和物模型怎么对应?

data内的字段需和物模型中事件的输出参数完全匹配(字段名、数据类型一致),对应关系看下图一目了然:

图1:物模型事件输出参数与报文data字段对应关系

注意:data这几个字符串不要改哈,jetlink官方的文档有时候看起不不是特别直接。

3. 物模型事件创建

3.1 创建步骤

在JetLinks平台对应产品下创建事件,核心配置如下:

  1. 事件ID:switchchange(需和代码中EVENT_ID一致)
  2. 事件名称:继电器状态变更
  3. 输出参数:
    • 参数名:switch2
    • 数据类型:字符串
    • 可选值:on(开启)、off(关闭)

3.2 概念解析

(1)输出参数的含义?

事件的“输出参数”是事件触发时携带的业务数据,用于描述事件的具体状态。比如本场景中,通过switch2on/off区分继电器“开启”和“关闭”两种状态。

(2)明明属性也能传状态,为什么用事件?

很多新手会混淆“属性”和“事件”,其中最核心的差异是事件可触发规则引擎的复杂联动,其次是存储和语义的区别,完整对比如下:

维度属性上报事件上报
核心语义设备当前的静态状态设备发生的动态行为/动作
存储逻辑覆盖式存储(仅保留最新值)日志式存储(保留所有记录)
规则引擎支持仅能触发简单的“属性变更”规则可触发复杂联动(告警、设备控制、第三方接口调用等)
适用场景实时查看当前状态(如“现在开关是开的”)1. 追溯状态变更(如“什么时候从开变关”)
2. 触发业务联动(如开关关闭时推送告警、开启时控制其他设备)

继电器状态变更场景中,事件的核心价值并非仅记录历史,而是能基于“开关开启/关闭”这个行为触发规则引擎的复杂操作:比如开关关闭时推送短信告警、开关开启时联动其他设备启动,这些都是属性上报无法实现的核心能力。

(3)为什么不拆分成“开关开启/关闭”两个事件?
  • 冗余:两个事件本质都是“状态变更”,仅参数值不同,拆分无意义;
  • 维护成本高:代码和物模型需维护两个事件ID,增加复杂度;
  • 规则适配难:后续配置告警/联动时,需写两条规则,不如单事件+参数区分高效。

4. 完整代码实现(可直接运行,问题奀)

importjsonimportloggingimportrandomimporttimeimportuuidfrompaho.mqttimportclientasmqtt_clientfrompaho.mqtt.clientimportMQTTv311# ===================== 配置项 ======================MQTT_BROKER="192.168.111.53"MQTT_PORT=1883MQTT_USERNAME="admin"MQTT_PASSWORD="admin"CLIENT_ID="2019722818928308224"# 设备基础信息PRODUCT_ID="2019049642691198976"DEVICE_ID="2019722818928308224"# 事件配置(核心:事件标识 switchchange ,上报间隔5秒)EVENT_ID="switchchange"# 事件标识REPORT_INTERVAL=5# 上报间隔:5秒EVENT_REPORT_TOPIC=f"/{PRODUCT_ID}/{DEVICE_ID}/event/{EVENT_ID}"# 事件上报Topic# ========== 继电器设备状态 ==========DEVICE_STATE={"switch_status":False,# false=关闭,true=开启(核心布尔状态)}# 日志配置logging.basicConfig(level=logging.INFO,format="%(asctime)s - %(levelname)s - %(message)s")logger=logging.getLogger(__name__)defconnect_mqtt()->mqtt_client.Client:"""MQTT连接逻辑(保留核心,移除功能调用相关)"""defon_connect(client,userdata,flags,rc,properties=None):rc_msg={0:"连接成功",1:"协议版本错误",2:"客户端ID非法",3:"服务器不可用",4:"用户名/密码错误",5:"未授权",}ifrc==0:logger.info(f"✅ MQTT连接成功({MQTT_BROKER}:{MQTT_PORT})")else:logger.error(f"❌ 连接失败(rc={rc}):{rc_msg.get(rc,'未知错误')}")defon_disconnect(client,userdata,rc,properties=None):ifrc!=0:logger.warning(f"⚠️ MQTT被动断开,将自动重连(rc={rc})")# 创建客户端client=mqtt_client.Client(client_id=CLIENT_ID,callback_api_version=mqtt_client.CallbackAPIVersion.VERSION1,protocol=MQTTv311,)client.username_pw_set(MQTT_USERNAME,MQTT_PASSWORD)client.on_connect=on_connect client.on_disconnect=on_disconnect client.auto_reconnect=Trueclient.reconnect_delay_set(min_delay=2,max_delay=10)# 连接服务器try:client.connect(MQTT_BROKER,MQTT_PORT,keepalive=60)exceptExceptionase:logger.error(f"❌ TCP连接失败:{str(e)}")raisereturnclientdefgenerate_event_payload()->str:"""构造事件上报报文(严格匹配格式:timestamp+messageId+data(布尔类型))"""# ========== 关键修改:根据switch_status动态设置switch2的值 ==========switch2_value="on"ifDEVICE_STATE["switch_status"]else"off"payload=json.dumps({"data":{"switch":DEVICE_STATE["switch_status"],"switch2":switch2_value,# 动态赋值为on/off},},ensure_ascii=False,)returnpayloaddefupdate_switch_status():"""每5秒切换开关状态(true↔false)"""DEVICE_STATE["switch_status"]=notDEVICE_STATE["switch_status"]status_text="开启"ifDEVICE_STATE["switch_status"]else"关闭"logger.info(f"🔌 开关状态已更新:{status_text}{DEVICE_STATE['switch_status']})")defreport_event_periodically(client:mqtt_client.Client):"""周期性上报事件(每5秒一次)"""logger.info(f"📌 开始周期性上报事件(间隔{REPORT_INTERVAL}秒)")logger.info(f"📌 事件标识:{EVENT_ID}")logger.info(f"📌 事件上报Topic:{EVENT_REPORT_TOPIC}")logger.info(f"📌 初始开关状态:{'关闭'ifnotDEVICE_STATE['switch_status']else'开启'}")whileTrue:try:# 1. 切换开关状态update_switch_status()# 2. 生成事件上报报文payload=generate_event_payload()# 3. 发布事件消息publish_result=client.publish(EVENT_REPORT_TOPIC,payload,qos=0)ifpublish_result[0]==0:logger.info(f"✅ 事件上报成功:")logger.info(f" 📝 主题:{EVENT_REPORT_TOPIC}")logger.info(f" 📝 报文:{json.dumps(json.loads(payload),ensure_ascii=False,indent=2)}")else:logger.error(f"❌ 事件上报失败,状态码:{publish_result[0]}")# 4. 等待5秒后继续time.sleep(REPORT_INTERVAL)exceptKeyboardInterrupt:logger.info("\n🛑 停止事件上报服务")breakexceptExceptionase:logger.error(f"❌ 事件上报异常:{str(e)}",exc_info=True)time.sleep(REPORT_INTERVAL)# 异常后仍等待,避免频繁报错defrun():"""启动开关状态事件上报服务"""logger.info("🚀 启动JetLinks 开关状态MQTT事件上报服务")client=connect_mqtt()client.loop_start()# 启动非阻塞MQTT循环try:report_event_periodically(client)finally:client.loop_stop()client.disconnect()logger.info("🔌 MQTT连接已断开")if__name__=="__main__":try:run()exceptExceptionase:logger.error(f"❌ 程序异常退出:{str(e)}",exc_info=True)

4.3 代码说明

  • 配置项:需替换为自己平台的productIddeviceId、MQTT地址等信息;
  • 报文优化:移除了多余的switch字段,仅保留物模型定义的switch2,严格匹配平台规范;
  • 异常处理:包含连接失败、上报异常、用户终止等场景的友好提示,便于调试。

代码都在本人的gitee上,代码无保留,热心群众
https://gitee.com/fujianxinxi/tearcher.git

5. 测试验证(社区版“曲线救国”方案)

5.1 测试背景

JetLinks 2.10社区版存在限制:事件上报后,无法直接在“设备事件”页面看到记录,需通过规则引擎+告警记录验证,有点麻烦就是了,不过能用就行,先用再说

5.2 测试步骤

步骤1:配置规则引擎(事件触发告警)
  1. 进入平台「规则引擎」→ 新建规则;
  2. 触发条件:选择“设备事件”→ 关联对应产品+事件ID(switchchange);
  3. 执行动作:选择“发送告警”→ 配置告警标题(如“继电器状态变更”);
  4. 启用规则。

正常运行日志如下:

图2:Python代码运行日志—— 显示周期性上报on/off报文

步骤3:查看告警记录验证

进入平台「告警中心」→「告警记录」,可看到继电器状态变更的告警记录:

图3:Web端告警记录—— 事件触发的告警信息

5.3 验证结论

告警记录的触发时间、switch2参数值和代码上报的内容完全一致,证明事件已成功上报到平台。务必注意:不是代码发送有问题,是社区版本的功能限制导致无法直接看事件记录!

6. 踩坑记录(社区版必看)

坑点1:社区版事件页面无记录(最坑)

图4:社区版事件页面无记录—— 2.10版本社区版事件页面为空

  • 现象:事件上报后,「运行状态 tab」页面始终为空,上图所示;
  • 原因:JetLinks 2.10社区版未开放事件日志的直接展示功能;
  • 解决方案:通过规则引擎关联告警,用告警记录验证事件上报结果(曲线救国,没招只能这样)。

坑点2:规则引擎配置(事件关联告警)

图5:规则引擎配置—— 事件关联告警的规则配置

  • 关键:触发条件必须精准选择“对应产品+事件ID”,否则告警无法触发;
  • 提示:规则配置完成后需“启用”,否则不生效。

坑点3:最终验证(告警记录)

图6:告警记录页面—— 最终验证事件上报的告警记录

  • 核心:告警记录的“触发时间”“参数值”需和代码上报的一致,才算验证通过;
  • 吐槽:这种验证方式确实反人类,但社区版只能这么玩

7. 尴尬

突然想到可能和存储有关系,开启TimescaleDB-单列模式,果然看到事件日志了.

8. 结论

  1. JetLinks事件上报核心:主题固定、报文极简(仅保留物模型定义的data字段),无需额外元数据;
  2. 事件vs属性:状态变更用事件(追溯历史),实时状态用属性(查看当前),关联场景编辑复杂规则引擎;
  3. 社区版验证技巧:事件无直接展示时,通过“规则引擎+告警记录”曲线验证,这是2.10版本的可行方案;
  4. 避坑关键:代码配置项需和平台物模型/设备信息严格一致,报文字段不能多也不能少。

本文完整覆盖了JetLinks MQTT设备事件上报的全流程,解决了新手最易踩的“配置乱、验证难”问题。如果有其他JetLinks使用问题,欢迎在评论区交流~

http://www.jsqmd.com/news/855648/

相关文章:

  • 怎么在 Redis 中设置消息队列的过期时间自动清理?
  • 如何在5分钟内解锁所有Steam成就:Steam Achievement Manager完整使用指南
  • 基于ssm框架的警务信息管理系统(10072)
  • 2026年4月建筑资质代办机构推荐,许可资质代办/建筑资质代办/建筑资质办理/工商代办,建筑资质代办企业找哪家 - 品牌推荐师
  • 【权威实测】Perplexity vs PubMed vs Scite:在结构生物学领域,它为何将文献召回率提升68%?
  • 2026浙江多元升学机构推荐指南:小凡私塾实力上榜,艺术生升学路径全解析 - 栗子测评
  • 108、滑模控制:原理与设计
  • 基于Sakura实验板的STM32流水灯项目实战:从GPIO控制到模式切换
  • 软件工程师在智能体视觉时代的机遇(18)
  • 单片机编程规范1 ---阮丁远 20260509
  • jQuery虚拟键盘Keyboard无障碍访问(ARIA)实现:打造包容性Web应用
  • 2026浙江全日制文补学校推荐:浙江全日制文补机构推荐,闭眼选不踩坑 - 栗子测评
  • 109、滑模控制:抖振抑制方法
  • TMC8461/8462 EtherCAT从站控制器:集成实时控制与工业I/O的高性能方案
  • 别再死记公式了!用Python+SymPy自动推导星三角变换,附完整代码
  • 3步打造高效macOS菜单栏:Hidden Bar深度使用指南
  • Cakewalk编曲效率翻倍秘籍:巧用VMPK自定义键盘映射,打造你的专属快捷键
  • 【AI赋能测试笔记】5基于文档用例生成系统及skills
  • SINet-V2:高效隐蔽目标检测实战指南与深度解析
  • 从零开始学AI17——SVM的数学支撑知识
  • 2026金枪鱼罐头供应商指南汇总名录 - 栗子测评
  • Delphi二进制迷宫破解:IDR交互式重构器的逆向工程革命
  • php方案 原生协程支持(Fiber最佳实践完整的
  • RHEL9.6虚拟机安装配置攻略————安装虚拟机
  • LLM结构化输出工程:让模型输出你真正需要的格式
  • 【Perplexity天文知识搜索终极指南】:20年天体物理工程师亲授5大避坑法则与实时星图调用技巧
  • MTP:大模型推理提速黑科技(Multi-Token Prediction)
  • 告别HDR格式混乱:用Python代码实战HLG与PQ曲线互转(附完整代码)
  • Windows Phone Internals深度解析:3层技术架构解锁Lumia设备的完整方案
  • 软件工程师在智能体视觉时代的机遇(19)