Skip to content
Shiny.NET

Bluetooth LE

NuGet package Shiny.AppDeviceBridge.BluetoothLE

The Bluetooth LE bridge scans for peripherals, connects, lists services and characteristics, reads and writes them, and streams notifications to the page as events. It’s backed by Shiny.BluetoothLE.

Android, iOS, Mac Catalyst, macOS (AppKit) and Windows. Linux answers 501.

Terminal window
dotnet add package Shiny.AppDeviceBridge.BluetoothLE
builder.UseAppDeviceBridge(
bridge => bridge.AddBluetoothLEBridge(),
webApp => { /* … */ }
);
Client C# TypeScript
IBluetoothLEBridge AddBluetoothLEBridgeClient() BluetoothLEBridge

Listen before you start: a scan and a characteristic subscription run only while something listens to their events, and starting one without a listener answers 409 not_listening — see Events. Characteristic values travel as base64 in JSON.

import { BluetoothLEBridge } from "@shinyorg/appdevicebridge";
const ble = new BluetoothLEBridge();
const status = await ble.requestAccess();
const stopScan = await ble.onScanResult(r => console.log(r.uuid, r.name, r.rssi));
await ble.startScan({ serviceUuids: ["180D"] }); // heart rate
// …the user picks one
stopScan(); // the scan stops once nothing listens
const peripheral = await ble.connect(uuid, { timeoutMs: 10_000 });
const value = await ble.read(uuid, "180A", "2A29"); // manufacturer name, base64
await ble.onNotification(n => render(atob(n.data ?? "")));
await ble.startNotifications(uuid, "180D", "2A37");

Platform setup is Shiny.BluetoothLE’s: NSBluetoothAlwaysUsageDescription on Apple platforms, BLUETOOTH_SCAN and BLUETOOTH_CONNECT (location before Android 12) on Android.

GET/_bridge/ble/status#

Whether Bluetooth is available to the app, and whether a scan is running.

C#IBluetoothLEBridge.GetStatusAsyncTSBluetoothLEBridge.getStatus
Response
200 BleStatus
BleStatus · 2 fields
accessAccessState
isScanningboolean
POST/_bridge/ble/access#

Requests Bluetooth access.

C#IBluetoothLEBridge.RequestAccessAsyncTSBluetoothLEBridge.requestAccess
Response
200 BleStatus
BleStatus · 2 fields
accessAccessState
isScanningboolean
POST/_bridge/ble/scan#

Starts scanning; results arrive through onScanResult, so listen first: without a listener it fails with 409. Starting again replaces the running scan, and the scan stops once nothing listens to it.

C#IBluetoothLEBridge.StartScanAsyncTSBluetoothLEBridge.startScan
Body
application/json · BleScanRequest
BleScanRequest · 1 field
serviceUuidsstring[] | nullOnly report peripherals advertising one of these services; every peripheral when empty.
Response
204 No content
Errors
409Conflict
DELETE/_bridge/ble/scan#

Stops scanning.

C#IBluetoothLEBridge.StopScanAsyncTSBluetoothLEBridge.stopScan
Response
204 No content
GET/_bridge/ble/peripherals#

The peripherals connected now.

C#IBluetoothLEBridge.GetConnectedPeripheralsAsyncTSBluetoothLEBridge.getConnectedPeripherals
Response
200 BlePeripheral[]
BlePeripheral · 4 fields
uuidstring
namestring | null
statusBleConnectionState
mtunumber
GET/_bridge/ble/peripherals/{uuid}#

A peripheral seen in a scan.

C#IBluetoothLEBridge.GetPeripheralAsyncTSBluetoothLEBridge.getPeripheral
ParameterInType
uuidpathstringrequired
Response
200 BlePeripheral
BlePeripheral · 4 fields
uuidstring
namestring | null
statusBleConnectionState
mtunumber
POST/_bridge/ble/peripherals/{uuid}/connection#

Connects. Fails with 504 on timeout, 409 when the connection fails.

C#IBluetoothLEBridge.ConnectAsyncTSBluetoothLEBridge.connect
ParameterInType
uuidpathstringrequired
Body
application/json · BleConnectRequest
BleConnectRequest · 2 fields
autoConnectbooleanReconnect when the peripheral comes back in range.
timeoutMsnumber | nullHow long to wait for the connection; 30 seconds when null.
Response
200 BlePeripheral
BlePeripheral · 4 fields
uuidstring
namestring | null
statusBleConnectionState
mtunumber
Errors
409Conflict504
DELETE/_bridge/ble/peripherals/{uuid}/connection#

Disconnects, ending its notifications.

C#IBluetoothLEBridge.DisconnectAsyncTSBluetoothLEBridge.disconnect
ParameterInType
uuidpathstringrequired
Response
204 No content
GET/_bridge/ble/peripherals/{uuid}/rssi#

The connected peripheral's signal strength.

C#IBluetoothLEBridge.ReadRssiAsyncTSBluetoothLEBridge.readRssi
ParameterInType
uuidpathstringrequired
Response
200 BleRssi
BleRssi · 1 field
rssinumber
GET/_bridge/ble/peripherals/{uuid}/services#

The connected peripheral's services.

C#IBluetoothLEBridge.GetServicesAsyncTSBluetoothLEBridge.getServices
ParameterInType
uuidpathstringrequired
Response
200 BleService[]
BleService · 1 field
uuidstring
GET/_bridge/ble/peripherals/{uuid}/services/{service}/characteristics#

