npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

bluetooth-le-temp-dont-use

v0.4.2

Published

Capacitor Bluetooth Low Energy Plugin

Downloads

5

Readme

Maintainers

| Maintainer | GitHub | Social | | ------------- | ----------------------------------- | ------ | | Patrick Wespi | pwespi | |

Introduction

This is a Capacitor plugin for Bluetooth Low Energy. It supports the web, Android and iOS.

The goal is to support the same features on all platforms. Therefore the Web Bluetooth API is taken as a guidline for what features to implement.

For support of Web Bluetooth in various browsers, see implementation status.

Below is an index of all the methods available.

Installation

npm install @capacitor-community/bluetooth-le
npx cap sync

iOS

On iOS, add the NSBluetoothAlwaysUsageDescription to Info.plist, otherwise the app will crash when trying to use Bluetooth (see here).

If the app needs to use Bluetooth while it is in the background, you also have to add bluetooth-central to UIBackgroundModes (for details see here).

./ios/App/App/Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
  ...
+	<key>NSBluetoothAlwaysUsageDescription</key>
+	<string>Uses Bluetooth to connect and interact with peripheral BLE devices.</string>
+	<key>UIBackgroundModes</key>
+	<array>
+		<string>bluetooth-central</string>
+	</array>
</dict>
</plist>

Android

On Android, register the plugin in your main activity:

./android/app/src/main/java/<PATH>/MainActivity.java:

+ import com.capacitorjs.community.plugins.bluetoothle.BluetoothLe;

public class MainActivity extends BridgeActivity {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initializes the Bridge
    this.init(
        savedInstanceState,
        new ArrayList<Class<? extends Plugin>>() {
          {
            // Additional plugins you've installed go here
            // Ex: add(TotallyAwesomePlugin.class);
+           add(BluetoothLe.class);
          }
        }
      );
  }
}

Configuration

You can configure the strings that are displayed in the device selection dialog on iOS and Android when using requestDevice():

./capacitor.config.json:

{
  "...": "other configuration",
  "plugins": {
    "BluetoothLe": {
      "displayStrings": {
        "scanning": "Am Scannen...",
        "cancel": "Abbrechen",
        "availableDevices": "Verfügbare Geräte",
        "noDeviceFound": "Kein Gerät gefunden"
      }
    }
  }
}

The default values are:

{
  "plugins": {
    "BluetoothLe": {
      "displayStrings": {
        "scanning": "Scanning...",
        "cancel": "Cancel",
        "availableDevices": "Available devices",
        "noDeviceFound": "No device found"
      }
    }
  }
}

Usage

It is recommended to not use the plugin class directly. There is a wrapper class BleClient which makes events and method arguments easier to work with.

// Import the wrapper class directly
import { BleClient } from '@capacitor-community/bluetooth-le';

// DO NOT use this
import { Plugins } from '@capacitor/core';
const { BluetoothLe } = Plugins;

Here is an example of how to use the plugin. It shows how to read the heart rate from a BLE heart rate monitor such as the Polar H10.

import {
  BleClient,
  numbersToDataView,
  numberToUUID,
} from '@capacitor-community/bluetooth-le';

const HEART_RATE_SERVICE = '0000180d-0000-1000-8000-00805f9b34fb';
const HEART_RATE_MEASUREMENT_CHARACTERISTIC =
  '00002a37-0000-1000-8000-00805f9b34fb';
const BODY_SENSOR_LOCATION_CHARACTERISTIC =
  '00002a38-0000-1000-8000-00805f9b34fb';
const BATTERY_SERVICE = numberToUUID(0x180f);
const BATTERY_CHARACTERISTIC = numberToUUID(0x2a19);
const POLAR_PMD_SERVICE = 'fb005c80-02e7-f387-1cad-8acd2d8df0c8';
const POLAR_PMD_CONTROL_POINT = 'fb005c81-02e7-f387-1cad-8acd2d8df0c8';

