Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@stoprocent/noble

Package Overview
Dependencies
Maintainers
0
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stoprocent/noble

A Node.js BLE (Bluetooth Low Energy) central library.

  • 1.15.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
5.6K
increased by31.53%
Maintainers
0
Weekly downloads
 
Created
Source

noble

npm version npm downloads Build Status

A Node.js BLE (Bluetooth Low Energy) central module.

Want to implement a peripheral? Check out @stoprocent/bleno.

About This Fork

This fork of noble was created to introduce several key improvements and new features:

  1. HCI UART Support: This version enables HCI UART communication through the @stoprocent/node-bluetooth-hci-socket dependency, allowing more flexible use of Bluetooth devices across platforms.

  2. macOS Native Bindings Fix: I have fixed the native bindings for macOS, ensuring better compatibility and performance on Apple devices.

  3. New Features:

  • A setAddress function has been added, allowing users to set the MAC address of the central device.
  • A connect(...)/connectAsync(...) function has been added, allowing users to connect directly to specific device by address/identifier without a need to prior scan.
  • Additionally, I plan to add raw L2CAP channel support, enhancing low-level Bluetooth communication capabilities.

If you appreciate these enhancements and the continued development of this project, please consider supporting my work.

Buy me a coffee

Install

npm install @stoprocent/noble

Usage

const noble = require('@stoprocent/noble');

Documentation

Quick Start Example

// Read the battery level of the first found peripheral exposing the Battery Level characteristic

const noble = require('@stoprocent/noble');

noble.on('stateChange', async (state) => {
  if (state === 'poweredOn') {
    await noble.startScanningAsync(['180f'], false);
  }
});

noble.on('discover', async (peripheral) => {
  await noble.stopScanningAsync();
  await peripheral.connectAsync();
  const {characteristics} = await peripheral.discoverSomeServicesAndCharacteristicsAsync(['180f'], ['2a19']);
  const batteryLevel = (await characteristics[0].readAsync())[0];

  console.log(`${peripheral.address} (${peripheral.advertisement.localName}): ${batteryLevel}%`);

  await peripheral.disconnectAsync();
  process.exit(0);
});

Use Noble With BLE5 Extended Features With HCI

const noble = require('@stoprocent/noble/with-custom-binding')({extended: true});

Installation

Prerequisites

UART (Any OS)

Please refer to https://github.com/stoprocent/node-bluetooth-hci-socket#uartserial-any-os

Example 1 (UART port spcified as enviromental variable)
$ export BLUETOOTH_HCI_SOCKET_UART_PORT=/dev/tty...
$ export BLUETOOTH_HCI_SOCKET_UART_BAUDRATE=1000000

NOTE: BLUETOOTH_HCI_SOCKET_UART_BAUDRATE defaults to 1000000 so only needed if different.

const noble = require('@stoprocent/noble');
Example 2 (UART port spcified in bindParams)
$ export BLUETOOTH_HCI_SOCKET_FORCE_UART=1
const noble = require('@stoprocent/noble/with-custom-binding') ( { 
  bindParams: { 
    uart: { 
      port: '/dev/tty...', 
      baudRate: 1000000
    } 
  } 
} );

NOTE: There is a UART code example in the /examples directory.

OS X
  • Install Xcode
  • On newer versions of OSX, allow bluetooth access on the terminal app: "System Preferences" —> "Security & Privacy" —> "Bluetooth" -> Add terminal app (see Sandboxed terminal)
Linux
  • Kernel version 3.6 or above
  • libbluetooth-dev needs to be installed. For instructions for specific distributions, see below.
  • To set the necessary privileges to run without sudo, see this section. This is required for all distributions (Raspbian, Ubuntu, Fedora, etc). You will not get any errors if running without sudo, but nothing will happen.
Ubuntu, Debian, Raspbian

See the generic Linux notes above first.

sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev

Make sure node is on your PATH. If it's not, some options:

If you are having trouble connecting to BLE devices on a Raspberry Pi, you should disable the pnat plugin. Add the following line at the bottom of /etc/bluetooth/main.conf:

DisablePlugins=pnat

Then restart the system.

See Issue #425 · OpenWonderLabs/homebridge-switchbot.

Fedora and other RPM-based distributions

See the generic Linux notes above first.

sudo yum install bluez bluez-libs bluez-libs-devel
Intel Edison

See the generic Linux notes above first.

See Configure Intel Edison for Bluetooth LE (Smart) Development.

FreeBSD

Make sure you have GNU Make:

sudo pkg install gmake

Disable automatic loading of the default Bluetooth stack by putting no-ubt.conf into /usr/local/etc/devd/no-ubt.conf and restarting devd (sudo service devd restart).