A service's characteristics.

C#IBluetoothLEBridge.GetCharacteristicsAsyncTSBluetoothLEBridge.getCharacteristics
ParameterInType
uuidpathstringrequired
servicepathstringrequired
Response
200 BleCharacteristic[]
BleCharacteristic · 4 fields
serviceUuidstring
uuidstring
isNotifyingboolean
propertiesBleCharacteristicProperty[]
GET/_bridge/ble/peripherals/{uuid}/services/{service}/characteristics/{characteristic}#

Reads a characteristic.

C#IBluetoothLEBridge.ReadAsyncTSBluetoothLEBridge.read
ParameterInType
uuidpathstringrequired
servicepathstringrequired
characteristicpathstringrequired
Response
200 BleCharacteristicValue
BleCharacteristicValue · 3 fields
serviceUuidstring
characteristicUuidstring
datastring | nullBase64 in JSON.
PUT/_bridge/ble/peripherals/{uuid}/services/{service}/characteristics/{characteristic}#

Writes a characteristic.

C#IBluetoothLEBridge.WriteAsyncTSBluetoothLEBridge.write
ParameterInType
uuidpathstringrequired
servicepathstringrequired
characteristicpathstringrequired
Body
application/json · BleWriteRequest
BleWriteRequest · 2 fields
dataBlob | ArrayBuffer | Uint8ArrayBase64 in JSON.
withResponsebooleanWait for the peripheral to acknowledge the write.
Response
204 No content
POST/_bridge/ble/peripherals/{uuid}/services/{service}/characteristics/{characteristic}/notifications#

Subscribes to a characteristic; values arrive through onNotification, so listen first: without a listener it fails with 409. Every subscription ends once that has no listener left; peripherals stay connected.

C#IBluetoothLEBridge.StartNotificationsAsyncTSBluetoothLEBridge.startNotifications
ParameterInType
uuidpathstringrequired
servicepathstringrequired
characteristicpathstringrequired
Response
204 No content
Errors
409Conflict
DELETE/_bridge/ble/peripherals/{uuid}/services/{service}/characteristics/{characteristic}/notifications#

Unsubscribes from a characteristic.

C#IBluetoothLEBridge.StopNotificationsAsyncTSBluetoothLEBridge.stopNotifications
ParameterInType
uuidpathstringrequired
servicepathstringrequired
characteristicpathstringrequired
Response
204 No content
EVENTble.scan#

A peripheral seen by the running scan. Raised for every advertisement.

C#IBluetoothLEBridge.OnScanResultAsyncTSBluetoothLEBridge.onScanResult
Payload
BleScanResult
uuidstring
namestring | null
rssinumber
localNamestring | null
isConnectableboolean | null
serviceUuidsstring[] | null
txPowernumber | null
manufacturerIdnumber | null
manufacturerDatastring | nullBase64 in JSON.
EVENTble.status#

A peripheral this page connected to changed connection state.

C#IBluetoothLEBridge.OnStatusAsyncTSBluetoothLEBridge.onStatus
Payload
BlePeripheralStatus
uuidstring
statusBleConnectionState
EVENTble.notification#

A subscribed characteristic changed.

C#IBluetoothLEBridge.OnNotificationAsyncTSBluetoothLEBridge.onNotification
Payload
BleNotification
peripheralUuidstring
serviceUuidstring
characteristicUuidstring
datastring | nullBase64 in JSON.
EVENTble.error#

A scan or a notification subscription failed and has ended.

C#IBluetoothLEBridge.OnErrorAsyncTSBluetoothLEBridge.onError
Payload
BleError
operationstringscan or notify.
peripheralUuidstring | null
messagestring

JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.

AccessStateenum#

Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.

"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
BleScanRequestobject#
serviceUuids?string[] | nullOnly report peripherals advertising one of these services; every peripheral when empty.
BleConnectionStateenum#
"Disconnected""Disconnecting""Connected""Connecting"
BleConnectRequestobject#
autoConnect?booleanReconnect when the peripheral comes back in range.
timeoutMs?number | nullHow long to wait for the connection; 30 seconds when null.
BleRssiobject#
rssinumber
BleServiceobject#
uuidstring
BleCharacteristicPropertyenum#

What a characteristic supports.

"Broadcast""Read""WriteWithoutResponse""Write""Notify""Indicate""AuthenticatedSignedWrites""ExtendedProperties""NotifyEncryptionRequired""IndicateEncryptionRequired"
BleCharacteristicValueobject#
serviceUuidstring
characteristicUuidstring
datastring | nullBase64 in JSON.
BleWriteRequestobject#
dataBlob | ArrayBuffer | Uint8ArrayBase64 in JSON.
withResponse?booleanWait for the peripheral to acknowledge the write.
BleScanResultobject#

An advertisement.

uuidstring
namestring | null
rssinumber
localNamestring | null
isConnectableboolean | null
serviceUuidsstring[] | null
txPowernumber | null
manufacturerIdnumber | null
manufacturerDatastring | nullBase64 in JSON.
BleNotificationobject#
peripheralUuidstring
serviceUuidstring
characteristicUuidstring
datastring | nullBase64 in JSON.
BleErrorobject#
operationstringscan or notify.
peripheralUuidstring | null
messagestring

These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default. See Security.