小米米家 · 消息与规则引擎:事件驱动与App联动
技术栈:小米云事件路由 + 规则引擎 + 小爱同学(以小米米家 / MiOT 为案例) 适用场景:设备事件如何驱动 App 联动与场景编排
米家设备连上网只是开始。真正让消费级 IoT「有灵魂」的,是「开门了灯就亮、温度高了空调就降」这种联动。
本质是一个事件流:设备产生事件 → 小米云判断规则 → 推给 App 或其他设备。用户的诉求不是「看一个设备状态」,而是「让一堆设备按我的生活节奏协同」。工业里由 SCADA 脚本写死,消费级里必须由用户自助定义,甚至说一句「我睡了」就生效。
1.问题背景:米家事件驱动与联动的真实痛点
- 触发链长且跨端:一个「开门」事件,既要推给 App 通知主人,又要下发给灯去开,还要写日志、联动摄像头。单点硬编码很快变成意大利面
- 用户要的是「如果…就…」:米家 App 里的智能场景就是一套低代码 if-this-then-that 引擎,对普通用户要可读、可改、可撤销
- 离线消息不能丢:灯在门开的瞬间正好离线,开灯指令不能丢了。要能在设备上线后补发,且保证最终一致
- 十亿级的洪峰:每台设备都会上报事件,十亿台设备下规则匹配不能慢、推送不能丢、离线队列不能爆
2.设计理念:事件总线 + 离线补发 + 小爱入口
米家把消息层设计为四个组件:
| 组件 | 职责 |
|---|---|
| 事件总线 | 设备上报 → 统一 topic 路由 → 多消费者订阅 |
| 规则引擎 | 用户定义的「如果…就…」场景,命中后执行动作列表 |
| 离线队列 | 设备不在线时缓存下行指令,上线即补发 |
| 小爱映射 | 口语「打开客厅灯」→ 物模型写操作,与规则引擎共用动作出口 |
不同消息选不同可靠性:心跳用最多一次,门锁状态用至少一次,必要场景才用恰好一次。高优消息(告警、控制)优先落盘与重试,低优消息允许丢弃。
设备不在线时,小米云把下行指令缓存到设备影子(见第四篇),待设备上线立即补发。影子里的 desired(期望)与 reported(实际)对账,是弱网下状态不失控的关键。
这种分级的可靠性策略让米家避免了"为所有消息付出恰好一次的成本"。十亿台设备的心跳如果全部按至少一次处理,云端存储和重试开销会指数级膨胀——心跳丢了就丢了,下一轮还会来。
3.实际应用:从事件到联动的完整链路
3.1 事件上报与分发
米家设备经连接层(第二篇)上报属性变化事件(如门锁 opened = true)。小米云事件总线按 topic 分发,同时推给规则引擎、日志、监控等消费者。
3.2 规则引擎匹配
用户定义的「开门 → 开灯」命中后,规则引擎并发执行动作:给 App 推通知、向灯设备下发开灯指令、写一条日志。一条规则内部表达为 if 条件 then 动作列表,条件可叠加「时间窗」「人在家」等上下文。
3.3 小爱同学入口
用户说「打开客厅灯」,小爱解析意图后映射到具体设备的物模型写操作。超级小爱(大模型加持)还能听懂口语化表达,支持「语音创建自动化」——说一句即可生成一条规则。
3.4 推送与离线补发
关键事件(门锁异常)走高优先级系统推送,普通状态变化仅更新 App 内设备卡片,避免通知轰炸。设备休眠时下行指令进离线队列,设 TTL 防止堆积——门锁离线期间缓存了「反锁」指令,上线后补发一次即可。
一个典型场景:用户在公司远程关家里空调。空调此刻离线(家人拔了插头)。小米云把"关机"指令缓存到设备影子 desired 字段,设 30 分钟 TTL。30 分钟内空调上线,拉取 desired 后执行关机并上报 reported 对账;超时则丢弃并在 App 提示"设备离线过久,指令未送达"。
推送通道对 Android/iOS 分别走系统推送服务。关键事件(门锁异常)走高优先级通知确保用户及时感知,普通状态变化仅更新 App 内设备卡片,避免通知轰炸——十亿台设备如果每条心跳都推通知,用户手机早炸了。
4.注意事项
- 事件去重与幂等:弱网下同一事件可能重复上报,规则引擎要按事件 ID 去重,避免「开灯」被执行两次。
- 离线队列要设上限:队列满时必须选择丢弃最旧还是最新,并在 App 侧提示用户「设备离线过久,指令已过期」。
- 小爱与规则共用动作出口:避免「小爱开了灯,规则又关掉」,需要在动作层做冲突检测或优先级排序。
5.小结
米家消息层的核心是「事件总线 + 规则引擎 + 离线补发 + 小爱入口」四件套。事件总线负责一对多分发,规则引擎负责声明式联动,离线消息靠设备影子对账保证最终一致,小爱同学把口语变成物模型操作——是消费级「零门槛」的关键入口。
下一篇(四),我们钻进「物模型」——MIoT-Spec 如何用一套统一的词汇描述千奇百怪的设备。