Skip to main content

onDeviceFound

Optional callback prop on <BTProvider>. Fires once for each advertisement that matches your acceptedDevices filter. This is the callback responsible for actually pairing + connecting — the provider hands you a BTManagedDevice, and your handler must call device.connect() to start receiving data.

Signature

import type { BTManagedDevice, BTManager, IntegratedDevices } from "@ovok/native";

onDeviceFound?: <T extends readonly IntegratedDevices[]>(
device: BTManagedDevice<T[number]>,
manager: BTManager<T>,
) => Promise<void>;

device is a typed wrapper around the underlying react-native-ble-plx peripheral. manager is the BT manager instance that owns scanning — most apps never need it; it's available so multi-device flows can stop scanning while pairing.

What device.connect() does

public connect = async () => { ... }

It is the only public method on BTManagedDevice your handler needs to call. Internally it:

  1. Opens a GATT connection to the peripheral.
  2. Discovers all services + characteristics.
  3. Subscribes to every characteristic the device exposes (the subscribeToAllCharacteristics step is automatic and private — you do not call it).
  4. Wires up the device's internal BTDataProcessor so incoming bytes route through the parser.
  5. Triggers the first onDeviceStatusChangedConnected after step 1 succeeds.

If any step throws, the error surfaces via onError (not via the promise — connect() resolves either way, errors are reported through the provider's error channel).

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] as const;

function MyApp() {
const handleDeviceFound = useCallback(async (device) => {
// device.deviceData = { id, name, localName, sn, manufacturerData, measurementTypes }
console.log("Pairing with", device.deviceData.name, device.deviceData.sn);

await device.connect();
// Subscription happens inside `connect()`. Do NOT call
// `device.subscribeToAllCharacteristics()` — it is a private internal step.
}, []);

return (
<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
onDeviceFound={handleDeviceFound}
>
<YourScreens />
</BTProvider>
);
}

Filtering by device kind

If you accept multiple device kinds, branch in the handler before connecting (e.g. to surface different pairing UI):

const acceptedDevices = [
IntegratedDevices.BP2,
IntegratedDevices.SPO2,
IntegratedDevices.F4,
] as const;

const handleDeviceFound = useCallback(async (device) => {
switch (device.deviceData.name) {
case IntegratedDevices.BP2:
setPairingUI("bp-cuff");
break;
case IntegratedDevices.SPO2:
setPairingUI("pulse-oximeter");
break;
case IntegratedDevices.F4:
setPairingUI("scale");
break;
}
await device.connect();
}, []);

Refusing a device

onDeviceFound does not have an opt-out return value. If you want to refuse a peripheral your acceptedDevices filter matched (e.g. wrong serial number for a multi-device clinic), just don't call connect(). The provider will drop the reference and continue scanning; the device will fire onDeviceFound again on its next advertisement, and you can decide again.

const handleDeviceFound = useCallback(async (device) => {
if (!isAuthorizedSerial(device.deviceData.sn)) {
// Refuse — do not connect.
return;
}
await device.connect();
}, []);

Sequence after connect()

For a typical BP2 pairing:

  1. onDeviceFound → your handler calls await device.connect().
  2. onDeviceStatusChangedConnected.
  3. (User initiates a reading on the cuff.)
  4. onDeviceStatusChangedMeasuring (with measurementTypeKey).
  5. onResult → parsed measurement.
  6. onDeviceStatusChangedConnected.

If pairing fails:

  1. onDeviceFound → your handler calls await device.connect().
  2. onError fires with data.deviceData set and data.error describing the failure.
  3. No Connected status arrives. Scanning continues — the next advertisement re-fires onDeviceFound.

Memoize the handler

onDeviceFound is the most expensive callback to re-register. Each new prop identity tears down + re-registers the scan subscription, and during teardown an in-flight pairing can be aborted. Wrap with useCallback and pin its dependencies:

const handleDeviceFound = useCallback(async (device) => {
await device.connect();
}, []); // empty deps — body uses only `device`, no closures over state