Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add duration flag (in seconds) to scan with options #967

Merged
merged 5 commits into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,9 @@ See the [location permission notes](#location-permission-notes) above for inform

- **services**: List of services to discover, or [] to find all devices
- **options**: an object specifying a set of name-value pairs. The currently acceptable options are:
- _reportDuplicates_: _true_ if duplicate devices should be reported, _false_ (default) if devices should only be reported once. [optional]
- _scanMode_: String defines [setScanMode()](https://developer.android.com/reference/kotlin/android/bluetooth/le/ScanSettings.Builder#setscanmode) argument on Android.
- _reportDuplicates_: _true_ if duplicate devices should be reported, _false_ (default) if devices should only be reported once.
- _duration_: time in seconds to scan for. (default) Scans forever when not specified.
- _scanMode_: String defines [setScanMode()](https://developer.android.com/reference/kotlin/android/bluetooth/le/ScanSettings.Builder#setscanmode) argument on Android. Default on Android is _lowPower_. When interactive scanning from an app, _lowLatency_ can boost how quickly the device is found, at the expense of using more battery power.
Can be one of: _lowPower_ | _balanced_ | _lowLatency_ | _opportunistic_
- _callbackType_: String defines [setCallbackType()](https://developer.android.com/reference/kotlin/android/bluetooth/le/ScanSettings.Builder#setcallbacktype) argument on Android.
Can be one of: _all_ | _first_ | _lost_
Expand Down
32 changes: 7 additions & 25 deletions src/android/BLECentralPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ public class BLECentralPlugin extends CordovaPlugin {
private static final String BLUETOOTH_SCAN = "android.permission.BLUETOOTH_SCAN" ; // API 31

// actions
private static final String SCAN = "scan";
private static final String START_SCAN = "startScan";
private static final String STOP_SCAN = "stopScan";
private static final String START_SCAN_WITH_OPTIONS = "startScanWithOptions";
private static final String BONDED_DEVICES = "bondedDevices";
Expand Down Expand Up @@ -146,6 +144,7 @@ public class BLECentralPlugin extends CordovaPlugin {
private int scanSeconds;
private ScanSettings scanSettings;
private final Handler stopScanHandler = new Handler(Looper.getMainLooper());
private final Runnable stopScanRunnable = this::stopScan;

// Bluetooth state notification
CallbackContext stateCallback;
Expand Down Expand Up @@ -210,21 +209,7 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback
}

boolean validAction = true;

if (action.equals(SCAN)) {

UUID[] serviceUUIDs = parseServiceUUIDList(args.getJSONArray(0));
int scanSeconds = args.getInt(1);
resetScanOptions();
findLowEnergyDevices(callbackContext, serviceUUIDs, scanSeconds);

} else if (action.equals(START_SCAN)) {

UUID[] serviceUUIDs = parseServiceUUIDList(args.getJSONArray(0));
resetScanOptions();
findLowEnergyDevices(callbackContext, serviceUUIDs, -1);

} else if (action.equals(STOP_SCAN)) {
if (action.equals(STOP_SCAN)) {
stopScan();
callbackContext.success();

Expand Down Expand Up @@ -526,7 +511,8 @@ public boolean execute(String action, CordovaArgs args, CallbackContext callback
if (reportDelay >= 0L)
scanSettings.setReportDelay( reportDelay );

findLowEnergyDevices(callbackContext, serviceUUIDs, -1, scanSettings.build() );
int scanDuration = options.optInt("duration", -1);
findLowEnergyDevices(callbackContext, serviceUUIDs, scanDuration, scanSettings.build() );
}

} else if (action.equals(BONDED_DEVICES)) {
Expand Down Expand Up @@ -1186,10 +1172,6 @@ public void onScanFailed(int errorCode) {
};


private void findLowEnergyDevices(CallbackContext callbackContext, UUID[] serviceUUIDs, int scanSeconds) {
findLowEnergyDevices( callbackContext, serviceUUIDs, scanSeconds, new ScanSettings.Builder().build() );
}

private void findLowEnergyDevices(CallbackContext callbackContext, UUID[] serviceUUIDs, int scanSeconds, ScanSettings scanSettings) {

if (!locationServicesEnabled() && Build.VERSION.SDK_INT < 31) {
Expand Down Expand Up @@ -1273,11 +1255,11 @@ private void findLowEnergyDevices(CallbackContext callbackContext, UUID[] servic
filters.add(filter);
}
}
stopScanHandler.removeCallbacks(this::stopScan);
stopScanHandler.removeCallbacks(stopScanRunnable);
bluetoothLeScanner.startScan(filters, scanSettings, leScanCallback);

if (scanSeconds > 0) {
stopScanHandler.postDelayed(this::stopScan, scanSeconds * 1000);
stopScanHandler.postDelayed(stopScanRunnable, scanSeconds * 1000);
}

PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
Expand All @@ -1286,7 +1268,7 @@ private void findLowEnergyDevices(CallbackContext callbackContext, UUID[] servic
}

private void stopScan() {
stopScanHandler.removeCallbacks(this::stopScan);
stopScanHandler.removeCallbacks(stopScanRunnable);
if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
LOG.d(TAG, "Stopping Scan");
try {
Expand Down
9 changes: 1 addition & 8 deletions src/browser/BLECentralPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ function formatUUID(uuid) {

module.exports = {
deviceInfos: new Map(),

scan: function(services, seconds, success, failure) {
return this.startScanWithOptions(services, {}, success, failure);
},
startScan: function(services, success, failure) {
return this.startScanWithOptions(services, {}, success, failure);
},
startScanWithOptions: function(services, options, success, failure) {
startScanWithOptions: function (services, options, success, failure) {
if (!navigator.bluetooth) {
failure('Bluetooth is not supported on this browser.');
return;
Expand Down
3 changes: 1 addition & 2 deletions src/ios/BLECentralPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,12 @@
NSMutableDictionary *readRSSICallbacks;
NSDictionary<NSString*,id> *restoredState;
NSMutableDictionary *l2CapContexts;
NSTimer *scanTimer;
}

@property (strong, nonatomic) NSMutableSet *peripherals;
@property (strong, nonatomic) CBCentralManager *manager;

- (void)scan:(CDVInvokedUrlCommand *)command;
- (void)startScan:(CDVInvokedUrlCommand *)command;
- (void)startScanWithOptions:(CDVInvokedUrlCommand *)command;
- (void)stopScan:(CDVInvokedUrlCommand *)command;
- (void)connectedPeripheralsWithServices:(CDVInvokedUrlCommand*)command;
Expand Down
80 changes: 26 additions & 54 deletions src/ios/BLECentralPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -354,50 +354,6 @@ - (void)isEnabled:(CDVInvokedUrlCommand*)command {
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void)scan:(CDVInvokedUrlCommand*)command {
NSLog(@"scan");
if ([manager state] != CBManagerStatePoweredOn) {
NSString *error = @"Bluetooth is disabled";
NSLog(@"%@", error);
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
messageAsString:error];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

discoverPeripheralCallbackId = [command.callbackId copy];

NSArray<NSString *> *serviceUUIDStrings = [command argumentAtIndex:0];
NSNumber *timeoutSeconds = [command argumentAtIndex:1];
NSArray<CBUUID *> *serviceUUIDs = [self uuidStringsToCBUUIDs:serviceUUIDStrings];

[manager scanForPeripheralsWithServices:serviceUUIDs options:nil];

[NSTimer scheduledTimerWithTimeInterval:[timeoutSeconds floatValue]
target:self
selector:@selector(stopScanTimer:)
userInfo:[command.callbackId copy]
repeats:NO];
}

- (void)startScan:(CDVInvokedUrlCommand*)command {
NSLog(@"startScan");
if ([manager state] != CBManagerStatePoweredOn) {
NSString *error = @"Bluetooth is disabled";
NSLog(@"%@", error);
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
messageAsString:error];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

discoverPeripheralCallbackId = [command.callbackId copy];
NSArray<NSString *> *serviceUUIDStrings = [command argumentAtIndex:0];
NSArray<CBUUID *> *serviceUUIDs = [self uuidStringsToCBUUIDs:serviceUUIDStrings];

[manager scanForPeripheralsWithServices:serviceUUIDs options:nil];
}

- (void)startScanWithOptions:(CDVInvokedUrlCommand*)command {
NSLog(@"startScanWithOptions");
if ([manager state] != CBManagerStatePoweredOn) {
Expand All @@ -420,16 +376,24 @@ - (void)startScanWithOptions:(CDVInvokedUrlCommand*)command {
[scanOptions setValue:reportDuplicates
forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
}
NSNumber *timeoutSeconds = [options valueForKey: @"duration"];

[manager scanForPeripheralsWithServices:serviceUUIDs options:scanOptions];

[scanTimer invalidate];
scanTimer = nil;
if (timeoutSeconds) {
scanTimer = [NSTimer scheduledTimerWithTimeInterval:[timeoutSeconds floatValue]
target:self
selector:@selector(stopScanTimer:)
userInfo:[command.callbackId copy]
repeats:NO];
}
}

- (void)stopScan:(CDVInvokedUrlCommand*)command {
NSLog(@"stopScan");

if ([manager state] == CBManagerStatePoweredOn) {
[manager stopScan];
}
[self internalStopScan];

if (discoverPeripheralCallbackId) {
discoverPeripheralCallbackId = nil;
Expand Down Expand Up @@ -639,12 +603,7 @@ - (void)writeL2Cap:(CDVInvokedUrlCommand *)command {

-(void)stopScanTimer:(NSTimer *)timer {
NSLog(@"stopScanTimer");

[manager stopScan];

if (discoverPeripheralCallbackId) {
discoverPeripheralCallbackId = nil;
}
[self internalStopScan];
}

#pragma mark - CBCentralManagerDelegate
Expand Down Expand Up @@ -1251,4 +1210,17 @@ - (id) tryDecodeBinaryData:(id)value {
return value;
}

- (void) internalStopScan {
[scanTimer invalidate];
scanTimer = nil;

if ([manager state] == CBManagerStatePoweredOn) {
[manager stopScan];
}

if (discoverPeripheralCallbackId) {
discoverPeripheralCallbackId = nil;
}
}

@end
2 changes: 2 additions & 0 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ declare namespace BLECentralPlugin {
reportDelay?: number;

reportDuplicates?: boolean;
/** Scanning duration in seconds */
duration?: number;
}

interface L2CAPOptions {
Expand Down
12 changes: 2 additions & 10 deletions www/ble.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,11 @@ var autoconnected = {};

module.exports = {
scan: function (services, seconds, success, failure) {
var successWrapper = function (peripheral) {
convertToNativeJS(peripheral);
success(peripheral);
};
cordova.exec(successWrapper, failure, 'BLE', 'scan', [services, seconds]);
module.exports.startScanWithOptions(services, { duration: seconds }, success, failure);
},

startScan: function (services, success, failure) {
var successWrapper = function (peripheral) {
convertToNativeJS(peripheral);
success(peripheral);
};
cordova.exec(successWrapper, failure, 'BLE', 'startScan', [services]);
module.exports.startScanWithOptions(services, undefined, success, failure);
},

stopScan: function (success, failure) {
Expand Down