『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 117236|回复: 254

[修仙教程] 【ESPHome】ESP32 DIY通用蓝牙网关,接入米家系门锁等各种设备

  [复制链接]

105

主题

2954

帖子

1万

积分

超级版主

智能家居&单板滑雪痴迷爱好者

Rank: 8Rank: 8

积分
12103
金钱
9084
HASS币
460

教程狂人突出贡献

发表于 2021-5-2 19:34:43 | 显示全部楼层 |阅读模式
本帖最后由 XCray 于 2023-8-10 17:43 编辑

注:本贴最早是讨论解密米家系蓝牙门锁广播消息并将之接入HA的,这也是这个蓝牙网关最关键、最有价值的部分。在此基础上,yaml里直接添加其它蓝牙设备(如温湿度计)即可。参考:【足够强大】ESPHome+ESP32打造通用蓝牙网关,比小米自家的好

重大进展:已实现全部有用数据的接入,可满足日常使用。见此文最后部分。注意:我这儿上传的代码是用于榉树门锁的,141楼 ghostist 朋友上传了可用于米家门锁的代码。
~~~~~~~~~~~~~~~~
0. 背景

之前一直用蓝牙网关TTL接口转mqtt的方法将榉树蓝牙智能门锁(兼容米家app)接入HA。
这个方法有一个致命的缺陷:蓝牙网关只有在完成与服务器的通讯后才会从TTL接口输出我们关注的事件消息,这样一来有时候时延就会很大,并且在外网不通时无法使用。

那么,能否在空中监听门锁发给蓝牙网关的消息、避免延迟和对外网的依赖呢?

显然,理论上上是可行的,难度也是可以想象的——加密。不过经过不断摸索,已经基本实现。
可惜理解这一思路的人太少了。

1. 要求
有ESP32,会用ESPHome,了解自己所用的门锁。不需要硬件连接(WiFi就行,以太网更好)。

2. 基本原理

- 榉树门锁会不断地发送两种广播报文:

a:“Service Data”(0x16)含有Mi Service(UUID:0xFE95),MAC地址就在这个报文里。这种报文未加密、长度固定12字节,内容中只有一个字节的序号约6分钟变一次,其余固定不变  这条报文根据官方文档里MiBeacon的说明可以完美解读,不过这个消息与本文应用没啥关系;

b:“Manufacturer Specific Data”(0xFF)含有小米公司识别码(ID:0x038F),这类消息是加密的,又分为两种(已可以解密,见下文):
   b.1:待机状态下,长度固定为16,这是锁属性/状态报告,大概十来秒发一次;
- 在开锁或发生其他事件时,门锁会发出长度不同的“Manufacturer Specific Data”广播报文:
   b.2:比如开锁时,长度为25,对应小米文档里的“锁事件”。显然,这个是很重要的;

(米家门锁只有Service Data一种报文,当然,也是加密的)
这类消息包含Object,即各种事件之类的有用信息,内容是加密的,需要获取密钥才可以解密。

具体型号会存在差异,以小米文档和实测为准。

我们就用一个 esp32 接收并解密这些消息,然后把解密结果以传感器状态的方式推送到HA。

3. 解密算法,与蓝牙温湿度计2是一样的,这里就不展开了,感兴趣的见后面的回复

4. 重大关节——取得门锁的加密密钥

据ruan_yhang朋友实践发现,创米小白万能遥控器作为蓝牙网关,会在连上Wi-Fi时以及周期性地在TTL口打印这个非常重要的beaconkey、并且验证成功!

可惜又可气的是,我用的榉树网关死活不会在TTL口输出这个重要数据。

前几天买了个多模网关,发现和我用的榉树门锁根本不兼容(门锁的协议版本太低且厂家不再提供升级,小米已经放弃了),不过一个小惊喜是在多模网关上找到了我的门锁的beaconkey,经验证确实可以正确解密(在/var/log/messages里,看不到的话重置多模网关或重新绑定门锁)。

