Skip to content

Commit

Permalink
feat(bluetooth-low-energy): auto-attempt to recover scanning states
Browse files Browse the repository at this point in the history
  • Loading branch information
mKeRix committed Dec 16, 2020
1 parent cbf8bac commit df72629
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 1 deletion.
25 changes: 24 additions & 1 deletion src/integrations/bluetooth/bluetooth.service.spec.ts
Expand Up @@ -255,7 +255,7 @@ Requesting information ...
it('should only setup noble listeners once', () => {
service.onLowEnergyDiscovery(() => undefined);
service.onLowEnergyDiscovery(() => undefined);
expect(mockNoble.on).toHaveBeenCalledTimes(4);
expect(mockNoble.on).toHaveBeenCalledTimes(5);
});

it('should enable scanning when the adapter is inactive', () => {
Expand Down Expand Up @@ -420,6 +420,29 @@ Requesting information ...

expect(peripheral.disconnectAsync).toHaveBeenCalled();
});

it('should restart scanning if nothing has been detected for a while', async () => {
jest.useFakeTimers('modern');

// eslint-disable-next-line @typescript-eslint/no-empty-function
service.onLowEnergyDiscovery(() => {});
const stateCallback = mockNoble.on.mock.calls.find(
(call) => call[0] === 'stateChange'
)[1];
const discoveryCallback = mockNoble.on.mock.calls.find(
(call) => call[0] === 'discover'
)[1];
await stateCallback('poweredOn');
jest.resetAllMocks();

discoveryCallback();
jest.setSystemTime(Date.now() + 31 * 1000);

await service.verifyLowEnergyScanner();

expect(mockNoble.stopScanning).toHaveBeenCalledTimes(1);
expect(mockNoble.startScanningAsync).toHaveBeenCalledTimes(1);
});
});

it('should reset adapters that have been locked for too long', () => {
Expand Down
23 changes: 23 additions & 0 deletions src/integrations/bluetooth/bluetooth.service.ts
Expand Up @@ -11,6 +11,7 @@ import { Interval } from '@nestjs/schedule';

const RSSI_REGEX = new RegExp(/-?[0-9]+/);
const INQUIRY_LOCK_TIMEOUT = 30 * 1000;
const SCAN_NO_PERIPHERAL_TIMEOUT = 30 * 1000;

const execPromise = util.promisify(exec);

Expand Down Expand Up @@ -38,6 +39,7 @@ export class BluetoothService {
private readonly classicConfig: BluetoothClassicConfig;
private readonly adapters = new BluetoothAdapterMap();
private lowEnergyAdapterId: number;
private lastLowEnergyDiscovery: Date;

constructor(
private readonly configService: ConfigService,
Expand Down Expand Up @@ -294,13 +296,34 @@ export class BluetoothService {
});
}

/**
* Restarts the scanning process if nothing has been detected for a while.
*/
@Interval(5 * 1000)
async verifyLowEnergyScanner(): Promise<void> {
if (
this.lowEnergyAdapterId != undefined &&
this.adapters.getState(this.lowEnergyAdapterId) == 'scan' &&
this.lastLowEnergyDiscovery != undefined &&
this.lastLowEnergyDiscovery.getTime() <
Date.now() - SCAN_NO_PERIPHERAL_TIMEOUT
) {
this.logger.warn(
'Did not detect any low energy advertisements in a while, restarting scanner'
);
await this.handleAdapterStateChange('poweredOff');
await this.handleAdapterStateChange('poweredOn');
}
}

/**
* Sets up Noble hooks.
*/
private setupNoble(): void {
this.lowEnergyAdapterId = parseInt(process.env.NOBLE_HCI_DEVICE_ID) || 0;

noble.on('stateChange', this.handleAdapterStateChange.bind(this));
noble.on('discover', () => (this.lastLowEnergyDiscovery = new Date()));
noble.on('warning', (message) => {
if (message == 'unknown peripheral undefined RSSI update!') {
return;
Expand Down

0 comments on commit df72629

Please sign in to comment.