GithubHelp home page GithubHelp logo

capacitor-community / bluetooth-le Goto Github PK

View Code? Open in Web Editor NEW
245.0 10.0 77.0 1.73 MB

Capacitor plugin for Bluetooth Low Energy

License: MIT License

Ruby 0.43% Java 0.54% Objective-C 1.33% Swift 25.66% JavaScript 0.25% TypeScript 39.47% Kotlin 32.32%
bluetooth capacitor plugin native

bluetooth-le's Introduction


Bluetooth Low Energy

@capacitor-community/bluetooth-le

Capacitor plugin for Bluetooth Low Energy


Maintainers

Maintainer GitHub Social
Patrick Wespi pwespi

Versions

Plugin Capacitor Documentation
3.x 5.x README
2.x 4.x README
1.x 3.x README
0.x 2.x README

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.

This plugin only supports Bluetooth Low Energy, not Bluetooth serial / classic.

Furthermore the plugin only supports the central role of the Bluetooth Low Energy protocol. If you need the peripheral role, take a look a these plugins:

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

Below is an index of all the methods available.

See Platform Support for an overview of supported methods on Android, iOS and web.

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>

Note: Bluetooth is not available in the iOS simulator. The initialize call will be rejected with an error "BLE unsupported". You have to test your app on a real device.

Android

On Android, no further steps are required to use the plugin.

(Optional) Android 12 Bluetooth permissions

If your app targets Android 12 (API level 31) or higher and your app doesn't use Bluetooth scan results to derive physical location information, you can strongly assert that your app doesn't derive physical location. This allows the app to scan for Bluetooth devices without asking for location permissions. See the Android documentation.

The following steps are required to scan for Bluetooth devices without location permission on Android 12 devices:

  • In android/variables.gradle, make sure compileSdkVersion and targetSdkVersion are at least 31 (changing those values can have other consequences on your app, so make sure you know what you're doing).
  • In android/app/src/main/AndroidManifest.xml, update the permissions:
        <!-- Permissions -->
    +   <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
    +   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
    +   <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    +     android:usesPermissionFlags="neverForLocation"
    +     tools:targetApi="s" />
  • Set the androidNeverForLocation flag to true when initializing the BleClient.
    import { BleClient } from '@capacitor-community/bluetooth-le';
    await BleClient.initialize({ androidNeverForLocation: true });

Note: If you include neverForLocation in your android:usesPermissionFlags, some BLE beacons are filtered from the scan results.

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"
      }
    }
  }
}

The display strings can also be set at run-time using setDisplayStrings(...).

Usage

There is a plugin wrapper class BleClient which makes events and method arguments easier to work with.

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

Note: It is not recommended to use the BluetoothLe plugin class directly.

Heart rate monitor

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(): Promise<void> {
  try {
    await BleClient.initialize();

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

    // connect to device, the onDisconnect callback is optional
    await BleClient.connect(device.deviceId, (deviceId) => onDisconnect(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));
      }
    );

    // disconnect after 10 sec
    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 onDisconnect(deviceId: string): void {
  console.log(`device ${deviceId} disconnected`);
}

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

Scanning API

Here is 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(): Promise<void> {
  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);
  }
}

Example Applications

Platform Support

Note: web support depends on the browser, see implementation status.

method Android iOS web
initialize()
isEnabled() --
requestEnable()
enable()
disable()
startEnabledNotifications(...) --
stopEnabledNotifications() --
isLocationEnabled()
openLocationSettings()
openBluetoothSettings()
openAppSettings()
setDisplayStrings(...) --
requestDevice(...)
requestLEScan(...) 🚩
stopLEScan() 🚩
getDevices(...) 🚩
getConnectedDevices(...) 🚩
connect(...)
createBond(...)
isBonded(...)
disconnect(...)
getServices(...)
discoverServices(...)
getMtu(...)
requestConnectionPriority(...)
readRssi(...)
read(...)
write(...)
readDescriptor(...)
writeDescriptor(...)
writeWithoutResponse(...)
startNotifications(...)
stopNotifications(...)

Legend

  • ✅ supported
  • ❌ not supported (throws an unavailable error)
  • 🚩 behind a flag in Chrome (see implementation status)
  • -- not supported, but does not throw an error

API

initialize(...)

initialize(options?: InitializeOptions | undefined) => 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.

Param Type
options InitializeOptions

isEnabled()

isEnabled() => Promise<boolean>

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

Returns: Promise<boolean>


requestEnable()

requestEnable() => Promise<void>

Request enabling Bluetooth. Show a system activity that allows the user to turn on Bluetooth. See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#ACTION_REQUEST_ENABLE Only available on Android.


enable()

enable() => Promise<void>

Enable Bluetooth. Only available on Android. Deprecated Will fail on Android SDK >= 33. Use requestEnable instead. See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#enable()


disable()

disable() => Promise<void>

Disable Bluetooth. Only available on Android. Deprecated Will fail on Android SDK >= 33. See https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#disable()


startEnabledNotifications(...)

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

Register a callback function that will be invoked when Bluetooth 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 Bluetooth state changes.

stopEnabledNotifications()

stopEnabledNotifications() => Promise<void>

Stop the enabled notifications registered with startEnabledNotifications.


isLocationEnabled()

isLocationEnabled() => Promise<boolean>

Reports whether Location Services are enabled on this device. Only available on Android.

Returns: Promise<boolean>


openLocationSettings()

openLocationSettings() => Promise<void>

Open Location settings. Only available on Android.


openBluetoothSettings()

openBluetoothSettings() => Promise<void>

Open Bluetooth settings. Only available on Android.


openAppSettings()

openAppSettings() => Promise<void>

Open App settings. Not available on web. On iOS when a user declines the request to use Bluetooth on the first call of initialize, it is not possible to request for Bluetooth again from within the app. In this case Bluetooth has to be enabled in the app settings for the app to be able use it.


setDisplayStrings(...)

setDisplayStrings(displayStrings: DisplayStrings) => Promise<void>

Set the strings that are displayed in the requestDevice dialog.

Param Type
displayStrings DisplayStrings

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.


getDevices(...)

getDevices(deviceIds: string[]) => Promise<BleDevice[]>

On iOS and web, if you want to connect to a previously connected device without scanning first, you can use getDevice. Uses retrievePeripherals on iOS and getDevices on web. On Android, you can directly connect to the device with the deviceId.

Param Type Description
deviceIds string[] List of device IDs, e.g. saved from a previous app run.

Returns: Promise<BleDevice[]>


getConnectedDevices(...)

getConnectedDevices(services: string[]) => Promise<BleDevice[]>

Get a list of currently connected devices. Uses retrieveConnectedPeripherals on iOS, getConnectedDevices on Android and getDevices on web.

Param Type Description
services string[] List of services to filter the devices by. If no service is specified, no devices will be returned. Only applies to iOS.

Returns: Promise<BleDevice[]>


connect(...)

connect(deviceId: string, onDisconnect?: ((deviceId: string) => void) | undefined, options?: TimeoutOptions | 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
options TimeoutOptions Options for plugin call

createBond(...)

createBond(deviceId: string, options?: TimeoutOptions | undefined) => Promise<void>

Create a bond with a peripheral BLE device. Only available on Android. On iOS bonding is handled by the OS.

Param Type Description
deviceId string The ID of the device to use (obtained from requestDevice or requestLEScan)
options TimeoutOptions Options for plugin call

isBonded(...)

isBonded(deviceId: string) => Promise<boolean>

Report whether a peripheral BLE device is bonded. Only available on Android. On iOS bonding is handled by the OS.

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

Returns: Promise<boolean>


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)

getServices(...)

getServices(deviceId: string) => Promise<BleService[]>

Get services, characteristics and descriptors of a device.

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

Returns: Promise<BleService[]>


discoverServices(...)

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

Discover services, characteristics and descriptors of a device. You only need this method if your peripheral device changes its services and characteristics at runtime. If the discovery was successful, the remote services can be retrieved using the getServices function. Not available on web.

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

getMtu(...)

getMtu(deviceId: string) => Promise<number>

Get the MTU of a connected device. Note that the maximum write value length is 3 bytes less than the MTU. Not available on web.

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

