# Advanced Bluetooth usage

Canonical page: https://docs.ovok.com/native-sdk/guide/components/bt-management/advanced-usage

Most apps should use `BTProvider`. Use `BTManager` directly only when the app needs to own permission handling, runtime context, scanning, and cleanup itself.

## Manage a session directly [#manage-a-session-directly]

`BTManager` takes the app-owned `BleManager`, a stable accepted-device tuple, and optional manager options. The direct manager API does not request platform permissions or provide the React runtime hooks.

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

const bleManager = new BleManager();
const acceptedDevices = [IntegratedDevices.BP2] as const;
const manager = new BTManager(bleManager, acceptedDevices, {
  autoConnect: true,
  autoConnectDelayMs: 700,
  oneReadingPerConnection: true,
});

manager.onDeviceFound(async (device) => {
  await device.connect();
});
manager.onResult((result) => {
  saveMeasurement(result.id, result.data);
});
manager.onError((failure) => reportBleError(failure));

manager.startScan();

// In the owning effect or service teardown:
manager.destroy();
~~~

Register handlers before starting discovery. The manager exposes callbacks for discovery, status, connection events, results, and errors. Its lifecycle controls include `startScan`, `pauseScan`, `resumeScan`, `setBackgroundScanEnabled`, `restoreDevices`, `keepConnecting`, `forget`, `stopScan`, and `destroy`.

Use `pauseScan` and `resumeScan` for a temporary discovery pause; they preserve managed devices and callback registrations. `stopScan` removes callbacks, disconnects managed devices, and clears the device set. `destroy` additionally marks the manager unusable. Do not start or stop discovery directly through `BleManager` while the SDK manager owns the scan.

## What a connection does before the first command [#what-a-connection-does-before-the-first-command]

Once a device is connected and its services are discovered, the SDK reads Device Information and the battery level, then subscribes to every declared characteristic. The first command is written only after the device reports notifications or indications as enabled on each of them, so a device that answers a control-point write with nothing until indications are on receives its command in the right order. Where the platform cannot read that setting, the SDK waits a short fixed time instead.

When a read, a subscription or the first command fails because the device needs pairing, the SDK keeps the link, emits a `pairing` connection event with `pairingCodeRequired` set, and tries the step again while the user finishes the system pairing dialog, for up to 30 seconds. If the link drops or the time runs out, the step is abandoned and the connection carries on without it.

A declared device that has no clock protocol of its own (`clockSync` is `unknown`, as for the Bluetooth SIG profiles) and exposes a writable Current Time characteristic (service `0x1805`, characteristic `0x2A2B`) gets the phone's time written on connect, with a `clock-synchronized` connection event. This follows `BTProvider`'s `clockSync` option: `enabled: false` leaves the device clock alone, `timezone` picks the local or UTC time, and `now` supplies the time. A device whose clock cannot be written keeps its own time.

## React runtime hooks [#react-runtime-hooks]

The runtime hooks below require `BTProvider`, except for the two state and permission hooks.

| Hook | Behavior |
| --- | --- |
| `useNearbyDevices()` | Returns discovered runtime devices and `pair(id)`, which connects the matching device. |
| `usePairedDevices({ storage, storageKey?, autoPair? })` | Stores remembered peripheral IDs with app-provided async storage. With `autoPair`, connects a remembered device when it is discovered. |
| `useDeviceConnection(id)` | Returns the current status and the most recent connection events for that device. |
| `useMeasurements(onResult?, options?)` | Collects a bounded in-memory list of results, deduplicated by stable result ID. |
| `useScanControl()` | Exposes pause and resume controls for the provider's manager. |
| `useRuntimeEvent(event, callback)` | Subscribes to a runtime event; earlier events are not replayed. |
| `useBluetoothState({ bleManager?, requestPermission? })` | Observes radio state and exposes a permission request function. |
| `useRequestPermissions({ onAccessPermissionChanged? })` | Requests platform Bluetooth permission and returns the current access result. |

`useMeasurements` supports `all`, `first-reading-per-kind-per-connection`, and `daily-totals-only`. The first-reading policy resets when the runtime receives a bond event. Daily totals are deduplicated by measurement type and UTC calendar day using the measurement's end, start, or recorded-at time. The list defaults to the latest 100 accepted results; call `clear()` to reset its local state.

`useBackgroundSync` exposes a UI setting with `on`, `off`, `restricted`, and `blocked` states. It toggles the manager's background-scan flag and calls the optional app callback. It does not persist the preference or replace the durable queue configured through `BTProvider.backgroundSync`. Keep the provider prop and the UI setting connected to the same app state.

`useHealthImport` is an adapter-driven health-store hook, not a BLE permission helper. Its adapter reports authorization per type and requests the selected set. Keep its `types` array and adapter identity stable so configuration changes do not trigger repeated refreshes.

## Compose the runtime manually [#compose-the-runtime-manually]

`createBluetoothRuntime` and `BluetoothRuntimeProvider` are exported for advanced composition. The runtime provider supplies context only; it does not create a manager, request permissions, scan, forward events, or clean up devices. See [BluetoothRuntimeProvider](/native-sdk/guide/components/bt-management/runtime-provider/) and prefer the provider's callbacks for ordinary app integration.

Runtime events are `deviceFound`, `pairedDevices`, `error`, `status`, `result`, and `connection`. Hooks subscribe from the time they mount and do not replay earlier events.

## Deterministic BLE fixtures [#deterministic-ble-fixtures]

The `@ovok/native/bt-management` testing exports include `createFakeBleManager` and `createFakeBleDevice`. The fake manager can emit discovery and radio-state changes. A fake device supports connection, characteristic reads and notifications, notification-state (Client Characteristic Configuration) reads, MTU requests, and disconnects. These helpers do not simulate operating-system permissions or real platform background execution.

`defineRecordedDeviceFrames` groups app-owned recorded frames by device key. `replayRecordedFrames` sends each frame to an app-provided sink in order and can apply a delay per frame. Use these helpers to exercise parsing and result-handling code without live hardware.

See the [Bluetooth guide](/native-sdk/guide/modules/bt-management), [BTProvider reference](/native-sdk/guide/components/bt-management/bt-provider/), and [runtime hooks](/native-sdk/guide/components/bt-management/hooks/).