5. 20210604进展:调测蓝牙ble扫描参数,发现window和interval设置成一样,嗅探效果会很好,不会错过门锁的广播消息,但wifi连接耗时特别长。  而默认的间隔是320ms、窗口只有30ms!经常出现收不到蓝牙广播的现象!改成180ms,wifi连接速度不如以前,但可以接受。
  没有找到小米关于蓝牙广播方面的定时规范(也就是发送间隔),不然的话可以设置的更加合理。

6. 0606接近完美
想了个偷懒的方法:把esphome的xiaomi_lywsd03mmc和xiaomi_ble的代码改了改,偷梁换柱,让ESP32直接把所有有用的数值以传感器的形式直接发布到HA,共计8个传感器,分别是操作方式(指纹密码等)、KeyID、开锁时间、锁状态报告、门事件报告及其时间戳、电量报告及时间戳,我用的锁也就这些有用数据。数值到了HA,剩下的格式转换还是由HA自己完成吧。

修改后的代码如下,注意这个代码只针对榉树门锁,其他型号需要根据自己的门锁的pdid、eid修改才行。
把文件解压到esphome的工作目录(config)里的mine子目录下,然后yaml就按照如下填写即可(当然,mine子目录可以是其他名称,和yaml里一致即可):
esp32_ble_tracker:
  scan_parameters:
   interval: 180ms
   window: 120ms
external_components:
  - source: mine
sensor:
  - platform: xiaomi_zelkova
    mac_address: $mac_of_lock
    bindkey: $beaconkey
    lockattr:
      name: "LockAttr"
      id: LockAttr
    opmethod:
      name: "OpMethod"
    doorevt:
      name: "DoorEvt"
    battlvl:
      name: "BattLvl"
    keyid:
      name: "KeyID"
    opts:
      name: "OpTS"
    doorevtts:
      name: "DoorEvtTS"
    battlvlts:
      name: "BattLvlTS"

米家门锁可以在 esp32_ble_tracker: 下面的 scan_parameters: 下面加上 active: false,更省电
mible.0.3.zip (7.68 KB, 下载次数: 239) 文后有新版的

这是实际效果,上面是原来的 mqttl 实现的,下面是这次用 esp32 直接解密蓝牙实现的,门事件和电量报告还没出现:
succ.png

这是 ESPHome的日志:
ESPHome生成8个传感器:
[11:16:34][C][xiaomi_zelkova:014]:   OpMethod 'OpMethod'
[11:16:34][C][xiaomi_zelkova:014]:     Unit of Measurement: ''
[11:16:34][C][xiaomi_zelkova:014]:     Accuracy Decimals: 0
[11:16:34][C][xiaomi_zelkova:015]:   OpKeyID 'KeyID' ...
[11:16:34][C][xiaomi_zelkova:016]:   OpTS 'OpTS' ...
[11:16:35][C][xiaomi_zelkova:019]:   BattLvlTS 'BattLvlTS'...
[11:16:35][C][xiaomi_zelkova:020]:   DoorEvt 'DoorEvt' ...
[11:16:35][C][xiaomi_zelkova:021]:   DoorEvtTS 'DoorEvtTS' ...
[11:16:34][C][xiaomi_zelkova:017]:   LockAttr 'LockAttr' ...
[11:16:35][C][xiaomi_zelkova:018]:   BattLvl 'BattLvl'
[11:16:35][C][xiaomi_zelkova:018]:     Device Class: 'battery'
[11:16:35][C][xiaomi_zelkova:018]:     Unit of Measurement: '%'
一次锁状态报告:
[11:16:51][D][mnf_adv:036]: (length 16)48409701FCAE38267D2FB1F62220C251
[11:16:51][D][xiaomi_ble:213]: decrypt_xiaomi_payload(): authenticated decryption passed.
[11:16:51][D][xiaomi_ble:215]:   Plaintext : 0E.10.01.02 (4), Packet : 254
[11:16:51][D][xiaomi_ble:078]: value_length:1;payload_length:4
[11:16:51][D][xiaomi_ble:227]: Got Xiaomi ZELKOVA (xx:xx:xx:xx:xx:xx):
[11:16:51][D][xiaomi_ble:233]:   LockAttr:2
[11:16:51][D][sensor:099]: 'LockAttr': Sending state 2.00000  with 0 decimals of accuracy
一次开锁记录:
[11:18:33][D][mnf_adv:036]: (length 25)484097010AF3DAC7237757C2DF38B5EF12DEFE15616D019FDC
[11:18:33][D][xiaomi_ble:213]: decrypt_xiaomi_payload(): authenticated decryption passed.
[11:18:33][D][xiaomi_ble:215]:   Plaintext : 05.00.0A.00.02.01.00.01.00.85.3E.BC.60 (13), Packet : 11
[11:18:33][D][xiaomi_ble:078]: value_length:10;payload_length:13
[11:18:33][D][xiaomi_ble:227]: Got Xiaomi ZELKOVA (xx:xx:xx:xx:xx:xx):
[11:18:33][D][xiaomi_ble:230]:   OpMethod:2
[11:18:33][D][xiaomi_ble:242]:   OpTS:1622949509
[11:18:33][D][xiaomi_ble:245]:   KeyID:65537
[11:18:33][D][sensor:099]: 'OpMethod': Sending state 2.00000  with 0 decimals of accuracy
[11:18:33][D][sensor:099]: 'KeyID': Sending state 65537.00000  with 0 decimals of accuracy
[11:18:33][D][sensor:099]: 'OpTS': Sending state 1622949504.00000  with 0 decimals of accuracy

