Skip to main content

BTProvider

BTProvider is the React entry point for BLE device integration. The app owns the BleManager instance and the persistence or upload policy; the provider owns the scan lifecycle while it is mounted and exposes typed callbacks for discovery, connection, errors, status, and readings.

Minimal integration​

import { BleManager } from "react-native-ble-plx";
import { BTProvider, IntegratedDevices } from "@ovok/native/bt-management";

const bleManager = new BleManager();
const acceptedDevices = [IntegratedDevices.BP2] as const;

export function BluetoothRoot({ children }: { children: React.ReactNode }) {
return (
<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
onDeviceFound={(device) => device.connect()}
onResult={({ id, deviceData, data }) => {
saveMeasurement({ id, deviceData, data });
}}
>
{children}
</BTProvider>
);
}

Create the manager once and keep the same instance for the app flow. BTProvider starts discovery after permission is available and destroys its BTManager when it unmounts. It does not use React Navigation or useFocusEffect; if a screen needs to pause discovery, use the manager from the runtime or mount the provider at the appropriate app boundary.

Props​

PropTypeDescription
bleManagerBleManagerHost-owned react-native-ble-plx manager.
acceptedDevicesreadonly AcceptedDevice[]Built-in IntegratedDevices values and/or validated defineCustomDevice declarations.
onDeviceFound(device, manager) => Promise<void>Called for a matching peripheral. Call device.connect() when the app wants to connect.
onResult(result) => voidReceives { id, deviceData, data }. id is stable for the same recorded reading.
onError(error) => voidReceives { error, code?, deviceData? }. error is `BluetoothError
onDeviceStatusChanged(change) => voidReceives the device, one of the four DeviceStatus values, and an optional measurement type.
onConnectionEvent(event) => voidReceives connection/disconnection events with the device data.
oneReadingPerConnectionbooleanStops a streaming device after its first delivered reading for that connection.
deliveredEcgFileNamesreadonly string[]Names of Viatom/compatible ECG files already delivered by the app.
autoConnectbooleanConnects a discovered device automatically after the settle window.
autoConnectDelayMsnumberDelay used by automatic connection before selecting a candidate.
onDeviceSelectionRequired({ devices, select }) => voidReceives multiple candidates when automatic connection needs an app selection UI.
resultPoliciesPartial<Record<string, DeviceResultPolicy>>Select all, daily-totals-only, or first-reading-per-kind-per-connection per device; use byMeasurement for per-measurement overrides.
backgroundSyncBackgroundSyncOptionsEnables durable result delivery and platform restoration. See Background sync.
permissionFallback() => ReactNodeOptional replacement UI when permission gating is enabled and Bluetooth permission is unavailable.
gateOnPermissionbooleanWhen true, renders permissionFallback (or the default fallback) instead of children until permission is granted. Defaults to false, so children remain mounted while permission is pending or refused.
onAccessPermissionChanged(granted: boolean) => voidCalled when the permission state changes.
childrenReactNodeThe subtree that consumes the provider/runtime.

onDeviceFound is optional. If the app does not connect a discovered device, the SDK leaves it available to the runtime and does not invent a connection policy. The callback is asynchronous so the app can await connect() and handle its own errors.

When autoConnect is enabled, the manager connects the only candidate found in its settle window. If several candidates are available, onDeviceSelectionRequired receives devices and a select(device) function. The SDK does not choose between the candidates for the app.

<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
autoConnect
autoConnectDelayMs={750}
onDeviceSelectionRequired={({ devices, select }) => {
setCandidates({ devices, select });
}}
>
{children}
</BTProvider>

Permission states​

By default, children remain mounted while permission is pending or refused, and the provider reports access changes through onAccessPermissionChanged. Set gateOnPermission to true when Bluetooth-dependent children must not render without permission. In that mode, the provider renders permissionFallback, or the SDK's DefaultPermissionFallback when no replacement was supplied. A refused device is not repeatedly re-emitted just because the scan continues. Use onAccessPermissionChanged or useBluetoothState to offer a settings action.

Device statuses​

The enum intentionally has only four values:

enum DeviceStatus {
CONNECTED = "Connected",
DISCONNECTED = "Disconnected",
MEASURING = "Measuring",
LOW_BATTERY = "LowBattery",
}

Connecting, Pairing, Ready, Syncing, Complete, Idle, and Error are not SDK status values. If an app needs those labels, derive them from its own connection state, callbacks, and error state.

measurementTypeKey may be present on MEASURING and on the following CONNECTED notification when the device has finished that measurement.

Callback data​

type ErrorCallback = {
error: BluetoothError | Error | string;
code?: BluetoothErrorCode;
deviceData?: DeviceData;
};

type DeviceData = {
id: string;
name: DeviceKey;
localName: string;
manufacturerData: string;
manufacturerName?: string;
model?: string;
sn: string;
measurementTypes: MeasurementTypeKey[];
};

The data object in onResult is an @ovok/core measurement shape selected by the accepted device. The native layer does not add a patient, account, upload, or storage field. Add those in the host application's adapter.

When backgroundSync is enabled, the callback result also includes queueId, queuedAt, and attempt. id identifies the recorded reading and remains the same when that reading is retried; queueId identifies one queue insertion and can change when the same reading is enqueued again.

App lifecycle​

The provider creates its manager in an effect, starts scanning after Bluetooth is powered on, and cleans up the scan, subscriptions, devices, and callbacks on unmount. Do not create a second provider with the same BleManager for the same flow. For background restoration, create the manager with createBackgroundBleManager and pass the same stable restoration identifier in backgroundSync.