React.js & Node.js open source developer
📦 NPM
Bluetooth Low Energy (BLE) library written with pure Node.js (no bindings) - baked by Bluez via DBus
Home Page: https://www.npmjs.com/package/node-ble
License: MIT License
1. const { createBluetooth } = require('node-ble')
2. const { TEST_DEVICE, TEST_SERVICE, TEST_CHARACTERISTIC, TEST_NOTIFY_SERVICE, TEST_NOTIFY_CHARACTERISTIC } = process.env
3.
4. async function main () {
5. const { bluetooth, destroy } = createBluetooth()
6.
7. // get bluetooth adapter
8. const adapter = await bluetooth.defaultAdapter()
9. await adapter.startDiscovery()
10. console.log('discovering')
11.
12. // get device and connect
13. const device = await adapter.waitDevice(TEST_DEVICE)
14. console.log('got device', await device.getAddress(), await device.getName())
15. await device.connect()
16. console.log('connected')
17. // console.log(device)
18. console.log(await device.getRSSI())
19. const gattServer = await device.gatt()
20. console.log(gattServer)
21. // read write characteristic
22. const service1 = await gattServer.getPrimaryService("0000180f-0000-1000-8000-00805f9b34fb")
23. // console.log(service1)
24. const characteristic1 = await service1.getCharacteristic("00002a19-0000-1000-8000-00805f9b34fb")
25. // await characteristic1.writeValue(Buffer.from('Hello world'))
26. const buffer = await characteristic1.readValue()
27. console.log('read', buffer, buffer.toString())
28.
29. destroy()
30. }
31.
32. main()
33. .then(console.log)
34. .catch(console.error)
35.
out:
discovering
got device EC:21:E5:43:94:CA BLESmart_00000044EC21E54394CA
connected
-43
I have trying to connect my blood pressure monitor to extract the data,
but in line 19 - (const gattServer = await device.gatt()) doesn't seem to work.
The code stay in line 19 and never get the service in line 22.
May some one help me? please!
I'm trying to write to a MIDI characteristic of a MIDI service. The MIDI UUIDs are:
const midiServiceUUID = '03b80e5a-ede8-4b33-a751-6ce34ec4c700';
const midiCharacteristicUUID = '7772e5db38684112a1a9f2669d106bf3';
I can see the MIDI service UUID in the UUIDs
property list:
> gattServer.helper.props().then(x => console.log(x))
> { Address: 'C7:26:DE:18:ED:28',
AddressType: 'random',
Name: 'MD-BT01',
Alias: 'MD-BT01',
Paired: true,
Trusted: false,
Blocked: false,
LegacyPairing: false,
RSSI: -91,
Connected: true,
UUIDs:
[ '00001800-0000-1000-8000-00805f9b34fb',
'00001801-0000-1000-8000-00805f9b34fb',
'0000180a-0000-1000-8000-00805f9b34fb',
'03b80e5a-ede8-4b33-a751-6ce34ec4c700' ], // <- midiServiceUUID
Adapter: '/org/bluez/hci0',
ServicesResolved: true }
However I can only see two service children nodes:
> gattServer.helper.children().then(x => console.log(x))
> [ 'service0008', 'service000c' ]
Trying to get to the MIDI service UUID fails:
> gattServer.getPrimaryService(midiServiceUUID).then(s => {
console.log(s);
}, (e) => console.log(e));
> Error: Service not available
at GattServer.getPrimaryService (/home/pi/apps/mpr121_touch_midi/node_modules/node-ble/src/GattServer.js:41:11)
I can get two of the other service UUIDs:
> gattServer.getPrimaryService('00001801-0000-1000-8000-00805f9b34fb').then(s => {
... s.helper.props().then(x => console.log(x));
... }, (e) => console.log(e));
> { UUID: '00001801-0000-1000-8000-00805f9b34fb',
Device: '/org/bluez/hci0/dev_C7_26_DE_18_ED_28',
Primary: true,
Includes: [] }
> gattServer.getPrimaryService('0000180a-0000-1000-8000-00805f9b34fb').then(s => {
... s.helper.props().then(x => console.log(x));
... }, (e) => console.log(e));
> { UUID: '0000180a-0000-1000-8000-00805f9b34fb',
Device: '/org/bluez/hci0/dev_C7_26_DE_18_ED_28',
Primary: true,
Includes: [] }
Any ideas on how to access the MIDI Service UUID 03b80e5a-ede8-4b33-a751-6ce34ec4c700
?
I was trying to write data to a GATT server but I got this error:
Operation failed with ATT error: 0x82
Do you have any idea what this means? I can write to it just fine with Chrome Web Bluetooth.
When subscribing a characteristic it give an empty buffer.
Here is my code running on rpi4 with Debian Buster:
await characteristic.startNotifications()
characteristic.on('valuechanged', buffer => {
console.log(buffer) // Show empty Buffer: <Buffer >
})
await characteristic.readValue() // Trigger the callback
The requirements is missing the package dotenv.
const {createBluetooth} = require('node-ble')
const {bluetooth, destroy} = createBluetooth()
async function main(){
const adapter = await bluetooth.defaultAdapter()
async function search(){
if (! await adapter.isDiscovering()){
await adapter.startDiscovery()
}
const devices = await adapter.devices()
devices.forEach(async mac => {
const device = await adapter.waitDevice(mac)
let rssi;
try {
rssi = await device.getRSSI()
} catch (error) {
//console.error(error)
}
console.log(mac, rssi)
device.disconnect()
});
//destroy()
//await device.disconnect()
}
setInterval(() => {
search()
}, 5000);
}
main()
When I run this code it works for a minute and prints mac addresses and their RSSI value
Then i start getting warnings
(node:2234) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 {"path":"/org/bluez/hci0/dev_CC_98_8B_A7_XX_XX","interface":"org.freedesktop.DBus.Properties","member":"PropertiesChanged"} listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
After about 10 minutes it crashes with the following error message
/home/compulab/projects/newNode-ble/node_modules/node-ble/src/Adapter.js:158
reject(new Error('operation timed out'))
^
Error: operation timed out
at Timeout._onTimeout (/home/compulab/projects/newNode-ble/node_modules/node-ble/src/Adapter.js:158:16)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7)
Node.js v18.15.0
I'm running Debian 11 with kernel version 5.15.5 on a board manufactured by Compulab
Any suggestions?
npm WARN deprecated [email protected]: request has been deprecated, see request/request#3142
npm WARN deprecated [email protected]: this library is no longer supported
Node version: v12.22.1
Raspberry 4 Buster
Hello,
using Adapter.getDevice
followed by any property getter like Device.getName
seems to leave PropertiesChanged
event listeners.
Using device Device.disconnect
(even when never having used Device.connect
) removes listeners on Device.helper
but not on Device.helper._propsProxy
.
This is one of two issues I noticed while repeatedly trying to enumerate a devices and read some properties from them (in a heavy BLE traffic area).
Does this library support Mac and Windows, in addition to Linux?
If it does, are there any different setup instructions?
I have tested the readValue to a readable characteristic and it worked fine:
const characteristic1 = await service1.getCharacteristic(TEST_CHARACTERISTIC_WRITE)
const buffer = await characteristic1.readValue()
console.log('read', buffer, buffer.toString())
But, when I try to execute writeValue in an writable characteristic with the following code, it is throwing the exception:
const characteristic2 = await service1.getCharacteristic(TEST_CHARACTERISTIC_WRITE)
await characteristic2.writeValue(Buffer.from('s'))
The error:
{ DBusError: In Progress
at _methodReturnHandlers.(anonymous function) (/home/ica/git/ica/fecon_beta/node-ble/node_modules/dbus-next/lib/bus.js:337:27)
at handleMessage (/home/ica/git/ica/fecon_beta/node-ble/node_modules/dbus-next/lib/bus.js:96:11)
at EventEmitter.MessageBus.conn.on (/home/ica/git/ica/fecon_beta/node-ble/node_modules/dbus-next/lib/bus.js:146:9)
at EventEmitter.emit (events.js:182:13)
at /home/ica/git/ica/fecon_beta/node-ble/node_modules/dbus-next/lib/connection.js:116:14
at Socket.<anonymous> (/home/ica/git/ica/fecon_beta/node-ble/node_modules/dbus-next/lib/message.js:63:9)
at Socket.emit (events.js:182:13)
at emitReadable_ (_stream_readable.js:540:12)
at process._tickCallback (internal/process/next_tick.js:174:19)
name: 'DBusError',
type: 'org.bluez.Error.InProgress',
text: 'In Progress',
reply:
Message {
type: 3,
_sent: false,
_serial: 2677,
path: undefined,
interface: undefined,
member: undefined,
errorName: 'org.bluez.Error.InProgress',
replySerial: 37,
destination: ':1.165',
sender: ':1.12',
signature: 's',
body: [ 'In Progress' ],
flags: 1 } }
Could someone help me to solve this? Any sugestion or idea?
I had to dig through the closed issues tab to find out that this library is incompatible with windows, this should be specified on the readme to prevent people from wasting their morning like I did
Hello,
using Device.getDevice
repeatedly causes registered dbus "matches" to accumulate. I traced it until that _removeMatch
(dbus-next) triggers on event listener removal but doesn't really send the RemoveMatch
message. Now not sure if this is a bug of this library or of dbus-next
or if there's a version solving it.
This is one of two issues I noticed while repeatedly trying to enumerate a devices and read some properties from them (in a heavy BLE traffic area).
Hey,
When running my Node.js application that uses the @abandonware/noble
library to interact with Bluetooth devices, the application crashes with the following error message:
libc++abi: terminating with uncaught exception of type Napi::Error
zsh: abort node getDTC.js
This occurs after successfully discovering a device and attempting to send a command to a characteristic.
/Relevant part of the code that leads to the crash
function sendCommand(characteristic, command) {
const bufferCommand = Buffer.from(command, 'hex');
characteristic.write(bufferCommand, false, (error) => {
if (error) {
console.error(Error writing command ${command} to characteristic ${characteristic.uuid}:, error);
} else {
console.log(Command ${command} sent to characteristic ${characteristic.uuid});
}
});
}
Any idea?
Hi
I couldn't find any way to get access to the advertising data sent out by a device. It's the service data, I am especially interested in, but manufacturer data would be nice, as well.
I was looking for something like Device.getAdvertisingData()
, but if there is a way to achieve this, please educate a fool. :-)
EDIT: I just noticed that pull request #61 might address my problem.
hello ,
I have trouble , i try to get characteristics from my BLE sensor:
const characteristicMagneticFlux = await peripheral.discoverSomeServicesAndCharacteristicsAsync(['183b'], ['2aa1']);
but when i add an other characteristics like this :
const characteristics2 = await peripheral.discoverSomeServicesAndCharacteristicsAsync(['183b'], ['2a19']);
I have an error like this
noble: unknown peripheral c44f3371766b, 183b, 2aa1 read! noble: unknown peripheral c44f3371766b, 183b, 2aa1 read!
Thanks for helping me.
Can you mark supported os in readme?
Thank you so much for this project!
I was wondering if it is possible to create a ble peripheral that can advertise some static data?
Something similar to bleno's example.
bleno.startAdvertising(name, serviceUuids[, callback(error)]);
// or
bleno.startAdvertisingIBeacon(uuid, major, minor, measuredPower[, callback(error)]);
I'm using RPi 4 (with node 14.9.0, BlueZ 5.50). I can discover the device, but not the service.
require('dotenv').config()
const { createBluetooth } = require('.')
const { TEST_DEVICE, TEST_SERVICE, TEST_CHARACTERISTIC, TEST_NOTIFY_SERVICE, TEST_NOTIFY_CHARACTERISTIC } = process.env
async function main () {
const { bluetooth, destroy } = createBluetooth()
// get bluetooth adapter
const adapter = await bluetooth.defaultAdapter()
await adapter.startDiscovery()
console.log('discovering')
// get device and connect
const device = await adapter.waitDevice('D4:CA:6E:F1:84:BE')
console.log('got device', await device.getAddress(), await device.getName())
await device.connect()
console.log('connected')
const gattServer = await device.gatt()
// read write characteristic
const service1 = await gattServer.getPrimaryService('15172000-4947-11e9-8646-d663bd873d93')
console.log('got primary service.')
const characteristic1 = await service1.getCharacteristic('15172001-4947-11e9-8646-d663bd873d93')
console.log('got control characteristic.')
//await characteristic1.writeValue(Buffer.from('Hello world'))
const buffer = await characteristic1.readValue()
console.log('read', buffer, buffer.toString())
}
main()
.then(console.log)
.catch(console.error)
Console output
discovering
got device D4:CA:6E:F1:84:BE Xsens DOT
DBusError: Software caused connection abort
at _methodReturnHandlers.<computed> (/home/pi/node-ble-master/node_modules/dbus-next/lib/bus.js:339:27)
at handleMessage (/home/pi/node-ble-master/node_modules/dbus-next/lib/bus.js:98:11)
at EventEmitter.<anonymous> (/home/pi/node-ble-master/node_modules/dbus-next/lib/bus.js:147:9)
at EventEmitter.emit (events.js:314:20)
at /home/pi/node-ble-master/node_modules/dbus-next/lib/connection.js:112:14
at Socket.<anonymous> (/home/pi/node-ble-master/node_modules/dbus-next/lib/message.js:63:9)
at Socket.emit (events.js:314:20)
at emitReadable_ (_stream_readable.js:563:12)
at processTicksAndRejections (internal/process/task_queues.js:79:21) {
type: 'org.bluez.Error.Failed',
text: 'Software caused connection abort',
reply: Message {
type: 3,
_sent: false,
_serial: 1494,
path: undefined,
interface: undefined,
member: undefined,
errorName: 'org.bluez.Error.Failed',
replySerial: 13,
destination: ':1.68',
sender: ':1.22',
signature: 's',
body: [ 'Software caused connection abort' ],
flags: 1
}
}
I'm using a, Raspberry Pi Zero when I try to write a string of 21 characters to a characteristic the application just hangs and hangs the receiving device as well. Where as if I send a string that 20 characters it sends perfectly.
So the weird thing is that if I use the gatttool console interface I can send significantly longer strings to that same characteristic.
Any ideas would be much appriciated,
Thanks
pascal@pascal-XPS-13-9370:$ npm i node-ble
> [email protected] install /home/pascal/Development/erg-mode/node_modules/abstract-socket
> node-gyp rebuild
make: Entering directory '/home/pascal/Development/erg-mode/node_modules/abstract-socket/build'
CXX(target) Release/obj.target/bindings/src/abstract_socket.o
In file included from ../src/abstract_socket.cc:5:
../../nan/nan.h: In function ‘void Nan::AsyncQueueWorker(Nan::AsyncWorker*)’:
../../nan/nan.h:2294:62: warning: cast between incompatible function types from ‘void (*)(uv_work_t*)’ {aka ‘void (*)(uv_work_s*)’} to ‘uv_after_work_cb’ {aka ‘void (*)(uv_work_s*, int)’} [-Wcast-function-type]
2294 | , reinterpret_cast<uv_after_work_cb>(AsyncExecuteComplete)
| ^
In file included from ../../nan/nan.h:56,
from ../src/abstract_socket.cc:5:
../src/abstract_socket.cc: At global scope:
/home/pascal/.cache/node-gyp/13.11.0/include/node/node.h:618:43: warning: cast between incompatible function types from ‘void (*)(v8::Local<v8::Object>)’ to ‘node::addon_register_func’ {aka ‘void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)’} [-Wcast-function-type]
618 | (node::addon_register_func) (regfunc), \
| ^
/home/pascal/.cache/node-gyp/13.11.0/include/node/node.h:652:3: note: in expansion of macro ‘NODE_MODULE_X’
652 | NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
| ^~~~~~~~~~~~~
../src/abstract_socket.cc:181:1: note: in expansion of macro ‘NODE_MODULE’
181 | NODE_MODULE(abstract_socket, Initialize)
| ^~~~~~~~~~~
SOLINK_MODULE(target) Release/obj.target/bindings.node
COPY Release/bindings.node
make: Leaving directory '/home/pascal/Development/erg-mode/node_modules/abstract-socket/build'
npm WARN [email protected] No description
npm WARN [email protected] No repository field.
+ [email protected]
added 20 packages from 64 contributors and audited 508 packages in 13.424s
44 packages are looking for funding
run `npm fund` for details
found 2 high severity vulnerabilities
run `npm audit fix` to fix them, or `npm audit` for details
pascal@pascal-XPS-13-9370:~/Development/erg-mode$ npm --version
6.13.7
pascal@pascal-XPS-13-9370:~/Development/erg-mode$ node --version
v13.11.0
So I'm currently trying to get some characteristic values from a device, but unfortunately I can't fix this error. Using bluetoothctl I can easily extract the characteristic values, but with the package things fail.
const { bluetooth, destroy } = createBluetooth()
const adapter = await bluetooth.defaultAdapter()
const device = await adapter.waitDevice(uuid);
if(! await device.isConnected()) {
await device.connect();
}
console.log(await device.isConnected());
await device.gatt();
throw new Error(`interface not found in proxy object: ${name}`);
^
Error: interface not found in proxy object: org.bluez.GattService1
Got alerts from npm audit of 7 moderate severity vulnerabilities when using this package:
# npm audit report
request *
Severity: moderate
Server-Side Request Forgery in Request - https://github.com/advisories/GHSA-p8p7-x288-28g6
Depends on vulnerable versions of tough-cookie
No fix available
node_modules/request
node-gyp <=7.1.2
Depends on vulnerable versions of request
node_modules/node-gyp
usocket 0.2.2 - 0.3.0
Depends on vulnerable versions of node-gyp
node_modules/usocket
dbus-next *
Depends on vulnerable versions of usocket
Depends on vulnerable versions of xml2js
node_modules/dbus-next
node-ble >=0.0.2
Depends on vulnerable versions of dbus-next
node_modules/node-ble
tough-cookie <4.1.3
Severity: moderate
tough-cookie Prototype Pollution vulnerability - https://github.com/advisories/GHSA-72xf-g2v4-qvf3
No fix available
node_modules/tough-cookie
xml2js <0.5.0
Severity: moderate
xml2js is vulnerable to prototype pollution - https://github.com/advisories/GHSA-776f-qx25-q3cc
No fix available
node_modules/xml2js
7 moderate severity vulnerabilities
Sometimes the test suite crashes with the following error (observed on NodeJS v17.0.1 and v16.13.0)
> [email protected] test:jest
> jest --testPathIgnorePatterns=e2e.spec.js
PASS test/Adapter.spec.js
PASS test/BusHelper.spec.js
RUNS test/GattCharacteristic.spec.js
node: ../deps/uv/src/unix/core.c:258: uv__finish_close: Assertion `handle->flags & UV_HANDLE_CLOSING' failed.
Aborted (core dumped)
I'm transitioning from @abandonware/noble to node-ble.
I have the following code in noble to enable notifications:
let descriptors = characteristic.discoverDescriptorsAsync()
let descriptor = descriptors[0]
descriptor.once('valueWrite', this.sendConnectSequence.bind(this));
characteristic.on('data', this.onReceive.bind(this))
await descriptor.writeValueAsync(Buffer.from([0x01, 0x00]))
How do I achieve this with node-ble?
I currently have the characteristic and tried this:
await this.mepResponseChar.on('valuechanged', this.onMepResponse.bind(this))
await this.mepResponseChar.startNotifications()
but this isn't triggering any of the callbacks.
I'm assuming this because I need access to the descriptors?
My use-case involves multiple devices which may sporadically disconnect, and I need to intercept the disconnection event - as it appears in the bluetooth logs:
src/adapter.c:dev_disconnected()
How can I assign a callback on this event using node-ble?
I mean, something similar to the callback for intercepting notifications (once registered to a Notifications characteristic), e.g.:
characteristic.on('valuechanged', buffer => {
console.log(buffer);
});
Does the current version support this? If not, is there an easy way to add such a feature? It's an absolute must for noisy Bluetooth environments.
Hello guys
I had try to call startNotifications and got this error:
DBusError: Write not permitted
at _methodReturnHandlers.<computed> (/home/ricardo/Documentos/testes/scannerBlue/node_modules/dbus-next/lib/bus.js:339:27)
at handleMessage (/home/ricardo/Documentos/testes/scannerBlue/node_modules/dbus-next/lib/bus.js:98:11)
at EventEmitter.<anonymous> (/home/ricardo/Documentos/testes/scannerBlue/node_modules/dbus-next/lib/bus.js:147:9)
at EventEmitter.emit (events.js:314:20)
at /home/ricardo/Documentos/testes/scannerBlue/node_modules/dbus-next/lib/connection.js:112:14
at Socket.<anonymous> (/home/ricardo/Documentos/testes/scannerBlue/node_modules/dbus-next/lib/message.js:63:9)
at Socket.emit (events.js:314:20)
at emitReadable_ (_stream_readable.js:567:12)
at processTicksAndRejections (internal/process/task_queues.js:79:21) {
type: 'org.bluez.Error.NotPermitted',
text: 'Write not permitted',
reply: Message {
type: 3,
_sent: false,
_serial: 5364,
path: undefined,
interface: undefined,
member: undefined,
errorName: 'org.bluez.Error.NotPermitted',
replySerial: 171,
destination: ':1.401',
sender: ':1.5',
signature: 's',
body: [ 'Write not permitted' ],
flags: 1
}
}
I'm using ubuntu 20.04, mi band 4 and node-ble 1.2.0.
Trying to get Heart Rate Measure.
This is the code I had use:
await device.connect();
const rssi = await device.getRSSI();
const gattServer = await device.gatt();
const service = await gattServer.getPrimaryService('0000180d-0000-1000-8000-00805f9b34fb');
const characteristic = await service.getCharacteristic('00002a37-0000-1000-8000-00805f9b34fb');
const flags = await characteristic.getFlags();
characteristic.on('valuechanged', buffer => {
console.log(buffer);
device.disconnect();
});
await characteristic.startNotifications();
Can anyone tells me why it is throwing this error ?
The README has instructions about setting permissions here: https://github.com/chrvadala/node-ble#provide-permissions
In order to allow a connection with the DBus daemon, you have to set up right permissions.
Create the file /etc/dbus-1/system.d/node-ble.conf with the following content (customize with userid)
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy user="%userid%"> <allow own="org.bluez"/> <allow send_destination="org.bluez"/> <allow send_interface="org.bluez.GattCharacteristic1"/> <allow send_interface="org.bluez.GattDescriptor1"/> <allow send_interface="org.freedesktop.DBus.ObjectManager"/> <allow send_interface="org.freedesktop.DBus.Properties"/> </policy> </busconfig>
I am adding node-ble to an electron app, which is using electron-forge to create a .deb
package.
My plan to set up this file is for the deb installer to run a simple script during post-install:
#!/bin/bash
cp conf/node-ble.conf /etc/dbus-1/system.d/node-ble.conf
My issue is that I can't know at the time of packaging, what the username of whoever is installing the package will be.
Would it work to specify a permissions group instead of a username for the policy? And just require the user to be in that group?
...
<policy group="bluetooth">
...
Should be pretty straightforward.
https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.50/doc/advertising-api.txt
I would do implementation for my own project, but my code is usually not neat and clean enough for pulls.
Some BLE lights I have do expect commands as an advertisment with manufacturer-specific data.
Does it support/handle encrypted characteristics ?
I experimented a bit and knowing the UUID of the device I can work with it.
As I found I can get the list of the UUIDs of the discovered devices.
How can I find out the name of the device belonging to the UUID?
Background: I like to connect to a special device who's name I will know but not its UUID.
A hint would be appreciated.
Thanks DrCWO
When subscribing to a notify attribute on a BLE device with startNotifications() the created listener is not removed when a device.disconnect() is called.
This will cause the subsequent connect and subscription to have corrupt data in the valuedchanged event.
e.g.
`
const readNotify = await service.getCharacteristic('aUUid');
await readNotify .startNotifications();
readNotify.on('valuechanged',buffer=>{ buffer has some data});
await device.disconnect();
await device.connect();
await readNotify .startNotifications();
readNotify.on('valuechanged',buffer=>{ buffer has previous data and some new data});
`
In order to closer follow method signatures of the web bluetooth API, i propose the following changes:
writeValue
, writeValueWithResponse
and writeValueWithoutResponse
should be of type BufferSource
readValue
should be Promise<DataView>
This would make it easier to switch between web and node implementations.
I could implement this if you're interested in merging.
I'm trying to use it on a raspberry pi 3B, but I got an error from DBus
const {createBluetooth} = require('node-ble')
async function main () {
const {bluetooth, destroy} = createBluetooth()
const adapter = await bluetooth.defaultAdapter()
const discovery = await adapter.startDiscovery()
console.log('discovering')
console.log(await adapter.devices());
const device = await adapter.waitDevice('04:EE:03:B1:47:DF')
await device.connect()
console.log('connected')
const gattServer = await device.gatt()
console.log(await gattServer.services());
}
main()
.then(console.log)
.catch(console.error)
busconfig (also tried with pi
user)
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.GattCharacteristic1"/>
<allow send_interface="org.bluez.GattDescriptor1"/>
<allow send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
</busconfig>
discovering
[ '04:EE:03:B1:47:DF' ]
DBusError: Software caused connection abort
at _methodReturnHandlers.<computed> (/home/pi/bergepi/node_modules/dbus-next/lib/bus.js:339:27)
at handleMessage (/home/pi/bergepi/node_modules/dbus-next/lib/bus.js:98:11)
at EventEmitter.<anonymous> (/home/pi/bergepi/node_modules/dbus-next/lib/bus.js:147:9)
at EventEmitter.emit (events.js:315:20)
at EventEmitter.emit (domain.js:482:12)
at /home/pi/bergepi/node_modules/dbus-next/lib/connection.js:112:14
at Socket.<anonymous> (/home/pi/bergepi/node_modules/dbus-next/lib/message.js:63:9)
at Socket.emit (events.js:315:20)
at Socket.EventEmitter.emit (domain.js:482:12)
at emitReadable_ (_stream_readable.js:556:12) {
type: 'org.bluez.Error.Failed',
text: 'Software caused connection abort',
reply: Message {
type: 3,
_sent: false,
_serial: 58,
path: undefined,
interface: undefined,
member: undefined,
errorName: 'org.bluez.Error.Failed',
replySerial: 12,
destination: ':1.26',
sender: ':1.13',
signature: 's',
body: [ 'Software caused connection abort' ],
flags: 1
}
}
BlueZ version 5.50
pi@raspberrypi:/etc/dbus-1/system.d $ bluetoothctl -v
bluetoothctl: 5.50
Here is my code where the gatt.services()
function returns nothing:
const connectDevice = async (deviceName) => {
const adapter = await bluetooth.defaultAdapter()
if (! await adapter.isDiscovering())
await adapter.startDiscovery()
var devices = await adapter.devices()
for (deviceMac of devices) {
const device = await adapter.waitDevice(deviceMac)
const name = await device.getAlias()
if (name == deviceName) {
console.log(deviceName + " found !!") // the device is found
const gatt = await device.gatt()
var services = await gatt.services();
console.log(services) // give me nothing here
} else {
device.disconnect()
}
}
}
connectDevice("myDevice")
Also i'm running that on a raspberry pi 4 with sudo and nodeJS v10.21.0 from apt if ever that matters.
I try to implement reconnecting device after loosing bluetooth connection (for example because of leaving the bluetooth range). Therefore I register a listener with "Device.on('disconnect', ...)". One issue is if I call this after "Adapter.waitDevice(...)" promise on the received device, no events will be emitted. Therefore I call "Adapter.getDevice(...)" and register disconnect listener. Now the event is emitted. But for some reasons I like remove the "disconnect event listener" from Device, but this does not work.
I can see in your source, that the 'disconnect' - event is forwarded from BusHelper-propertiesChanged-event. Is this the reason that I can not unregister the listener for 'disconnect'-events on Device?
(node:1883) UnhandledPromiseRejectionWarning: DBusError: No such property 'rssi'
at _methodReturnHandlers.(anonymous function) (/root/bergepi/node_modules/dbus-next/lib/bus.js:339:27)
at handleMessage (/root/bergepi/node_modules/dbus-next/lib/bus.js:98:11)
at EventEmitter.MessageBus.conn.on (/root/bergepi/node_modules/dbus-next/lib/bus.js:147:9)
at EventEmitter.emit (events.js:198:13)
at EventEmitter.emit (domain.js:448:20)
at /root/bergepi/node_modules/dbus-next/lib/connection.js:112:14
at Socket.<anonymous> (/root/bergepi/node_modules/dbus-next/lib/message.js:63:9)
at Socket.emit (events.js:198:13)
at Socket.EventEmitter.emit (domain.js:448:20)
at emitReadable_ (_stream_readable.js:555:12)
(node:1883) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 9)
Hi out there,
I got an issue I cannot solve by myself so some help would be appreciated. Here my configuration.
I am on a Raspberry with Raspbian. I have a Microsoft Surface Dial (which is a HCI device). The Surface Dial is paired with the Raspberry. I did this with bluetoothctl.
I like to get notifications directly from D-Bus so I disabled the HCI service in /lib/systemd/system/bluetooth.service
by setting ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,input,hog
to be able to access the HCI service via D-Bus.
In my first implementation I used RAW HCI to get the data but after a disconnect of the device it takes more than three seconds after touching the Surface Dial until the HCI-Service was available again. This is much to long as rotating the dial a sudden reaction must occur. This is why I tried to use D-Bus. So in this implementation here I get the connect event immediately but I was not able to get the data any more 👎
I used the code below to get notification from the HCI Report Characteristic of the Surface Dial but it failed:
var deviceUuid = '70:BC:10:87:C1:B8';
var genericServiceUuid = '1812';
var genericCharacteristicUuid = '2a4d';
async function startBleServer() {
console.log('************************************** Start BLE-Server: ' + deviceUuid);
var { createBluetooth } = require('node-ble');
var { bluetooth, destroy } = createBluetooth();
var adapter = await bluetooth.defaultAdapter();
console.log('adapter: ');
// discover surfache Dial
if (! await adapter.isDiscovering()) {
console.log('Start discovery: ');
await adapter.startDiscovery()
}
console.log('Wait for device: ' + deviceUuid);
var device = await adapter.waitDevice(deviceUuid);
device.on('disconnect', function () {
console.log('######## Ble >>>>> EVENT: ' + 'Device disconnected');
})
device.on('connect', function () {
console.log('######## Ble >>>>> EVENT: ' + 'Device connected');
})
console.log('stop discovery');
await adapter.stopDiscovery()
console.log('discovered, wait till connected');
await device.connect()
console.log('connected, wait for GATT server');
var gattServer = await device.gatt();
console.log('gatt server ready!');
// Get service UUIDs and search for service 1812
var myServices = await gattServer.services();
var hciServiceUuid = null;
for (var i = 0; i < myServices.length; i++) {
if (myServices[i].indexOf(genericServiceUuid + '-') >= 0) hciServiceUuid = myServices[i];
}
console.log('HCI service UUID: ' + hciServiceUuid);
// If I found the UUID of the HCI-Service get the HCI service
if (hciServiceUuid) {
//********************* get hciService
var hciService = await gattServer.getPrimaryService(hciServiceUuid);
console.log('Serice ' + hciServiceUuid + ' discovered');
// get the characteristic UUIDs
var myCharacteristics = await hciService.characteristics();
console.log('Got HCI characteristic UUIDs');
// find Reprt Characteristi UUIDs suchen
var reportCharacteristicUuid = null;
for (var i = 0; i < myCharacteristics.length; i++) {
if (myCharacteristics[i].indexOf(genericCharacteristicUuid + '-') >= 0) reportCharacteristicUuid = myCharacteristics[i];
}
console.log('HCI Characteristic UUID' + reportCharacteristicUuid);
// get Report Characteristic
var reportCharacteristic = await hciService.getCharacteristic(reportCharacteristicUuid);
console.log('Report Characteristics ' + reportCharacteristicUuid + ' found');
// Setup callback for Notifications
reportCharacteristic.on('valuechanged', buffer => {
console.log('Data received');
console.log(buffer)
})
console.log('callback set');
await reportCharacteristic.startNotifications();
console.log('Notification started');
}
}
startBleServer();
Running this code anything worked fine at the beginning. The service and the characteristic were found. But trying to setup the notification I get an error message. See output below:
root@DrCWO:/home/pi# node xx_ble.js
************************************** Start BLE-Server: 70:BC:10:87:C1:B8
adapter:
Start discovery:
Wait for device: 70:BC:10:87:C1:B8
stop discovery
discovered, wait till connected
######## Ble >>>>> EVENT: Device connected
connected, wait for GATT server
gatt server ready!
HCI service UUID: 00001812-0000-1000-8000-00805f9b34fb
Serice 00001812-0000-1000-8000-00805f9b34fb discovered
Got HCI characteristic UUIDs
HCI Characteristic UUID00002a4d-0000-1000-8000-00805f9b34fb
Report Characteristics 00002a4d-0000-1000-8000-00805f9b34fb found
callback set
/home/pi/rooExtend/node_modules/dbus-next/lib/bus.js:343
return reject(new DBusError(reply.errorName, reply.body[0], reply));
^
DBusError: Operation is not supported
at _methodReturnHandlers.<computed> (/home/pi/rooExtend/node_modules/dbus-next/lib/bus.js:343:27)
at handleMessage (/home/pi/rooExtend/node_modules/dbus-next/lib/bus.js:101:11)
at EventEmitter.<anonymous> (/home/pi/rooExtend/node_modules/dbus-next/lib/bus.js:151:9)
at EventEmitter.emit (node:events:513:28)
at /home/pi/rooExtend/node_modules/dbus-next/lib/connection.js:132:14
at USocket.<anonymous> (/home/pi/rooExtend/node_modules/dbus-next/lib/message.js:65:9)
at USocket.emit (node:events:513:28)
at emitReadable_ (node:internal/streams/readable:590:12)
at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
type: 'org.bluez.Error.NotSupported',
text: 'Operation is not supported',
reply: Message {
type: 3,
_sent: false,
_serial: 3374,
path: undefined,
interface: undefined,
member: undefined,
errorName: 'org.bluez.Error.NotSupported',
replySerial: 100,
destination: ':1.3859',
sender: ':1.3820',
signature: 's',
body: [ 'Operation is not supported' ],
flags: 1
}
}
Node.js v18.7.0
Any idea why I cannot start the notification?
Best DrCWO
I'm on Ubuntu 22.04.
Running this command (from the Useful commands list)...
rm -r /var/lib/bluetooth/*
... prevents bluetooth from restarting after a reboot. All attempts to get the bluetooth service to start fail.
For example all suggestions in this question fail: https://askubuntu.com/questions/1403817/i-cant-turn-on-bluetooth-in-ubuntu-22-04-lts
The service status has a 226/NAMESPACE
error message:
$ sudo systemctl status bluetooth.service
× bluetooth.service - Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Sun 2024-04-21 19:08:23 CEST; 2min 11s ago
Docs: man:bluetoothd(8)
Main PID: 9746 (code=exited, status=226/NAMESPACE)
CPU: 12ms
Apr 21 19:08:23 bo-didley systemd[1]: bluetooth.service: Scheduled restart job, restart counter is at 5.
Apr 21 19:08:23 bo-didley systemd[1]: Stopped Bluetooth service.
Apr 21 19:08:23 bo-didley systemd[1]: bluetooth.service: Start request repeated too quickly.
Apr 21 19:08:23 bo-didley systemd[1]: bluetooth.service: Failed with result 'exit-code'.
Apr 21 19:08:23 bo-didley systemd[1]: Failed to start Bluetooth service.
Hunting for that error brings up this rather strange answer which suggests running:
sudo install -dm700 /var/lib/bluetooth
This fixes the problem (after a further reboot). If that is the correct command to re-instate the cache, could I suggest adding it to the readme?
Hi @chrvadala,
Thank you for this project. Is there a way to discover devices by services? Is there a way to get the name of devices instead of the uuid when calling getDevices()?
Thanks
I am implemented connecting heartrate sensor with another API (@ionic-nativ/bluetooth-le) successful. Now I try implementing connecting the same heartrate sensor with your API on Raspberry Pi. Connecting and start notification works well. But I don't know how I must interpret the incoming data. In "@ionic-native/bluetooth-le" there is a function "encodeStringToBytes(string): Uint8Array" (the incoming data are strings ). Then I can interpret the Uint8Array as described in Bluetooth specification.
Your interface deliver "Buffer". I don't know, how I must interpret this. Can you help me?
Hi,
first thank you for making node-ble. Hope it will help me connecting to my devices 👍
I have a question but maybe it is also an issue, I don't know.
I like to start discovery and get the devices discovered so I can decide to which one I like to connect. In my example below I use myAdapter.devices()
but is only returns an empty object. Any ideas?
I have bluetoothctl
open at the same time and see that discovery starts. I also see the device announcing its UUID after some time but my code shows nothing.
Here my code:
'use strict';
async function main() {
const { createBluetooth } = require('node-ble');
const { bluetooth, destroy } = createBluetooth();
var myAdapter = await bluetooth.defaultAdapter();
console.log(myAdapter.adapter);
if (! await myAdapter.isDiscovering()) {
console.log ("startDiscovery");
myAdapter.startDiscovery();
}
await myAdapter.isDiscovering();
console.log ('isDiscovering');
showDevices();
function showDevices () {
console.log (JSON.stringify(myAdapter.devices(), null,2));
setTimeout (showDevices, 2000);
}
}
main();
I can connect to one of my devices using its UUID (see working code blow). But before being able to do so I first have to find out the UUID which I expected to get with myAdapter.devices()
.
const device = await adapter.waitDevice('70:BC:10:86:85:C5');
await device.connect();
Support would be appreciated.
Best DrCWO
Hi;
Very frustrated with noble so testing out your libraries.
I am able to discover my devices, but how would I use this to listen for beacon transmissions and read the device manufacturer's data ?
I don't see any method or property related to the manufacturer's data.
Id not want to connect to the device, but to simply read the raw data and decode it ?
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.