onResult
Optional callback prop on <BTProvider>. Fires after the SDK has parsed a complete measurement from a connected device's GATT characteristics.
This is the callback you wire to your data layer — persisting Observations to FHIR, syncing to a backend, updating local state.
Signature
import type { DeviceData, MeasurementTypeForDevices } from "@ovok/native";
import type { IntegratedDevices } from "@ovok/native";
export interface ResultCallback<T extends IntegratedDevices> {
deviceData: DeviceData;
data: Omit<MeasurementTypeForDevices<[T]>, "caseType">;
}
onResult?: (data: ResultCallback<T[number]>) => void;
T is the tuple you pass as acceptedDevices — the callback is typed against the union of all device kinds you accept, so data.data is a discriminated union you can switch on.
Measurement type per device
The SDK's source-of-truth map is src/modules/bt-management/types/device-measurement-map.ts. Currently:
| Device kind | data.data shape |
|---|---|
IntegratedDevices.F4 | BodyWeightMeasurement (from @ovok/core) |
IntegratedDevices.BP2 | BloodPressureMeasurement | EcgMeasurement |
IntegratedDevices.SPO2 (Oxyfit) | PulseOximeterMeasurement |
IntegratedDevices.PC60WF (OxySmart) | PulseOximeterMeasurement |
Other IntegratedDevices.* entries | never — devices are connectable but don't emit onResult yet |
The full measurement type definitions live in @ovok/core and are documented per measurement family. The caseType discriminator is stripped before reaching your callback — you don't need it.
Usage
import React, { useCallback } from "react";
import { BleManager } from "react-native-ble-plx";
import { BTProvider, IntegratedDevices } from "@ovok/native";
const bleManager = new BleManager();
const acceptedDevices = [IntegratedDevices.BP2, IntegratedDevices.SPO2] as const;
function MyApp() {
const handleResult = useCallback(async (data) => {
// data.deviceData = { id, name, localName, sn, manufacturerData, measurementTypes }
// data.data = parsed measurement for the matched device kind
switch (data.deviceData.name) {
case IntegratedDevices.BP2:
// data.data is BloodPressureMeasurement | EcgMeasurement
if ("systolic" in data.data) {
await persistBp(data.data, data.deviceData);
} else {
await persistEcg(data.data, data.deviceData);
}
break;
case IntegratedDevices.SPO2:
// data.data is PulseOximeterMeasurement
await persistSpo2(data.data, data.deviceData);
break;
}
}, []);
return (
<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
onResult={handleResult}
// ... other props
>
<YourScreens />
</BTProvider>
);
}
Timing relative to other callbacks
For a typical BP2 reading the sequence is:
onDeviceFound— peripheral matchesacceptedDevices, your handler connects + subscribes.onDeviceStatusChanged→Connected.onDeviceStatusChanged→Measuring(carriesmeasurementTypeKey).onResult— cuff finishes inflating + measuring, parsed result delivered.onDeviceStatusChanged→Connected(back to idle-but-connected).
If the cuff fails to read a stable measurement, onResult is skipped and onError fires instead.
Persisting to FHIR
@ovok/core exposes resource hooks (useCreate, useUpdate) and the OvokClient instance via useClient(). Convert the parsed measurement to an Observation shape and POST:
import { useClient } from "@ovok/core";
const client = useClient();
const handleResult = useCallback(async (data) => {
await client.create({
resourceType: "Observation",
status: "final",
subject: { reference: `Patient/${currentPatientId}` },
device: { reference: `Device/${data.deviceData.sn}` },
// ...map data.data fields onto LOINC codes for the measurement type
});
}, [client, currentPatientId]);
For multi-component measurements (BP carries systolic + diastolic + pulse, BP2 ECG carries a waveform), persist them as a single Observation with component[] rather than multiple Observations.
Don't confuse with DeviceStatus.Complete
There is no DeviceStatus.Complete enum value — the DeviceStatus enum has only 4 values. After onResult resolves, onDeviceStatusChanged reports Connected again, not a synthetic "Complete". If your UI needs a "Complete" label, set it in your own state when onResult fires.
Related
- BTProvider — full provider reference
- on-device-found — the upstream
device.connect()+ subscribe call - on-device-status-changed —
Measuringprecedes everyonResult - on-error — failure path when a measurement aborts