Returns: Promise<number>


requestConnectionPriority(...)

requestConnectionPriority(deviceId: string, connectionPriority: ConnectionPriority) => Promise<void>

Request a connection parameter update. Only available on Android. https://developer.android.com/reference/android/bluetooth/BluetoothGatt#requestConnectionPriority(int)

Param Type Description
deviceId string The ID of the device to use (obtained from requestDevice or requestLEScan)
connectionPriority ConnectionPriority Request a specific connection priority. See ConnectionPriority

readRssi(...)

readRssi(deviceId: string) => Promise<number>

Read the RSSI value of a connected device. Not available on web.

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

Returns: Promise<number>


read(...)

read(deviceId: string, service: string, characteristic: string, options?: TimeoutOptions | undefined) => 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)
options TimeoutOptions Options for plugin call

Returns: Promise<DataView>


write(...)

write(deviceId: string, service: string, characteristic: string, value: DataView, options?: TimeoutOptions | undefined) => 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])
options TimeoutOptions Options for plugin call

writeWithoutResponse(...)

writeWithoutResponse(deviceId: string, service: string, characteristic: string, value: DataView, options?: TimeoutOptions | undefined) => 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])
options TimeoutOptions Options for plugin call

readDescriptor(...)

readDescriptor(deviceId: string, service: string, characteristic: string, descriptor: string, options?: TimeoutOptions | undefined) => Promise<DataView>

Read the value of a descriptor.

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)
descriptor string UUID of the descriptor (see UUID format)
options TimeoutOptions Options for plugin call

Returns: Promise<DataView>


writeDescriptor(...)

writeDescriptor(deviceId: string, service: string, characteristic: string, descriptor: string, value: DataView, options?: TimeoutOptions | undefined) => Promise<void>

Write a value to a descriptor.

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)
descriptor string UUID of the descriptor (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])
options TimeoutOptions Options for plugin call

startNotifications(...)

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

Start listening to changes of the value of a characteristic. Note that you should only start the notifications once per characteristic in your app and share the data and not call startNotifications in every component that needs the data. 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

InitializeOptions

Prop Type Description Default
androidNeverForLocation boolean If your app doesn't use Bluetooth scan results to derive physical location information, you can strongly assert that your app doesn't derive physical location. (Android only) Requires adding 'neverForLocation' to AndroidManifest.xml https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location false

DisplayStrings

Prop Type Default Since
scanning string "Scanning..." 0.0.1
cancel string "Cancel" 0.0.1
availableDevices string "Available devices" 0.0.1
noDeviceFound string "No device found" 0.0.1

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 peripheral device.
uuids string[]

RequestBleDeviceOptions

Prop Type Description
services string[] Filter devices by service UUIDs. UUIDs have to be specified as 128 bit UUID strings, 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 peripheral device that was found in the scan. Android and web: device.name is always identical to localName. iOS: device.name is identical to localName the first time a device is discovered, but after connecting device.name is the cached GAP name in subsequent scans.
localName string The name of the peripheral device from the advertisement data.
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.

TimeoutOptions

Prop Type Description
timeout number Timeout in milliseconds for plugin call. Default is 10000 for connect and 5000 for other plugin methods.

BleService

Prop Type
uuid string
characteristics BleCharacteristic[]

BleCharacteristic

Prop Type
uuid string
properties BleCharacteristicProperties
descriptors BleDescriptor[]

BleCharacteristicProperties

Prop Type
broadcast boolean
read boolean
writeWithoutResponse boolean
write boolean
notify boolean
indicate boolean
authenticatedSignedWrites boolean
reliableWrite boolean
writableAuxiliaries boolean
extendedProperties boolean
notifyEncryptionRequired boolean
indicateEncryptionRequired boolean

BleDescriptor

Prop Type
uuid string

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

ConnectionPriority

Members Value Description
CONNECTION_PRIORITY_BALANCED 0 Use the connection parameters recommended by the Bluetooth SIG. This is the default value if no connection parameter update is requested. https://developer.android.com/reference/android/bluetooth/BluetoothGatt#CONNECTION_PRIORITY_BALANCED
CONNECTION_PRIORITY_HIGH 1 Request a high priority, low latency connection. An application should only request high priority connection parameters to transfer large amounts of data over LE quickly. Once the transfer is complete, the application should request CONNECTION_PRIORITY_BALANCED connection parameters to reduce energy use. https://developer.android.com/reference/android/bluetooth/BluetoothGatt#CONNECTION_PRIORITY_HIGH
CONNECTION_PRIORITY_LOW_POWER 2 Request low power, reduced data rate connection parameters. https://developer.android.com/reference/android/bluetooth/BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER

UUID format

All UUIDs have to be provided in 128 bit format as string, 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'

Troubleshooting

Connection fails on Android

On some Android devices connect() may fail when the device was connected before, even if the device is not actually connected. In that case you should first call disconnect(), e.g.:

const device = await BleClient.requestDevice({
   // ...
});
// ...
await BleClient.disconnect(device.deviceId);
await BleClient.connect(device.deviceId);

No devices found on Android

On Android, the initialize call requests the location permission. However, if location services are disable on the OS level, the app will not find any devices. You can check if the location is enabled and open the settings when not.

async function initialize() {
  // Check if location is enabled
  if (this.platform.is('android')) {
    const isLocationEnabled = await BleClient.isLocationEnabled();
    if (!isLocationEnabled) {
      await BleClient.openLocationSettings();
    }
  }
  await BleClient.initialize();
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):

pwespi
pwespi

💻 📖
Dennis Ameling
Dennis Ameling

💻
Johannes la Poutre
Johannes la Poutre

📖 💻
Kasymbekov Sultanmyrza
Kasymbekov Sultanmyrza

💻
Mutasim Issa
Mutasim Issa

📖
Marco Marche
Marco Marche

💻
Johannes Koch
Johannes Koch

💻
Johnny Robeson
Johnny Robeson

💻
Aadit Olkar
Aadit Olkar

💻
Yoann N.
Yoann N.

💻
Andy3189
Andy3189

💻
Sammy
Sammy

📖
td-tomasz-joniec
td-tomasz-joniec

💻
Michele Ferrari
Michele Ferrari

💻
mchl18
mchl18

📖
Daniel Stefani
Daniel Stefani

💻
Laurent
Laurent

💻

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

bluetooth-le's People

Contributors

aadito123 avatar allcontributors[bot] avatar andy3189 avatar dependabot[bot] avatar jfkakajfk avatar lhd-biosency avatar mchl18 avatar micheleypf avatar mutasimissa avatar opensrcerer avatar pwespi avatar rfm69cw avatar squio avatar td-tomasz avatar trik avatar y3nd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bluetooth-le's Issues

Scanning any services

Hello sir,
I came here for your help,
For scanning BLE devices how can I scan for devices independent of the service it is providing.
That is when I scan, I want the list of devices currently advertising, independent of the service it is providing,

await BleClient.requestLEScan( { services: [HEART_RATE_SERVICE], }, result => { console.log('received new scan result', result); setDevicesFound([...devicesFound,result]); }, );
this scans only devices advertising only heart_rate_service.
is there another version of requestLEScan()

Scan for device -> connect to found device not working after first time (android)

Describe the bug

Scanning for an existing and available BLE device and then connecting works the very first time my app is installed and run but fails always on subsequent runs.

To Reproduce
In my code I basically have these steps:

  • BleClient.initialize()
  • BleClient.requestLEScan() -> scan for one device name and store the found device object
  • BleClient.stopLEScan()
  • BleClient.connect(deviceId) -> try to connect to the found device

This works the very first time the app is installed and run but fails always on subsequent runs.

I kept the default timeout for connecting (which is 30 seconds I believe) but the connection fails almost immediately.
The console shows:

D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=8 device=B6:24:53:6D:8A:4D

