gwerneckp/garmin-ble
GitHub: gwerneckp/garmin-ble
该项目通过纯 Python 逆向实现了 Garmin 专有 BLE 协议,让用户能绕过云服务直接从电脑读取 Garmin 手表的实时遥测数据。
Stars: 4 | Forks: 2
#
garmin-ble
[](https://pypi.org/project/garmin-ble/)
[](https://pypi.org/project/garmin-ble/)
[](https://www.gnu.org/licenses/agpl-3.0)
Garmin 专有 BLE 协议 (GFDI V2) 的 clean-room Python 实现。将你的 Garmin 手表中的实时遥测数据直接传输到你的计算机:无需云服务,无需手机,无需 Garmin Connect。
## 功能特性
- **实时遥测** — 通过 BLE 传输实时传感器数据,无需 Garmin Connect:
- ❤️ 心率与静息心率
- 🚶 每日步数与目标
- 📊 心率变异性 (HRV)
- 🫁 血氧 (SpO2)
- 🌬️ 呼吸频率
- 🔥 卡路里 (总消耗与活动消耗)
- ⚡ 强度分钟数
- 🧘 压力水平
- 🔋 身体电量
- ⌚ 加速度计
- **类型化指标** — 每个读数都是一个带有命名字段的 dataclass,而不是基于位置的 tuple
- **按需服务注册** — 订阅某个指标会启动其服务;最后一次取消订阅会停止该服务
- **协议解码** — 完整实现了 Garmin GFDI V2 协议栈:
- 自动握手 (`CLOSE_ALL`, `REGISTER_ML`)
- MLR (Multi-Link Routing) 数据包多路复用
- COBS (Consistent Overhead Byte Stuffing) 编码/解码
- 为 `gdi_smart_proto` 编译的 Protobuf
- CRC16 完整性校验
- **自动重新连接** — 通过指数退避在 BLE 断开时保持存活,并恢复订阅
- **Keep-Alive 心跳** — 定期进行时间同步以维持连接
- **模拟器与重放** — 无需硬件即可开发、测试和重现 bug
- **数据帧追踪** — 每个会话生成捕获文件,并提供实时协议诊断
- **可定制性** — 纯 Python 实现,无二进制文件块,无专有 SDK
## 安装
```
pip install garmin-ble
```
或者从源码安装并包含开发依赖:
```
git clone https://github.com/gwerneckp/garmin-ble.git
cd garmin-ble
pip install -e ".[dev]"
```
## 快速入门
```
import asyncio
from garmin_ble import Watch, metrics
async def main():
async with Watch.discover() as watch:
print(f"Connected to {watch.info.name}")
async for reading in watch.stream(metrics.HEART_RATE):
print(f"❤️ {reading.bpm} BPM (resting {reading.resting_bpm})")
asyncio.run(main())
```
该会话负责管理连接、GFDI 握手、keep-alive 心跳和重新连接 —— 并且在退出该代码块时总是会断开连接(包括按下 `Ctrl+C` 时)。
订阅某个指标会在手表上注册并启动其服务;最后一次取消订阅会停止该服务。
更倾向于使用回调?同步和 `async def` 处理程序均支持:
```
@watch.on(metrics.HEART_RATE)
async def _(reading: metrics.HeartRate) -> None:
await store(reading.bpm)
```
### 手头没有手表
替换工厂类,相同的代码即可在无硬件环境下运行 —— 非常适合用于开发、示例和 CI:
```
async with Watch.simulated(profile="fenix7") as watch: # in-process watch
async with Watch.replay("session.gble") as watch: # a recorded session
```
`watch.record(path)` 会写入可被 `Watch.replay` 读取回的捕获文件,这样即使是没有该手表的人也能重现协议 bug。
### 读取设备状态
```
battery = await watch.battery() # -> Battery(percent=88, status="ok")
result = await watch.collect(timeout=60) # one sample of every metric
print(result) # renders a ✅/⏳ checklist
```
失败时会抛出异常:`WatchNotFound` 会携带扫描 *确实* 发现的设备,`HandshakeError` 会指明停止的阶段,而当手表拒绝提供某项指标而不是留下一个永不产生数据的 stream 时,就会抛出 `ServiceUnavailable`。
查看 [`examples/`](./examples/) 目录以了解完整的用法模式 —
`telemetry_basic.py` (最简单的入门)、`telemetry_advanced.py` (同时使用所有 stream、重新连接和诊断)、`device_state.py` (双向 Protobuf)、`full_walkthrough.py` (验证所有功能) 以及 `accelerometer_3d.py` (实时 3D 方向)。大多数示例都支持 `--simulate`。
## 状态与路线图
查看 [GitHub Issues](https://github.com/gwerneckp/garmin-ble/issues) 获取计划功能、已知缺陷和正在进行的工作的完整明细。里程碑对应着发布版本:
| 发布版本 | 目标 | 状态 |
|---------|------|--------|
| v0.1.0 | 🏗️ BLE 传输与握手 | ✅ 已完成 |
| v0.2.0 | 📡 实时遥测流传输 | ✅ 已完成 |
| v0.3.0 | ⌚ `Watch` API、传输、模拟器与重放 | ✅ 已完成 |
| v0.4.0 | 🧠 Protobuf 设置与设备状态 | 🔄 进行中 |
| v0.5.0 | 🔔 通知与媒体控制 | ⏳ 计划中 |
| v0.6.0 | 📁 文件传输 (下载 FIT / GPX) | ⏳ 计划中 |
| v1.0.0 | 🗄️ 稳定版发布 | ⏳ 计划中 |
## 设计理念
`garmin-ble` 是一个**通信协议库**,而不是一个功能完整的应用程序。它只知道如何编码、发送、解码和响应 Garmin 的 BLE Protobuf —— 仅此而已。
这意味着:
- 本库 **不会** 从 OpenWeatherMap 获取天气,不会通过 CalDAV 同步日历,也不会在手表触发“寻找手机”时播放声音。
- 相反,它提供了基础构建块 —— Protobuf 编码/解码、针对传入消息的回调以及传输辅助工具。
- **调用者** 负责连接操作系统集成、外部 API 和面向用户的功能。
这使得本库保持专注、易于测试,并且免受那些困扰着集成密集型项目的无休止的功能蔓延的影响。
**如果你有以下需求,请考虑改用 Gadgetbridge:** 你想要在 Android 手机上获得一个功能完整的开源替代方案来取代 Garmin Connect 应用。
## 项目使命
**掌控你的数据。** Garmin 设备捕获了丰富的生理数据,但 Garmin Connect 将其锁定在云服务之后。本库允许你通过 BLE 直接以编程方式访问你的手表 —— 无需互联网。
## 致谢与许可证
本项目建立在 [Gadgetbridge](https://codeberg.org/Freeyourgadget/Gadgetbridge) 团队非凡的逆向工程工作之上。协议逻辑、COBS 解码以及 `.proto` schema 均源自他们的开源 Java 实现。
本项目基于 **GNU Affero General Public License v3.0 (AGPL-3.0)** 许可证授权 —— 详情请参阅 [`LICENSE`](./LICENSE)。
标签:Garmin, Python, 可穿戴设备, 数据解析, 无后门, 物联网, 蓝牙低功耗, 计算机取证, 逆向工具, 通信协议