export async function main() {
  try {
    await BleClient.initialize();

    const device = await BleClient.requestDevice({
      services: [HEART_RATE_SERVICE],
      optionalServices: [BATTERY_SERVICE, POLAR_PMD_SERVICE],
    });

    await BleClient.connect(device.deviceId);
    console.log('connected to device', device);

    const result = await BleClient.read(
      device.deviceId,
      HEART_RATE_SERVICE,
      BODY_SENSOR_LOCATION_CHARACTERISTIC,
    );
    console.log('body sensor location', result.getUint8(0));

    const battery = await BleClient.read(
      device.deviceId,
      BATTERY_SERVICE,
      BATTERY_CHARACTERISTIC,
    );
    console.log('battery level', battery.getUint8(0));

    await BleClient.write(
      device.deviceId,
      POLAR_PMD_SERVICE,
      POLAR_PMD_CONTROL_POINT,
      numbersToDataView([1, 0]),
    );
    console.log('written [1, 0] to control point');

    await BleClient.startNotifications(
      device.deviceId,
      HEART_RATE_SERVICE,
      HEART_RATE_MEASUREMENT_CHARACTERISTIC,
      value => {
        console.log('current heart rate', parseHeartRate(value));
      },
    );

    setTimeout(async () => {
      await BleClient.stopNotifications(
        device.deviceId,
        HEART_RATE_SERVICE,
        HEART_RATE_MEASUREMENT_CHARACTERISTIC,
      );
      await BleClient.disconnect(device.deviceId);
      console.log('disconnected from device', device);
    }, 10000);
  } catch (error) {
    console.error(error);
  }
}

function parseHeartRate(value: DataView): number {
  const flags = value.getUint8(0);
  const rate16Bits = flags & 0x1;
  let heartRate: number;
  if (rate16Bits) {
    heartRate = value.getUint16(1, true);
  } else {
    heartRate = value.getUint8(1);
  }
  return heartRate;
}

An example of using the scanning API:

import { BleClient, numberToUUID } from '@capacitor-community/bluetooth-le';

const HEART_RATE_SERVICE = numberToUUID(0x180d);

export async function scan() {
  try {
    await BleClient.initialize();

    await BleClient.requestLEScan(
      {
        services: [HEART_RATE_SERVICE],
      },
      result => {
        console.log('received new scan result', result);
      },
    );

    setTimeout(async () => {
      await BleClient.stopLEScan();
      console.log('stopped scanning');
    }, 5000);
  } catch (error) {
    console.error(error);
  }
}

API

initialize()

initialize() => Promise<void>

Initialize Bluetooth Low Energy (BLE). If it fails, BLE might be unavailable on this device. On Android it will ask for the location permission. On iOS it will ask for the Bluetooth permission. For an example, see usage.


getEnabled()

getEnabled() => Promise<boolean>

Reports whether BLE is enabled on this device. Always returns true on web.

Returns: Promise<boolean>


startEnabledNotifications(...)

startEnabledNotifications(callback: (value: boolean) => void) => Promise<void>

Register a callback function that will be invoked when BLE is enabled (true) or disabled (false) on this device. Not available on web (the callback will never be invoked).

| Param | Type | Description | | -------------- | ---------------------------------------- | ---------------------------------------------------- | | callback | (value: boolean) => void | Callback function to use when the BLE state changes. |


stopEnabledNotifications()

stopEnabledNotifications() => Promise<void>

Stop the enabled notifications registered with startEnabledNotifications.


requestDevice(...)

requestDevice(options?: RequestBleDeviceOptions | undefined) => Promise<BleDevice>

Request a peripheral BLE device to interact with. This will scan for available devices according to the filters in the options and show a dialog to pick a device. For an example, see usage.

| Param | Type | Description | | ------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | options | RequestBleDeviceOptions | Device filters, see RequestBleDeviceOptions |

Returns: Promise<BleDevice>


requestLEScan(...)

requestLEScan(options: RequestBleDeviceOptions, callback: (result: ScanResult) => void) => Promise<void>

