Migrating from noble (Node.js)
A side-by-side mapping between noble's event-driven Node.js API and SimpleJSBLE.
noble was the de-facto BLE central library for Node.js for a decade. Its maintained fork was archived in July 2025. SimpleJSBLE is SimpleBLE's Node.js and Electron binding: promises, EventEmitter events, and TypeScript types over the same core as every other SimpleBLE binding. Migrating is mostly a mechanical translation that removes code.
What changes
- No raw HCI access. SimpleJSBLE uses each operating system's own Bluetooth stack: WinRT on Windows, CoreBluetooth on macOS, and BlueZ over D-Bus on Linux. There is no
setcapon the Node.js binary, no root, and no replacement USB driver. - No build step.
npm install simplejsblebrings prebuilt addons for Windows, macOS, and Linux on x64 and arm64. Nothing compiles on install, and Electron uses the same package. - No
poweredOndance. Adapters are ready whenAdapter.getAdapters()resolves; there is no state machine to wait for. - Service discovery is part of
connect(). When the promise resolves,peripheral.services()lists every service, characteristic, and descriptor. - Characteristics are addressed by UUID pair, not by objects held from a discovery callback:
peripheral.read(serviceUuid, characteristicUuid). UUIDs are full 128-bit strings, so noble's'180d'is'0000180d-0000-1000-8000-00805f9b34fb'. - One object per device. The
PeripheralfromscanFoundis the same object as the one inscanGetResults(), so===works and state can live on it.
Concept mapping
| noble | SimpleJSBLE |
|---|---|
noble.on('stateChange', ...), wait for poweredOn | await Adapter.bluetoothEnabled() |
noble.startScanningAsync(serviceUuids) | adapter.scanStart(), or adapter.scanFor(ms) for a bounded scan; filter on peripheral.services() |
noble.on('discover', peripheral => ...) | adapter.on('scanFound', peripheral => ...), and scanUpdated for later advertisements |
noble.stopScanningAsync() | adapter.scanStop() |
peripheral.connectAsync() | peripheral.connect() |
peripheral.discoverAllServicesAndCharacteristicsAsync() | peripheral.services() after connect() |
characteristic.readAsync() | peripheral.read(service, characteristic) |
characteristic.writeAsync(data, false) | peripheral.writeRequest(service, characteristic, data) |
characteristic.writeAsync(data, true) | peripheral.writeCommand(service, characteristic, data) |
characteristic.subscribeAsync() and characteristic.on('data', ...) | peripheral.notify(service, characteristic, data => ...), or indicate |
characteristic.unsubscribeAsync() | peripheral.unsubscribe(service, characteristic) |
descriptor.readValueAsync() / writeValueAsync(data) | peripheral.readDescriptor(...) / writeDescriptor(...) |
peripheral.once('disconnect', ...) | peripheral.on('disconnected', ...) |
peripheral.disconnectAsync() | peripheral.disconnect() |
peripheral.address, advertisement.localName | peripheral.address(), peripheral.identifier() |
peripheral.rssi, advertisement.txPowerLevel | peripheral.rssi(), peripheral.txPower() |
advertisement.manufacturerData | peripheral.manufacturerData(), a Map by company identifier |
advertisement.serviceUuids, serviceData | peripheral.services() before connecting, with each service's data |
| bleno, for the peripheral role | adapter.createLocalPeripheral(), see Advertise |
Values are Buffers in both libraries, and failures reject with a SimpleBLEError whose code names the cause.
noble's heart-rate example, translated
noble's README example scans for the heart rate service, connects, and subscribes to measurements. With SimpleJSBLE:
import { Adapter } from 'simplejsble';
const HEART_RATE = '0000180d-0000-1000-8000-00805f9b34fb';
const MEASUREMENT = '00002a37-0000-1000-8000-00805f9b34fb';
const [adapter] = await Adapter.getAdapters();
const found = new Promise(resolve => {
adapter.on('scanFound', peripheral => {
if (peripheral.services().some(service => service.uuid === HEART_RATE)) resolve(peripheral);
});
});
await adapter.scanStart();
const peripheral = await found;
await adapter.scanStop();
peripheral.on('disconnected', () => console.log('Disconnected'));
await peripheral.connect();
await peripheral.notify(HEART_RATE, MEASUREMENT, data => {
console.log(`Heart rate payload: ${data.toString('hex')}`);
});The heart rate monitor recipe covers parsing the measurement payload. For events, lifetime, Electron packaging, and Dongl pairing, see the SimpleJSBLE usage guide.
Licensing
noble is MIT-licensed; SimpleBLE is BUSL-1.1, free for non-commercial use, with commercial licenses described in Licensing.
Migrating from Bleak
A side-by-side mapping between bleak's asyncio API and SimplePyBLE's synchronous API for Python developers.
Migrating from pc-ble-driver
Move from pc-ble-driver and pc-ble-driver-py to SimpleBLE with a Dongl, keeping a dedicated USB radio and replacing the event dispatcher with blocking calls.
