先说结论
Odoo IoT 真正难的,不是“能不能发现一个设备”,而是:
- 系统如何知道自己在和哪一个 IoT Box 说话
- 这台 Box 上具体要操作哪一个设备
- 指令该走 longpolling、websocket 还是远程连接
- 远程能力开启后,凭什么保证不是谁都能接进来
从 /home/ubuntu/odoo-temp/addons/iot_base/static/src/network_utils/longpolling.js、device_controller.js、/home/ubuntu/odoo-temp/addons/iot_drivers/websocket_client.py 与 tools/helpers.py 来看,Odoo 实际上把 IoT 通信拆成了两层标识:
- box 级别的 identifier
- 设备级别的
device_identifier
所以它真正解决的问题不是:
浏览器里点一下按钮,设备动起来。
而是:
一个动作如何跨过浏览器、Odoo、IoT Box 与具体驱动,准确落到目标设备。
第一层:为什么 identifier 和 device_identifier 必须分开
在前端 DeviceController 里,实例拿到的典型信息有:
iot_ipidentifieriot_id
这里的 identifier 其实是设备标识,而 Box 本身还有另一层来自 helpers.get_identifier() 的机器标识。
这两层不能混。
因为一台 IoT Box 往往挂着多个外设:
- 打印机
- 扫码枪
- 秤
- 显示器
- 工业按钮或传感器
如果只有 box 级 id,你只能知道“发给这台盒子”,却无法知道“盒子上哪个设备该执行动作”。
所以 Odoo 的标识体系天然是分层的:
- Box identifier:回答“是哪一台盒子”
- device_identifier:回答“盒子上的哪一个设备”
第二层:为什么前端动作路由默认走 /iot_drivers/action
longpolling.js 里把动作端点单独定义成:
actionRoute = '/iot_drivers/action'pollRoute = '/iot_drivers/event'
前端调用 action() 时,会明确把:
device_identifierdata
发到 IoT Box。
这说明 Odoo 没把 IoT 交互设计成“前端直接懂设备协议”,而是统一收口到 Box 的驱动层。
也就是说,浏览器只负责表达:
- 我要哪个设备
- 我想做什么动作
至于底层怎么把动作翻译成打印、称重、开抽屉或读信号,是驱动的事。
这种分层特别重要,因为它让业务前端不用感知各种硬件差异,扩展新驱动时也不必重写整套业务界面。
第三层:为什么监听机制是“按设备订阅”,不是“按盒子一锅端”
addListener() 时,longpolling 服务会把监听挂到:
- 某个
iot_ip - 该 ip 下的多个
device_identifier - 每个 device 对应自己的 callback
收到轮询结果后,又会按 result.device_identifier 找对应监听器。
这说明 Odoo 监听事件时,粒度并不是“这台盒子有消息”,而是“这台盒子上的哪个设备有消息”。
这对现实硬件场景非常重要。因为一台 Box 上可能同时接着:
- 一台打印机正在工作
- 一台秤在持续回传数值
- 一个按钮在上报触发事件
如果不按设备分发,前端就得自己从混杂事件流里做二次拆包,复杂度会非常高。
第四层:为什么 websocket 也要再次校验盒子身份
在 websocket_client.py 里,收到消息后第一件事之一,就是检查:
- 当前 Box 的
helpers.get_identifier() - 是否在 payload 的
iot_identifiers列表里
也就是说,即使消息已经通过 websocket 通道进来了,Box 仍然不会无条件处理,而是先确认:这条消息是不是发给我的。
这是一道很关键的护栏。
因为远程通道一旦建立,真正危险的不是“连不上”,而是“连上了以后消息边界不清”。
Odoo 在这里的思路很明确:
- 通道是共享机制
- 处理动作必须按 identifier 精准命中
这样才能避免多盒子环境里发生串话。
第五层:为什么远程连接要单独用 token 开关
helpers.toggle_remote_connection(token="") 的语义很清楚:
- 有 token,就配置 ngrok authtoken 并重启相关服务
- 没 token,就停掉远程连接服务
同时还有 genproxytoken.py 负责生成 proxy access token,并把哈希值写入配置。
这说明 Odoo 并没有把“远程可访问”当成默认常驻能力,而是把它设计成:
- 明确开启
- 明确持有令牌
- 明确可关闭
这很合理,因为 IoT Box 往往接的是现实世界设备。打印机、门店外设、工厂终端一旦暴露错误,风险远高于普通网页功能。
所以远程能力必须可审计、可开关、可换 token,而不是配好一次后长期裸奔。
第六层:为什么 devtools 还要能禁掉 longpolling 或指定动作
helpers.py 里还有一层经常被忽略的保护:
- 可以关闭 longpolling
- 可以禁用某些 action,甚至全禁
这说明官方在 IoT 设计上并不盲目迷信“只要能发命令就好”,而是承认硬件联调阶段需要更细粒度的保险丝。
这类机制对实施尤其有帮助,因为现场问题常常不是纯软件 bug,而是:
- 某类动作在当前环境下不该触发
- 某个驱动正在排障
- 某个通道需要临时熔断
能在驱动层和工具层设置护栏,比让业务端自己绕开更可靠。
最容易误解的三个点
误区一:IoT 只要知道盒子 IP 就够了
不够。还必须知道具体 device_identifier。
误区二:前端直接调用设备就行
不是。前端只表达动作意图,真正执行要收口到驱动路由。
误区三:远程连接打开后就是一直可用的基础能力
也不是。它依赖 token、服务切换和身份校验,应该被谨慎控制。
实战上怎么用最稳
如果你在做 IoT 实施,我建议:
- 把 Box 标识和设备标识分开管理,排障时不要混说“identifier”
- 任何业务动作先确认最终命中了哪个
device_identifier - 多设备场景重点验证事件回传是否按设备正确分发
- 远程连接只在需要时开启,并定期轮换 token
- 联调期间善用禁用动作/禁用 longpolling 的护栏,而不是让测试人员硬碰真设备
最后总结
Odoo IoT 真正成熟的地方,不是它能接几种外设,而是它把 盒子身份、设备身份、动作路由、事件分发与远程令牌控制 串成了一条可控通信链。
这让 IoT 不只是“设备能连上”,而是“设备能被准确、安全、可维护地驱动”。
DISCUSSION
评论区