Expected behavior
The device is discovered (it is) and connected to (it isn't)

work around
I discovered a work-around: to disconnect before trying to connect

  • BleClient.initialize()
  • BleClient.requestLEScan() -> scan for one device name and store the found device object
  • BleClient.stopLEScan()
  • BleClient.disconnect(deviceId); -> this fails with error "device not connected"; ignore
  • BleClient.connect(deviceId) -> now connect to the found device

This appears to solve the issue completely for my Android device

Plugin version:

  • @capacitor-community/bluetooth-le: 1.0.0-3

Desktop (please complete the following information):

  • OS: MacOS 11
  • Browser N/A
  • Version Ionic 6.13.1 / Angular 9.1.14 / Capacitor 3.0.0-rc.0

Smartphone (please complete the following information):

  • Device: Nokia 6.1
  • OS: Android 10
  • Browser System Webview (chrome)
  • Version N/A

Additional context
Android studio console log output, the relevant part:

V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 91203091, pluginId: BluetoothLe, methodName: addListener
V/Capacitor: callback: 91203091, pluginId: BluetoothLe, methodName: addListener, methodData: {"eventName":"disconnected|C9:54:C0:6D:30:4C"}
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 91203092, pluginId: BluetoothLe, methodName: connect
V/Capacitor: callback: 91203092, pluginId: BluetoothLe, methodName: connect, methodData: {"deviceId":"C9:54:C0:6D:30:4C"}
D/BluetoothGatt: connect() - device: C9:54:C0:6D:30:4C, auto: false
    registerApp()
D/BluetoothGatt: registerApp() - UUID=15cda7ec-0be6-47ba-be2a-65c5e240fb58
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=8
D/Device: reject: connect Connection timeout.
D/Capacitor: Sending plugin error: {"save":false,"callbackId":"91203092","pluginId":"BluetoothLe","methodName":"connect","success":false,"error":{"message":"Connection timeout."}}
I/Capacitor/Console: File: http://localhost/ ... - Msg: Connect - Connection failed: Error: Connection timeout.

D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=8 device=C9:54:C0:6D:30:4C
V/Capacitor/BluetoothLe: Notifying listeners for event disconnected|C9:54:C0:6D:30:4C
D/Device: Disconnected from GATT server.
W/Device: Resolve callback not registered for key: disconnect
I/Capacitor/Console: File: capacitor-runtime.js - Line 427 - Msg: undefined
I/Capacitor/Console: File: http://localhost/... - Msg: onDisconnect event callback from BLE, device ID: C9:54:C0:6D:30:4C
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 91203093, pluginId: BluetoothLe, methodName: stopNotifications

Check location on Android

Hello!
It would be very useful if this plugin is able to check if location is enabled or not on Android.
Permissions are handled in initialize() which si perfect, but the state of location not.

Thank you !!!

Module not found: Error: Can't resolve 'throat'

Describe the bug
After upgrading from 1.0.0-1 to 1.0.0-2 I'm getting this error when compiling:
Module not found: Error: Can't resolve 'throat'

To Reproduce
Steps to reproduce the behavior:

  1. Upgrade plugin to version 1.0.0-2 'npm install @capacitor-community/[email protected]'
  2. npx cap sync
  3. Try to compile with ionic build
  4. See error
Error: ./node_modules/@capacitor-community/bluetooth-le/dist/esm/queue.js
Module not found: Error: Can't resolve 'throat'

Expected behavior
Dependencies should be automatically updated and build should run through

Plugin version:

  • @capacitor-community/bluetooth-le: 1.0.0-2

(Capacitor 3) iOS: declining Bluetooth permissions causes the initialize() method to hang

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Create a Capacitor 3 app with the @capacitor-community/bluetooth-le plugin installed
  2. Run the BleClient.initialize() method on iOS 13+
  3. See the request for permission. Make sure to press "Decline":

IMG_0038

  1. Notice the BleClient.initialize() method never finishes (the promise never resolves):
⚡️  To Native ->  BluetoothLe initialize 102368098

NOTE: when you reject once, the app will never re-request permissions. You'll have to manually enable the permission in iOS settings or reinstall the app.

Expected behavior

The promise should trigger a rejection, like it does on Android

Screenshots
See above

Desktop (please complete the following information):

  • OS: macOS
  • Browser: Chrome
  • Version Big Sur 11.2.3

Smartphone (please complete the following information):

  • Device: iPhone7
  • OS: iOS 14.4 (but applicable to iOS 13+)
  • Browser built-in WebView
  • Version ?

Additional context
Apparently in Capacitor 3, the checkPermissions() and requestPermissions() functions have to be implemented: https://capacitorjs.com/docs/v3/plugins/ios#permissions

in iOS 13+, the user has to be asked for Bluetooth permission explicitly: https://medium.com/flawless-app-stories/handling-ios-13-bluetooth-permissions-26c6a8cbb816

Let me know if I can help with the implementation of those methods!

Writing text to characteristic fails

Describe the bug
Using .write() doesn't seem to work for any DataView I send. I tried textToDataView('string') and new DataView( new TextEncoder().encode('string').buffer ) but neither one seem to get written to the endpoint (bleno running on a raspberry pi). Using LightBlue, it works fine, and other Pis are able to write to it, so I don't think it's the receiver. The log in XCode says it was sent successfully, it just doesn't get there.

Reads also work fine on this characteristic, it's just the writes that have an issue.

[log] - - writing: log::hello-1622051334558
⚡️  To Native ->  BluetoothLe write 48144307
Resolve write|0000EC00-0000-1000-8000-00805F9B34FB|0000EC0E-0000-1000-8000-00805F9B34FB Successfully written value.
⚡️  TO JS undefined

To Reproduce
Raspberry Pi 3b running a Bleno server.
Performing the write from an iPad, if that matters.

Expected behavior
I expect the message to go through.

Plugin version:

  • @capacitor-community/bluetooth-le: [e.g. 0.4.0]

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: Ipad Pro 2018
  • OS: 14.4.2

startNotifications doesn't clean up after reconnect

related to #65: the same happens when reconnecting to the same device after connection broke up. I have a routine which automatically searches for the device in case the connection gets lost and restarts all running notifications.
after the reconnect and restart of notifications, every notification fires twice or more (one more per reconnect)
I restarted my BLE device to simulate a connection loss (e.g. distance between ble-device and smart-device is to large)
There has to be a cleanup of native notification listeners in case of broken connection.

Differing requestLEScan results to OS BT settings

Describe the bug
I'm trying to connect to a device for the first time, I'm using the following:

await BleClient.initialize()

await BleClient.requestLEScan({}, result => { console.log('received new scan result', result) })

and I get the following list:

IMG_3731

However it doesn't contain the device I'm looking for, which immediately shows up in the iOS settings bluetooth section:

IMG_3727

Would there be any reason for this? I'm pretty new to the BT world. It also shows up fine in my macbook bluetooth settings:

image

'capacitorExports is not defined' error during bundling

First of all, thank you for your work on the plugin @pwespi, I believe it will help us greatly when we make it work :)

Here is the issue:

Describe the bug
Our project's web assets (webpack) build is failing if the following import { BleClient } from '@capacitor-community/bluetooth-le'; defined somewhere in the code.

The error is the following:

ReferenceError: capacitorExports is not defined
at Object. (/projectDir/node_modules/@capacitor-community/bluetooth-le/dist/plugin.js:403:7)

The contents of the plugin.js file at line 403 (with some additional lines above, for context):

   ...

   exports.BleClient = BleClient;
    exports.BluetoothLe = BluetoothLe;
    exports.BluetoothLeWeb = BluetoothLeWeb;
    exports.dataViewToHexString = dataViewToHexString;
    exports.dataViewToNumbers = dataViewToNumbers;
    exports.dataViewToText = dataViewToText;
    exports.hexStringToDataView = hexStringToDataView;
    exports.mapToObject = mapToObject;
    exports.numberToUUID = numberToUUID;
    exports.numbersToDataView = numbersToDataView;
    exports.textToDataView = textToDataView;
    exports.webUUIDToString = webUUIDToString;

 Object.defineProperty(exports, '__esModule', { value: true });

 return exports;

}({}, capacitorExports)); // <-- line 403 from the error

We are using the NextJS's next build command to run build, but I guess it doesn't matter which bundler is used.

To Reproduce
Steps to reproduce the behavior:

  1. install bluetooth-le capacitor plugin and import it somewhere.
  2. Run the build to bundle your app.

