react-native-bluetooth-classic
Version:
Implement bluetooth classic (serial) on Android (Bluetooth Adapter) and IOS (External Accessory)
395 lines (394 loc) • 15.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import BluetoothDevice from './BluetoothDevice';
import { BluetoothEventType, } from './BluetoothEvent';
import { NativeEventEmitter, Platform } from 'react-native';
import { Buffer } from 'buffer';
/**
* Provides access to native module. In general the methods will be direct calls
* through to {@code NativeModules.RNBluetoothClassc}, although there are instances
* where methods are overwritten where additional information is required. These
* methods are related to {@code BluetoothConnection} requests, where the response
* must be wrapped.
*
* @author kenjdavidson
*/
export default class BluetoothModule {
constructor(nativeModule) {
this._nativeModule = nativeModule;
this._eventEmitter = new NativeEventEmitter(new NativeModule());
}
/**
* Requests availability status of Bluetooth for the device.
*
* @return Promise resolved with whether Bluetooth is available for the current device.
*/
isBluetoothAvailable() {
if (Platform.OS == 'ios')
return Promise.resolve(true);
return this._nativeModule.isBluetoothAvailable();
}
/**
* Requests enabled status from the BluetoothAdapter.
*
* @return Promise resolved with whether Bluetooth is enabled
*/
isBluetoothEnabled() {
return this._nativeModule.isBluetoothEnabled();
}
/**
* Requests whether there is data availabled on the Device. At this point
* this is a yes/no, it may be wise to turn it into number of bytes based
* on devices that send messages of set size with no delimiter.
*
* @param address of the Device we wish to check
* @return Promise resolved with whether data is available
*/
availableFromDevice(address) {
return this._nativeModule.availableFromDevice(address);
}
/**
* Read from the specified device. This uses the configured device read
* functionality - see the Native documentation for how that is configured.
*
* @param address address from which to read
* @return Promise resovled with individual read
*/
readFromDevice(address) {
return this._nativeModule.readFromDevice(address);
}
/**
* Clears the device buffer.
*
* @param address for which device we will clear
* @return Promise resolved with whether clear was successful
*/
clearFromDevice(address) {
return this._nativeModule.clearFromDevice(address);
}
/**
* Disconnect from device.
*
* @param address of Device we will disconnect
* @return Promise resolved with disconnection success status
*/
disconnectFromDevice(address) {
return this._nativeModule.disconnectFromDevice(address);
}
/**
* Determines whether device is currently connected. Connected means that there
* is an actual Socket open (not just pairing)
*
* @param address of which we are checking for connection
* @returns Promise resolved with whether there is a connection
*/
isDeviceConnected(address) {
return this._nativeModule.isDeviceConnected(address);
}
/**
* Wraps the bonded native devices with a BluetoothDevice to allow for
* event management.
*
* @return Promise containing array of pair devices
*/
getBondedDevices() {
return __awaiter(this, void 0, void 0, function* () {
let bonded = yield this._nativeModule.getBondedDevices();
let devices = [];
for (let device of bonded) {
devices.push(new BluetoothDevice(device, this));
}
return devices;
});
}
/**
* Wraps the connected native devices with a BluetoothDevice to allow for
* event management.
*
* @return Promise resolved with array of connected devices
*/
getConnectedDevices() {
return __awaiter(this, void 0, void 0, function* () {
let connected = yield this._nativeModule.getConnectedDevices();
let devices = [];
for (let device of connected) {
devices.push(new BluetoothDevice(device, this));
}
return devices;
});
}
/**
* Attempts to connect to the requested device. Defaults the properties to an empty
* map.
*
* @param address the address to which we are connecting
* @param properties extra properties required for the connection.
*/
connectToDevice(address, options) {
return __awaiter(this, void 0, void 0, function* () {
// Comming from the Java world this is nuts - not being able to assign anything to
// options because it's a <T extends StandardOptions> even with something that matches
// the StandardOptions interface
let connected = yield this._nativeModule.connectToDevice(address, options || {});
return new BluetoothDevice(connected, this);
});
}
/**
* Wraps connected NativeDevice.
*
* @param address the address to check for connection
*/
getConnectedDevice(address) {
return __awaiter(this, void 0, void 0, function* () {
let nativeDevice = yield this._nativeModule.getConnectedDevice(address);
return new BluetoothDevice(nativeDevice, this);
});
}
/**
* Write the provided message to the device. The `message` can be provided
* as either:
* - A String which will be encoded (using the optional `encoding`)
* - A Buffer
*
* Both of which will eventually be base64 encoded in order to ensure that the data
* is transferred in a common format through React Natives allowed parameter types.
*
* @param address the address to which we will send data
* @param message string|Buffer which will be sent.
* @param encoding "utf-8" | "ascii" | "utf8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex" | undefined
*/
writeToDevice(address, message, encoding) {
let data = Buffer.isBuffer(message) ? message : Buffer.from(message, encoding);
return this._nativeModule.writeToDevice(address, data.toString('base64'));
}
/**
* Starts discovery on the bluetooth adatper.
*
* This is an Anroid only function.
*/
startDiscovery() {
return __awaiter(this, void 0, void 0, function* () {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
let discoveredDevices = yield this._nativeModule.startDiscovery();
let devices = [];
for (let discovered of discoveredDevices) {
devices.push(new BluetoothDevice(discovered, this));
}
return devices;
});
}
/**
* Cancels discovery. If discovery was alreayd stopped, this will end gracefully
* by resolving the promise.
*
* This is an Android only feature.
*/
cancelDiscovery() {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.cancelDiscovery();
}
/**
* Pair the device request.
*
* This is an Android only feature.
*
* @param address address of the device we wish to pair
*/
pairDevice(address) {
return __awaiter(this, void 0, void 0, function* () {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
let paired = yield this._nativeModule.pairDevice(address);
return new BluetoothDevice(paired, this);
});
}
/**
* Unpair the device request.
*
* This is an Android only feature.
*
* @param address address of the device we wish to unpair
*/
unpairDevice(address) {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.unpairDevice(address);
}
/**
* Attempt to start accepting connections. Accepts only one connection at a time,
* once this has been established the device is returned and accepting is disabled.
*
* This is an Android only feature.
*
* @param properties used during the connection and connected process(es)
*/
accept(properties) {
return __awaiter(this, void 0, void 0, function* () {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
let paired = yield this._nativeModule.accept(properties);
return new BluetoothDevice(paired, this);
});
}
/**
* Attempt to cancel the accepting state.
*
* This is an Android only feature.
*/
cancelAccept() {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.cancelAccept();
}
/**
* Request user to turn on Bluetooth Adapter
*
* This is an Android only feature.
*
* @param state
*/
requestBluetoothEnabled() {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.requestBluetoothEnabled();
}
/**
* Attempts to set the bluetooth adapter name.
*
* This is an Android only feature.
*
* @param name the name to which we will change BluetoothAdapter
*/
setBluetoothAdapterName(name) {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.setBluetoothAdapterName(name);
}
createBluetoothEventSubscription(eventType, listener) {
this._nativeModule.addListener(eventType);
let subscription = this._eventEmitter.addListener(eventType, listener);
return {
remove: () => {
this._nativeModule.removeListener(eventType);
subscription.remove();
},
};
}
/**
* Creates an EventSubscription which calls the provided listener when the native
* device is notified of the BluetoothAdapter being enabled.
*
* @param listener
*/
onBluetoothEnabled(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.BLUETOOTH_ENABLED, listener);
}
/**
* Creates an EventSubscription which calls the provided listener when the native
* device is notified of the BluetoothAdapter being disabled.
*
* @param listener
*/
onBluetoothDisabled(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.BLUETOOTH_DISABLED, listener);
}
/**
* Creates an EventSubscription which wraps both enabled and disabled.
*
* @param listener
*/
onStateChanged(listener) {
let enabledSubscription = this._eventEmitter.addListener(BluetoothEventType.BLUETOOTH_ENABLED, listener);
let disabledSubscription = this._eventEmitter.addListener(BluetoothEventType.BLUETOOTH_DISABLED, listener);
return {
remove() {
enabledSubscription.remove();
disabledSubscription.remove();
},
};
}
/**
* Creates an EventSubscription which wraps the DEVICE_CONNECTED event type.
*
* @param listener
*/
onDeviceConnected(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.DEVICE_CONNECTED, listener);
}
/**
* Creates an EventSubscription which wraps the DEVICE_DISCONNECTED event type. Device disconnected events
* can be thrown for the following:
* - During a read the DeviceConnection receives an un-cancelled exception (generally a closure)
* - The AclReceiver receives an on disconnect (this seems less informative as it will still fire a disconnect
* event if the connect had been cancelled. So at this point it may need to be removed.)
*
* @param listener
*/
onDeviceDisconnected(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.DEVICE_DISCONNECTED, listener);
}
/**
* Creates an EventSubscription based on the read event from a specified device. If the device
* is not currently connected an exception will be thrown, although I'm not sure if
* this is required, since it may be annoying to continually add/remove subscriptions.
*
* @param address device address to which we will start listening
* @param listener onReadListener
*/
onDeviceRead(address, listener) {
let eventType = `${BluetoothEventType.DEVICE_READ}@${address}`;
this._nativeModule.addListener(eventType);
let subscription = this._eventEmitter.addListener(eventType, listener);
return {
remove: () => {
this._nativeModule.removeListener(eventType);
subscription.remove();
},
};
}
/**
* Creates an EventSubscription which wraps the ERROR event.
*
* @param listener
*/
onError(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.ERROR, listener);
}
/**
* Creates an event subscription wrapping the DEVICE_DISCOVERED events. DEVICE_DISCOVERED is fired during the
* discovery process, when a new device is found. Note this is only fired on the first discovery, it will not
* be fired (at this point) with the updated RSSI value on the next device discovery.
*
* Remember to remove the subscription when you've found your device, or you stop discovery.
*
* @param listener
*/
onDeviceDiscovered(listener) {
return this.createBluetoothEventSubscription(BluetoothEventType.DEVICE_DISCOVERED, listener);
}
/**
* Opens Android's Bluetooth Settings activity.
*/
openBluetoothSettings() {
if (Platform.OS == 'ios')
throw new Error('Method not implemented.');
return this._nativeModule.openBluetoothSettings();
}
}
/**
* Internal `NativeModule` to get around the fact that React doesn't actually make this
* type available, but we need it in order to create our BluetoothModule.
*/
class NativeModule {
addListener(eventType) { }
removeListeners(count) { }
}