@nabeeln7/noble
    TypeScript icon, indicating that this package has built-in type declarations

    1.0.1 • Public • Published

    noble

    Build Status Gitter OpenCollective OpenCollective

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

    Want to implement a peripheral? Check out bleno.

    Note: macOS / Mac OS X, Linux, FreeBSD and Windows are currently the only supported OSes.

    Documentation

    Quick Start Example

    // Read the battery level of the first found peripheral exposing the Battery Level characteristic
    
    const noble = require('@abandonware/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);
    });

    Installation

    Prerequisites

    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:

    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 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 sur your container runs with --network=host options and all specific environment preriquisites are verified.

    Installing and using the package

    npm install @abandonware/noble
    const noble = require('@abandonware/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

    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

    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('@abandonware/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('@abandonware/noble/lib/hci-socket/bindings');
    const Noble = require('@abandonware/noble/lib/noble');
    
    const params = {
      deviceId: 0,
      userChannel: true
    };
    
    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-dependent 5 (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.

    Backers

    Support us with a monthly donation and help us continue our activities. [Become a backer]

    Sponsors

    Become a sponsor and get your logo on our README on Github with a link to your site. [Become a sponsor]

    Useful links

    License

    Copyright (C) 2015 Sandeep Mistry sandeep.mistry@gmail.com

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    Analytics

    Install

    npm i @nabeeln7/noble

    DownloadsWeekly Downloads

    25

    Version

    1.0.1

    License

    MIT

    Unpacked Size

    414 kB

    Total Files

    76

    Last publish

    Collaborators

    • nabeeln7