Expected behavior
The build is passing without errors from the plugin.

startNotifications doesn't clean up after livereload

Describe the bug
Old notifications don't stop after Ionic Capacitor livereload
Callback gets called multiple times per notification in the reloaded app.

To Reproduce
Steps to reproduce the behavior:

  1. create ionic app with capacitor support and bluetooth-le plugin
  2. create service which:
  3. connect to ble device
  4. start notification and logs data to console in callback
  5. start app with ionic capacitor run --livereload --external
  6. save project file to trigger livereload
  7. app again connects to ble device and starts notification
  8. all notification data received twice

Expected behavior
native notification listeners needs to get stopped when app is reloaded.
notification data has to receive one per time after livereload.

Screenshots
image
console gets flooded with these messages after multiple live reloads

Desktop:

  • OS: MacOS
  • Browser Chrome
  • Version 88.0.4324.192

Smartphone:

  • Device: Google Pixel 2
  • OS: Android 11
  • Browser Chrome
  • Version 88.0.4324.181

Additional context
I tried to do some cleanup (disconnect, stopNotifications) in ngOnDestroy but i doesn't get called in livereload
stopNotifications before startNotifications results in Errors.
Is there any other way to do the cleanup when using livereload?

Not able to connect till allowDuplicates became true

Hi All

I am not sure if this is a bug or something I have done wrong.

My BLE device was able to communicate with my app on all my platform devices such as the Andriod phone, IPAD, Macbook Pro M1.

Until recently, after an update on my Macbook, I noticed my BLE device was no longer able to connect or communicate.

After some investigation I set
allowDuplicates: true

After which i was able to connect to that device.

Now when i set
allowDuplicates: false

I am still able to connect to that device.

Is there a reason why this has happened?

Maybe it's best to keep it true.

Check if device is connected

Hi!

I see there is a method for Android to check if the device is connected. Is there an equivalent for iOS? If not, how do I check if the device is already connected or if I lost connection with the device?

Thanks!

Turn on Bluetooth on device

To automate the process and give a better experience to the end user.
Is it possible to turn on Bluetooth when invoking Initialize or as a separate method just like cordova's solution BLE-central

Some useful features not found in the web bluetooth spec

Is your feature request related to a problem? Please describe.
Some useful features found in other common bluetooth plugins/modules.

This plugin seems most promising for the future as it uses modern android (at least, can't speak for ios) features and seems relatively well designed. I might be wanting too much from it though.

  • reporting duplicate advertisements - boolean in scan filters to allow reporting the same device as many times as it advertises
  • scan timeout - ability to restrict the time spent scanning
  • bonding - but maybe i missed that when reading the spec

Anything else that might seem useful from: https://github.com/randdusing/cordova-plugin-bluetoothle

Notify not working after writing

Describe the bug
Notify not working after writing to BLE device on Android.

Plugin version:

  • @capacitor-community/bluetooth-le: 0.4.0

Smartphone (please complete the following information):

  • Device: OnePlus 8
  • OS: Android 11

Logs from Android Studio

D/BluetoothGatt: setCharacteristicNotification() - uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e enable: true
D/Device: resolve: setNotifications|6e400001-b5a3-f393-e0a9-e50e24dcca9e|6e400003-b5a3-f393-e0a9-e50e24dcca9e Notification enabled.
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 23593037, pluginId: BluetoothLe, methodName: write
V/Capacitor: callback: 23593037, pluginId: BluetoothLe, methodName: write, methodData: {"deviceId":"E9:4C:F8:D1:E2:6C","service":"6e400001-b5a3-f393-e0A9-e50e24dcca9e","characteristic":"6e400002-b5a3-f393-e0A9-e50e24dcca9e","value":"fd 7b 22 43 61 65 73 63 22 3a 7b 22 69 76 22 3a 22 44 44 30 43 43 42 39 46 33 37 38 30 39 42 34 44 38 30 30 41 37 34 41 38 38 35 38 37 34 41 31 42 22 2c 22 6b 65 79 49 6e 64 65 78 22 3a 31 7d 7d fe"}
D/Device: resolve: write|6e400001-b5a3-f393-e0a9-e50e24dcca9e|6e400002-b5a3-f393-e0a9-e50e24dcca9e Characteristic successfully written.
V/Capacitor/BluetoothLe: Notifying listeners for event notification|E9:4C:F8:D1:E2:6C|6e400001-b5a3-f393-e0a9-e50e24dcca9e|6e400003-b5a3-f393-e0a9-e50e24dcca9e
D/Capacitor/BluetoothLe: No listeners found for event notification|E9:4C:F8:D1:E2:6C|6e400001-b5a3-f393-e0a9-e50e24dcca9e|6e400003-b5a3-f393-e0a9-e50e24dcca9e

BleClient.requestLEScan gives exception 'TypeError: navigator.bluetooth.requestLEScan is not a function' in Chrome

Describe the bug
BleClient.requestLEScan gives exception 'TypeError: navigator.bluetooth.requestLEScan is not a function' in Chrome, but works fine on my Android tablet.
BleClient.requestDevice works fine in Chrome and on my Android tablet.

To Reproduce in Chrome
await BleClient.initialize();
await BleClient.requestLEScan({services: ['01ee0100-ba5e-f4ee-5ca1-eb1e5e4b1ce0']}, (scanResult: ScanResult) => {
this.driversSvc.addDriver({device: scanResult.device});
});

Observed behavior
exception 'TypeError: navigator.bluetooth.requestLEScan is not a function'

Expected behavior
No exception, yields found device as it does on my Android tablet.

Plugin version etc.:
{
"name": "uvapp",
"version": "0.0.1",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/common": "~11.2.0",
"@angular/core": "~11.2.0",
"@angular/forms": "~11.2.0",
"@angular/platform-browser": "~11.2.0",
"@angular/platform-browser-dynamic": "~11.2.0",
"@angular/router": "~11.2.0",
"@capacitor-community/bluetooth-le": "^0.5.1",
"@capacitor/android": "^2.4.7",
"@capacitor/core": "2.4.7",
"@ionic/angular": "^5.5.2",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1102.4",
"@angular/cli": "~11.2.4",
"@angular/compiler": "~11.2.0",
"@angular/compiler-cli": "~11.2.0",
"@angular/language-service": "~11.2.0",
"@capacitor/cli": "2.4.7",
"@ionic/angular-toolkit": "^3.1.1",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.2.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
},
"description": "An Ionic project"
}

Desktop (where it fails):

  • OS: Microsoft Windows 10 Enterprise, Version 10.0.17763 Build 17763
  • Browser: Google Chrome Versie 89.0.4389.114 (Officiële build) (64-bits)

Tablet (where it works fine):

  • Device: Samsung Galaxy Tab A (2016)
  • OS: Android 8.1.0

Error TS2665: Invalid module name in augmentation.

Hi
I am trying to get plugin to compile on my Mac Ionic Project version, but i am getting following error message:

[ng] ERROR in node_modules/@capacitor-community/bluetooth-le/dist/esm/config.d.ts:1:16 - error TS2665: Invalid module name in augmentation. Module '@capacitor/cli' resolves to an untyped module at '/Users/user922263/Desktop/app/node_modules/@capacitor/cli/dist/index.js', which cannot be augmented.

Has anyone had this issue before?

Connection attempt times out when trying to connect to a dual-mode Bluetooth chip

Hi,

Thank you very much for creating and sharing this plugin. Great work!

Is your feature request related to a problem? Please describe.
I've used the plugin in a simple Android test app and found a possible issue when connecting to some Bluetooth devices. Connecting to a device that includes a Bluetooth chip that only supports BLE works as expected. However, when attempting to connect to a device that contains a dual mode Bluetooth chip fails - connection attempt times out.

Describe the solution you'd like
Would it be possible to amend the Device.kt file of Android native code, line 165 to explicitly set the transport flag to BluetoothDevice.BluetoothDevice.TRANSPORT_LE?
Current version uses : device.connectGatt(context, false, gattCallback)

My request would change that to:
Current version uses : device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)

