Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card
FHIR 中的可穿戴设备数据(Apple Health、Health Connect)
将智能手表、戒指和体重秤数据导入 FHIR 记录:哪些设备连接到哪个健康存储,LOINC 映射表,每日摘要和经过测试的 upsert。
要将来自智能手表、智能戒指或智能体重秤的数据导入医疗记录,请在手机上从操作系统的健康存储(iOS 上的 Apple Health,Android 上的 Health Connect)读取数据,按天进行汇总,将每个指标映射到 LOINC 代码和 UCUM 单位,然后将其作为标记为设备数据的 FHIR R4 Observations 写入。 从手机健康存储读取数据而不是从每个供应商的云读取,可以使一次集成覆盖 Apple Watch、Galaxy Watch、Pixel Watch、Garmin、Fitbit、Oura 和 Withings,只要用户已允许其供应商应用程序共享数据。本页提供了数据路径、可重用的映射表、经过验证的 FHIR 示例以及已在平台上运行过的代码。
数据路径
watch, ring, scale ──► vendor app (Garmin Connect, Fitbit, Oura, Withings, Samsung Health…)
│ shares, with the person's permission
▼
Apple Health (iOS) / Health Connect (Android)
│ your app reads, with the person's permission, on the phone
▼
your app ──► your backend ──► FHIR R4 Observations in the person's record
Apple Health 和 Health Connect 都没有服务器 API:数据只能由手机上的应用程序读取。您的应用程序读取数据,将每日的精简值发送到您的后端,然后后端将其写入记录。
哪些设备可以访问哪些健康数据存储
每个供应商共享的内容会随着应用版本的更新而变化;供应商自己的文档是参考依据。
| 设备或应用 | Apple Health (iOS) | Health Connect (Android) | 备注 |
|---|---|---|---|
| Apple Watch | 是,原生支持 | — | 手表测量到的所有数据都会进入 Apple Health |
| Samsung Galaxy Watch, Samsung Health | — | 部分支持 | Samsung Health 决定导出哪些类型的数据以及导出的频率;请参阅 Samsung Health |
| Pixel Watch, Fitbit | 无官方同步 | 是,通过 Fitbit 应用 (Google 帮助) | |
| Garmin | 是,在每次设备同步后通过 Garmin Connect | 是,Garmin Connect 支持 Android 14 及更高版本的 Health Connect (Android Developers, August 2025) | Garmin 特有的分数(如 Body Battery)不共享 |
| Oura | 是 | 是 (Oura) | Readiness 和其他 Oura 分数保留在 Oura 应用中 |
| Withings | 是,通过 Health Mate | 是 (Android Developers blog) | 智能秤、血压计、手表 |
两个实际的后果。首先,您的应用取决于用户在其供应商应用中开启了共享功能;入门指南应说明这一点。其次,健康数据存储可能包含来自两个来源(手机和手表)的相同步数数据;请读取健康数据存储的每日总数,而不是累加样本数据,因为 Apple Health 和 Health Connect 已经对重叠的来源进行了去重处理。
映射表:健康存储类型 → FHIR Observation
Anpheros Daily 在将可穿戴设备数据写入 FHIR 记录时使用的代码:
| 指标 | Apple HealthKit | Health Connect | LOINC | UCUM 单位 | 类别 |
|---|---|---|---|---|---|
| 心率 | heartRate |
HeartRateRecord |
8867-4 | /min |
vital-signs |
| 静息心率 | restingHeartRate |
RestingHeartRateRecord |
40443-4 | /min |
vital-signs |
| 心率变异性 | heartRateVariabilitySDNN |
HeartRateVariabilityRmssdRecord |
80404-7 (仅 SDNN) | ms |
vital-signs |
| 血氧 (SpO₂) | oxygenSaturation |
OxygenSaturationRecord |
59408-5 | % |
vital-signs |
| 呼吸频率 | respiratoryRate |
RespiratoryRateRecord |
9279-1 | /min |
vital-signs |
| 每日步数 | stepCount |
StepsRecord |
41950-7 | {steps} |
activity |
| 活动能量 | activeEnergyBurned |
ActiveCaloriesBurnedRecord |
41981-2 | kcal |
activity |
| 睡眠时长 | sleepAnalysis (类别) |
SleepSessionRecord |
93832-4; 阶段 93831-6 深睡, 93830-8 浅睡, 93829-0 REM | min |
activity |
| 体重 | bodyMass |
WeightRecord |
29463-7 | kg |
vital-signs |
| 血压 | bloodPressure (相关性) |
BloodPressureRecord |
85354-9 配合 8480-6 和 8462-4 | mm[Hg] |
vital-signs |
| 血糖 (仪表) | bloodGlucose |
BloodGlucoseRecord |
2339-0 | mg/dL |
vital-signs |
| 体温 | bodyTemperature |
BodyTemperatureRecord |
8310-5 | Cel |
vital-signs |
请注意心率变异性这一行:Apple Health 存储 SDNN,Health Connect 存储 RMSSD。它们是不同的统计数据,不得在同一代码下写入。LOINC 80404-7 是 SDNN;对于 RMSSD,Anpheros Daily 使用自己的代码系统,而不是假装两者相同。
摘要,而非所有样本
手表每天会产生数千个心率样本。病历需要的是人或医生能够阅读的内容:
- 每天每个指标一个 Observation,其中
effectivePeriod涵盖该用户的本地日期,valueQuantity中的日平均值或总值,以及(对于速率)作为组件的最小值和最大值。 - 更精细的系列保留在它们有用的地方。 Anpheros Daily 会保留其自身图表的 4 小时心率摘要,并将每日值写入 FHIR 记录。
- 事件是独立的。 不规则心律通知或单次血压读数是其自身的 Observation,具有自己的时间,不属于日平均值的一部分。
作为 FHIR R4 的每日心率摘要(它通过了 FHIR R4 验证器):
{
"resourceType": "Observation",
"status": "final",
"identifier": [{"system": "https://your-app.test/fhir/sid/daily-wearable", "value": "p-123:2026-10-01:heart_rate"}],
"category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "vital-signs"}]}],
"code": {"coding": [{"system": "http://loinc.org", "code": "8867-4", "display": "Heart rate"}]},
"subject": {"reference": "Patient/p-123"},
"effectivePeriod": {"start": "2026-10-01T00:00:00-04:00", "end": "2026-10-02T00:00:00-04:00"},
"valueQuantity": {"value": 71.4, "unit": "/min", "system": "http://unitsofmeasure.org", "code": "/min"},
"component": [
{"code": {"text": "minimum"}, "valueQuantity": {"value": 52, "unit": "/min", "system": "http://unitsofmeasure.org", "code": "/min"}},
{"code": {"text": "maximum"}, "valueQuantity": {"value": 138, "unit": "/min", "system": "http://unitsofmeasure.org", "code": "/min"}}
],
"device": {"display": "Apple Watch (via Apple Health)"}
}
写入每日值而不重复
手机每天同步数次,一天的最后一个值在午夜前会不断变化。为每个 Observation 赋予一个稳定的标识符——用户、日期、指标——并更新它而不是创建新的。使用 Anpheros TypeScript SDK,在您的后端:
import { Anpheros, apiKey } from '@anpheros/sdk';
const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) });
// What the phone sends: one value per metric per local day
type DailyValue = { metric: 'heart_rate' | 'steps'; day: string; value: number; min?: number; max?: number };
const CODES = {
heart_rate: { code: '8867-4', display: 'Heart rate', unit: '/min', category: 'vital-signs' },
steps: { code: '41950-7', display: 'Number of steps in 24 hour Measured', unit: '{steps}', category: 'activity' },
} as const;
// One Observation per person, metric and day: re-sending a day updates it instead of adding a copy.
async function upsertDaily(patientId: string, v: DailyValue, source: string) {
const c = CODES[v.metric];
const identifier = { system: 'https://your-app.test/fhir/sid/daily-wearable', value: `${patientId}:${v.day}:${v.metric}` };
const resource: any = {
resourceType: 'Observation', status: 'final',
identifier: [identifier],
category: [{ coding: [{ system: 'http://terminology.hl7.org/CodeSystem/observation-category', code: c.category }] }],
code: { coding: [{ system: 'http://loinc.org', code: c.code, display: c.display }] },
subject: { reference: `Patient/${patientId}` },
effectivePeriod: { start: `${v.day}T00:00:00Z`, end: `${v.day}T23:59:59Z` },
valueQuantity: { value: v.value, unit: c.unit, system: 'http://unitsofmeasure.org', code: c.unit },
};
if (v.min !== undefined && v.max !== undefined) {
resource.component = [
{ code: { text: 'minimum' }, valueQuantity: { value: v.min, unit: c.unit, system: 'http://unitsofmeasure.org', code: c.unit } },
{ code: { text: 'maximum' }, valueQuantity: { value: v.max, unit: c.unit, system: 'http://unitsofmeasure.org', code: c.unit } },
];
}
const found = await anpheros.fhir.search('Observation', { identifier: `${identifier.system}|${identifier.value}` });
const existing = found.entry?.[0]?.resource;
return existing
? anpheros.fhir.update({ ...resource, id: existing.id }, { author_type: 'device' })
: anpheros.fhir.create(resource, { author_type: 'device', source_system: source });
}
在本地平台实例上使用沙盒密钥运行,两天内的两个值,随后是同一天步数的同步,留下了 两个 Observation,步数 Observation 版本为 2,包含新值和 provenance v1 device apple-health · v2 device。历史记录保留了早期值,每个读取者都能看到数据来自设备,而非临床医生。
在生产环境中,请使用用户时区中的实际日期边界(上面的示例为了简洁使用了 UTC),正如平台页面上的 HealthKit 和 Health Connect 代码所做的那样。
在手机上读取数据
- iPhone 和 Apple Watch: Apple Health (HealthKit) 转 FHIR,包含每日统计数据的 Swift 代码。
- Android、Wear OS、Fitbit、Garmin、Oura、Withings: Health Connect 转 FHIR,包含可在两个平台上运行的 Flutter 代码。
- Samsung Galaxy Watch 和 Samsung Health: Samsung Health 转 FHIR:哪些数据会进入 Health Connect,哪些需要使用 Samsung 自有的 SDK。
值得遵循的规则
- 只读取您需要的数据,并单独请求每种类型的数据;用户会授予部分权限。
- 标记数据来源。 消费者可穿戴设备并非诊断设备。将其数据标记为设备数据 (
author_type: device),并注明来自哪个应用程序,以便临床医生能够据此进行权衡。 - 遵守平台规则。 Apple 的 App Review Guidelines (5.1.3) 禁止将健康数据用于广告宣传,以及将个人健康信息存储在 iCloud 中;Google Play 要求使用 Health Connect 的应用程序进行健康应用声明。
- 允许用户断开连接,并决定已复制的数据是否保留在记录中。
Anpheros 的功能与局限
Anpheros 平台本身不直接连接 Apple Health、Health Connect 或任何供应商云:您的应用程序读取手机上的数据,并通过 API 写入。Anpheros 自有的应用程序 Anpheros Daily 正是这样做的:它读取 iPhone 上的 Apple Health 和 Android 上的 Health Connect,保留每日摘要,并使用上述代码将其写入用户的 FHIR 记录。连接设备是其 Plus 和 Max 计划的一部分。
常见问题解答
如何将 Apple Watch 数据导入 FHIR 服务器?
使用 HealthKit 从 iPhone 上的 Apple Health 读取数据(没有服务器 API),按天汇总,然后让您的后端将每个指标写入一个 Observation,并附带 LOINC 代码和 UCUM 单位,标记为设备数据。
Health Connect 或 Apple Health 是否有云 API?
没有。两者都将数据保留在手机上;安装在手机上的应用程序会在获得用户许可后读取数据。Garmin、Fitbit 或 Oura 等供应商云有自己的 API,但读取手机的健康存储可以涵盖其中大部分,只需一次集成。
应该为步数使用哪个 LOINC 代码?
41950-7,“24 小时测量步数”,每日 effectivePeriod。对于心率使用 8867-4,对于 SpO₂ 使用 59408-5,对于睡眠时长使用 93832-4。
我应该将每个心率样本都存储在 FHIR 中吗?
通常不。日均值、最小值和最大值的每日摘要是用户或临床医生阅读的内容;单独的读数仅在它们本身有意义时才属于记录,例如不规则心律事件。
我可以使用一个代码来表示 Apple Health 和 Health Connect 的 HRV 吗?
不可以。Apple Health 报告 SDNN,Health Connect 报告 RMSSD;它们测量不同的内容,需要不同的代码。
相关资源
- 可穿戴设备 API:Fitbit、Garmin、Oura 和 Withings
- 健康追踪器 API:在获得同意的情况下,将追踪器数据存储在服务器上
- 如何构建个人健康记录应用
- 医疗保健集成:设备、实验室、诊所和应用
- FHIR 代码系统:LOINC 和 UCUM
- 医疗数据溯源详解
- 如何规范化医疗数据
- 使用 FHIR 进行远程患者监控:单次读数、Webhooks 和警报
Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card