这样,代码基本成型。

另外,现在的“密钥”是以配置方式填进去的,如果密钥会周期性改变,需要手动填入、重新编译。

因为我用的榉树蓝牙网关不会在 TTL 口打印这个密钥,自动获取密钥的部分代码我就不弄了。
需要的话可以自行加入这个功能、一个ESP32模块实现自动获取密钥和蓝牙嗅探解密的所有事情。最简单的方法就是这个帖子的代码和基于 esphome 的 mqttl 代码拼在一起,都是 ESPHome 了,组合在一起也就很容易了,简单地改改yaml就可以。

需要注意的是:如果一直是某个固定的 KeyID 开锁,传感器状态是不会变化的,定制自动化时需结合时间戳,这是HA的事情,不展开了。

有心折腾的朋友,建议先把本贴及所有回复仔细阅读几遍,很多问题实在不想重复解答了。
~~~~~20220331更新~~~~~~

随着ESPHome版本升级,底层框架做了替换(Arduino -> ESPressIF)等原因,这个自制组件代码也需要随之更新,否则无法成功编译。
mible_0.4.zip (10.56 KB, 下载次数: 279) 适配2022.3.2版本,其它版本未测试
~~~~20230810更新~~~~~
其实没更新啥,简单总结一下最新状态:
- 迄今为止各版本都可以编译使用,但2022.12.0开始esphome基础代码中的蓝牙协议栈部分存在严重bug,会导致蓝牙吊死、传感器停止更新(具体我也搞不懂,观察出来的)。所以我就停留在了2022.11.5,一直没啥问题。
- 多位朋友已经简单修改代码后接入了自己的米家智能锁,非常有参考价值。


评分

参与人数 8金钱 +70 HASS币 +20 收起 理由
隔壁的王叔叔 + 5 这个是真牛逼
laken_abc + 2 大神666!
wlcdbb + 1
jyz_0501 + 20 墙都不扶,就服楼主!
+ 20 + 20 厉害了word楼主!
Jerrylee + 10 厉害了word楼主!
blackcui + 10 大神666!
z741554038 + 2 墙都不扶,就服楼主!

查看全部评分

回复

使用道具 举报

40

主题

2176

帖子

8294

积分

元老级技术达人

积分
8294
金钱
6103
HASS币
110
发表于 2021-8-3 15:21:02 | 显示全部楼层
交作业了:
xiaomi_lock_esphome.zip (11.67 KB, 下载次数: 257)
适用于米家指纹锁
esphome的配置参考1楼,压缩包里两个文件夹替换1楼原来的,编译下载
yaml文件格式化了有用的5个sensor:

  sensor.door_action        上提把手锁门        friendly_name: 锁事件
  sensor.door_event_time        2021-08-03 08:08:32        friendly_name: 门事件更新时间
  sensor.door_method        人工        friendly_name: 开锁方式
  sensor.door_state        门关着        friendly_name: 门状态
  sensor.door_state_time        2021-08-03 08:08:32        friendly_name: 门状态更新时间
  sensor.door_user        默认(管理员)        friendly_name: 操作人

