Bluetooth devices
Scan for supported BLE peripherals and collect their readings.
- Stable unknown
- Release candidate since 0.1.0-rc.4
- Development since 0.1.0-dev.27+7d4ccf0
BluetoothRuntime is the framework-free core for scanning, connecting to supported
peripherals, receiving readings, and saving them. It uses small platform ports so
device protocols and application code do not depend on a BLE plugin.
Define device support
Each definition matches an advertisement and turns the peripheral's GATT stream into Ovok measurements:
defineDevice(
BluetoothDeviceDefinition(
key: 'portable-scale',
matches: (advertisement) =>
advertisement.serviceUuids.contains('scale-service-uuid'),
protocol: (link) async* {
await for (final bytes in link.subscribe(
'scale-service-uuid',
'weight-characteristic-uuid',
)) {
yield decodeScaleReading(bytes);
}
},
),
)The sigProtocols map provides decoders for the standard Bluetooth SIG blood pressure,
temperature, weight, pulse oximetry, heart rate, and glucose services. The glucose protocol
requests stored records through the Record Access Control Point and omits control-solution
samples. Pass a profile protocol to a BluetoothDeviceDefinition when your app wants to
support a SIG-compatible peripheral.
Boso Medicus system monitors use the standard blood-pressure profile. Register them as a custom device so the app opts into their advertised name:
final bosoName = RegExp(r'^boso medicus [0-9a-f]{6}$', caseSensitive: false);
final bosoSupport = defineDevice(
BluetoothDeviceDefinition(
key: 'boso-medicus-system',
matches: (advertisement) => bosoName.hasMatch(advertisement.name),
protocol: sigProtocols['standard-blood-pressure']!,
),
);For A&D UA-651/656/1200BLE and UB-1100BLE monitors, the UC-352BLE scale, Viatom/Lepu ZH-SG-A606
blood-glucose meters, Viatom BPW1 blood-pressure watches, Joytech and Controly
blood-pressure monitors, Veroval BPU26/BPW26 and UEBE Visomat Comfort Soft BT monitors, Joytech DET and Genial T31 thermometers,
Yonker YK-BPA1–7 and YK-IBPA1 blood-pressure monitors and YK-IRT4/BPTI 24 thermometers,
Lepu F4/F8 and S5 scales, Lescale F3/F4P/P3 scales, Lepu F5 and MY_SCALE body-weight scales,
TaiDoc TD4216 glucose meters and TD1241-family thermometers, Viatom TMB BP3-B, BP3-C1, BP3-C2,
BP2A Nexus, BP4 Flex, TMB-2288, and TMB_2088 blood-pressure monitors, GlucoCheck-compatible meters,
TeleBGM meters, Famidoc blood-pressure monitors, Famidoc contact and infrared thermometers,
Contec BC401/BC01 urine analyzers, Cofoe KF-66EP blood-pressure monitors, AOJ-70A/70B
pulse oximeters, Jumper JPD-500D family oximeters, and Viatom/Lepu OxySmart PC-60FW
pulse oximeters, add the
accepted integrated devices to BluetoothRuntimeOptions.devices with builtInDevices:
devices: [
builtInDevices([
IntegratedDevices.aoj20a,
IntegratedDevices.aoj70b,
IntegratedDevices.pc60wf,
IntegratedDevices.andBpm,
IntegratedDevices.andBpmCts,
IntegratedDevices.andScale,
IntegratedDevices.bc01,
IntegratedDevices.cofoeBpm,
IntegratedDevices.zhSgA606,
IntegratedDevices.viatomBpw1Bpm,
IntegratedDevices.viatomTmbBpm,
IntegratedDevices.famidocBpm,
IntegratedDevices.famidocContactThermometer,
IntegratedDevices.famidocIrThermometer,
IntegratedDevices.genialT31,
IntegratedDevices.joytechBpm,
IntegratedDevices.joytechThermometer,
IntegratedDevices.jumperBpm,
IntegratedDevices.jumperOximeter,
IntegratedDevices.jumperThermometer,
IntegratedDevices.f4,
IntegratedDevices.lepuF4Scale,
IntegratedDevices.s5,
IntegratedDevices.lepuF5Scale,
IntegratedDevices.taidocGlucose,
IntegratedDevices.taidocThermometer,
IntegratedDevices.yonkerBpm,
IntegratedDevices.yonkerThermometer,
IntegratedDevices.verovalBpm,
IntegratedDevices.uebeVisomatComfortSoft,
IntegratedDevices.glucoCheck,
IntegratedDevices.bgm,
]),
],The Jumper HA120 blood-pressure and FR302 thermometer variants are also available through
IntegratedDevices.jumperOximeter, IntegratedDevices.jumperBpm, and
IntegratedDevices.jumperThermometer.
Viatom TMB monitors are available through IntegratedDevices.viatomTmbBpm. The driver
downloads stored blood-pressure readings and leaves the device clock unchanged.
The Viatom BPW1 watch is available through IntegratedDevices.viatomBpw1Bpm. It reads
stored blood-pressure records without changing the device clock.
The Jumper JPD-500D oximeter reads live pulse, oxygen saturation, and perfusion index values. It reports the last values after three seconds without a new reading and selects the GATT service advertised by the device.
Veroval BPU26 and BPW26 monitors are available through IntegratedDevices.verovalBpm.
Both report triple-measurement status; BPU26 also reports atrial-fibrillation and resting-mode
status.
IntegratedDevices.uebeVisomatComfortSoft reads the Visomat Comfort Soft BT standard
blood-pressure profile without sending pairing or setup commands.
The Genial T31 thermometer is available through IntegratedDevices.genialT31.
AOJ-70A and AOJ-70B pulse oximeters are available through IntegratedDevices.aoj70b.
Their live pulse, oxygen saturation, and perfusion index values are reported together after
three seconds without a new value; the driver does not write setup commands.
Viatom/Lepu OxySmart PC-60FW pulse oximeters are available through
IntegratedDevices.pc60wf. The driver emits the last valid pulse and oxygen sample when the
device reports that the probe has been removed.
TaiDoc TD1241-family thermometers are available through
IntegratedDevices.taidocThermometer; the driver sets the clock before reading stored results.
IntegratedDevices.bc01 retrieves stored urine-strip readings without deleting device
history. Flutter does not expose a clock-sync option, so keep the device clock current.
IntegratedDevices.cofoeBpm supports KF-66EP live blood-pressure monitors and sets the
device clock before reading measurements.
Yonker YK-BPA1–7 and YK-IBPA1 blood-pressure monitors sync their clock when connected. YK-IRT4 and Balans Plus BPTI 24 thermometers report live readings and suppress repeats for 30 seconds.
IntegratedDevices.aoj20a reads AOJ-20A realtime and stored thermometer results after setting
the device clock. Stored readings retain their device timestamps.
decodeScaleReading is application code that validates a device frame and returns
the measurement shape accepted by ovok.measurements. Include recordedAt when the
device supplies a timestamp; the runtime uses it to recognize a resent reading.
Create a runtime
Pass a scanner, a permission adapter, and a reading sink. The first device definition that matches an advertisement handles that peripheral.
import 'package:ovok_flutter/ovok_flutter.dart';
final scanner = FlutterReactiveBleScanner();
final bluetooth = BluetoothRuntime(
BluetoothRuntimeOptions(
scanner: scanner,
permissions: permissions,
sink: readingSink,
devices: [scaleSupport],
pairedStorage: secureStore,
resultPolicy: everyReading,
),
);BluetoothScanner reports adapter state and discovered peripherals. A
BluetoothPeripheral opens a BluetoothLink; the link subscribes to or writes GATT
characteristics and closes the connection. FlutterReactiveBleScanner supplies these
scanner and GATT ports through flutter_reactive_ble. BluetoothPermissions checks,
requests, and opens system settings for the platform permissions; provide an adapter
for your app's permission library. BluetoothReadingSink saves readings or queues
them for offline retry.
Configure Bluetooth permissions in the host app. Add these Android manifest entries:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" />Request BLUETOOTH_SCAN and BLUETOOTH_CONNECT at runtime on Android 12 and later,
and location permission on earlier versions. On iOS 13 and later add
NSBluetoothAlwaysUsageDescription to Info.plist; iOS 12 and earlier use
NSBluetoothPeripheralUsageDescription.
When using osBluetoothPermissions(), enable PERMISSION_BLUETOOTH=1 in the
iOS Podfile's GCC_PREPROCESSOR_DEFINITIONS; permission_handler disables
permissions that the app does not enable at build time.
Manage devices
createBluetoothRuntime connects Flutter's BLE scanner and OS permission adapter
to the Ovok measurement API and secure paired-device storage. The app owns the
runtime and disposes it when the signed-in session ends.
final bluetooth = createBluetoothRuntime(
client: ovok,
devices: [
builtInDevices([
IntegratedDevices.aoj20a,
IntegratedDevices.pc60wf,
IntegratedDevices.taidocGlucose,
]),
],
);Place BluetoothDeviceManager inside BluetoothProvider and an app-owned
Scaffold. It scans for supported devices and lets users pair, reconnect,
disconnect, forget devices, and open the available manufacturer manuals. Paired
IDs are stored with client.storage when using the runtime factory.
BluetoothDevicePicker.show(context) must be called below BluetoothProvider.
The runtime must already be scanning; selecting a device starts its connection
and returns that device from the bottom sheet. It does not add a pairing.
BluetoothProvider refreshes Bluetooth permission when the app returns from
system settings. Call bluetooth.dispose() when removing the provider's runtime.
Scan and connect
startScan() requests permission when needed and keeps scanning while Bluetooth is
on. It throws BluetoothError with code permission-denied when access is not
granted. Stop discovery with stopScan(); connected peripherals stay connected.
await bluetooth.startScan();
final snapshot = bluetooth.getSnapshot();
final device = snapshot.device(peripheralId);
if (device != null) {
await bluetooth.connect(device.id);
}Pair a device to reconnect when a later scan finds it. Pass pairedStorage to persist
paired IDs between runtime instances. disconnect() pauses automatic reconnect for
the current session; forget() removes the pairing and disconnects the device.
Listen to subscribe() for adapter state, permission, scan errors, nearby devices,
pairings, recent measurements, and each measurement's save state. Call dispose()
when the runtime is no longer needed; it stops scanning and disconnects every device.
Select readings
everyReading is the default. firstReadingPerKind keeps only the first reading of
each measurement type per connection. dailyTotalsOnly keeps one closed-day activity
total per type and connection. Use policyByKind to combine policies by
measurementTypeKey. keepReadings(events, policy) filters reading events while
preserving driver status and recoverable error events.
Saving and retrying
Each new reading is sent to BluetoothReadingSink.save(). The snapshot reports
saving, saved, queued, or failed; failed readings and offline queue entries
can be retried with retrySaves(). The runtime retains the 100 most recent readings
in its snapshot and recognizes the last 1,000 timestamped reading IDs per runtime.
Last updated on