这次用 Mac 给自己的 Apple Watch Series 9 开启了高血压通知。过程是先通过 USB 给配对的 iPhone 提供香港定位,再进入“健康”完成设置,最后停止定位并确认恢复。
最终检查了三件事:地图蓝点确实到了香港;健康 App 显示高血压通知已设置;停止程序后,地图回到真实位置,通知仍然开启。认证和定位发送都在本机完成,没有使用 AnyGo 云端。
下面按本次实际操作整理,适合愿意使用终端、能自行判断错误输出的读者。
这是一次个人设备实测,不保证其他系统版本、销售地区或后续更新仍然有效。成功完成设置,也不能证明后续一定会收到通知。苹果说明该功能按 30 天周期分析心率传感器数据,并非直接测量血压,也不能用于诊断或管理高血压。资格条件和功能说明以苹果官方文档为准。
实测环境与前提
| 项目 | 本次环境 |
|---|---|
| Mac | Apple 芯片,macOS 27.0.1 |
| iPhone | iPhone 16 Pro Max,iOS 27.2 |
| Apple Watch | Series 9,已与这台 iPhone 配对 |
| watchOS | 本次没有单独记录准确版本 |
| 连接方式 | USB 数据线,iPhone 已解锁并信任 Mac |
| 构建环境 | JDK 21、Python 3.12、uv、Git |
| 目标位置 | 香港中环附近,北纬 22.2793、东经 114.1628 |
先检查正常开通条件:苹果列出的机型包括 Series 9;还需符合年龄等资格条件、开启手腕检测,并满足当地功能可用性要求。设置时按真实情况填写健康问题,不要为了通过向导而修改既往诊断等信息。
如果在“健康”里已经可以正常开通,直接走官方设置即可。本文记录的是通过临时香港定位完成设置的实验路径。
这套方法做了什么
Mac 通过系统的 USB 配对服务打开 iPhone 的 com.apple.carkit.service,在这个连接上协商 iAP2,完成配件声明和 MFi-v3 认证。手机请求外部定位后,程序每秒发送一对香港位置的 GPGGA、GPRMC 句子。
这里参考了 PlayPort 的协议代码。PlayPort 本身是无线 CarPlay 接收器,README 明确说明不支持有线 USB CarPlay;本文另写了 USB CarKit 桥接程序,复用它的协议模块。单独安装并启动 PlayPort,不能直接得到本文的 USB 定位功能。
本地认证需要配件证书与匹配的私钥。本次使用的是 DiPlay v0.2.7 APK 中的实验性认证数据。上游说明这组数据来自公开 Carlinkit 固件,并非为本文程序新签发的凭据,未来可能被拒绝或撤销。源码附件不包含这些文件,也不要把它们重新打包发布。
准备源码与环境
1. 下载桥接程序
下载本文附件:本地 USB 定位桥接程序源码。
压缩包包含本次使用的 Kotlin 程序、Python 桥接、构建配置、依赖版本清单和两项本地测试,不含 APK、证书、私钥或设备配对记录。
下载后,在终端进入下载目录并校验:
cd ~/Downloads
shasum -a 256 s9-local-location-source-20261015.zip
应得到:
12726513840d0c8e0b94069f71695ccca00d85cdedc683986e6d68f247bde02e
解压并进入项目目录:
unzip s9-local-location-source-20261015.zip
cd s9-local-location
后面的命令都在这个目录执行。本文命令面向 Apple 芯片 Mac,随附启动脚本使用 /opt/homebrew 下的 JDK 路径。
2. 安装运行依赖
Mac 需要先有 Homebrew 和 Git;git --version 若提示安装命令行工具,先完成系统提示的安装。
brew install openjdk@21 uv
uv venv .venv --python 3.12
uv pip install --python .venv/bin/python -r requirements.lock.txt
只为当前终端设置 Java 环境,不需要改系统 Java 配置:
export JAVA_HOME="$(brew --prefix openjdk@21)/libexec/openjdk.jdk/Contents/Home"
"$JAVA_HOME/bin/java" -version
3. 准备固定版本的协议源码
mkdir -p vendor
git clone https://github.com/youcci/playport.git vendor/playport
git -C vendor/playport checkout --detach 9a0882dd0ffe48e467b59d58b12d81391df55ade
固定提交是为了对应本次实测代码,避免上游后续修改带来接口差异。这一步只取协议依赖,不需要启动 PlayPort 的无线 CarPlay 服务或构建它的网页端。
准备本地认证文件
下载本次使用的 APK:
curl -fL --retry 1 -o DiPlay-0.2.7.apk \
https://github.com/shihabal3amri/DiPlay/releases/download/v0.2.7/DiPlay-0.2.7.apk
shasum -a 256 DiPlay-0.2.7.apk
本次文件的 SHA-256:
27b434462da7e78b1ff0987d129ed0a4b0a0e56beceaf8ec221d47150d4675bd
校验一致后,只提取指定目录:
mkdir -p identity/offline-mfi
chmod 700 identity identity/offline-mfi
unzip -j DiPlay-0.2.7.apk 'assets/offline-mfi/*' -d identity/offline-mfi
chmod 600 identity/offline-mfi/certificate.p7b identity/offline-mfi/identity.pk8
chmod 600 DiPlay-0.2.7.apk
目录中应有:
identity/offline-mfi/
├── certificate.p7b
└── identity.pk8
文件名和目录结构要保持一致。程序只在本机读取它们,用私钥签署手机发来的认证挑战;博客不提供私钥内容。
编译并检查手机连接
先构建并运行测试:
vendor/playport/gradlew -p . installDist test --console=plain
./run-local.sh --self-test
本次上游协议测试 89 项、本地 NMEA 封装测试 2 项全部通过。自检也确认了证书和私钥匹配,能够产生 MFi-v3 签名。
这里的 PASS 只证明本地文件和编码检查通过。是否被 iPhone 接受,要看连接后的认证结果。
把与手表配对的 iPhone 用数据线接到 Mac,解锁,并在出现提示时选择“信任”。检查设备数量:
.venv/bin/python carkit_bridge.py list
本次输出:
{ "usb_iphone_count": 1 }
程序要求恰好连接一台 USB iPhone。数量为 0 时先检查线缆、解锁状态与信任提示;连接多台时,先断开其他设备。
开始发送香港定位
运行 10 分钟的定位会话:
./run-local.sh 600
保持这个终端窗口打开。600 是会话秒数,可以随时用 Ctrl+C 提前停止;到时也会自动关闭连接。运行期间,其他依赖系统定位的应用也可能采用临时位置,完成后应先确认真实定位恢复。
正常连接后会依次看到类似输出,以下省略时间戳:
Local com.apple.carkit.service opened
iAP2 link ready
Identification ACCEPTED
MFi-v3 authentication ACCEPTED by iPhone
iPhone requested accessory location; parameter IDs=[0, 1, 32769, 2, 32770]
HK NMEA sent: count=1; Maps/Health acceptance still requires observation
其中认证成功和 HK NMEA sent 都不能代替地图验证。手机也可能暂时停止定位订阅,程序会等待它再次请求,而不是无条件发送。
接着在 iPhone 上操作:
- 打开苹果“地图”。
- 进入“设置 → 隐私与安全性 → 定位服务”。
- 关闭定位服务,等待 10 秒,再重新开启。
- 返回地图,点“当前位置”,确认蓝点是否到了香港。
本次生效后的地图如下,蓝点位于香港中环附近:
如果蓝点仍在真实位置,就还没有完成这一步。此时先排查定位,不要仅凭终端的发送计数判断已成功。
在健康中完成设置
确认香港定位后,保持程序运行,在配对的 iPhone 上进入:
健康 → 右上角头像 → 健康清单 → 高血压通知
按真实情况回答年龄、是否曾确诊高血压等问题,完成后续说明页面。
本次最终显示“高血压通知功能软件已设置”:
如果仍提示所在地区不支持,记录具体提示和系统版本。本次结果不能推导成所有型号、版本或地区都能开通,也不要修改健康资料来绕过资格问题。
停止程序并验证恢复
完成设置后,回到 Mac 的终端按 Ctrl+C,等待退出。程序会关闭自己持有的 CarKit 连接。
本次停止时累计发送了 92 对 NMEA,日志包含:
STOPPED: CarKit closed, sent=92. Real-location restoration and Health state need on-device verification.
CarKit closed; accessory location feed stopped
可以检查相关进程是否还在:
pgrep -fl 'LocalLocationKt|carkit_bridge.py bridge'
相关进程退出后,再在手机上完成两项检查:
- 打开地图并点“当前位置”,确认蓝点恢复真实位置。
- 回到健康清单,确认高血压通知仍显示开启。
本次两项都已确认。若位置还停留在香港,先断开 USB、重新打开地图;仍未恢复时,可以重新开关定位服务或重启 iPhone,恢复后再检查健康设置。后面这些是恢复排查步骤,本次没有用到重启手机。
程序停止、真实定位恢复、通知保持开启,是三个需要分别验证的状态。
本次遇到的两个问题
认证通过,但地图没有变化
初版直接复用了上游生成的 CRLF 拼接字符串,把 GPGGA 和 GPRMC 一起放进一个以 NUL 结尾的参数。日志显示认证成功、开始发送,但地图仍然显示真实位置。
修正版做了两处调整:每条 NMEA 句子单独封装,分别以 NUL 结尾;为 RMC 补上自主定位模式字段,并重新计算校验和。之后重新连接,再关闭定位服务 10 秒并开启,地图才显示香港。
附件包含的是修正版。因为代码修正和定位服务重启共同发生,不能单独认定某一项就是决定因素。
定位服务收到数据,仍有筛选失败日志
手机日志确认 locationd 收到了 NMEA,同时也出现过:
GPRMC NMEA Screening failed
检查时 Mac 与手机时钟仅差约 0.25 秒,句子时间戳也接近当前时间,未发现明显时钟偏差。这个日志的具体原因没有完全确定;即使地图后来已显示香港,也不能据此断言所有发送的数据都被定位服务采用。
因此排查时应逐层确认:手机是否接受认证、是否请求定位、地图是否改变、健康是否完成设置。最后两项需要看手机界面。
清理实验文件
我在确认真实定位恢复、通知仍开启后,删除了 APK、提取的证书与私钥、虚拟环境、上游源码副本及构建缓存,只保留操作记录和自编代码。
读者完成后,也可以在确认程序退出后清理这个独立实验目录。APK 同样包含认证数据,不要只删除解压出的私钥而留下 APK,更不要把整个目录提交到公开仓库。
JDK、uv 等工具如果原本就已安装,继续保留即可;只有确认是此次新装、且没有其他软件使用时才考虑卸载。不要为清理一次实验而删除整个 ~/.gradle 或共享 Python 缓存。
后续如果系统更新、重新配对或功能状态变化,需要重新检查“健康”中的状态。本次恢复后的开关保持开启,不等于长期永久有效。
参考资料
本文附件代码按 GPL-3.0 提供;所用协议模块来自 PlayPort、DiPlay 与 xcertplay,认证数据不属于该源码许可证。