AnpherosAnpheros PlatformDevelopers

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 使用自己的代码系统,而不是假装两者相同。

摘要,而非所有样本

手表每天会产生数千个心率样本。病历需要的是人或医生能够阅读的内容:

作为 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 代码所做的那样。

在手机上读取数据

值得遵循的规则

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;它们测量不同的内容,需要不同的代码。

相关资源

Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card

Product updates by email

New features, guides and the end of the beta. One email a month at most.

Send me occasional Anpheros Platform updates by email (new features, API changes, guides). I'll confirm by email and can unsubscribe in one click from any email. Privacy