blandisc/noop
GitHub: blandisc/noop
Cénit 是一款完全在 iPhone 本地运行的 WHOOP 腕带配套应用,通过蓝牙直连读取生物特征数据,无需账户和云端即可完成健康评分与分析。
Stars: 0 | Forks: 0
Cénit
你的腕带。你的数据。你的 iPhone。完全在设备端运行,无需云端。
Cénit 最初诞生时名为 NOOP —— 这个代码仓库至今仍保留着这个名字。
## 下载 Cénit 是一款 iOS 应用 —— 你需要从源代码构建它: | 平台 | 构建 | 备注 | |---|---|---| | **iOS** | 从源代码构建 —— 参见 [`docs/BUILD.md`](docs/BUILD.md) | 包含 App + widgets + HealthKit。**不以下载形式分发:** iOS 没有匿名安装途径 —— App Store 和 TestFlight 都需要真实的 Apple Developer 身份 —— 因此只能由你在 Xcode 中自行构建。 | 完整的构建说明请参见 [`docs/BUILD.md`](docs/BUILD.md)。 一切都**在你的设备上**运行。内置的 Coach 在本地进行回答,完全不使用网络;唯一可能离开你的 iPhone 的内容是可选的外部 AI Coach(仅在你添加了自己的 API key 之后)以及自愿开启的锻炼媒体下载器(默认关闭 —— 参见 [隐私](#privacy))。 Cénit 是一款独立、完全**在设备端运行**的 WHOOP 腕带(4.0 和 5.0/MG)配套应用。它通过 Bluetooth 直接与腕带配对,将所有数据存储在你自己的 iPhone 的 SQLite 中,导入你现有的 WHOOP 和 Apple Health 历史记录,并**在本地**计算 recovery、strain、HRV 和睡眠,无需 WHOOP 账户,也无需 WHOOP 云端。 它建立在先前社区的逆向工程工作之上,其存在的原因只有一个:让拥有 WHOOP 腕带的人能够在**他们自己**控制的机器上,从**他们自己的设备**中读取**他们自己的生物特征数据**。 ## 目录 - [为什么选择 Cénit](#why-cénit) - [功能](#features) - [平台状态](#platform-status) - [架构](#architecture) - [快速入门](#quickstart) - [你的数据如何流动](#how-your-data-flows) - [隐私](#privacy) - [致谢](#attribution) - [免责声明](#disclaimer) - [许可证](#license) - [文档](#docs) ## 为什么选择 Cénit 你买下了腕带。它产生的生物特征数据流归你所有。Cénit 就是建立在这个前提之上的: - **拥有你的数据。** Cénit 通过 Bluetooth 直接从腕带读取心率、R-R 间期、SpO₂、皮肤温度、呼吸频率、加速度计/重力、电池和事件数据,并将其写入本地 SQLite 数据库。没有任何内容会被上传到任何地方。 - **无需账户,完全在设备端运行。** Cénit 从不登录 WHOOP 账户,也从不访问 WHOOP 服务器。它不会绕过任何登录、付费墙或 DRM;它只是与你拥有的设备通信,并读取你生成的数据。 - **导入你的历史记录。** 已经在官方 App 或 Apple Health 中拥有数年的数据了?只需导入一次 WHOOP CSV 导出文件和/或你的 Apple Health `export.xml`,它就会永久保存在你的机器上。 - **透明的数学计算。** Recovery、strain、HRV 和睡眠均使用已记录、可引用的方法(Task Force 1996 HRV、Karvonen %HRR、Edwards / Banister TRIMP、Tanaka HRmax 等)在设备上重新计算。这些算法是对任何专有模型的近似——而非复刻,并且每个分析器文件都准确记录了它的具体作用。 ## 功能 该应用被组织为**五个标签页**。 | 标签页 | 包含内容 | |---|---| | **Today** | 主仪表盘。一个主要数字 —— 你的 **recovery** —— 以及 24 小时表盘上的**今日结论**(Primed / Balanced / Strained / Run down),并显示其背后的驱动因素。下方:统计磁贴网格(日 strain、睡眠、HRV、心率、静息心率、血氧、步数、压力),每个都带有 14 天的迷你图,还有最近的锻炼和数据来源页脚。下拉可同步腕带并重新计算。**Live** —— 实时 HR/帧数据流(约 1 Hz)—— 会从这里以全屏覆盖层的形式打开。 | | **Body** | 深入了解你的生物特征。统一的**指标详情**界面,专门用于读取 recovery、strain、**睡眠**(睡眠分期图、阶段细分、效率、作息规律性)、压力、皮肤温度、**体能年龄**,以及 HRV、静息心率、SpO₂、呼吸频率和步数 —— 还有跨越所有这些指标的长周期**趋势**。Apple Health 数据也会在此进行核对。 | | **Coach** | 一个构建为闭环的单一屏幕 —— **发现 → 测试 → 行动 → 学习**。顶部是你**今日的决策**(做什么),以 recovery 作为依据。下方:**向你的数据提问** —— **在设备端**回答自由文本问题(参见 [Coach](#the-coach--ask-your-data-on-device));**什么对你有效** —— 与你最佳 recovery 最相关的习惯,作为一个因果注册表;**发现** —— 设备端引擎揭示的异常、趋势和相关性;**记录你的一天** —— 一个为你的杠杆提供数据的“是/否”日记;以及 **N-of-1 实验** —— 测试一个杠杆 7 天,并获得真实的结论(有效 / 无效 / 信号不足)。 | | **Train** | 两个腕带原生工具。**Breathe** —— HRV **触觉呼吸生物反馈**:腕带既*测量* HRV(R-R 间期),又通过其触觉马达*发出震动*,因此 Cénit 能用你能感觉到的提示引导你的呼吸,并显示实时心率 + 滚动 RMSSD。预设:放松 4-6、连贯 5.5、方块 4-4。**Intervals** —— 一个**静音触觉 HIIT 计时器**:腕带在每次过渡时都会震动(进入工作时三次震动,进入休息时一次震动,3-2-1 滴答声,结束时长震动),让你可以解放双手进行训练,在未连接腕带时提供一目了然的可视化备用方案。 | | **Settings** | 个人资料、偏好设置、应用内的 **What's new** 更新日志,以及一个自愿开启的 **Experimental** 部分(WHOOP 5/MG 协议探测)。**More** 区域包含供高级用户使用的屏幕:**Metric Explorer**(随时间查询任何单个指标)、**Compare**(将两个指标绘制在一起)、**Workouts**(检测到的会话,包含 strain + 心率详情)、**Data Sources**(一键导入 WHOOP CSV 或 Apple Health 导出文件,以及实时腕带状态)和 **Automations**(自愿开启的 HealthKit 回写)。 | 此外还有一个首次运行的**引导向导**,用于设定预期(独立/实验性质,WHOOP 4.0 对比 5/MG,仅限设备端),主屏幕/锁屏** widgets **,以及每次更新后显示的应用内 **"What's new"** 更新日志。 ### Coach —— “向你的数据提问”,完全在设备端运行 Coach 过去是与外部 LLM 的聊天。现在不再是了。**向你的数据提问**现在分三个层级回答你的自由文本问题,默认路径完全不触及网络: - **设备端(Apple Intelligence)。** 在搭载 Apple Intelligence 的 iPhone(iOS 26+)上,你可以输入一个开放式问题 —— *“为什么我醒来时很累?”* —— 然后你手机自带的模型会作答,**无需网络**。一个确定性的引擎(`StrandAnalytics` 中的 `CoachGrounding`)根据**你的真实数值**构建答案,而模型只负责**措辞** —— 因此它永远不会凭空捏造你的任何指标。防护机制会拒绝任何包含引擎未提供数值的答案。 - **Essential 模式。** 在没有 Apple Intelligence 的 iPhone 上,你可以通过预设的问题进行提问,这些问题使用与引擎相同的数值 —— 完全是确定性的,同样在设备端运行。 - **外部 AI(可选,自带 key)。** 为了获得更深入、开放式的回答,你可以选择使用**你自己**的 OpenAI/Anthropic key 接入外部 LLM。这是整个应用中**唯一**会使用网络的功能,并且它只发送近期指标的简短文本摘要加上你的问题 —— 绝不发送原始数据流或标识符。在你添加 key 之前保持关闭。参见 [`docs/PRIVACY_SECURITY.md`](docs/PRIVACY_SECURITY.md)。 ## 平台状态 Cénit 的逻辑存在于跨平台的 Swift 包中。iOS 应用与腕带配对,并**在你自己的设备上对 recovery、strain 和睡眠进行评分** —— 无需导入。 | 平台 | 状态 | |---|---| | **iOS** | ✅ 该应用(`Cenit`,SwiftUI,**iOS 17+**) —— 应用目标 + widgets + HealthKit。通过 BLE 配对,卸载腕带的历史记录,并在设备端对 recovery / strain / 睡眠进行评分。**仅限从源代码构建,不进行分发:** iOS 没有匿名的分发途径(App Store 和 TestFlight 都需要真实的 Apple Developer 身份),这与本项目保持匿名的初衷根本冲突。 | Cénit 目前**仅限 iOS** —— 早期的 macOS 和 Android 实验已经退役。协议事实以及组帧/CRC 规则是语言无关的,因此通信行为是可移植的,未来的移植在技术上是可能的;但目前没有任何正在进行开发的移植版本。 ### 腕带支持 Cénit 是一个独立的、**实验性**的项目 —— 功能完备,但仍在开发中。 | 腕带 | 状态 | |---|---| | **WHOOP 4.0** | ✅ 经过测试和支持的路径。实时心率、recovery、strain、睡眠、历史记录卸载 —— 完整的体验。 | | **WHOOP 5.0 / MG** | 🧪 **实时心率、电池和底层的记录卸载均可工作**(已在真实硬件上确认)。连接前请选择“WHOOP 5.0 / MG” —— 并查看下方的配对说明,因为你不能直接扫描到它。5/MG 的衍生指标(recovery、strain、睡眠)仍在逆向工程中;有一个自愿开启的 **Settings → Experimental** 开关,可以记录原始的“puffin”帧,以帮助映射协议。 | 应用始终会在引导过程和每个屏幕上告诉你哪些功能目前已上线,哪些仍在开发中。 ### 开始使用时的预期 Cénit 在你自己的设备上计算你的分数,因此像任何 recovery 可穿戴设备一样,在所有数据填满之前,它需要一点数据: - **实时心率**在腕带连接的那一刻就会显示。 - **Strain 和睡眠**在你佩戴并同步后出现 —— 腕带最后约 14 天的数据会在最初的几分钟内自动卸载。 - **Recovery** 需要几个晚上的时间让应用了解你的个人基线,然后每天晚上都会变得更加精准。WHOOP 出于同样的原因让你等待。 - **赶时间?** 在 **Settings → Data Sources** 中导入你的 WHOOP 导出文件,你的完整历史记录大约一分钟内就会填满。 ## 架构 该仓库被拆分为平台无关的 Swift 包以及 iOS 应用目标(`Cenit`)。所有包都同时声明了 `.iOS(.v16)` 和 `.macOS(.v13)`,以便纯逻辑可以在没有应用或腕带的情况下构建和测试;针对特定 framework 的 UI 通过 `#if canImport(UIKit)` / `#if canImport(AppKit)` 进行保护。 ``` Cenit/ SwiftUI app layer — BLE, Collect, Data, Screens, System (built by the Cenit target) CenitApp/ iOS app shell — HealthKit, widgets, intents CenitWidgets/ WidgetKit extension (Home / Lock-screen widget) CenitShared/ code shared between the app and the widgets Packages/ WhoopProtocol/ BLE frame parsing, CRC, command/event/packet decode (pure, no CoreBluetooth) WhoopStore/ GRDB/SQLite persistence (versioned migrations, through v12) StrandAnalytics/ HRV / recovery / strain / sleep / correlation math + Coach grounding (pure, DB-free) StrandImport/ WHOOP CSV + Apple Health importers StrandDesign/ SwiftUI design system (palette, components, charts) Tools/ developer scripts (localization, screen-map, protocol decode) Fixtures/ sample WHOOP export for tests ``` ### `WhoopProtocol` —— 逆向工程核心 平台无关(不导入 CoreBluetooth),因此它可以在测试和 CLI 工具中保持不变地运行。它解码两代腕带的在线帧数据格式: ``` public enum DeviceFamily: String, Sendable, CaseIterable { case whoop4 // CRC8 (poly 0x07) header check; service 61080001-… case whoop5 // CRC16-Modbus header check, "puffin" packet types; service fd4b0001-… } ``` 解码是 schema 驱动的(`Resources/whoop_protocol.json`),包括 CRC8、CRC16-Modbus 和 zlib CRC-32 实现、帧组帧、值解释以及历史流重组。应用层(`Cenit/BLE/`、`Cenit/Collect/`)将这些 UUID *字符串*封装在 `CBUUID` 中,并处理绑定、卸载和实时通知。 ### `WhoopStore` —— 通过 GRDB 实现的本地 SQLite 所有内容都通过 [GRDB.swift](https://github.com/groue/GRDB.swift) 存储在设备端的 SQLite 中。schema 是一个版本化的迁移器(`Database.swift`,目前更新至 **`v`**)。在 `v1`–`v3` 中创建的解码流表: ``` CREATE TABLE hrSample (deviceId TEXT, ts INTEGER, bpm INTEGER, PRIMARY KEY(deviceId, ts)); CREATE TABLE rrInterval (deviceId TEXT, ts INTEGER, rrMs INTEGER, PRIMARY KEY(deviceId, ts, rrMs)); CREATE TABLE spo2Sample (deviceId TEXT, ts INTEGER, red INTEGER, ir INTEGER, PRIMARY KEY(deviceId, ts)); CREATE TABLE skinTempSample(deviceId TEXT, ts INTEGER, raw INTEGER, PRIMARY KEY(deviceId, ts)); CREATE TABLE respSample (deviceId TEXT, ts INTEGER, raw INTEGER, PRIMARY KEY(deviceId, ts)); ``` 随后的迁移添加了服务器派生的指标缓存(`sleepSession`、`dailyMetric`)、日记和锻炼数据表、一个通用的长格式 `metricSeries` 存储 (v9)、WHOOP 5 计步 (v10–v11) 以及 **N-of-1 `experiment`** 数据表 (v12)。 ### `StrandAnalytics` —— 透明的、设备端数学计算 纯粹的、无数据库的分析器。每一个都有文档记录,并以已发表的方法为基础(并且明确指出是近似值,并非对任何专有模型的复刻): | 文件 | 计算 | |---|---| | `HRVAnalyzer.swift` | 根据 R-R 间期(Task Force 1996)计算 RMSSD + SDNN,带有极差 + Malik 异常搏动过滤。 | | `RecoveryScorer.swift` | 0–100 的 recovery 评分:以 HRV 为主的 z-score(在对数尺度上,即 lnRMSSD)+ 相对于个人基线的逻辑复合。 | | `StrainScorer.swift` | 基于 %HRR (Karvonen) 和 Edwards / Banister TRIMP 的 0–21 对数 strain 刻度。 | | `SleepStager.swift` | 睡眠/清醒检测 + 基于心肺和重力特征的近似 4 阶段睡眠分期。 | | `ReadinessEngine.swift` | Today 结论 —— HRV 与基线对比 (Plews/Buchheit)、RHR 漂移、呼吸频率漂移、负荷平衡(ACWR, Gabbett)和单调性。 | | `CorrelationEngine.swift`, `BehaviorInsights.swift` | Pearson r / OLS / 滞后相关性,带有 Student-t *p*-值和 FDR 校正;行为洞察。 | | `CoachGrounding.swift`, `ExperimentVerdict.swift` | 为设备端 Coach 提供确定性的基础数据,以及 N-of-1 实验的结论。 | ### `StrandImport` —— 导入你自己的历史记录 - **WHOOP CSV 导出**(`WhoopExportImporter.swift`):由表头名称驱动的、具有容错性的解析器,用于从文件夹或 `.zip` 中解析 `physiological_cycles.csv`、`sleeps.csv`、`workouts.csv` 和 `journal_entries.csv`。相同的 schema 覆盖 WHOOP 4 / 5 / MG。 - **Apple Health 导出**(`AppleHealthImporter.swift`):用于 `export.xml`(大小可能超过 1 GB)的**流式** SAX 解析器(`XMLParser`),带有相关性去重、单位标准化(例如 SpO₂ 分数 → %)和睡眠阶段映射。 ### `StrandDesign` —— SwiftUI 设计系统 调色板、排版、动效和可重用的组件/图表 —— 没有任何外部 UI 依赖。它承载了**两种语言**:作为新设计和重新设计屏幕标准的 **«Instrumento diurno»** 日间语言(温暖的纸张色调,一个主要的数字,颜色仅出现在数据中),以及其余旧版屏幕仍在使用的最初的**深色**系统。 ## 快速入门 **要求:** 最新版本的 Xcode 以及一台 **iOS 17+** 的 iPhone 用于安装。设备端 Coach 使用 Apple 的 **FoundationModels** framework 构建,因此构建它需要 **Xcode 26**,并且它只能在拥有 **iOS 26 + Apple Intelligence** 的 iPhone 上运行;在较旧的版本上,Coach 会被编译排除,并回退到“Essential 模式”。要进行实时配对,你需要有自己的 WHOOP 腕带;如果只是想探索,你可以改为导入 CSV / Apple Health 导出文件。 Xcode 项目是使用 [XcodeGen](https://github.com/yonaskolb/XcodeGen) 从 [`project.yml`](project.yml) 生成的。 ``` # 1. Clone git clone标签:iOS应用, WHOOP, 健康数据, 可穿戴设备, 本地计算, 蓝牙通信