myselfshravan/miband-lit

GitHub: myselfshravan/miband-lit

一个通过 BLE 在 Mac 上直接读取小米手环 5 实时心率、推送通知并联动智能灯泡的 Python 工具。

Stars: 1 | Forks: 0

# miband-lit ⌚️❤️ 通过 Bluetooth LE 在 **Mac 上实时读取你的 Mi Band 5 心率** —— 包含一个 Streamlit 仪表盘,支持向手环推送文本通知和震动,以及(纯属为了好玩)一个由心率驱动的智能灯泡。一旦设置成功,就不再需要 Zepp 应用了。 **技术栈:** Python · [`bleak`](https://github.com/hbldh/bleak) (BLE) · Streamlit · SQLite · macOS。已在 Mi Band 5 上测试;该协议与 Mi Band 4 通用。 ## 安装说明 ``` git clone https://github.com/myselfshravan/miband-lit.git cd miband-lit python3 -m venv venv ./venv/bin/python -m pip install -r requirements.txt # 获取 auth-key 提取器(只需使用一次,见下文) git clone https://github.com/argrento/huami-token.git ./venv/bin/python -m pip install loguru # 你的密钥放在这里(已被 gitignored) cp config.example.sh config.sh # then edit with your UUID + auth key ``` 所有操作都在本地的 `venv/` 中运行。请使用 `./venv/bin/python ...` 来运行脚本。 ## 一次性操作:获取你的手环 auth key 在使用 Zepp 首次配对手环时会生成一个 16 字节的密钥,在你使用该密钥进行身份验证之前,手环不会传输传感器数据。`huami-token` 会从你的 Zepp 账户中提取该密钥。 ``` # 你登录你自己的 Zepp 账户;它会与 Zepp 的服务器通信,而不是与我。 ./venv/bin/python huami-token/main.py --method amazfit -e you@example.com -b ``` 它会提示你输入 Zepp 密码,然后打印出每个已配对的设备: ``` Device 0: MAC: C8:0F:xx:xx:xx:xx, Active: Yes Key: 0xagh4d8e1... <-- this 32-hex-char value is your auth key ``` 复制该密钥(`0x` 之后的部分)。除非你重新配对手环,否则它不会改变,因此你只需执行一次此操作。 ## 每次想读取心率时 —— 最简单的方法 1. **让手环脱离手机的束缚** —— 它一次只能与一个设备通信。 关闭手机上的蓝牙(开启飞行模式最稳妥)。仅仅关闭 Zepp 应用通常是不够的。 2. **运行启动器**(它会重置 Mac 的蓝牙以释放手环,然后连接并传输数据): ./run.sh # 持续传输数据直到按下 Ctrl-C DURATION=60 ./run.sh # 约 60 秒后停止 将手环紧贴手腕佩戴。你会看到 `Auth: SUCCESS`,绿色传感器 LED 亮起,然后会出现 `HR: NN bpm` 的字样(大约每隔几秒出现一次)。 你的手环 UUID 和 auth key 已经内置在 `run.sh` 中了。 ## 仪表盘(实时图表 + 发送通知) Streamlit 仪表盘会显示实时心率图表,并允许你向手环推送文本通知和震动。 ``` ./start_dashboard.sh ``` 这会重启蓝牙并启动所有服务:**手环服务** (BLE)、 **WiZ 灯泡同步** 和 **Streamlit** 都在后台运行,而实时的 **黑客风格控制台反馈** (`console.py`) 则占据前台终端 —— 每次心跳都会输出一行带有颜色编码的数据,附带趋势箭头和实时的灯泡颜色。 仪表盘地址为 http://localhost:8501。按下 Ctrl-C 会停止所有进程。 非常适合演示:屏住呼吸,看着终端反馈中的 BPM 下降,然后 指向仪表盘图表、手环和灯光,它们会同步联动。 架构(为什么需要两个进程): - `band_service.py` 维持单一的 BLE 连接,将 HR 数据流写入共享的 SQLite 数据库 (`miband.db`),并消耗 commands 数据表。 - `dashboard.py` (Streamlit) 仅负责读取数据库和写入命令。它从不 触碰蓝牙 —— Streamlit 会在每次点击时从头到尾重新运行,无法 维持实时连接,而且手环本身也只允许建立一个连接。 仪表盘的功能: - 实时 BPM 图表(窗口可调),当前/最小/最大/平均值,连接状态。 - **发送通知** —— 文本会显示在手环屏幕上。“type”用于选择 ANS 类别(短信/通话/电子邮件/...),这会改变图标。请保持简短 (最多约 80 个字符 —— 这是一个单一的 BLE 写入操作,没有分块)。 - **震动提醒** —— 让手环震动(作为“寻找我的手环”的信号非常方便)。 手环服务日志:`band_service.log`。如果连接断开,它会自动重连。 ## ⚡ 心率 → WiZ 灯泡颜色 如果你在同一个 Wi-Fi 网络下有一个 WiZ 智能灯泡,你的心率将实时控制其颜色:休息时为 **绿色** → 黄色 → 心率升高时变为 **红色**。 灯泡同步会随 `./start_dashboard.sh` 自动启动(设置 `WIZ_BULB=0` 可跳过此步骤),或者在手环服务旁边独立运行它: ``` ./venv/bin/python wiz_pulse.py # auto-discovers bulb + auto-scales range WIZ_IP=192.168.1.4 ./venv/bin/python wiz_pulse.py HR_LOW=80 HR_HIGH=115 ./venv/bin/python wiz_pulse.py # fixed range overrides auto ``` 默认情况下,颜色范围会根据你最近的 HR(过去 3 分钟)**自动缩放**,因此 完整的 绿色→红色 过程始终会映射到你实际的波动情况。像 50–150 这样固定且宽泛的范围,会将静息 HR(比如 83–109)压缩成单一的黄绿色 —— 导致灯泡看起来像卡住了一样。设置 `HR_LOW`/`HR_HIGH` 可以固定范围。 它只是从 `miband.db` 读取最新的 BPM,并向灯泡发送 UDP 颜色命令 (WiZ 监听端口 38899)。`WizLight` 类复用自我的 [wiz-hack](https://github.com/myselfshravan/wiz-hack) 项目。 ### 将任何其他事物接入到手环 这个存储机制在双向交互上都是一个极佳的集成点: ``` import store # Outbound:通过任何 script / webhook / cron 震动你的手腕 store.add_command("notify", {"text": "Deploy finished ✅", "category": "sms"}) # Inbound:对实时心率做出反应 ts, bpm = store.latest_reading() ``` 因此,AI token 使用量警报、CI 通知,或者任何带有 API 的东西都可以推送到 手环,同时任何事物也都能对你的心率做出反应。 ## 实际可行的情况(安装过程中的笔记) - **两种 HR 模式都不太稳定。** `MODE=manual` 会以短促的 爆发形式触发传感器;`MODE=continuous`(仪表盘的默认设置)大约每 3 秒传输一次真实数值。无论哪种方式,手环都会运行一个约 15–20 秒的测量 *会话*,然后 停止。重新发送 `START` 可以再次启动它;在会话期间发送 `STOP` 会直接 掐断数据流,因此该服务在运行期间绝不会发送 `STOP`。 - **数据频率上限 ≈ 每隔几秒读取一次 —— 并非按每一下心跳。** Mi Band 5 上报的是 *平均 BPM*,而不是单次心跳的时间点(无法通过 BLE 获取 RR-interval 数据),并且即便如此,数据到达也是不稳定的突发形式。真正的单次心跳在当前的硬件条件下是 无法实现的。 - **平滑处理填补了空白。** `wiz_pulse.py` 会以约 12 fps (`WIZ_FPS` / `WIZ_EASE`) 的速度将灯泡颜色平滑过渡到 最新读数,因此灯光会在稀疏的读数之间 *滑行*,即使数据比较粗糙,看起来也是连续的。 ## 手动调用(不通过启动器) ``` ./venv/bin/python scan.py # find the band's UUID ./venv/bin/python heartrate.py # MODE=manual by default ``` ## 故障排除 - **`Auth: FAILED - wrong key`** —— 密钥错误或手环已被重新配对。重新运行 huami-token。确保你复制的是 `0x` *之后* 的数值,并且去掉了 `0x`。 - **`Auth: timed out`** —— 通常是因为手环仍与手机绑定,或者它 在握手过程中重新连接到了 Zepp。彻底关闭 Zepp / 手机蓝牙并重试。 - **scan.py 没有任何显示 / “Band not found”** —— 手环没有广播信号。 要么是手机占用着它(关闭手机蓝牙),要么是 Mac 上存在 之前运行遗留的僵死半开连接。通过以下命令修复 Mac 端问题: `blueutil -p 0 && sleep 3 && blueutil -p 1`(启动器已为你执行此操作)。 此外,请确认 macOS 已授予你的终端应用蓝牙权限 (系统设置 → 隐私与安全性 → 蓝牙)。 - **连接卡住且无任何输出** —— 几乎总是因为手机再次抢占了 手环,或者是 Mac 连接僵死。重启 Mac 的蓝牙并重试。 - **能连接但没有 HR 数值** —— 确保手环确实戴在了你的手腕上; 光学传感器在暴露在空气中时不会启动。
标签:Kubernetes, Python, Streamlit, 无后门, 智能穿戴设备, 物联网, 蓝牙低功耗, 访问控制, 逆向工具