# Удаленный мониторинг пациентов с использованием FHIR

> От показаний артериального давления на дому до оповещений: наблюдения устройства, подписанные веб-хуки, идемпотентное правило и то, что остается за пределами слоя данных. Протестированный код.

Source: https://developers.anpheros.com/ru/guides/remote-patient-monitoring-fhir

**Удаленный мониторинг пациентов (RPM) собирает измерения, которые пациент проводит дома — артериальное давление, вес, уровень глюкозы, насыщение кислородом — и позволяет команде по уходу видеть их и реагировать на них между визитами.** Часть, связанная с данными, состоит из четырех шагов: внести каждое измерение в запись пациента как кодированное наблюдение, атрибутированное устройством; реагировать при поступлении нового измерения; решить с помощью четкого правила, нуждается ли оно во внимании; и вести учет того, что сделала команда. Эта страница описывает создание этого конвейера на основе HL7 FHIR R4 с использованием веб-хуков Anpheros и TypeScript SDK, с кодом, который был запущен на платформе, и перечисляет то, что остается за пределами слоя данных.

## Конвейер

```
 cuff, scale, glucose meter, pulse oximeter
        │ (Bluetooth / vendor app / Apple Health / Health Connect)
        ▼
 patient's phone app ──► your backend ──► FHIR Observation (author: device) in the patient's record
                                                     │ webhook: resource.created (signed)
                                                     ▼
                                     your monitoring service: rule → alert → care team
                                                     │
                                     note, call, CarePlan change, next appointment
```

Три фактора определяют, будет ли это работать на практике:

- **Одно измерение, одно наблюдение (Observation).** Каждое измерение манжеты является наблюдением (Observation) с кодом LOINC 85354-9 и двумя компонентами (8480-6 систолическое, 8462-4 диастолическое), временем измерения и `author_type: device`. В отличие от ежедневных сводок носимых устройств, показания RPM являются индивидуальными измерениями, которые клиницист может просматривать по одному.
- **Реагировать на события, а не на таймер.** Вебхук для каждого нового наблюдения (Observation) позволяет правилу запускаться через несколько секунд после получения показаний.
- **Правила для нескольких измерений.** Одно высокое показание часто является шумом; большинство протоколов рассматривают серию. Правило ниже запрашивает три последовательных показания на уровне 140/90 или выше. Этот порог является примером, а не клинической рекомендацией: команда по уходу устанавливает правило для каждого пациента.

## Запись показаний с устройства

Приложение на телефоне (или облако поставщика устройства через ваш бэкенд) записывает каждое показание:

```ts
await anpheros.observations.create(patientId, {
  code: '85354-9', category: 'vital-signs', effective_at: '2026-10-13T07:15:00-04:00', author_type: 'device',
  components: [{ code: '8480-6', value: 152, unit: 'mm[Hg]' }, { code: '8462-4', value: 95, unit: 'mm[Hg]' }],
}, { source_system: 'bp-cuff-app' });
```