Unload ng_ubt kernel module if already loaded:

sudo kldunload ng_ubt

Make sure you have read and write permissions on the /dev/usb/* device that corresponds to your Bluetooth adapter.

Windows

node-gyp requirements for Windows

Install the required tools and configurations using Microsoft's windows-build-tools from an elevated PowerShell or cmd.exe (run as Administrator).

npm install --global --production windows-build-tools

node-bluetooth-hci-socket prerequisites

  • Compatible Bluetooth 5.0 Zephyr HCI-USB adapter (you need to add BLUETOOTH_HCI_SOCKET_USB_VID and BLUETOOTH_HCI_SOCKET_USB_PID to the process env)
  • Compatible Bluetooth 4.0 USB adapter
  • WinUSB driver setup for Bluetooth 4.0 USB adapter, using Zadig tool

See @don's setup guide on Bluetooth LE with Node.js and Noble on Windows

Docker

Make sure your container runs with --network=host options and all specific environment preriquisites are verified.

Installing and using the package

npm install @stoprocent/noble

In Windows OS add your custom hci-usb dongle to the process env

set BLUETOOTH_HCI_SOCKET_USB_VID=xxx
set BLUETOOTH_HCI_SOCKET_USB_PID=xxx
const noble = require('@stoprocent/noble');

API docs

All operations have two API variants – one expecting a callback, one returning a Promise (denoted by Async suffix).

Additionally, there are events corresponding to each operation (and a few global events).

For example, in case of the "discover services" operation of Peripheral:

  • There's a discoverServices method expecting a callback:
    peripheral.discoverServices((error, services) => {
      // callback - handle error and services
    });
    
  • There's a discoverServicesAsync method returning a Promise:
    try {
      const services = await peripheral.discoverServicesAsync();
      // handle services
    } catch (e) {
      // handle error
    }
    
  • There's a servicesDiscover event emitted after services are discovered:
    peripheral.once('servicesDiscover', (services) => {
      // handle services
    });
    

API structure:

Scanning and discovery

Event: Adapter state changed
noble.on('stateChange', callback(state));

state can be one of:

  • unknown
  • resetting
  • unsupported
  • unauthorized
  • poweredOff
  • poweredOn
Set address
noble.setAddress('00:11:22:33:44:55'); // set adapter's mac address

NOTE: Curently this feature is only supported on HCI as it's using vendor specific commands. Source of the commands is based on the BlueZ bdaddr.c. NOTE: noble.state must be poweredOn before address can be set. noble.on('stateChange', callback(state)); can be used to listen for state change events.

Start scanning
noble.startScanning(); // any service UUID, no duplicates


noble.startScanning([], true); // any service UUID, allow duplicates


var serviceUUIDs = ['<service UUID 1>', ...]; // default: [] => all
var allowDuplicates = falseOrTrue; // default: false

noble.startScanning(serviceUUIDs, allowDuplicates[, callback(error)]); // particular UUIDs

NOTE: noble.state must be poweredOn before scanning is started. noble.on('stateChange', callback(state)); can be used to listen for state change events.

Event: Scanning started
noble.on('scanStart', callback);

The event is emitted when:

  • Scanning is started
  • Another application enables scanning
  • Another application changes scanning settings
Stop scanning
noble.stopScanning();
Event: Scanning stopped
noble.on('scanStop', callback);

The event is emitted when:

  • Scanning is stopped
  • Another application stops scanning
Connect by UUID

The connect function is used to establish a Bluetooth Low Energy connection to a peripheral device using its UUID. It provides both callback-based and Promise-based interfaces.

Usage
// Callback-based usage
connect(peripheralUuid: string, options?: object, callback?: (error?: Error, peripheral: Peripheral) => void): void;

// Promise-based usage
connectAsync(peripheralUuid: string, options?: object): Promise<Peripheral>;
Parameters
  • peripheralUuid: The UUID of the peripheral to connect to.
  • options: Optional parameters for the connection (this may include connection interval, latency, supervision timeout, etc.).
  • callback: An optional callback that returns an error or the connected peripheral object.
Description

The connect function initiates a connection to a BLE peripheral. The function immediately returns, and the actual connection result is provided asynchronously via the callback or Promise. If the peripheral is successfully connected, a Peripheral object representing the connected device is provided.

Example
const noble = require('@stoprocent/noble');

// Using callback
noble.connect('1234567890abcdef', {}, (error, peripheral) => {
  if (error) {
    console.error('Connection error:', error);
  } else {
    console.log('Connected to:', peripheral.uuid);
  }
});

// Using async/await
async function connectPeripheral() {
  try {
    const peripheral = await noble.connectAsync('1234567890abcdef');
    console.log('Connected to:', peripheral.uuid);
  } catch (error) {
    console.error('Connection error:', error);
  }
}
connectPeripheral();
Event: Peripheral discovered
noble.on('discover', callback(peripheral));
  • peripheral:
    {
      id: '<id>',
      address: '<BT address'>, // Bluetooth Address of device, or 'unknown' if not known
      addressType: '<BT address type>', // Bluetooth Address type (public, random), or 'unknown' if not known
      connectable: trueOrFalseOrUndefined, // true or false, or undefined if not known
      advertisement: {
        localName: '<name>',
        txPowerLevel: someInteger,
        serviceUuids: ['<service UUID>', ...],
        serviceSolicitationUuid: ['<service solicitation UUID>', ...],
        manufacturerData: someBuffer, // a Buffer
        serviceData: [
            {
                uuid: '<service UUID>',
                data: someBuffer // a Buffer
            },
            // ...
        ]
      },
      rssi: integerValue,
      mtu: integerValue // MTU will be null, until device is connected and hci-socket is used
    };
    

Note: On macOS, the address will be set to '' if the device has not been connected previously.

Event: Warning raised
noble.on('warning', callback(message));

Reset device

noble.reset()

Peripheral

Connect
peripheral.connect([callback(error)]);

Some of the bluetooth devices doesn't connect seamlessly, may be because of bluetooth device firmware or kernel. Do reset the device with noble.reset() API before connect API.

Event: Connected
peripheral.once('connect', callback);
Cancel a pending connection
peripheral.cancelConnect();
// Will emit a 'connect' event with error
Disconnect
peripheral.disconnect([callback(error)]);
Event: Disconnected
peripheral.once('disconnect', callback);
Update RSSI
peripheral.updateRssi([callback(error, rssi)]);
Event: RSSI updated
peripheral.once('rssiUpdate', callback(rssi));
Discover services
peripheral.discoverServices(); // any service UUID

var serviceUUIDs = ['<service UUID 1>', ...];
peripheral.discoverServices(serviceUUIDs[, callback(error, services)]); // particular UUIDs
Discover all services and characteristics
peripheral.discoverAllServicesAndCharacteristics([callback(error, services, characteristics)]);
Discover some services and characteristics
var serviceUUIDs = ['<service UUID 1>', ...];
var characteristicUUIDs = ['<characteristic UUID 1>', ...];
peripheral.discoverSomeServicesAndCharacteristics(serviceUUIDs, characteristicUUIDs, [callback(error, services, characteristics));
Event: Services discovered
peripheral.once('servicesDiscover', callback(services));
Read handle
peripheral.readHandle(handle, callback(error, data));
Event: Handle read
peripheral.once('handleRead<handle>', callback(data)); // data is a Buffer

<handle> is the handle identifier.

Write handle
peripheral.writeHandle(handle, data, withoutResponse, callback(error));
Event: Handle written
peripheral.once('handleWrite<handle>', callback());

<handle> is the handle identifier.

Service

Discover included services
service.discoverIncludedServices(); // any service UUID

var serviceUUIDs = ['<service UUID 1>', ...];
service.discoverIncludedServices(serviceUUIDs[, callback(error, includedServiceUuids)]); // particular UUIDs
Event: Included services discovered
service.once('includedServicesDiscover', callback(includedServiceUuids));
Discover characteristics
service.discoverCharacteristics() // any characteristic UUID

var characteristicUUIDs = ['<characteristic UUID 1>', ...];
service.discoverCharacteristics(characteristicUUIDs[, callback(error, characteristics)]); // particular UUIDs
Event: Characteristics discovered
service.once('characteristicsDiscover', callback(characteristics));
  • characteristics
    {
      uuid: '<uuid>',
      properties: ['...'] // 'broadcast', 'read', 'writeWithoutResponse', 'write', 'notify', 'indicate', 'authenticatedSignedWrites', 'extendedProperties'
    };
    

Characteristic

Read
characteristic.read([callback(error, data)]);
Event: Data read
characteristic.on('data', callback(data, isNotification));

characteristic.once('read', callback(data, isNotification)); // legacy

Emitted when:

  • Characteristic read has completed, result of characteristic.read(...)
  • Characteristic value has been updated by peripheral via notification or indication, after having been enabled with characteristic.notify(true[, callback(error)])

Note: isNotification event parameter value MAY be undefined depending on platform. The parameter is deprecated after version 1.8.1, and not supported on macOS High Sierra and later.

Write
characteristic.write(data, withoutResponse[, callback(error)]); // data is a Buffer, withoutResponse is true|false
  • withoutResponse:
    • false: send a write request, used with "write" characteristic property
    • true: send a write command, used with "write without response" characteristic property
Event: Data written
characteristic.once('write', withoutResponse, callback());

Emitted when characteristic write has completed, result of characteristic.write(...).

Broadcast
characteristic.broadcast(broadcast[, callback(error)]); // broadcast is true|false
Event: Broadcast sent
characteristic.once('broadcast', callback(state));

Emitted when characteristic broadcast state changes, result of characteristic.broadcast(...).

Subscribe
characteristic.subscribe([callback(error)]);

Subscribe to a characteristic.

Triggers data events when peripheral sends a notification or indication. Use for characteristics with "notify" or "indicate" properties.

Event: Notification received
characteristic.once('notify', callback(state));

Emitted when characteristic notification state changes, result of characteristic.notify(...).

Unsubscribe
characteristic.unsubscribe([callback(error)]);

Unsubscribe from a characteristic.

Use for characteristics with "notify" or "indicate" properties

Discover descriptors
characteristic.discoverDescriptors([callback(error, descriptors)]);
Event: Descriptors discovered
characteristic.once('descriptorsDiscover', callback(descriptors));
  • descriptors:
    [
      {
        uuid: '<uuid>'
      },
      // ...
    ]
    

Descriptor

Read value
descriptor.readValue([callback(error, data)]);
Event: Value read
descriptor.once('valueRead', data); // data is a Buffer
Write value
descriptor.writeValue(data[, callback(error)]); // data is a Buffer
Event: Value written
descriptor.once('valueWrite');

Advanced usage

Override default bindings

By default, noble will select appropriate Bluetooth device bindings based on your platform. You can provide custom bindings using the with-bindings module.

var noble = require('@stoprocent/noble/with-bindings')(require('./my-custom-bindings'));

Running without root/sudo (Linux-specific)

Run the following command:

sudo setcap cap_net_raw+eip $(eval readlink -f `which node`)

This grants the node binary cap_net_raw privileges, so it can start/stop BLE advertising.

Note: The above command requires setcap to be installed. It can be installed the following way:

  • apt: sudo apt-get install libcap2-bin
  • yum: su -c \'yum install libcap2-bin\'

Multiple Adapters (Linux-specific)

hci0 is used by default.

To override, set the NOBLE_HCI_DEVICE_ID environment variable to the interface number.

For example, to specify hci1:

sudo NOBLE_HCI_DEVICE_ID=1 node <your file>.js

If you are using multiple HCI devices in one setup you can run two instances of noble with different binding configurations by initializing them seperatly in code:

const HCIBindings = require('@stoprocent/noble/lib/hci-socket/bindings');
const Noble = require('@stoprocent/noble/lib/noble');

const params = {
  deviceId: 0,
  userChannel: true,
  extended: false //ble5 extended features
};

const noble = new Noble(new HCIBindings(params));

Reporting all HCI events (Linux-specific)

By default, noble waits for both the advertisement data and scan response data for each Bluetooth address. If your device does not use scan response, the NOBLE_REPORT_ALL_HCI_EVENTS environment variable can be used to bypass it.

sudo NOBLE_REPORT_ALL_HCI_EVENTS=1 node <your file>.js

bleno compatibility (Linux-specific)

By default, noble will respond with an error whenever a GATT request message is received. If your intention is to use bleno in tandem with noble, the NOBLE_MULTI_ROLE environment variable can be used to bypass this behaviour.

Note: this requires a Bluetooth 4.1 adapter.

sudo NOBLE_MULTI_ROLE=1 node <your file>.js

Common problems

Maximum simultaneous connections

This limit is imposed by the Bluetooth adapter hardware as well as its firmware.

Platform
OS X 10.11 (El Capitan)6
Linux/Windows - Adapter-dependent5 (CSR based adapter)

Sandboxed terminal

On newer versions of OSX, the terminal app is sandboxed to not allow bluetooth connections by default. If you run a script that tries to access it, you will get an Abort trap: 6 error.

To enable bluetooth, go to "System Preferences" —> "Security & Privacy" —> "Bluetooth" -> Add your terminal into allowed apps.

Adapter-specific known issues

Some BLE adapters cannot connect to a peripheral while they are scanning (examples below). You will get the following messages when trying to connect:

Sena UD-100 (Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)): Error: Command disallowed

Intel Dual Band Wireless-AC 7260 (Intel Corporation Wireless 7260 (rev 73)): Error: Connection Rejected due to Limited Resources (0xd)

You need to stop scanning before trying to connect in order to solve this issue.

Keywords

FAQs

Package last updated on 01 Oct 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc