# Zdalne monitorowanie pacjentów z FHIR

> Od odczytów ciśnienia krwi w domu po alerty: obserwacje urządzenia, podpisane webhooki, idempotentna reguła i to, co pozostaje poza warstwą danych. Przetestowany kod.

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

**Zdalne monitorowanie pacjentów (RPM) gromadzi pomiary, które pacjent wykonuje w domu — ciśnienie krwi, wagę, poziom glukozy, saturację tlenem — i pozwala zespołowi opieki medycznej je zobaczyć i reagować na nie między wizytami.** Część dotycząca danych obejmuje cztery kroki: wprowadzenie każdego odczytu do dokumentacji pacjenta jako zakodowana obserwacja przypisana do urządzenia; reakcja na przybycie nowego odczytu; podjęcie decyzji za pomocą jasnej reguły, czy wymaga ona uwagi; oraz prowadzenie rejestru działań zespołu. Ta strona buduje ten potok na HL7 FHIR R4 z webhookami Anpheros i TypeScript SDK, z kodem, który został uruchomiony na platformie, i wymienia, co pozostaje poza warstwą danych.

## Potok

```
 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
```

Trzy wybory decydują o tym, czy zadziała to w praktyce:

- **Jedno odczyt, jedna obserwacja.** Każdy odczyt z mankietu to Obserwacja z LOINC 85354-9 i dwoma komponentami (8480-6 skurczowe, 8462-4 rozkurczowe), czasem pomiaru i `author_type: device`. W przeciwieństwie do codziennych podsumowań z urządzeń noszonych, odczyty RPM to indywidualne pomiary, które lekarz może przeglądać pojedynczo.
- **Reaguj na zdarzenia, nie na zegar.** Webhook dla każdej nowej Obserwacji pozwala na uruchomienie reguły sekundy po otrzymaniu odczytu.
- **Reguły dla wielu odczytów.** Pojedynczy wysoki odczyt jest często szumem; większość protokołów analizuje serię. Poniższa reguła wymaga trzech kolejnych odczytów na poziomie 140/90 lub wyższym. Ten próg jest przykładem, a nie poradą kliniczną: zespół opieki ustala regułę dla każdego pacjenta.

## Zapisywanie odczytów z urządzenia

Aplikacja na telefon (lub chmura dostawcy urządzenia, za pośrednictwem Twojego backendu) zapisuje każdy odczyt:

```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' });
```

Jeśli odczyty pochodzą z Apple Health lub Health Connect zamiast bezpośrednio z urządzenia, odczytaj je na telefonie zgodnie z opisem w sekcji [Dane z urządzeń noszonych w FHIR](https://developers.anpheros.com/pl/guides/wearable-data-fhir), ale zachowaj indywidualne odczyty ciśnienia krwi jako pojedyncze Obserwacje, a nie średnie dzienne.

## Reagowanie na nowe odczyty

Zarejestruj punkt końcowy webhooka raz (`POST /v1/webhooks` z `events: ["resource.created"]`); odpowiedź zawiera sekret podpisu. Następnie uruchom małą usługę, która weryfikuje każdą dostawę i ocenia regułę:

```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);
```

Jak testowaliśmy: cztery poranne odczyty (136/86, 144/92, 149/94, 152/95) zostały zapisane dla syntetycznego pacjenta na lokalnej instancji platformy, a platforma wygenerowała jedno podpisane zdarzenie `resource.created` na odczyt. Te zdarzenia, podpisane własnym kodem podpisu platformy, zostały wysłane do powyższej usługi wraz z dwoma duplikatami i jednym żądaniem podpisanym nieprawidłowym sekretem. Wynik: nieprawidłowy podpis otrzymał `400`, duplikaty zostały zignorowane, a usługa podniosła dokładnie jeden alert — `152/95, 149/94, 144/92` — a nie jeden na zdarzenie.

W produkcji przechowuj `seen` i `alerted` w bazie danych, a nie w pamięci, aby restart nie powtarzał alertów.

**Odbieranie webhooków podczas tworzenia.** Anpheros akceptuje i dostarcza tylko do publicznych punktów końcowych HTTPS: `localhost` lub adres prywatny jest odrzucany podczas rejestracji (`400`, pole `url`), również w piaskownicy, a każda dostawa ponownie sprawdza adres. Aby odbierać zdarzenia na swoim laptopie, udostępnij usługę przez publiczny tunel HTTPS i zarejestruj adres tunelu. Zobacz [Webhooks](https://developers.anpheros.com/pl/guides/webhooks).

## Co powinien pokazywać rekord po tym

Sam alert znajduje się w Twojej usłudze monitorowania, ale rekord powinien pokazywać, co z niego wynikło, aby kolejny lekarz i pacjent widzieli całą historię:

- odczyty, każdy oznaczony jako dane urządzenia z jego systemem źródłowym ([provenance](https://developers.anpheros.com/pl/guides/medical-data-provenance));
- `CarePlan` z planem monitorowania (co jest mierzone, jak często, cel);
- `Encounter` (klasa `VR` dla kontaktu zdalnego), gdy zespół dzwoni do pacjenta, oraz wszelkie zmiany w lekach jako `MedicationStatement`.

Anpheros przechowuje te zasoby; nie posiada własnego zasobu alertów ani zadań (`Flag`, `Task` i `Communication` nie należą do jego typów zasobów), więc kolejki alertów i routing na dyżurze należą do Twojej usługi.

## Rozliczenia, regulacje i bezpieczeństwo

Programy RPM często zależą od zasad refundacji — w Stanach Zjednoczonych Medicare płaci za zdalne monitorowanie fizjologiczne na podstawie określonych kodów CPT z minimalną liczbą dni z danymi i czasem spędzonym przez zespół opieki. Te zasady się zmieniają i nie są czymś, co platforma danych może certyfikować; sprawdź aktualne zasady CMS i przechowuj dowody (odczyty i czas zespołu) we własnym systemie. Oprogramowanie interpretujące odczyty w celu kierowania diagnozą lub leczeniem może być regulowane jako urządzenie medyczne. Przekroczenia progów powinny pozostać w gestii lekarzy, a każdy alert i reakcja powinny być rejestrowane.

## Gdzie Anpheros pasuje

Anpheros zapewnia rekord (FHIR R4 Observations z proweniencją, CarePlan, Encounter), podpisane webhooki z ponownymi próbami, zgodę, jeśli aplikacja innej organizacji potrzebuje odczytać pomiary, oraz dziennik dostępu, który pacjent może przeglądać. Nie łączy się ze sprzedawcami urządzeń, nie decyduje, co jest nieprawidłowe, nie wysyła alertów ani nie zajmuje się rozliczeniami.

## Najczęściej zadawane pytania

### Który zasób FHIR przechowuje odczyt ciśnienia krwi w domu?
Obserwacja (Observation) z LOINC 85354-9 (panel ciśnienia krwi) i dwoma komponentami, 8480-6 (skurczowe) i 8462-4 (rozkurczowe), każdy jako `valueQuantity` w `mm[Hg]`, z czasem pomiaru i urządzeniem jako autorem.

### Czy dane RPM powinny być średnimi dziennymi czy indywidualnymi odczytami?
Indywidualne odczyty dla pomiarów klinicznych, takich jak ciśnienie krwi, waga lub glukoza, ponieważ lekarze je przeglądają i liczą. Dzienne podsumowania pasują do danych z urządzeń noszonych o wysokiej częstotliwości, takich jak tętno lub kroki.

### Jak wywołać alert, gdy pojawi się nowy odczyt?
Subskrybuj webhaki `resource.created`, zweryfikuj podpis, pobierz nową Obserwację i zastosuj swoją regułę do ostatniej serii. Dostarczenia mogą się powtarzać, więc usuń duplikaty według identyfikatora zdarzenia i spraw, aby alert był idempotentny.

### Czy Anpheros może wysyłać alerty do lekarzy?
Nie. Powiadamia Twój serwis za pomocą webhooków; kto jest powiadamiany, jak i kiedy, jest ustalane w Twoim systemie.

## Powiązane

- [Dane z urządzeń noszonych w FHIR](https://developers.anpheros.com/pl/guides/wearable-data-fhir)
- [Opieka wirtualna i lekarze AI: dane, których potrzebują](https://developers.anpheros.com/pl/guides/virtual-care-ai-data)
- [Webhooks](https://developers.anpheros.com/pl/guides/webhooks)
- [Wyjaśnienie pochodzenia danych medycznych](https://developers.anpheros.com/pl/guides/medical-data-provenance)
- [Integracje opieki zdrowotnej](https://developers.anpheros.com/pl/guides/healthcare-integrations)