This would require incrementing the minimum Android SDK to 23. Hopefully that wouldn't be too much of a problem as the plugin uses root project Gradle configuration anyway.
Alternatively one could also use conditional logic to support Android SDK < 23 using the existing version and >= 23 with the potential change to connectGatt() signature.

I would be happy to submit a PR with the abovementioned code change.

Describe alternatives you've considered
I tried the following to attempt to connect to my test Bluetooth device (all attempts failed):

  1. Increasing connection timeout.
  2. Sending connection request multiple times.
  3. Creating a bond with the test device before trying to connect.

Additional context
My request only applies to Android. I don't have access to Apple development environment so I don't know if this applies to iOS too.

Plugin version
@capacitor-community/bluetooth-le: 1.1.1

Thank you in advance for your support!

Connection Timeout error on IoS

Hi

When I try to connect to my BLE device I am getting a Connection Timeout Error. This is only noticeable on a IOS , I am using X code with my M1 chipset base Macbook to simulate the app. After few times, I am able to connect to the device, but I am getting timeout issues very often.

What would be the cause of this? Is there any information I can provide to help solve this issue?

I also ran my app on a IPAD, connected to Xcode, and i am getting a "XPC connection invalid" error, if this give any more information

Reading the multiple characteristic with the same UUID from the same service

Hi,

I am not sure if this is a bug issue or something I am doing wrong.

I have a BLE device with WIFI module(WINC3400).

I am able to perform a WIFI scan by writing to the corresponding Characteristics, but the results are read from particular Characteristics. It seems that each result is saved under the same Characteristics UUID.

When i perform a read on that Characteristics, it simply only outputting the first result only, which is the first Characteristics, but not the remaining ones.

So how can I read multiple Characteristics, which share the same UUID?

Error: Characteristic not found.

I always have the problem Error: Characteristic not found.

can anyone help with this problem?

const bluetooth = async () => {
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";
await BleClient.initialize();
const device = await BleClient.requestDevice({
// services: [HEART_RATE_SERVICE],
// optionalServices: [BATTERY_SERVICE, POLAR_PMD_SERVICE],
});
await BleClient.connect(device.deviceId);
await BleClient.write(
device.deviceId,
POLAR_PMD_SERVICE,
POLAR_PMD_CONTROL_POINT,
numbersToDataView([1, 0])
);
};

bluetooth();

Queueing

Is your feature request related to a problem? Please describe.
By using this plugin my goal is to write my BLE business logic once and can run it on Android, iOS and maybe someday on the web.
As I mentioned few days ago I started developing against iOS devices what worked out really well with this awesome plugin!
Two days ago I received an Android phone to evaluate this platform as well.

Unfortunately there seems to be a quite notable difference running the same codebase which uses this plugin on iOS and Android.

While reading two characteristics in parallel works without any trouble on iOS it fails on Android.
The same happens for starting or stopping two notifications in parallel.
The underlying function rejects with Reading characteristic failed. and Setting notification failed. respectively.

After some research I found some articles (e.g. this one) complaining about BLE usage on Android compared to iOS among others because of missing queueing in the Android BLE stack.
A quick look at the Cordova BLE plugins revealed that those seem to have queueing implemented for the Android side of the plugin (see randdusing/cordova-plugin-bluetoothle#261 and don/cordova-plugin-ble-central#2).

Describe the solution you'd like
As a user of this plugin I expect that my app works on all supported platforms similarly.

I would not want to implement some kind of queuing in my capacitor app which only gets executed if running on Android.
Neither I want to implement queueing in general in my app.
I think this should not be handled in the code of the user utilizing this plugin.

Do you think this should be handled by this plugin?

Describe alternatives you've considered
None so far.

Additional context
Next to the Android platform the web platform needs such queueing as well.
We did some development with BLE enabled PWAs in the past which run on Google Chrome on Android.
We utilized the Web Bluetooth API directly and ended up implementing a queue for most of the BLE operations.
As a side note, from my experience running such BLE enabled web apps with Chrome on macOS don't need to have queueing, it seems macOS has some kind of queueing in place as iOS does.
But of course web development should in general be handled independent of the clients operating system which means that such a queue should also be implemented there.

Out of curiosity, what are your BLE use cases and which platforms do you utilize?

Bonding

Feature request mentioned in #5.

This will be Android only:

On iOS there is no API to handle bonding. It is performed automatically when accessing a characteristic that requires security. See e.g. https://devzone.nordicsemi.com/f/nordic-q-a/8507/getting-an-ios-central-app-to-bond

Web:

  • Chrome on macOS: automatically performs bonding when accessing a secure characteristic (as on iOS)
  • Chrome on Windows: does not perform bonding
  • Chrome on Android: does not perform bonding

Write without response

Hey @pwespi!

Great work with this plugin, thank you very much!
I really like the clean API of this plugin. Connecting to a device and reading characteristics works perfectly on my iOS devices as well as with Google Chrome.

Unfortunately I failed to write to a characteristic with an iOS device. It works well with Google Chrome but on iOS i got the following error:

Reject write|SOME_SERVICE_UUID|SOME_CHARACTERISTIC_UUID Writing is not permitted.

After a while of debugging I found the cause for my issue. The .write() function exposed by this plugin always uses .withResponse as type in the call below:

self.peripheral.writeValue(data, for: characteristic, type: .withResponse)

Unfortunately my characteristic only allows write without response.

Could we get such write operations supported on iOS as well? That would be awesome!

Runtime display text

It would be best to let me control my dialog text at runtime when my user changes languages internally versus a fixed language setting in the capacitory.config.json

Android app crashes when sending binary data to characteristic - StringIndexOutOfBoundsException

Describe the bug
Sending binary data to a characteristic does not work on Android. In Web-version it works as indended. It seems an empty value is send to the BLE stack

Expected behavior
Sending of binary data should be possible

Plugin version:

  • @capacitor-community/bluetooth-le: 1.1.0

Smartphone (please complete the following information):

  • Device:HUWEI Mate 20 lite
  • OS: Android 10

Additional context
Stacktrace:

2021-06-09 22:02:51.050 28610-28749/org.thankthemaker.rescueapp V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 45646988, pluginId: BluetoothLe, methodName: write
2021-06-09 22:02:51.051 28610-28749/org.thankthemaker.rescueapp V/Capacitor: callback: 45646988, pluginId: BluetoothLe, methodName: write, methodData: {"deviceId":"08:3A:F2:9A:E4:BE","service":"c8659210-af91-4ad3-a995-a58d6fd26145","characteristic":"c8659211-af91-4ad3-a995-a58d6fd26145","value":""}
2021-06-09 22:02:51.058 28610-28610/org.thankthemaker.rescueapp I/Capacitor/Console: File: http://localhost/ - Line 213 - Msg: undefined
2021-06-09 22:02:51.100 28610-28697/org.thankthemaker.rescueapp E/Capacitor: Serious error executing plugin
    java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:121)
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$Bridge(Bridge.java:584)
        at com.getcapacitor.-$$Lambda$Bridge$25SFHybyAQk7zS27hTVXh2p8tmw.run(Unknown Source:8)
        at android.os.Handler.handleCallback(Handler.java:888)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:213)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.StringIndexOutOfBoundsException: length=0; index=0
        at java.lang.String.charAt(Native Method)
        at com.capacitorjs.community.plugins.bluetoothle.ConversionKt.hexToByte(Conversion.kt:23)
        at com.capacitorjs.community.plugins.bluetoothle.ConversionKt.stringToBytes(Conversion.kt:17)
        at com.capacitorjs.community.plugins.bluetoothle.Device.write(Device.kt:283)
        at com.capacitorjs.community.plugins.bluetoothle.BluetoothLe.write(BluetoothLe.kt:351)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:121) 
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$Bridge(Bridge.java:584) 
        at com.getcapacitor.-$$Lambda$Bridge$25SFHybyAQk7zS27hTVXh2p8tmw.run(Unknown Source:8) 
        at android.os.Handler.handleCallback(Handler.java:888) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:213) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
2021-06-09 22:02:51.101 28610-28697/org.thankthemaker.rescueapp I/QarthLog: [PatchStore] createDisableExceptionQarthFile
2021-06-09 22:02:51.101 28610-28697/org.thankthemaker.rescueapp I/QarthLog: [PatchStore] create disable file for org.thankthemaker.rescueapp uid is 10204
    
    --------- beginning of crash