不兼容原本楼主榉树门锁,有兴趣的自己合并
用着有问题的自行排查...前面帖子看3遍都有结论的

评分

参与人数 1金钱 +20 收起 理由
XCray + 20 可喜可贺!

查看全部评分

回复

使用道具 举报

5

主题

109

帖子

2021

积分

金牌会员

Rank: 6Rank: 6

积分
2021
金钱
1907
HASS币
20
发表于 2022-11-2 12:31:43 | 显示全部楼层
我做的是这款门锁小米智能门锁
xiaomi_blez.cpp
140行增加了
} else if ((raw[2] == 0x99) && (raw[3] == 0x05)) {  // 米家门锁,加密
    result.type = XiaomiParseResult::TYPE_ZELKOVA;
    result.name = "MijiaLock";

70行修改了
  //const uint8_t *payload = message.data()+5;// + result.raw_offset;
  const uint8_t *payload = message.data()+5+6;
  //uint8_t payload_length = message.size()-12;// - result.raw_offset;
  uint8_t payload_length = message.size()-11;


评分

参与人数 1金钱 +20 收起 理由
XCray + 20 赠人玫瑰,手留余香!

查看全部评分

回复

使用道具 举报

40

主题

3056

帖子

1万

积分

超级版主

Nero

Rank: 8Rank: 8

积分
11149
金钱
8042
HASS币
182
发表于 2021-5-2 21:39:53 | 显示全部楼层
据大佬说蓝牙是自带 AES-128 硬件加解密,key是临时的,根据米家服务器下发的 token 来生成,也就是说,抓到 key 也没用。因为 key 会根据时间推移而失效。如果没有和服务器连接就会无法控制(因为 key 过期了)。

这也是米家蓝牙网关以前为何无法离线自动化的原因(最近支持了,估计是给 key 的更长的有效时间,如果长期断网应该依然无法控制)

所以,抓 key 方法除非能找到每次更新 key 之后立刻抓到新 key,然后把加密内容解密。要么能有算力能短时间暴力算 AES-128 emmm 这除了 google的量子计算机估计还没有啥办法吧。

以上都是据说,听说,猜测,未经证实。
Nero
回复

使用道具 举报

105

主题

2954

帖子

1万

积分

超级版主

智能家居&单板滑雪痴迷爱好者

Rank: 8Rank: 8

积分
12103
金钱
9084
HASS币
460

教程狂人突出贡献

 楼主| 发表于 2021-5-2 21:57:34 | 显示全部楼层
neroxps 发表于 2021-5-2 21:39
据大佬说蓝牙是自带 AES-128 硬件加解密,key是临时的,根据米家服务器下发的 token 来生成,也就是说,抓 ...

哎~~~

小米现在好像不在乎用户体验了,或者说只关注忽悠小白用户了。

不知道为啥,小米正式宣布不再采用zigbee,而是全面转向蓝牙。可是蓝牙又非得到服务器上转一圈才行,这整个和智能家居的核心诉求背道而驰啊!

暴力破解肯定不现实。找加密算法和key倒是一个方向,就是难度有点太大了。

至于你说的离线自动化,我还没观察到,无法支持本地自动化,是我最痛恨小米蓝牙设备的点。
回复

使用道具 举报

40

主题

3056

帖子

1万

积分

超级版主

Nero

Rank: 8Rank: 8

积分
11149
金钱
8042
HASS币
182
发表于 2021-5-3 15:18:37 | 显示全部楼层
XCray 发表于 2021-5-2 21:57
哎~~~

小米现在好像不在乎用户体验了,或者说只关注忽悠小白用户了。

群友说支持的,我家里也只有米家的 zigbee 设备,其他传感器用 mysensor 挺香的。
Nero
回复

使用道具 举报

32

主题

986

帖子

4233

积分

论坛元老

Rank: 8Rank: 8

积分
4233
金钱
3207
HASS币
110

教程狂人论坛风云人物

发表于 2021-5-4 02:29:58 | 显示全部楼层
本帖最后由 27hh 于 2021-5-4 02:31 编辑
XCray 发表于 2021-5-2 21:57
哎~~~

小米现在好像不在乎用户体验了,或者说只关注忽悠小白用户了。

小米何时正式宣布了不再采用zigbee?

我看最近米家里还新增了一个设备“Aqara网关E1(青春版)”,就在添加设备页面的“新品”栏目中,不知道这是个什么信号?
回复

使用道具 举报

105

主题

2954

帖子

1万

积分

超级版主

智能家居&单板滑雪痴迷爱好者

Rank: 8Rank: 8

积分
12103
金钱
9084
HASS币
460

教程狂人突出贡献

 楼主| 发表于 2021-5-4 06:36:44 | 显示全部楼层
27hh 发表于 2021-5-4 02:29
小米何时正式宣布了不再采用zigbee?

我看最近米家里还新增了一个设备“Aqara网关E1(青春版)”,就在添 ...

哈哈,这是雷军的工作失误。这么重大的事情竟然没专门给你汇报。。。

开发文档里说的,小米IoT开发者平台
iot.png
我开始时只是好奇为啥小米老是强推蓝牙的东西,zigbee不是挺好吗。后来看到这个说法,只能猜测蓝牙对小米来说更加有利可图。
Aqara毕竟是另一个公司的产品,米家虽然起步阶段和aqara高度捆绑,但现在兼容的东西多了去了。

他们之间怎么扯是一回事,米家现在主推的蓝牙方案无法实现本地自动化、高延迟确实太恶心人了,以后也再不购买或者推荐别人购买米家的蓝牙协议的智能家居产品。
回复

使用道具 举报

24

主题

884

帖子

5055

积分

论坛元老

Rank: 8Rank: 8

积分
5055
金钱
4166
HASS币
20
发表于 2021-5-4 11:30:51 来自手机 | 显示全部楼层
本帖最后由 chenquanhao 于 2021-5-4 11:38 编辑
XCray 发表于 2021-5-2 21:57
哎~~~

小米现在好像不在乎用户体验了,或者说只关注忽悠小白用户了。

我觉得zigbee对于互联网公司来说,都能在本地完成,也容易第三方接入,这不利于用户数据采集和培养用户粘性,另外会不会是蓝牙模块更便宜。小米现在更倾向做智能设备而不是智能家居,分别在于智能设备有蓝牙就能连接,现在手机基本都有蓝牙,减低网关的存在感,虽然目前来说我认为zigbee更好,但无法忽视资本和企业推动的力量,可能以后很多产品都往蓝牙方向走……
回复

使用道具 举报

4

主题

605

帖子

2170

积分

金牌会员

Rank: 6Rank: 6

积分
2170
金钱
1560
HASS币
20
发表于 2021-5-4 12:47:40 | 显示全部楼层
本帖最后由 zsqduke 于 2021-5-4 13:14 编辑

用多模网关接入HA不就行了?
回复

使用道具 举报

4

主题

605

帖子

2170

积分

金牌会员

Rank: 6Rank: 6

积分
2170
金钱
1560
HASS币
20
发表于 2021-5-4 13:13:37 | 显示全部楼层
XCray 发表于 2021-5-2 21:57
哎~~~

小米现在好像不在乎用户体验了,或者说只关注忽悠小白用户了。

多模可以本地
回复

使用道具 举报

105

主题

2954

帖子

1万

积分

超级版主

智能家居&单板滑雪痴迷爱好者

Rank: 8Rank: 8

积分
12103
金钱
9084
HASS币
460

教程狂人突出贡献

 楼主| 发表于 2021-5-4 15:25:44 | 显示全部楼层
chenquanhao 发表于 2021-5-4 11:30
我觉得zigbee对于互联网公司来说,都能在本地完成,也容易第三方接入,这不利于用户数据采集和培养用户粘 ...

有些道理
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-11-28 14:47 , Processed in 0.179929 second(s), 40 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表