Если показания поступают через Apple Health или Health Connect, а не напрямую с устройства, считывайте их на телефоне, как описано в [Данные носимых устройств в FHIR](https://developers.anpheros.com/ru/guides/wearable-data-fhir), но сохраняйте отдельные показания артериального давления как отдельные Observations, а не как ежедневные средние значения.

## Реакция на новые показания

Зарегистрируйте конечную точку веб-перехватчика один раз (`POST /v1/webhooks` с `events: ["resource.created"]`); ответ содержит секретный ключ подписи. Затем запустите небольшой сервис, который проверяет каждую доставку и оценивает правило:

```ts
import { createServer } from 'node:http';
import { Anpheros, apiKey, verifyWebhookSignature } from '@anpheros/sdk';

const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) });
const SECRET = process.env.WEBHOOK_SECRET!;
const seen = new Set<string>();                       // deliveries are at-least-once: de-duplicate by event id
const alerted = new Set<string>();                    // one alert per newest reading, however many events arrive

// Rule: the three most recent home readings all at or above 140/90 → tell the care team once
async function checkBloodPressure(patientId: string, observationId: string) {
  const obs = await anpheros.observations.get(observationId);
  if (obs.code !== '85354-9') return;                  // only blood pressure readings trigger the rule
  const { data } = await anpheros.observations.list(patientId, { code: '85354-9', limit: 3 });   // newest first
  const value = (o: typeof obs, code: string) => o.components.find(c => c.code === code)?.value ?? 0;
  const allHigh = data.length === 3 && data.every(o => value(o, '8480-6') >= 140 || value(o, '8462-4') >= 90);
  if (!allHigh || alerted.has(data[0].id)) return;
  alerted.add(data[0].id);
  console.log(`ALERT patient ${patientId}: last 3 readings ≥ 140/90 →`,
    data.map(o => `${value(o, '8480-6')}/${value(o, '8462-4')}`).join(', '));   // in production: notify the care team
}

createServer(async (req, res) => {
  const chunks: Buffer[] = [];
  for await (const c of req) chunks.push(c as Buffer);
  const body = Buffer.concat(chunks).toString('utf8');
  const ok = await verifyWebhookSignature({ secret: SECRET, header: req.headers['anpheros-signature'] as string, body });
  if (!ok) { res.writeHead(400).end(); return; }       // not from Anpheros, or too old
  res.writeHead(204).end();                            // answer fast; do the work after
  const event = JSON.parse(body);
  if (seen.has(event.id)) return;
  seen.add(event.id);
  const d = event.data;
  if (event.type === 'resource.created' && d.resource_type === 'Observation') await checkBloodPressure(d.patient, d.id);
}).listen(8799);
```

Как мы тестировали: четыре утренних показания (136/86, 144/92, 149/94, 152/95) были записаны синтетическому пациенту на локальном экземпляре платформы, и платформа сгенерировала одно подписанное событие `resource.created` на каждое показание. Эти события, подписанные собственным кодом подписи платформы, были отправлены в вышеуказанный сервис вместе с двумя дубликатами и одним запросом, подписанным неправильным секретным ключом. Результат: неправильная подпись получила `400`, дубликаты были проигнорированы, а сервис выдал ровно одно оповещение — `152/95, 149/94, 144/92` — а не одно на событие.

В производственной среде храните `seen` и `alerted` в базе данных, а не в памяти, чтобы перезапуск не приводил к повторным оповещениям.

**Получение веб-перехватчиков во время разработки.** Anpheros принимает и доставляет только на публичные HTTPS конечные точки: `localhost` или частный адрес отклоняются при регистрации (`400`, поле `url`), даже в песочнице, и каждая доставка повторно проверяет адрес. Чтобы получать события на своем ноутбуке, сделайте сервис доступным через публичный HTTPS туннель и зарегистрируйте адрес туннеля. См. [Веб-перехватчики](https://developers.anpheros.com/ru/guides/webhooks).

## Что должно отображаться в записи впоследствии

Сам алерт находится в вашей службе мониторинга, но запись должна отражать его результат, чтобы следующий клиницист и пациент видели полную картину:

- показания, каждое помеченное как данные устройства с указанием его исходной системы ([provenance](https://developers.anpheros.com/ru/guides/medical-data-provenance));
- `CarePlan` с планом мониторинга (что измеряется, как часто, цель);
- `Encounter` (класс `VR` для удаленного контакта) при звонке пациента командой, и любое изменение медикаментов как `MedicationStatement`.

Anpheros хранит эти ресурсы; у него нет собственных ресурсов для оповещений или задач (`Flag`, `Task` и `Communication` не входят в число его типов ресурсов), поэтому очереди оповещений и маршрутизация дежурных относятся к вашей службе.

## Оплата, регулирование и безопасность

Программы удаленного мониторинга физиологических показателей (RPM) часто зависят от правил возмещения расходов. В Соединенных Штатах Medicare оплачивает удаленный мониторинг физиологических показателей по определенным CPT-кодам с минимальным количеством дней с данными и временем, затраченным командой по уходу. Эти правила меняются, и платформа данных не может их сертифицировать; ознакомьтесь с текущими правилами CMS и сохраняйте доказательства (показания и время работы команды) в своей собственной системе. Программное обеспечение, которое интерпретирует показания для постановки диагноза или назначения лечения, может регулироваться как медицинское изделие. Оставляйте пороговые значения в ведении клиницистов и регистрируйте каждое оповещение и ответ.

## Место Anpheros

Anpheros предоставляет запись (FHIR R4 Observations с указанием источника, CarePlan, Encounter), подписанные веб-хуки с повторными попытками, согласие, если приложение другой организации нуждается в чтении показаний, и журнал доступа, который может просмотреть пациент. Он не подключается к поставщикам устройств, не определяет, что является аномальным, не отправляет оповещения и не занимается выставлением счетов.

## Часто задаваемые вопросы

### Какой ресурс FHIR хранит показания артериального давления в домашних условиях?
Observation с кодом LOINC 85354-9 (панель артериального давления) и двумя компонентами: 8480-6 (систолическое) и 8462-4 (диастолическое), каждый из которых является `valueQuantity` в `mm[Hg]`, с указанием времени измерения и устройства в качестве автора.

### Следует ли использовать ежедневные средние значения или отдельные показания для данных RPM?
Отдельные показания для клинических измерений, таких как артериальное давление, вес или уровень глюкозы, поскольку клиницисты просматривают и подсчитывают их. Ежедневные сводки подходят для высокочастотных данных с носимых устройств, таких как частота сердечных сокращений или количество шагов.

### Как инициировать оповещение при поступлении новых показаний?
Подпишитесь на веб-хуки `resource.created`, проверьте подпись, получите новое Observation и оцените ваше правило по последней серии данных. Доставки могут повторяться, поэтому дедуплицируйте по идентификатору события и сделайте оповещение идемпотентным.

### Может ли Anpheros отправлять оповещения клиницистам?
Нет. Anpheros уведомляет ваш сервис через веб-хуки; кто получает оповещение, как и когда, решается в вашей системе.

## Связанные материалы

- [Данные носимых устройств в FHIR](https://developers.anpheros.com/ru/guides/wearable-data-fhir)
- [Виртуальная помощь и ИИ-врачи: необходимые данные](https://developers.anpheros.com/ru/guides/virtual-care-ai-data)
- [Вебхуки](https://developers.anpheros.com/ru/guides/webhooks)
- [Происхождение медицинских данных: объяснение](https://developers.anpheros.com/ru/guides/medical-data-provenance)
- [Интеграции в здравоохранении](https://developers.anpheros.com/ru/guides/healthcare-integrations)