Start scanning for BLE devices to interact with according to the filters in the options. The callback will be invoked on each device that is found. Scanning will continue until stopLEScan is called. For an example, see usage. NOTE: Use with care on web platform, the required API is still behind a flag in most browsers.

| Param | Type | | -------------- | --------------------------------------------------------------------------- | | options | RequestBleDeviceOptions | | callback | (result: ScanResult) => void |


stopLEScan()

stopLEScan() => Promise<void>

Stop scanning for BLE devices. For an example, see usage.


connect(...)

connect(deviceId: string, onDisconnect?: ((deviceId: string) => void) | undefined) => Promise<void>

Connect to a peripheral BLE device. For an example, see usage.

| Param | Type | Description | | ------------------ | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | onDisconnect | ((deviceId: string) => void) | Optional disconnect callback function that will be used when the device disconnects |


disconnect(...)

disconnect(deviceId: string) => Promise<void>

Disconnect from a peripheral BLE device. For an example, see usage.

| Param | Type | Description | | -------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) |


read(...)

read(deviceId: string, service: string, characteristic: string) => Promise<DataView>

Read the value of a characteristic. For an example, see usage.

| Param | Type | Description | | -------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | service | string | UUID of the service (see UUID format) | | characteristic | string | UUID of the characteristic (see UUID format) |

Returns: Promise<DataView>


write(...)

write(deviceId: string, service: string, characteristic: string, value: DataView) => Promise<void>

Write a value to a characteristic. For an example, see usage.

| Param | Type | Description | | -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | service | string | UUID of the service (see UUID format) | | characteristic | string | UUID of the characteristic (see UUID format) | | value | DataView | The value to write as a DataView. To create a DataView from an array of numbers, there is a helper function, e.g. numbersToDataView([1, 0]) |


writeWithoutResponse(...)

writeWithoutResponse(deviceId: string, service: string, characteristic: string, value: DataView) => Promise<void>

Write a value to a characteristic without waiting for a response.

| Param | Type | Description | | -------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | service | string | UUID of the service (see UUID format) | | characteristic | string | UUID of the characteristic (see UUID format) | | value | DataView | The value to write as a DataView. To create a DataView from an array of numbers, there is a helper function, e.g. numbersToDataView([1, 0]) |


startNotifications(...)

startNotifications(deviceId: string, service: string, characteristic: string, callback: (value: DataView) => void) => Promise<void>

Start listening to changes of the value of a characteristic. For an example, see usage.

| Param | Type | Description | | -------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | service | string | UUID of the service (see UUID format) | | characteristic | string | UUID of the characteristic (see UUID format) | | callback | (value: DataView) => void | Callback function to use when the value of the characteristic changes |


stopNotifications(...)

stopNotifications(deviceId: string, service: string, characteristic: string) => Promise<void>

Stop listening to the changes of the value of a characteristic. For an example, see usage.

| Param | Type | Description | | -------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | | deviceId | string | The ID of the device to use (obtained from requestDevice or requestLEScan) | | service | string | UUID of the service (see UUID format) | | characteristic | string | UUID of the characteristic (see UUID format) |


Interfaces

BleDevice

| Prop | Type | Description | | -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | deviceId | string | ID of the device, which will be needed for further calls. On Android this is the BLE MAC address. On iOS and web it is an identifier. | | name | string | Name of the device. | | uuids | string[] | |

RequestBleDeviceOptions

| Prop | Type | Description | | ---------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | services | string[] | Filter devices by service UUIDs. UUIDs have to be specified as 128 bit UUID strings in lowercase, e.g. ['0000180d-0000-1000-8000-00805f9b34fb'] There is a helper function to convert numbers to UUIDs. e.g. [numberToUUID(0x180f)]. (see UUID format) | | name | string | Filter devices by name | | namePrefix | string | Filter devices by name prefix | | optionalServices | string[] | For web, all services that will be used have to be listed under services or optionalServices, e.g. [numberToUUID(0x180f)] (see UUID format) | | allowDuplicates | boolean | Normally scans will discard the second and subsequent advertisements from a single device. If you need to receive them, set allowDuplicates to true (only applicable in requestLEScan). (default: false) | | scanMode | ScanMode | Android scan mode (default: ScanMode.SCAN_MODE_BALANCED) |

