dsh 0.1.13发布:Garmin Connect插件重点修复与接入实践 dsh这套客户端我一直在跟进它本质上是一个轻量级的数据聚合面板靠插件机制把各种来源的数据汇到同一套视图里。这次0.1.13补丁发布标题里最醒目的是dsh-plugin-garmin-connect也就是Garmin Connect数据源插件。坦白讲这个插件在之前的版本里属于能用但不太顺手的状态授权流程偶尔抽风、令牌过期后要手动重登、部分活动记录抓不全。0.1.13把这一堆问题集中修了一轮同时对插件加载器也做了兼容性调整。这篇文章就围绕这次更新的实际内容、插件的工作原理、接入步骤和一些踩坑经验展开适合正在用或准备用dsh做健康数据整合的开发者参考。1. 0.1.13补丁到底改了什么1.1 从版本号看发布节奏先说版本号。dsh目前还处于0.1.x阶段按语义化版本规则0.x阶段的版本号变化不承诺向后兼容但也不是说每个补丁都会破坏现有配置。0.1.12到0.1.13之间间隔大概三周算是正常的小迭代。这类客户端在正式1.0之前补丁的核心任务是修bug、补边缘情况而不是加新功能——0.1.13把重心放在稳定性上是符合预期的。这次更新涉及插件加载器和Garmin Connect插件两部分。我在实际测试中感受到的最明显变化是插件加载器对低版本运行环境的兼容性更好了。之前遇到过一个情况设备系统库版本较旧加载器在初始化插件上下文时直接抛出缺少依赖的异常整个面板无法启动。0.1.13里加载器改为延迟加载策略并加入了独立的依赖隔离层这个问题基本解决。1.2 Garmin Connect插件的重点修复dsh-plugin-garmin-connect在0.1.13里是重点对象。基于本次补丁列表和实际测试主要修复集中在以下四点OAuth令牌刷新逻辑重写。原先的令牌刷新只在启动时做一次如果当时网络不通或服务端返回异常本轮运行期间不会再自动重试。新版本把刷新机制挪到了同步流程内每次同步前先检查令牌有效期剩余时间小于10分钟就主动刷新刷新失败则继续沿用旧令牌并标注警告状态。活动数据分页抓取漏项问题。Garmin Connect接口对活动列表采用分页返回旧版插件在翻页时使用了错误的游标偏移导致每翻页一次就可能少一条数据活动数量多的时候漏得尤其明显。新版本改用增量游标策略完整跑了一遍分页链路。时区处理修正。同步心率与睡眠数据时旧版直接采用设备记录的UTC时间面板展示时再按本机时区偏移。如果用户跨时区出差睡眠和步数的日期归属会发生错位。新版本在同步阶段就把时间统一转换到用户配置的时区展示层不再做二次偏移。构建体积优化。插件内部打包的工具库做了精简安装包体积减少约30%冷启动时插件注册时间明显缩短。这个优化对插件数量多的dsh实例比较友好。1.3 更新后需要留意的一个行为变化0.1.13之后插件在首次启动时会自动做一次全量元数据校准简单说就是重新拉取Garmin Connect端的设备列表、单位设置和最近90天的活动摘要用于刷新本地索引。如果你的账号下Garmin设备比较多这个过程可能需要几分钟期间面板对应模块会显示正在校准状态这是正常的不是卡死。我在测试中确认过一个细节校准过程不会触发令牌刷新除非令牌已过期。也就是说校准期间网络异常不会影响令牌有效期不需要担心出现一校准就把令牌搞失效这种连锁问题。2. Garmin Connect插件的核心工作机制2.1 授权流程OAuth 2.0的实际落地Garmin Connect开放平台采用的是标准OAuth 2.0授权码模式。dsh插件在工作时流程是这样的插件启动后向Garmin认证服务器发起授权请求跳转到Garmin登录页。用户在浏览器中完成登录和授权Garmin返回一个一次性授权码。dsh插件用这个授权码换取访问令牌和刷新令牌。后续每次同步都携带访问令牌调用Garmin数据接口。访问令牌过期后用刷新令牌重新换取整个过程对用户透明。这个逻辑听起来简单但实际开发中有很多细节。最容易被忽略的就是令牌有效期。Garmin Connect接口对访问令牌的默认有效期是30分钟刷新令牌是30天。如果插件长期不运行刷新令牌也会过期这时只能重新走完整的授权流程。在0.1.13版本中插件把刷新令牌剩余有效期纳入同步前的检查项如果发现剩余不足7天就在日志中输出预警提示。这样一来开发者或用户可以提前处理避免某天突然发现同步完全停摆了。2.2 数据模型映射从Garmin字段到dsh统一实体dsh之所以需要插件机制核心原因是它不绑定任何单一数据源而是定义了一套统一的健康数据模型。Garmin Connect插件的工作就是把这套模型与Garmin返回的JSON字段做映射。dsh健康数据模型包含以下核心实体活动Activity运动类型、起止时间、距离、卡路里、心率区间分布。体征Vitals静息心率、呼吸频率、血氧、压力指数。睡眠Sleep入睡时间、醒来时间、睡眠阶段分布。身体构成BodyComposition体重、体脂率、内脏脂肪等级。Garmin Connect接口同样返回这些数据但字段名和单位体系不同。举例来说Garmin活动数据中的距离字段用distanceInMeters体脂率用bodyFatPercent而dsh统一模型直接用distance和bodyFat。插件在同步时干的主要工作就是字段名映射、单位换算和时间归一化。单位换算是比较容易出错的地方。Garmin返回的距离单位是米而dsh默认展示单位支持公里和英里需要在配置中声明。如果配置的是英里插件内部负责把米转成英里精度保留到小数点后两位后续计算卡路里与速度时按照英里体系处理。旧版本中如果配置切换为英里后未重启展示面板里距离偶尔会出现不刷新0.1.13修复了这个状态同步问题。2.3 同步策略全量同步与增量同步的取舍dsh插件默认采用增量同步策略。启动同步时插件会记录每次同步完成后的游标位置通常是数据时间戳下一次只拉取游标之后的新数据。这种方式的优势是请求量小、同步速度快、对Garmin服务端压力小。不过增量同步也有一个缺点如果某次同步时网络连接中断数据拉取了一半就停止了游标可能没有正确推进导致下一次同步会重复拉取同样的数据。dsh处理重复数据的手段是主键去重以Garmin返回的活动ID或记录时间戳作为唯一标识本地入库前先比对重复记录直接丢弃。这保证了即便游标出问题数据不会重复最多是多消耗一点请求流量。用户也可以主动触发全量同步操作方法是进入插件设置页点击强制全量同步。这个操作会忽略游标位置从90天前开始重新抓取。适合在发现历史数据缺失时使用。3. 五个步骤完成dsh接入Garmin Connect3.1 前置条件你必须有的东西开始之前确保满足以下条件已安装dsh客户端版本不低于0.1.13。一个有效的Garmin账号账号下至少绑定了一台Garmin设备手表、手环均可。能在本地访问Garmin官方服务。如果网络环境受限同步会失败这一点要靠自己确认。基本的YAML编辑能力。dsh的插件配置采用YAML格式不会也没关系改两个参数就行。3.2 在Garmin开发者平台创建应用这一步是为dsh插件获取Client ID和Client Secret。进入Garmin开发者平台后选择创建新应用需要填写应用名称、描述和回调地址。回调地址是最容易写错的地方。dsh插件本地绑定的是http://127.0.0.1:8765/callback不光协议、地址、端口要一致路径也不能多写或少写。我曾经在测试时把回调地址写成了http://127.0.0.1:8765/callback/尾部多了一个斜杠导致Garmin服务器回调过来时路径匹配不上授权页面一直显示无法完成登录。创建成功后页面会显示Client ID和Client Secret。注意Client Secret只在创建时完整显示一次务必立刻复制到本地配置文件里。3.3 安装并配置dsh插件dsh插件安装有两种方式一种是在客户端内通过插件市场搜索garmin-connect直接安装另一种是从源码构建后手动放置到插件目录。对普通用户推荐第一种源码构建适合需要修改插件代码的进阶玩家。安装完成后编辑插件配置文件内容大致如下plugin: garmin-connect version: 0.1.13 settings: client_id: 你的Client_ID client_secret: 你的Client_Secret redirect_uri: http://127.0.0.1:8765/callback timezone: Asia/Shanghai unit_system: metric sync_interval_minutes: 30 start_date: 2025-01-01逐个解释关键字段timezone统一时区。这里填的是你所在时区的IANA名称插件同步的所有时间都会转换到这个时区再做存储。注意不要用UTC8这种格式IANA名称更可靠。unit_system单位制metric是公制imperial是英制。建议和Garmin设备上的设置保持一致否则视力数据会出现6350公里这种单位换算混乱的情况。sync_interval_minutes增量同步间隔单位是分钟。默认30分钟我个人觉得30分钟对健康数据足够不需要更频繁毕竟Garmin端数据本身也不是实时更新的而是设备每记录一段就同步到Garmin云。start_date首次全量同步的起始日期。如果只需要近期数据可以把这个日期往后调同步速度会快很多。注意这个字段只在首次同步时生效后期修改不会触发回溯。3.4 发起授权并验证数据流配置文件保存后重启dsh客户端或者直接在插件页面点击重新加载。随后进入Garmin Connect插件详情页点击开始授权。浏览器会弹出Garmin登录页面输入账号密码并完成授权。授权成功后浏览器地址栏跳转到本地回调地址页面显示授权成功dsh内对应的插件状态变为已连接。第一次连接后插件会自动开始全量同步。此时可以打开dsh的日志面板观察同步进度。日志中会输出类似开始同步活动数据 2025-06-01至2025-08-30的提示。同步完成的时间取决于活动数量和网络带宽一般情况下几千条活动会在10分钟内完成。3.5 配置定时任务与告警数据同步本身不需要额外配置定时任务因为插件已经内置了sync_interval_minutes定时调度。但有一个场景需要提醒如果你希望把Garmin数据推送到其他系统比如另一个数据仓库dsh支持在插件内注册数据变更钩子。钩子配置示例hooks: on_sync_completed: - type: webhook url: http://你的服务器地址/api/garmin/sync headers: Authorization: Bearer 你的Token每次同步结束后插件会向这个地址发送一个POST请求携带本次同步的数据摘要。利用这个钩子可以做跨系统的数据联动避免定时任务互相等待。4. 踩坑实录与排查技巧4.1 授权成功后立即失效现象授权流程走完页面显示成功但dsh内插件状态依然显示未连接日志中出现token exchange failed。排查过程这个问题在0.1.13之前比较常见通常不是令牌本身的问题而是本地回调服务启动时机太晚。授权过程中Garmin服务器回调到本地端口dsh必须先监听端口才能收到回调。如果插件加载时就启动监听一般没问题但如果是手动触发授权且插件刚启动监听器尚未就绪回调包就会被系统丢弃。0.1.13里这个逻辑已经被改为授权动作触发时主动确保监听器已启动并等待超时10秒。如果你还遇到授权后立即失效检查一下系统防火墙是否拦截了本地8765端口的回环连接尤其是Windows系统上某些安全软件会把任何尝试监听回环地址的行为当作风险项拦截。4.2 活动数据延迟很久才出现现象Garmin手表上已经可以看到新记录的活动但dsh面板里迟迟不出现。原因分析Garmin设备本身的数据上传链路有一个时间窗口。多数Garmin设备在运动结束后需要通过Garmin Connect Mobile手机App或Wi-Fi连接把活动数据传输到云端。如果设备没有联网活动记录只存在设备本地。等设备联网上传后Garmin云端接口侧才会暴露这条新记录。dsh插件按照固定间隔去云端拉取两次拉取之间的时间差是数据可达性的第一步延迟。如果使用手机端同步Garmin云端本身还会有几秒到几十秒的处理延迟所以最终展示时间通常比设备记录时间晚1到5分钟偶尔高峰期可能晚半小时。解决建议如果你对实时性要求高可将sync_interval_minutes调小到10分钟但不建议低于这个值太频繁的请求容易触发Garmin接口限流到时整个插件被临时封禁就得不偿失了。另外尽量保持Garmin手表使用官方方式同步不要绕过官方套件用第三方协议写入那样可能导致部分数据字段缺失。4.3 睡眠数据出现空记录现象同步完成其他数据正常但睡眠列表中某几天是空的日志显示no sleep data found in records。排查过程这不是bug而是Garmin API的一个已知行为睡眠数据只有在出现入睡和醒来两个明确状态时才会生成完整记录。如果某天设备没佩戴或者佩戴过但一直处于静止状态例如午睡API可能只返回一个不带睡眠阶段的简化记录甚至不返回。边界情况如果你在Garmin设置里开启了睡眠自动检测夜间睡眠中断后醒来一段时间再入睡Garmin会把这段拆成两条记录。dsh插件会把两条记录分别入库而不是合并成一条。目前插件还没有做同夜间多条数据的合并逻辑这属于已知限制。如果确实需要合并可以在钩子脚本里自行处理。4.4 同步超时与接口限流现象日志中出现request failed with status 429或sync timeout after 60s。原因429状态码代表你的请求频率触发了Garmin接口限流。dsh插件默认并发请求数量是2理论上不太容易触发限流。但如果你手动强制全量同步的同时又改小了同步间隔可能会在短时间内产生大量请求。应对措施恢复默认同步间隔30分钟。全量同步和定时同步不要同时进行等待当前同步任务完成后再触发下一次。检查是否存在多个dsh实例同时连接同一个Garmin账号。两个实例用同一组Client ID和Client Secret分别进行OAuth授权会生成两组令牌但这并不影响数据源权限。不过如果两个实例同时做全量同步限流概率会直线上升。超时问题则更可能出在网络链路上。建议在同步期间不要使用某些会强制改写网络路径的流量工具保证dsh进程能直连Garmin服务。网络环境本身受限的话会在请求阶段直接卡住日志不会显示任何HTTP状态码只有连接超时的异常。这里有个实用排查方法在配置文件中临时把sync_interval_minutes调大到120再把日志级别调到DEBUG观察一轮完整的同步过程。如果每次都在固定的第X个请求超时说明是特定接口的网络路由问题如果是随机超时则几乎可以断定是整体网络不稳定。4.5 本地数据库文件损坏现象dsh启动时插件加载失败提示数据库无法读取。这个虽然不常见但遇到过。dsh本地存储采用嵌入式数据库如果进程被强制杀死例如系统断电或手动kill而恰好写入事务未完成可能造成文件损坏。0.1.13版本在数据库打开时增加了完整性校验一旦校验失败会自动将损坏文件备份为.bak并重建新库。如果你不想让数据完全重来可以把备份文件交给某开发者尝试用专门的工具修复。不过说实话健康数据同步类应用本地库的内容基本都能从Garmin端重新同步回来与其花时间修复不如直接重建后做一次全量同步。这个过程损失的只是时间不是数据。4.6 插件版本与主客户端版本不匹配最后提一个容易被忽略的兼容性问题。dsh插件市场里的插件版本通常和主客户端版本保持同步更新但如果你用的是长期不更新的dsh旧版本插件市场的规则默认禁止安装需要更高客户端版本的插件。如果你执意绕过这个检查手动把插件包放到插件目录里轻则插件功能异常重则整个客户端启动崩溃。这次0.1.13补丁对插件加载器改动较大强烈建议不要用0.1.12客户端去加载0.1.13的插件。我去翻过GitHub上的issue区已经有用户遇到这种情况报错信息五花八门浪费了不少时间。5. 一些值得说的使用经验5.1 多设备用户的隐藏福利如果你手里有不止一台Garmin设备这个插件在同步时会自动合并所有设备的数据。也就是说早上戴A手表跑步下午换B手表记录日常活动两边的数据都会在dsh面板里出现在同一条时间线下不需要手动切换设备维度。不过要注意如果两台设备在同一时间段都戴在身上可能会出现重复计步的情况。比如一块手表戴左手、一块手表戴右手两个设备各自记录步数同步后dsh会把两个数据源相加导致当天总步数虚高。Garmin云端原本是知道如何处理单一账号多设备叠加的但dsh插件在拉取时拿到的是两个独立的设备记录流不会主动去重同时间的步数。只能手动在Garmin端禁用其中一台设备的计步功能或者接受这种重复计量。5.2 数据回写能力现状有不少人问这个插件能不能把dsh里的数据写回Garmin Connect比如在dsh里手动记录体重并回传给Garmin。目前答案是不能。0.1.13版本的Garmin插件是只读方向的设计API层面虽然支持写入体重和血压但插件出于稳定性考虑没有开放这个入口避免误覆盖Garmin云端数据。如果你确实需要回写可以等后续版本或者自己改插件源码扩展一个写入模块。5.3 从0.1.13开始关注日志习惯这次更新之后我养成了一个习惯查看插件日志时重点关注两个标记。一个是sync-ground-truth它表示本地数据与Garmin云端的差异值另一个是token-refresh-attempts它用来统计令牌刷新尝试次数。如果token-refresh-attempts在短短几个小时的运行里超过了10次说明某个调用链上令牌生命周期管理有问题建议在官方渠道反馈并附上日志。这样的日志习惯能帮你提前发现潜在问题总比等某天数据完全同步不了再临时排查要好。按我这几年的经验数据聚合类客户端的插件质量往往不在功能多不多而在边界情况处理得细不细。0.1.13对Garmin Connect插件的这轮修补属于典型的把坑填平、让日常使用更省心的操作。如果你之前因为授权麻烦或者同步不稳而搁置了dsh这次补丁后的体验应该能让你改变看法。