2021-06-09 22:02:51.110 28610-28697/org.thankthemaker.rescueapp E/AndroidRuntime: FATAL EXCEPTION: CapacitorPlugins
    Process: org.thankthemaker.rescueapp, PID: 28610
    java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$Bridge(Bridge.java:593)
        at com.getcapacitor.-$$Lambda$Bridge$25SFHybyAQk7zS27hTVXh2p8tmw.run(Unknown Source:8)
        at android.os.Handler.handleCallback(Handler.java:888)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:213)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:121)
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$Bridge(Bridge.java:584)
        at com.getcapacitor.-$$Lambda$Bridge$25SFHybyAQk7zS27hTVXh2p8tmw.run(Unknown Source:8) 
        at android.os.Handler.handleCallback(Handler.java:888) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:213) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
     Caused by: java.lang.StringIndexOutOfBoundsException: length=0; index=0
        at java.lang.String.charAt(Native Method)
        at com.capacitorjs.community.plugins.bluetoothle.ConversionKt.hexToByte(Conversion.kt:23)
        at com.capacitorjs.community.plugins.bluetoothle.ConversionKt.stringToBytes(Conversion.kt:17)
        at com.capacitorjs.community.plugins.bluetoothle.Device.write(Device.kt:283)
        at com.capacitorjs.community.plugins.bluetoothle.BluetoothLe.write(BluetoothLe.kt:351)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:121) 
        at com.getcapacitor.Bridge.lambda$callPluginMethod$0$Bridge(Bridge.java:584) 
        at com.getcapacitor.-$$Lambda$Bridge$25SFHybyAQk7zS27hTVXh2p8tmw.run(Unknown Source:8) 
        at android.os.Handler.handleCallback(Handler.java:888) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:213) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 
2021-06-09 22:02:51.171 28610-28697/org.thankthemaker.rescueapp I/Process: Sending signal. PID: 28610 SIG: 9

App crashing when using Android TV

Hello,

first of all I would like to say thanks for this library!

Describe the bug
I have developed an Android TV app, one part of it is scanning for BLE signals in the background. I am using different devices, for example a Fire TV stick, Mi TV Stick and some days ago I received the Nokia TV streaming box 8000 with which the app is crashing quite fast (after around 60 minutes). What the app does is run for 24h and just have the requestLEScan method running every 10 seconds.

To Reproduce
Steps to reproduce the behavior:

  1. Use the example code for the scanning API wrapped in a setInterval function that is running every 10 seconds.
  2. After around 1 hour I am receiving the following error in the Android Studio console:
V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 121276222, pluginId: BluetoothLe, methodName: stopLEScan
V/Capacitor: callback: 121276222, pluginId: BluetoothLe, methodName: stopLEScan, methodData: {}
D/DeviceScanner: Stop scanning.
D/BluetoothAdapter: isLeEnabled(): ON
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.app, PID: 31896
    java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.next(ArrayList.java:860)
        at com.getcapacitor.Plugin.notifyListeners(Plugin.java:671)
        at com.getcapacitor.Plugin.notifyListeners(Plugin.java:684)
        at com.capacitorjs.community.plugins.bluetoothle.BluetoothLe.access$notifyListeners(BluetoothLe.kt:37)
        at com.capacitorjs.community.plugins.bluetoothle.BluetoothLe$requestLEScan$2.invoke(BluetoothLe.kt:209)
        at com.capacitorjs.community.plugins.bluetoothle.BluetoothLe$requestLEScan$2.invoke(BluetoothLe.kt:37)
        at com.capacitorjs.community.plugins.bluetoothle.DeviceScanner$scanCallback$1.onScanResult(DeviceScanner.kt:71)
        at android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1.run(BluetoothLeScanner.java:486)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
I/Process: Sending signal. PID: 31896 SIG: 9

This only happens with the Nokia Box, Fire TV Stick and Mi Stick are working fine. Is there any way I can prevent it from crashing?

Any help is appreciated! Thanks so much.

Plugin version:

  • @capacitor-community/bluetooth-le: 1.0.0-4

  • OS: Android

  • Version 10, Stock Android TV running on the Nokia 8000 Box

Plugin with Ionic

Hi

I am very new to Capacitor plugins.

I am trying to install this plugin into my Ionic project so that I can use BLE on all platforms.

I have managed to install its as shown in the guide, but when I do a build I am getting the following error messages:

Can you please advice what I am doing wrong?

`
ERROR in node_modules/@capacitor-community/bluetooth-le/dist/esm/definitions.d.ts:1:13 - error TS1005: '=' expected.

1 import type { PluginListenerHandle } from '@capacitor/core';
~
node_modules/@capacitor-community/bluetooth-le/dist/esm/definitions.d.ts:1:43 - error TS1005: ';' expected.

1 import type { PluginListenerHandle } from '@capacitor/core';
~~~~~~~~~~~~~~~~~
node_modules/@capacitor-community/bluetooth-le/dist/esm/web.d.ts:3:13 - error TS1005: '=' expected.

3 import type { BleDevice, BluetoothLePlugin, ConnectOptions, ReadOptions, ReadResult, RequestBleDeviceOptions, WriteOptions } from './definitions';
~
node_modules/@capacitor-community/bluetooth-le/dist/esm/web.d.ts:3:131 - error TS1005: ';' expected.

3 import type { BleDevice, BluetoothLePlugin, ConnectOptions, ReadOptions, ReadResult, RequestBleDeviceOptions, WriteOptions } from './definitions';
~~~~~~~~~~~~~~~
node_modules/@capacitor-community/bluetooth-le/dist/esm/bleClient.d.ts:1:13 - error TS1005: '=' expected.

1 import type { PluginListenerHandle } from '@capacitor/core';
~
node_modules/@capacitor-community/bluetooth-le/dist/esm/bleClient.d.ts:1:43 - error TS1005: ';' expected.

1 import type { PluginListenerHandle } from '@capacitor/core';
~~~~~~~~~~~~~~~~~
node_modules/@capacitor-community/bluetooth-le/dist/esm/bleClient.d.ts:2:13 - error TS1005: '=' expected.

2 import type { BleDevice, RequestBleDeviceOptions, ScanResult } from './definitions';
~
node_modules/@capacitor-community/bluetooth-le/dist/esm/bleClient.d.ts:2:69 - error TS1005: ';' expected.

2 import type { BleDevice, RequestBleDeviceOptions, ScanResult } from './definitions';
~~~~~~~~~~~~~~~

[ERROR] An error occurred while running subprocess ng.
`

BLE not appearing on IOS app version

Hi

I have followed all the steps shown in setting the plugin for IOS, but when I start my IOS app version there is no BLE request or check my apps "Access List" I do not see the Bluetooth there.

It works well on Andriod.

What could i be doing wrong?

When connect fails due to timeout and BLE device becomes available, the stack connects to device, even though the Promise was already rejected

Describe the bug
If connect fails and promise is rejected, the stack should clear the connect request. Now, the connection may happen later but there is no way to notify the app as there is no 'onConnected' callback option.

To Reproduce
Steps to reproduce the behavior:

  1. Discover devices using requestLEScan
  2. Make sure your BLE device is discovered.
  3. Switch off the BLE device or move out of reach
  4. Try to establish connection to BLE device.
  5. Wait until Promise is Rejected due to timeout.
  6. Switch on the BLE device (or bring to connection reach).
  7. Stack connects to device automatically (Connected to device <CBPeripheral: 0x281d599a0, identifier = 1234, name = Something, state = connected>)

Expected behavior
If Connect promise is already rejected, nothing should happen even if the device could be connected later. Or alternatively, if there would be onConnected callback, this could be called but it might confuse the app logic.

Plugin version:

  • @capacitor-community/bluetooth-le: 1.0.0-1

Smartphone (please complete the following information):

  • Device: iPhone 8 Plus
  • OS: 14.4.1

test

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Plugin version:

  • @capacitor-community/bluetooth-le: [e.g. 0.4.0]

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Supports for Indication