ScanResult

| Prop | Type | Description | | ---------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------- | | device | BleDevice | The device that was found in the scan | | rssi | number | Received Signal Strength Indication | | txPower | number | Transmit power in dBm. A value of 127 indicates that it is not available. | | manufacturerData | { [key: string]: DataView; } | Manufacturer data, key is a company identifier and value is the data | | serviceData | { [key: string]: DataView; } | Service data, key is a service UUID and value is the data | | uuids | string[] | Advertised services | | rawAdvertisement | DataView | Raw advertisement data (Android only) |

DataView

| Prop | Type | | ---------------- | --------------------------------------------------- | | buffer | ArrayBuffer | | byteLength | number | | byteOffset | number |

| Method | Signature | Description | | -------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | getFloat32 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Float32 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getFloat64 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Float64 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getInt8 | (byteOffset: number) => number | Gets the Int8 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getInt16 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Int16 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getInt32 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Int32 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getUint8 | (byteOffset: number) => number | Gets the Uint8 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getUint16 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Uint16 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | getUint32 | (byteOffset: number, littleEndian?: boolean | undefined) => number | Gets the Uint32 value at the specified byte offset from the start of the view. There is no alignment constraint; multi-byte values may be fetched from any offset. | | setFloat32 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Float32 value at the specified byte offset from the start of the view. | | setFloat64 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Float64 value at the specified byte offset from the start of the view. | | setInt8 | (byteOffset: number, value: number) => void | Stores an Int8 value at the specified byte offset from the start of the view. | | setInt16 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Int16 value at the specified byte offset from the start of the view. | | setInt32 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Int32 value at the specified byte offset from the start of the view. | | setUint8 | (byteOffset: number, value: number) => void | Stores an Uint8 value at the specified byte offset from the start of the view. | | setUint16 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Uint16 value at the specified byte offset from the start of the view. | | setUint32 | (byteOffset: number, value: number, littleEndian?: boolean | undefined) => void | Stores an Uint32 value at the specified byte offset from the start of the view. |

ArrayBuffer

Represents a raw buffer of binary data, which is used to store data for the different typed arrays. ArrayBuffers cannot be read from or written to directly, but can be passed to a typed array or DataView Object to interpret the raw buffer as needed.

| Prop | Type | Description | | ---------------- | ------------------- | ------------------------------------------------------------------------------- | | byteLength | number | Read-only. The length of the ArrayBuffer (in bytes). |

| Method | Signature | Description | | --------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------- | | slice | (begin: number, end?: number | undefined) => ArrayBuffer | Returns a section of an ArrayBuffer. |

Enums

ScanMode

| Members | Value | Description | | --------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SCAN_MODE_LOW_POWER | 0 | Perform Bluetooth LE scan in low power mode. This mode is enforced if the scanning application is not in foreground. https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_LOW_POWER | | SCAN_MODE_BALANCED | 1 | Perform Bluetooth LE scan in balanced power mode. (default) Scan results are returned at a rate that provides a good trade-off between scan frequency and power consumption. https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_BALANCED | | SCAN_MODE_LOW_LATENCY | 2 | Scan using highest duty cycle. It's recommended to only use this mode when the application is running in the foreground. https://developer.android.com/reference/android/bluetooth/le/ScanSettings#SCAN_MODE_LOW_LATENCY |

UUID format

All UUIDs have to be provided in 128 bit format as string (lowercase), e.g. '0000180d-0000-1000-8000-00805f9b34fb'. There is a helper function to convert 16 bit UUID numbers to string:

import { numberToUUID } from '@capacitor-community/bluetooth-le';

const HEART_RATE_SERVICE = numberToUUID(0x180d);
// '0000180d-0000-1000-8000-00805f9b34fb'

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!