Skip to main content

onDeviceStatusChanged

Optional callback prop on <BTProvider>. Fires every time a connected device's status transitions to one of the four canonical values.

This is the single source of truth for "what is the device doing right now?". Other libraries' BLE status enums have 8–12 values; this one has 4 — anything richer than that must be derived in your app code.

Signature

import type {
DeviceData,
DeviceStatus,
} from "@ovok/native";
import type { MeasurementTypeKey } from "@ovok/core";

export interface DeviceStatusChangeCallback {
deviceData: DeviceData;
status: DeviceStatus;
measurementTypeKey?: MeasurementTypeKey;
}

onDeviceStatusChanged?: (data: DeviceStatusChangeCallback) => void;

measurementTypeKey is present only when status === DeviceStatus.MEASURING — it tells you WHICH measurement family the device is currently capturing (relevant for multi-mode devices like BP2 that can take blood-pressure or ECG).

The canonical enum — exactly 4 values

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

That's the entire enum. Source: src/modules/bt-management/types/device-status.ts.

There is NO Connecting, Pairing, Ready, Syncing, Complete, Error, Idle, Searching, Discovering, Scanning, or Pairing2. If you see those names in legacy code or in other library docs you're cross-referencing, they are not part of this SDK and will never be emitted on data.status.

Mapping the 4 values to UI labels

Most apps want a richer status display than the SDK emits. Derive it from local state:

App UI labelWhen to show it
"Idle"no callback has fired yet for this device
"Connecting" / "Pairing"onDeviceFound fired, but no Connected yet
"Connected"data.status === 'Connected'
"Measuring"data.status === 'Measuring'
"Complete"Connected after onResult resolved (track in your own state)
"Disconnected"data.status === 'Disconnected'
"Low Battery"data.status === 'LowBattery' (alongside another connection label)
"Error"onError fired (NOT a DeviceStatus value)

Usage

import React, { useCallback, useState } from "react";
import { BleManager } from "react-native-ble-plx";
import {
BTProvider,
DeviceStatus,
IntegratedDevices,
} from "@ovok/native";

type UILabel =
| "idle"
| "connecting"
| "connected"
| "measuring"
| "complete"
| "disconnected"
| "low-battery"
| "error";

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

function MyApp() {
const [labels, setLabels] = useState<Record<string, UILabel>>({});

const setLabel = useCallback((name: string, label: UILabel) => {
setLabels((prev) => ({ ...prev, [name]: label }));
}, []);

const handleDeviceFound = useCallback(async (device) => {
setLabel(device.deviceData.name, "connecting");
await device.connect();
}, [setLabel]);

const handleDeviceStatusChanged = useCallback((data) => {
const name = data.deviceData.name;
switch (data.status) {
case DeviceStatus.CONNECTED:
setLabel(name, "connected");
break;
case DeviceStatus.MEASURING:
setLabel(name, "measuring");
// data.measurementTypeKey identifies which measurement is in progress
break;
case DeviceStatus.DISCONNECTED:
setLabel(name, "disconnected");
break;
case DeviceStatus.LOW_BATTERY:
// Low battery is a flag layered ON TOP OF the connection state —
// most apps show a battery icon alongside the existing label
// instead of replacing it.
break;
}
}, [setLabel]);

return (
<BTProvider
bleManager={bleManager}
acceptedDevices={acceptedDevices}
onDeviceFound={handleDeviceFound}
onDeviceStatusChanged={handleDeviceStatusChanged}
>
<DeviceList labels={labels} />
</BTProvider>
);
}

Common bug: switching on an invented value

// WRONG — DeviceStatus.Connecting doesn't exist.
switch (data.status) {
case DeviceStatus.Connecting: // type error, and never matches at runtime
setUiStatus("connecting");
break;
}
// WRONG — string literal that never matches what the SDK emits.
switch (data.status) {
case "Searching": // never matches; the device tile spins forever
setUiStatus("searching");
break;
}
// CORRECT — switch on one of the 4 real values and derive richer UI elsewhere.
switch (data.status) {
case DeviceStatus.CONNECTED:
setUiStatus("connected");
break;
}

Timing

For a normal BP2 measurement, you see this sequence:

  1. onDeviceFound → handler runs, device.connect() succeeds.
  2. onDeviceStatusChangedConnected.
  3. User initiates a reading on the cuff.
  4. onDeviceStatusChangedMeasuring (with measurementTypeKey === 'bloodPressure').
  5. onResult → parsed measurement delivered.
  6. onDeviceStatusChangedConnected (back to idle-but-connected, NOT a "Complete" value).

If the user walks out of range:

  1. onDeviceStatusChangedDisconnected.
  2. Scanning continues; if the device returns, onDeviceFound fires again.

LowBattery can arrive at any point and does not change the connection state — it's an orthogonal signal you can layer on top of whatever connection label is current.

  • BTProvider — full provider reference
  • on-result — the measurement payload that follows Measuring
  • on-error — the failure path (not a DeviceStatus value)
  • on-device-found — what runs before Connected is reported