# Anpheros 中的可穿戴设备数据 (Apple Health, Health Connect)

> 将智能手表、智能戒指和智能体重秤数据导入 FHIR 记录：哪些设备连接到哪些健康存储，LOINC 映射表，每日摘要和经过测试的 upsert。

Source: https://developers.anpheros.com/zh/guides/wearable-data-fhir

**要将来自智能手表、智能戒指或智能体重秤的数据导入医疗记录，请在手机上从操作系统的健康存储（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](https://developers.anpheros.com/zh/guides/samsung-health-fhir) |
| Pixel Watch, Fitbit | 无官方同步 | 是，通过 Fitbit 应用 ([Google 帮助](https://support.google.com/fitbit/answer/14506680)) | |
| Garmin | 是，在每次设备同步后通过 Garmin Connect | 是，Garmin Connect 支持 Android 14 及更高版本的 Health Connect ([Android Developers, August 2025](https://developer.android.com/health-and-fitness/community/newsletters/2025/08)) | Garmin 特有的分数（如 Body Battery）不共享 |
| Oura | 是 | 是 ([Oura](https://ouraring.com/blog/data-sharing-partner-integrations/)) | Readiness 和其他 Oura 分数保留在 Oura 应用中 |
| Withings | 是，通过 Health Mate | 是 ([Android Developers blog](https://android-developers.googleblog.com/2023/03/withings-reduces-data-sync-code-with--health-and-fitness-api-health-connect.html)) | 智能秤、血压计、手表 |

两个实际的后果。首先，您的应用取决于用户在其供应商应用中开启了共享功能；入门指南应说明这一点。其次，健康数据存储可能包含来自两个来源（手机和手表）的相同步数数据；请读取健康数据存储的每日总数，而不是累加样本数据，因为 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 验证器](https://developers.anpheros.com/tools/fhir-validator)）：

```json
{
  "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，在您的后端：

```ts
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](https://developers.anpheros.com/zh/guides/apple-health-fhir)，包含每日统计数据的 Swift 代码。
- **Android、Wear OS、Fitbit、Garmin、Oura、Withings:** [Health Connect 转 FHIR](https://developers.anpheros.com/zh/guides/health-connect-fhir)，包含可在两个平台上运行的 Flutter 代码。
- **Samsung Galaxy Watch 和 Samsung Health:** [Samsung Health 转 FHIR](https://developers.anpheros.com/zh/guides/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](https://developers.anpheros.com/zh/guides/wearable-apis)
- [健康追踪器 API](https://developers.anpheros.com/zh/guides/health-tracker-api)：在获得同意的情况下，将追踪器数据存储在服务器上
- [如何构建个人健康记录应用](https://developers.anpheros.com/zh/guides/personal-health-record-app)
- [医疗保健集成](https://developers.anpheros.com/zh/guides/healthcare-integrations)：设备、实验室、诊所和应用
- [FHIR 代码系统](https://developers.anpheros.com/zh/guides/fhir-code-systems)：LOINC 和 UCUM
- [医疗数据溯源详解](https://developers.anpheros.com/zh/guides/medical-data-provenance)
- [如何规范化医疗数据](https://developers.anpheros.com/zh/guides/normalize-medical-data)
- [使用 FHIR 进行远程患者监控](https://developers.anpheros.com/zh/guides/remote-patient-monitoring-fhir)：单次读数、Webhooks 和警报
