
开发者用Garmin个人API将跑步数据接入本地Mac
来源标题:My running data, on my own Mac
一名开发者利用Garmin Connect个人API,把多年跑步、睡眠和心率数据拉到自己的Mac上自行分析,无需服务器。用户可在账户中直接创建密钥,用一次请求换取有效期24小时、以纯文本返回的token,之后即可读取活动、每日摘要、睡眠、HRV和全天心率等数据。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
一条 Garmin 密钥,不用服务器,还有 Apple Intelligence 把我的板网球比赛叫成网球。
和每个到了一定年纪的开发者一样,我开始跑步了。也和每个开发者一样,我对数据的兴趣大概超出了健康所需。
我的手表记录一切:每一次跑步和每一圈、整夜心率、睡眠情况、我自认为的恢复程度。多年的数据。但我只能按 Garmin Connect 的方式查看,一次一个活动,这从来回答不了我想知道的问题。我这周 HRV 偏低,还是只是比上周低?晚上高强度训练后我睡得更差吗?周二节奏跑我掉速了吗?
让这一切成为可能的是 Garmin 的 Personal API。你在 Garmin Connect 账户里创建一个密钥,这个密钥就能读取你自己的数据。不需要申请任何东西。
一个请求把密钥换成 token。token 以纯文本返回,不是 JSON,有效期 24 小时。
curl -X POST -H "Authorization: ApiKey $KEY"
https://personal.connectapi.garmin.com/personalapitoken-service/personal-api/personal-api-key/token
从那里你能拿到活动、每日摘要、睡眠、HRV(夜间每五分钟一个读数)、全天心率,以及每个活动的原始 FIT 文件,里面有路线和每一圈。文档很薄,速率限制没有公开,板网球被叫作 paddelball。首次同步约 2,000 个活动和一年的每日数据仍然只需要大约三十秒。
HRV 的比较方式与运动科学家一致:7 天平均值对比由你自己前 60 天构建的正常范围(Plews 等人)。这样单独一个糟糕的夜晚不会触发警报。睡眠债务按 14 个夜晚计算,对照手表说你需要多少。记录来自 FIT 文件,所以藏在长距离跑里的最佳 5K 也算数。
今天的数据也放在菜单栏里。点一下就能实时看到当天到目前为止的情况:醒来以来的能量和心率、压力、步数。打开时获取一次,之后每十五分钟一次,从不存储。大多数早晨我打开的就只有这个。
我不需要却最喜欢的是 Explorer。每个户外活动的起点都离线匹配到国家和城市,使用 Natural Earth 的边界和 GeoNames 的城市列表。任何位置都不会发给地理编码服务。然后它绘制地图:你跑过的每个国家,按你进入它们的顺序连接,并附上每年的计数。
显然我在 33 个国家跑过步。在 Splits 数出来之前我并不知道。
然后 macOS 26 发布了 Apple 的 Foundation Models 框架,它让任何 app 都能使用 Apple Intelligence 背后的语言模型。这是端侧模型,大约 30 亿参数,所以数据留在 Mac 上,每次调用没有成本。对这个 app 来说这是毫不犹豫的同意。我想在每次跑步下面加一段短评,就像一个跑友在看你的手表。
API 小得令人愉快。你检查 SystemLanguageModel.default.availability(Mac 可能不支持、Apple Intelligence 可能关着,或者模型可能还在下载),用你的指令创建一个 LanguageModelSession,然后流式获取响应。在我的 Mac 上,一段 55 词的评语大约需要六秒。我只用端侧模型,从不用 Private Cloud Compute。
我的第一版把整个训练交给它并让它评价。评语读起来不错,但很多是错的。4 区占 0% 时,它说我“大部分在 1 到 4 区”。它把我的板网球叫成网球,然后又叫成匹克球。我让它不要用破折号,它还是用了破折号。在 HRV 页面上,它曾经在结论完全相反的判断上方放了一个欢快的“HRV 偏高”标题。
公平地说,这是个很小的模型。它写得好,但不能信任它处理数字。所以我不再让它读数字。现在普通代码做每一个决定并把它写成一句话,Apple Intelligence 只是用更友好的语气再说一遍。对于那天的周二节奏跑,它拿到的是这些:
Distance: 8.02 km Average pace: 5:54 /km Pacing judgement: a structured workout, 2 reps with easier running before, between or after them, so the changes in pace were planned. The reps averaged 5:28 /km, 5:56 /km: the last rep was 29 s/km slower than the first, so it faded. Effort compared with usual: 44 s/km faster than their usual pace, at their usual heart rate. Heart rate zones judgement: a moderate effort, mostly zones 2 and 3 (86% of the time). Suggestion for what's next: A solid workout: an easy day next lets it sink in. The sport is running. Call it exactly that.
分区作为结论传入,因为它老是误读百分比。明天是否该轻松由我代码里的一个 if 语句决定。最后一行是因为板网球才有的。我也把数字保持在最少,因为它会复制它看到的每一个数字。破折号我放弃了,用正则表达式去掉。
一个不错的副作用是,错误现在落到我的代码里,我可以在那里测试它们。早期的一条评语抱怨同一次节奏跑有“71 秒/公里的波动”。模型没有编造。是我的代码比较了每一公里,包括热身,把一个计划好的训练说成不均匀。我修复了它,从手表的圈里找出重复段,那次跑步现在成了一个单元测试。
它还是会出错。根据上面的事实,它写我的“配速平滑变化”,而代码说我掉速了。所以每条评语都写着“由端侧 AI 撰写,可能出错”,并放在它所谈论的图表旁边。我还是喜欢早上读它们。我只是不让它做数学。
几天前我把 Splits 放到了 GitHub 和 Reddit 上。很快两个人发来了同一个错误的截图:对于一段没有 HRV 的数据,Garmin 返回 204 No Content 且没有正文,我的首次同步停在那里。我修复了它,写了与他们的截图完全一样失败的测试,并在同一天发布了 0.1.1。我从中获得的乐趣超出预期。
免费开源(MIT),适用于 macOS 14 及更高版本。评语需要 macOS 26 和 Apple Intelligence。没有账户,我永远看不到你的数据。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。