react-native-ble-qz
Version:
React Native Bluetooth Low Energy library
467 lines (408 loc) • 16.8 kB
JavaScript
// @flow
;
import { NativeModules, NativeEventEmitter } from 'react-native';
import Device from './Device';
import Service from './Service';
import Characteristic from './Characteristic';
import { fullUUID } from './Utils';
const BleModule = NativeModules.BleClientManager;
export type State =
'Unknown'
| 'Resetting'
| 'Unsupported'
| 'Unauthorized'
| 'PoweredOff'
| 'PoweredOn'
export type Subscription = {
remove: () => void
}
export type ScanOptions = {
allowDuplicates?: boolean,
autoConnect?: boolean
}
export type ConnectionOptions = {
// Not used for now
}
/**
*
* BleManager is an entry point for react-native-ble-plx library. It provides all means to discover and work with
* {@link Device} instances. It should be initialized only once with new keyword and method {@link destroy} should be called
* on its instance when user wants to deallocate all resources.
*
* @example
* const manager = new BleManager();
* // ... work with BLE manager ...
* manager.destroy();
*
* @export
* @class BleManager
*/
export default class BleManager {
_scanEventSubscription: ?NativeEventEmitter
_screenEventSubscription: ?NativeEventEmitter
_eventEmitter: NativeEventEmitter
_uniqueId: number
constructor() {
BleModule.createClient();
this._eventEmitter = new NativeEventEmitter(BleModule)
this._uniqueId = 0
}
/**
* Destroys BleManager instance. A new instance needs to be created to continue working with react-native-ble-plx.
*
* @memberOf BleManager
*/
destroy() {
BleModule.destroyClient();
}
_nextUniqueID(): string {
this._uniqueId += 1
return this._uniqueId.toString()
}
// Mark: Common --------------------------------------------------------------------------------------------------------
/**
* Cancels pending transaction.
*
* Few operations such as monitoring characteristic's value changes can be cancelled by a user. Basically every API
* entry which accepts `transactionId` allows to call `cancelTransaction` function. When cancelled operation is a
* promise or a callback which registers errors, `"Cancelled"` error will be emitted in that case.
*
* Cancelling transaction which doesn't exist is ignored.
*
* @param {string} transactionId Id of pending transactions.
*
* @memberOf BleManager
*/
cancelTransaction(transactionId: string) {
BleModule.cancelTransaction(transactionId)
}
// Mark: Monitoring state ----------------------------------------------------------------------------------------------
/**
* Current state of a manager.
*
* Available states:
* - `Unknown` - the current state of the manager is unknown; an update is imminent.
* - `Resetting` - the connection with the system service was momentarily lost; an update is imminent.
* - `Unsupported` - the platform does not support Bluetooth low energy.
* - `Unauthorized` - the app is not authorized to use Bluetooth low energy.
* - `PoweredOff` - Bluetooth is currently powered off.
* - `PoweredOn` - Bluetooth is currently powered on and available to use.
*
* @returns {Promise<State>} Promise which emits current state of BleManager.
*
* @memberOf BleManager
*/
state(): Promise<State> {
return BleModule.state()
}
/**
*
* Notifies about state changes of a manager.
*
* @param {function(newState: State)} listener Callback which emits state changes of BLE Manager.
* Look at {@link state} for possible values.
* @param {boolean} [emitCurrentState=false] If true, current state will be emitted as well. Defaults to false.
*
* @returns {Subscription} Subscription on which remove() function can be called to unsubscribe.
*
* @memberOf BleManager
*/
onStateChange(listener: (newState: State) => void, emitCurrentState: boolean = false): Subscription {
const subscription = this._eventEmitter.addListener(BleModule.StateChangeEvent, listener);
if (emitCurrentState) {
var cancelled = false;
this.state().then((currentState) => {
if (!cancelled) {
listener(currentState);
}
});
return {
remove: () => {
cancelled = true;
subscription.remove();
}
}
}
return subscription
}
onScreenStateChange(listener: (newState: State) => void, emitCurrentState: boolean = false): Subscription {
const subscription = this._eventEmitter.addListener(BleModule.ScreenEvent, listener);
if (emitCurrentState) {
listener(State);
return {
remove: () => {
cancelled = true;
subscription.remove();
}
}
}
return subscription
}
// Mark: Scanning ------------------------------------------------------------------------------------------------------
/**
* Starts device scanning.
*
* When previous scan is in progress it will be stopped before executing this command.
*
* @param {?string[]} UUIDs - Array of strings containing UUIDs of services which are registered in scanned devices.
* If null is passed all available devices will be scanned.
* @param {?ScanOptions} options - Optional configuration for scanning operation. Scan option object contains two
* optional fields: `allowDuplicates` for iOS when set to true scanned {@link Device}s will be emitted more
* frequently, `autoConnect` for Android - allows to connect to devices which are not in range.
* @param {function(error: ?Error, scannedDevice: ?Device)} listener - Function which will be called for every scanned
* {@link Device} (devices may be scanned multiple times). It's first argument is potential {@link Error} which is set
* to non `null` value when scanning failed. You have to start scanning process again if that happens. Second argument
* is a scanned {@link Device}.
*
* @memberOf BleManager
*/
startDeviceScan(UUIDs: ?string[], options: ?ScanOptions, listener: (error: ?Error, scannedDevice: ?Device) => void) {
this.stopDeviceScan()
const scanListener = ([error, device]) => {
listener(error, device ? new Device(device, this) : null)
};
this._scanEventSubscription = this._eventEmitter.addListener(BleModule.ScanEvent, scanListener);
BleModule.startDeviceScan(UUIDs, options);
}
/**
*
* Stops device scan if in progress.
*
* @memberOf BleManager
*/
stopDeviceScan() {
if (this._scanEventSubscription) {
this._scanEventSubscription.remove()
delete this._scanEventSubscription
}
BleModule.stopDeviceScan();
}
// Mark: Connection management -----------------------------------------------------------------------------------------
/**
* Connects to {@link Device} with provided ID.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {?ConnectionOptions} options - Platform specific options for connection establishment. Not used currently.
* @returns {Promise<Device>} Connected {@link Device} object if successful.
*
* @memberOf BleManager
*/
async connectToDevice(deviceIdentifier: string, options: ?ConnectionOptions): Promise<Device> {
const deviceProps = await BleModule.connectToDevice(deviceIdentifier, options);
return new Device(deviceProps, this);
}
/**
* Disconnects from device if it's connected or cancels pending connection.
*
* @param {string} deviceIdentifier - {@link Device} identifier to be closed.
* @returns {Promise<Device>} Returns closed {@link Device} when operation is successful.
*
* @memberOf BleManager
*/
async cancelDeviceConnection(deviceIdentifier: string): Promise<Device> {
const deviceProps = await BleModule.cancelDeviceConnection(deviceIdentifier);
return new Device(deviceProps, this);
}
/**
* Monitors if device was disconnected due to any errors or connection problems.
*
* @param {string} deviceIdentifier - {@link Device} identifier to be monitored.
* @param {function(error: ?Error, device: Device)} listener - callback returning error as a reason of disconnection
* if available and {@link Device} object.
* @returns {Subscription} Subscription on which `remove()` function can be called to unsubscribe.
*
* @memberOf BleManager
*/
onDeviceDisconnected(deviceIdentifier: string, listener: (error: ?Error, device: Device) => void): Subscription {
const disconnectionListener = ([error, device]) => {
if (deviceIdentifier !== device.id) return
listener(error, device)
};
const subscription = this._eventEmitter.addListener(BleModule.DisconnectionEvent, disconnectionListener);
return subscription
}
/**
* Check connection state of a device.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @returns {Promise<boolean>} - Promise which emits `true` if device is connected, and `false` otherwise.
*
* @memberOf BleManager
*/
async isDeviceConnected(deviceIdentifier: string): Promise<boolean> {
return BleModule.isDeviceConnected(deviceIdentifier)
}
// Mark: Discovery -------------------------------------------------------------------------------------------------
/**
* Discovers all services and characteristics for {@link Device}.
*
* @param {string} identifier - {@link Device} identifier.
* @returns {Promise<Device>} - Promise which emits {@link Device} object if all available services and
* characteristics have been discovered.
*
* @memberOf BleManager
*/
async discoverAllServicesAndCharacteristicsForDevice(identifier: string): Promise<Device> {
const deviceProps = await BleModule.discoverAllServicesAndCharacteristicsForDevice(identifier)
return new Device(deviceProps, this)
}
// Mark: Service and characteristic getters ------------------------------------------------------------------------
/**
* List of discovered services for device.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @returns {Promise<Service[]>} - Promise which emits array of {@link Service} objects which are discovered for a
* {@link Device}.
*
* @memberOf BleManager
*/
async servicesForDevice(deviceIdentifier: string): Promise<Service[]> {
const services = await BleModule.servicesForDevice(deviceIdentifier)
return services.map((serviceProps) => { return new Service(serviceProps, this) })
}
/**
* List of discovered characteristics for given {@link Device} and {@link Service}.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {string} serviceUUID - {@link Service} UUID.
* @returns {Promise<Characteristic[]>} - Promise which emits array of {@link Characteristic} objects which are
* discovered for a {@link Device} in specified {@link Service}.
*
* @memberOf BleManager
*/
async characteristicsForDevice(deviceIdentifier: string, serviceUUID: string): Promise<Characteristic[]> {
const characteristics = await BleModule.characteristicsForDevice(deviceIdentifier, serviceUUID);
return characteristics.map((characteristicProps) => { return new Characteristic(characteristicProps, this) });
}
// Mark: Characteristics operations --------------------------------------------------------------------------------
/**
* Read characteristic value.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {string} serviceUUID - {@link Service} UUID.
* @param {string} characteristicUUID - {@link Characteristic} UUID.
* @param {?string} transactionId - optional `transactionId` which can be used in {@link cancelTransaction} function.
* @returns {Promise<Characteristic>} - Promise which emits first {@link Characteristic} object matching specified
* UUID paths. Latest value of {@link Characteristic} will be stored inside returned object.
*
* @memberOf BleManager
*/
async readCharacteristicForDevice(
deviceIdentifier: string,
serviceUUID: string,
characteristicUUID: string,
transactionId: ?string): Promise<Characteristic> {
if (!transactionId) {
transactionId = this._nextUniqueID()
}
const characteristicProps = await BleModule.readCharacteristicForDevice(deviceIdentifier,
serviceUUID,
characteristicUUID,
transactionId);
return new Characteristic(characteristicProps, this)
}
/**
* Write characteristic value with response.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {string} serviceUUID - {@link Service} UUID.
* @param {string} characteristicUUID - {@link Characteristic} UUID.
* @param {string} base64Value - Value in Base64 format.
* @param {?string} transactionId - optional `transactionId` which can be used in {@link cancelTransaction} function.
* @returns {Promise<Characteristic>} - Promise which emits first {@link Characteristic} object matching specified
* UUID paths. Latest value of characteristic may not be stored inside returned object.
*
* @memberOf BleManager
*/
async writeCharacteristicWithResponseForDevice(
deviceIdentifier: string,
serviceUUID: string,
characteristicUUID: string,
base64Value: string,
transactionId: ?string): Promise<Characteristic> {
if (!transactionId) {
transactionId = this._nextUniqueID()
}
const characteristicProps = await BleModule.writeCharacteristicForDevice(deviceIdentifier,
serviceUUID,
characteristicUUID,
base64Value,
true,
transactionId);
return new Characteristic(characteristicProps, this)
}
/**
* Write characteristic value without response.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {string} serviceUUID - {@link Service} UUID.
* @param {string} characteristicUUID - {@link Characteristic} UUID.
* @param {string} base64Value - Value in Base64 format.
* @param {?string} transactionId - optional `transactionId` which can be used in {@link cancelTransaction} function.
* @returns {Promise<Characteristic>} - Promise which emits first {@link Characteristic} object matching specified
* UUID paths. Latest value of characteristic may not be stored inside returned object.
*
* @memberOf BleManager
*/
async writeCharacteristicWithoutResponseForDevice(
deviceIdentifier: string,
serviceUUID: string,
characteristicUUID: string,
base64Value: string,
transactionId: ?string): Promise<Characteristic> {
if (!transactionId) {
transactionId = this._nextUniqueID()
}
const characteristicProps = await BleModule.writeCharacteristicForDevice(deviceIdentifier,
serviceUUID,
characteristicUUID,
base64Value,
false,
transactionId);
return new Characteristic(characteristicProps, this)
}
/**
* Monitor value changes of a characteristic. If notifications are enabled they will be used
* in favour of indications.
*
* @param {string} deviceIdentifier - {@link Device} identifier.
* @param {string} serviceUUID - {@link Service} UUID.
* @param {string} characteristicUUID - {@link Characteristic} UUID.
* @param {function(error: ?Error, characteristic: ?Characteristic)} listener - callback which emits
* {@link Characteristic} objects with modified value for each notification.
* @param {?string} transactionId - optional `transactionId` which can be used in {@link cancelTransaction} function.
* @returns {Subscription} Subscription on which `remove()` function can be called to unsubscribe.
*
* @memberOf BleManager
*/
monitorCharacteristicForDevice(deviceIdentifier: string,
serviceUUID: string,
characteristicUUID: string,
listener: (error: ?Error, characteristic: ?Characteristic) => void,
transactionId: ?string): Subscription {
if (!transactionId) {
transactionId = this._nextUniqueID()
}
const monitorListener = ([error, characteristic, msgTransactionId]) => {
if (transactionId !== msgTransactionId) return
if (error) {
listener(error, null)
return
}
listener(null, new Characteristic(characteristic, this))
};
const subscription = this._eventEmitter.addListener(BleModule.ReadEvent, monitorListener);
BleModule.monitorCharacteristicForDevice(deviceIdentifier, serviceUUID, characteristicUUID, transactionId)
.then((finished) => {
subscription.remove()
}, (error) => {
listener(error, null)
subscription.remove()
})
return {
remove: () => {
BleModule.cancelTransaction(transactionId)
}
}
}
}