Is your feature request related to a problem? Please describe.
Indication support is also needed in some BLE modules which prefers to not use the notification in their generic transparent UART service.
The one I use is RN4871, it seems always to change the Notification back to Indication. Which might result in the issues discussed here:
#70 (comment)
I guess, this is because the enableNotification() only register a callback for Notification but not Indication, results in:
D/Capacitor/BluetoothLe: No listeners found for event notification|E8:EB:1B:53:D0:99

Describe the solution you'd like
Add enable/disable Indication just like enableNotification() API

Describe alternatives you've considered
Allow the current notification callback to handle indication incoming message is also very handy.

Additional context
Add any other context or screenshots about the feature request here.

WriteWithoutResponse permitted to characteristic with response (Android)

Describe the bug
First up, I'm not sure this even is a bug or just a platform dependent difference which doesn't harm.

We have a custom device with a custom characteristic which does create a response and doesn't support the withoutResponse option.

in iOS this is clearly visible in the error log:

[CoreBluetooth] WARNING:
Characteristic <CBCharacteristic: 0x283ced200, UUID = 65(...)66, properties = 0x8, value = (null), notifying = NO>
does not specify the "Write Without Response" property - ignoring response-less write

On Android the very same call sequence does work without any error messages.

To Reproduce
Steps to reproduce the behavior:

  1. connect to bluetooth device
  2. select a characteristic which implements write with response only
  3. send data using writeWithoutResponse()
  4. check for success
  5. check error messages (debugger log output in Xcode / Android Studio respectively)

Expected behavior
An error message stating that the writeWithoutResponse is not allowed

  • iOS: error is shown as stated above
  • Android: no error message, the writeWithoutResponse() call is successful

Plugin version:

  • @capacitor-community/bluetooth-le: 1.0.0-4

Desktop (please complete the following information):

  • OS: Mac OS 11.2.3
  • N/A
  • N/A

Smartphone (please complete the following information):

  • Device: iPhone 8 / Noklia 6.1
  • OS: iOS 14.4.2 / Android 10
  • Browser: System webview
  • Version app built using Capacitor 3.0.0

isEnabled does not exist on type 'BleClientClass'

Hello sir,
I am using this bluetooth-le plugin in my project,
When I call the method isEnabled on BleClient, I am getting the error that, property isEnabled does not exist on type 'BleClientClass'. Please help me with this issue. I am working on Ionic react project and using capacitor version 3.
I followed your GitHub readme.md file.

Also let me know if this can scan mobile devices also, I am newbie recently joined a firm and working.
Thank you.

pair method

I'm trying to connect to GoPro https://gopro.github.io/OpenGoPro/tutorials/python/connect-ble
I'm following their tutorial they use python library called Bleak to interact with GoPro

Bleak version is

client = BleakClient(bluetoothDevice) // GoPro in this case
await client.connect(timeout=15) // connects

The GoPro has encryption-protected characteristics which require us to pair before writing to them. Therefore now that we are connected, we need to attempt to pair:

try:
    await client.pair()
except NotImplementedError:
    # This is expected on Mac
    pass

ionic version

I was able to connect to go pro with bluetooth but when I try to send command to GoPro form ionic app I got this

    const commandReqUUID = 'b5f90072-aa8d-11e3-9046-0002a5d5c51b';
    const service = '0000fea6-0000-1000-8000-00805f9b34fb';
    try {
      await BleClient.write(
        device.deviceId,
        service,
        commandReqUUID,
        numbersToDataView([3, 1, 1, 1])
      );
    } catch (error) {
      console.log(`error: ${JSON.stringify(error)}`);

    }
{"errorMessage":"Encryption is insufficient.","message":"Encryption is insufficient."}

question

will we have something like BleClient.pair() alternative to BleacClient.connect(device).pair()

Combination with background-geolocation: No disconnect when app gets closed

Description
Using this Plugin together with capacitor-community/background-geolocation in Android: bluetooth connection doesn't get closed when app is closed.

Reproduce

  1. Create an ionic app for Android
  2. install background-geolocation
  3. install cordova-plugin-ble-central or capacitor-community/bluetooth-le
  4. connect a ble device with the app
  5. close the app
  6. device is still connected with phone and therefore doesn't get find when reopen the app
  7. to verify: disable Bluetooth in phone settings after app is closed and the device will notify a disconnect

Expected behavior
Bluetooth connection should get closed when app gets closed

Smartphone

  • Device: Motorola moto g 5G plus, Google Pixel 2
  • OS: Android 10
  • Version 29

Additional context
I am working on a ble-sensor combined with geolocation tracker project

I opened this also as an issue at capacitor-community/background-geolocation:
capacitor-community/background-geolocation#32

How to test in the browser using ionic serve

Sorry if I open an issue here. That is a question:

I'm experimenting with your plugin in an Ionic project. It could be very useful to test Bluetooth functionalities directly from my development laptop (which of course has bluetooth), from the browser. I mean starting ionic serve and going to http://localhost:8100/. I'm asking because I read that the plugin support Web Bluetooth and I'm on Chrome that should support this API.

Do you think it's possible or I have to test on a real Android device?

Thank you very much and keep up the good work!

getting read timeout on read characteristic from connected device (used in Ionic app)

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:
the reads were started in parallel
log of results, after scan, have array of peripherals

  [log] - peripheral info={"uuids":["00010202-27b9-42f0-82aa-2e951747bbf9"],"txPower":127,"localName":"SamsDevice","device":{"name":"SamsDevice","deviceId":"FF85CCC9-5925-DFB4-C2DE-A614F966D654"},"rssi":-63}
⚡️  [log] - peripheral info={"uuids":["00020202-27b9-42f0-82aa-2e951747bbf9"],"localName":"SamsDevice","rssi":-69,"txPower":127,"device":{"deviceId":"9DC84DDB-DCB5-8B9D-FAD3-D76C56DB31BD","name":"SamsDevice"}}
⚡️  To Native ->  BluetoothLe connect 32443200
Connecting to peripheral <CBPeripheral: 0x283d592c0, identifier = FF85CCC9-5925-DFB4-C2DE-A614F966D654, name = SamsDevice, mtu = 0, state = disconnected>
Connected to device <CBPeripheral: 0x283d592c0, identifier = FF85CCC9-5925-DFB4-C2DE-A614F966D654, name = SamsDevice, mtu = 23, state = connected>
Resolve connect|FF85CCC9-5925-DFB4-C2DE-A614F966D654 Successfully connected.
Connected to peripheral. Waiting for service discovery.
didDiscoverServices
didDiscoverCharacteristicsFor 1 1
Resolve connect Connection successful.
⚡️  TO JS undefined
⚡️  To Native ->  BluetoothLe connect 32443201
Connecting to peripheral <CBPeripheral: 0x283d595e0, identifier = 9DC84DDB-DCB5-8B9D-FAD3-D76C56DB31BD, name = SamsDevice, mtu = 0, state = disconnected>
Connected to device <CBPeripheral: 0x283d595e0, identifier = 9DC84DDB-DCB5-8B9D-FAD3-D76C56DB31BD, name = SamsDevice, mtu = 23, state = connected>
Resolve connect|9DC84DDB-DCB5-8B9D-FAD3-D76C56DB31BD Successfully connected.
Connected to peripheral. Waiting for service discovery.
didDiscoverServices
didDiscoverCharacteristicsFor 1 1
Resolve connect Connection successful.
⚡️  TO JS undefined
⚡️  To Native ->  BluetoothLe read 32443202
Reading value
Resolve callback not registered for key:  read|Optional("00010202-27B9-42F0-82AA-2E951747BBF9")|00009AFD-0000-1000-8000-00805F9B34FB
Reject read|00010202-27B9-42F0-82AA-2E951747BBF9|00009AFD-0000-1000-8000-00805F9B34FB Read timeout.
ERROR MESSAGE:  {"errorMessage":"Read timeout.","message":"Read timeout."}
⚡️  [error] - {"errorMessage":"Read timeout.","message":"Read timeout."}
⚡️  To Native ->  BluetoothLe read 32443203
Reading value
⚡️  [log] - BleClient read rssi error={"errorMessage":"Read timeout."}
Resolve callback not registered for key:  read|Optional("00010202-27B9-42F0-82AA-2E951747BBF9")|00009AFF-0000-1000-8000-00805F9B34FB
Reject read|00010202-27B9-42F0-82AA-2E951747BBF9|00009AFF-0000-1000-8000-00805F9B34FB Read timeout.
ERROR MESSAGE:  {"message":"Read timeout.","errorMessage":"Read timeout."}
⚡️  To Native ->  BluetoothLe read 32443204
⚡️  [error] - {"message":"Read timeout.","errorMessage":"Read timeout."}
Reading value
⚡️  [log] - BleClient read table error={"errorMessage":"Read timeout."}
Resolve callback not registered for key:  read|Optional("00020202-27B9-42F0-82AA-2E951747BBF9")|00009AFD-0000-1000-8000-00805F9B34FB
⚡️  
Reject read|00020202-27B9-42F0-82AA-2E951747BBF9|00009AFD-0000-1000-8000-00805F9B34FB Read timeout.
ERROR MESSAGE:  {"errorMessage":"Read timeout.","message":"Read timeout."}
⚡️  To Native ->  BluetoothLe read 32443205
⚡️  [error] - {"errorMessage":"Read timeout.","message":"Read timeout."}
Reading value
⚡️  [log] - BleClient read rssi error={"errorMessage":"Read timeout."}
Resolve callback not registered for key:  read|Optional("00020202-27B9-42F0-82AA-2E951747BBF9")|00009AFF-0000-1000-8000-00805F9B34FB
Reject read|00020202-27B9-42F0-82AA-2E951747BBF9|00009AFF-0000-1000-8000-00805F9B34FB Read timeout.
ERROR MESSAGE:  {"errorMessage":"Read timeout.","message":"Read timeout."}
⚡️  [error] - {"errorMessage":"Read timeout.","message":"Read timeout."}
⚡️  [log] - BleClient read table error={"errorMessage":"Read timeout."}

Expected behavior
expect read to succeed

Screenshots

Plugin version:

  • @capacitor-community/bluetooth-le: [e.g. 0.4.0]
  • 1.2.0

Desktop (please complete the following information):

  • OS: [e.g. iOS] mac osx big Sur, xcode 13

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6] iphone 7
  • OS: [e.g. iOS8.1] 14.7

Additional context
Add any other context about the problem here.

the device says that the characteristics were read twice

09:34:42.957 -> Connected event, central: 41:c2:1b:19:ad:9f
09:34:42.957 -> central rssi=-42
09:34:42.957 ->  address=41:c2:1b:19:ad:9f
09:34:44.475 -> RSSI characteristic read, central rssi=-42
09:34:44.475 -> RSSI characteristic read, central rssi=-42
09:34:49.824 -> hash characteristic read, central rssi=-42
09:34:49.824 -> hash characteristic read, central rssi=-42
09:35:28.576 -> Disconnected event, central: 41:c2:1b:19:ad:9f

code

foundDevices.forEach((peripheral)=>{
               console.log("peripheral info="+JSON.stringify(peripheral))
               BleClient.connect(peripheral.device.deviceId).then(()=>{
                promises.push(new Promise((resolve, reject)=>{
                  BleClient.read(peripheral.device.deviceId, peripheral.uuids[0], numberToUUID("9AFD")).then((receivedRssi)=>{
                    console.log('RSSI value=', receivedRssi.getUint8(0));
                    resolve()
                  }).catch((error)=>{
                      console.log("BleClient read rssi error="+JSON.stringify(error))
                      reject(error)
                  })
                }))                  
                promises.push(new Promise((resolve, reject)=>{
                  BleClient.read(peripheral.device.deviceId, peripheral.uuids[0], numberToUUID("9AFF")).then((receivedTable)=>{
                    console.log('Table value=', receivedTable);
                    resolve()
                  }).catch((error)=>{
                      console.log("BleClient read table error="+JSON.stringify(error))
                      reject(error)
                  })       
                 }))
                 Promise.all(promises).then(()=>{
                     console.log("BleClient disconnecting")
                   BleClient.disconnect(peripheral.device.deviceId)
                 }).catch(error=>{
                    console.log("read chars error="+JSON.stringiy(error))
                    console.log("BleClient disconnecting")
                    BleClient.disconnect(peripheral.device.deviceId)
                 })
               }).catch((error)=>{
                  BleClient.disconnect(peripheral.device.deviceId)
                      console.log("BleClient connect error="+JSON.stringify(error))
                  })
             })

Expose localName of advertising packets in ScanResult object

Is your feature request related to a problem? Please describe.
When a BLE peripheral is discovered the first time by an iOS device the localName from the advertising packet is shown to the user as device name.
After connecting to this device the GAP name is discovered and cached.
For subsequent connection attempts the cached GAP name is shown to the user instead of the localName in the advertising packet.

If you are using the requestDevice(...) function of this plugin a dialog is opened automatically which shows eventually cached names.

If you have a BLE device where the GAP name differs from the localName provided in the advertising packets you can not guarantee a proper user experience using the requestDevice(...) function.

By using the requestLEScan(...) function you could provide a proper user experience by fetching the localName from the advertising packet of each found device and render your own connection dialog.
Unfortunately the localName attribute is currently not accessible in the ScanResult object.

Describe the solution you'd like
Add the localName property from the advertising packet to the ScanResult object.

Additional context
I tested it out by adding the following snippet to the getScanResult() function of the Plugin.swift file right before the return Statement:

let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String
if localName != nil {
    data["localName"] = localName
}

But I am not sure if localName is a good attribute name next to the device.name which is already present in the ScanResult. Personally, I would prefer a name that makes it clear that it is the name from the advertisement packet.

Do you think this is something you could add to this plugin?
Thank you in advance for your time and efforts!

Bluetooth connection not updating on Bluetooth LE device?

Hello sir,
I am using your plugin with Ionic React,
I followed your steps in Readme file,
I scan for BLE devices and when I click on particular device, the connection gets established but the connection status is not updating in the BLE device that is connection status is not updated to connected.
I thought this is the issue with BLE device itself, but it is working fine with another app that is written in Qt.
please do help me with this,

I am using bluetooth-le api 1.1.1, capacitor 3,
I am developing this for android

Cannot preform multiple pairing

I am not sure if I have found a bug or am I doing something wrong.

What I am trying to do is scan for my ble device, connect to it, start the pairing scheme(typing the in number) and once successful repeat the whole process again. The reason for repeating the process of pairing is incase my device was to fail for any given reason, a user would reset the device and start the pairing process again.

What I am experiencing is that once my device is paired, if I was to repeat the cycle again on my app, I am able to scan and see the device, but when I connect I do not get an option to retype the pin in this 2nd instance. But I was re repeat this process again, the pin input screen does appear. It seems that the screen appears every other time.

Is it possible to force the plugin to unpair a device?

This is my code
`
async ble_test(){
console.log("Starting BLE TEST");

try {

  await BleClient.initialize();


console.log('BLE INT');    
await BleClient.requestLEScan(
  {
   // services: [HEART_RATE_SERVICE],
   name:"3400-DEMO",
  },
  result => {
    console.log('received new scan result', result);     
    this.Azure.ble_device_id = result.device.deviceId;
    this.connecttodevice(this.Azure.ble_device_id);
  },
); 

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

} catch (error) {
console.error(error);
}
}
`

implement getDevices() for android and ios

Is your feature request related to a problem? Please describe.
It doesn't seem possible to get multiple devices to interact with them.
requestDevice() will never return a single result for my usecases.

EDIT: after rereading the spec, it might make more sense to leave getDevices as is, and rather add a new method called requestDevices() which takes the same filter args as requestDevice or alternatively, add a new set of functions that more closely aligns with other existing bluetooth modules/plugins like a scan function that return an observable or operate on a callback.

multiple Notifications not working

It is not possible to do multiple Notifications on the same device but different characteristics.
It throws an error that the second notification can not be subscribed.

I stumbled over this some days ago using Android. I will post the exact error message when coming to this again. but it is easy to reproduce. Just start two notifications.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.