Skip to content

Commit

Permalink
feat(bluetooth-low-energy): always enforce whitelist
Browse files Browse the repository at this point in the history
To stop room-assistant from creating a whole bunch of unwanted sensors,
the whitelist is now always enforced. If there are no entries
configured, no sensors will be registered.
  • Loading branch information
mKeRix committed Jan 27, 2020
1 parent b18b812 commit 8807e89
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
@@ -1,5 +1,3 @@
import KalmanFilter from 'kalmanjs';

const mockNoble = {
on: jest.fn()
};
Expand Down Expand Up @@ -36,6 +34,7 @@ import { Sensor } from '../../entities/sensor.entity';
import c from 'config';
import { NewDistanceEvent } from './new-distance.event';
import { RoomPresenceDistanceSensor } from '../room-presence/room-presence-distance.sensor';
import KalmanFilter from 'kalmanjs';

describe('BluetoothLowEnergyService', () => {
let service: BluetoothLowEnergyService;
Expand All @@ -57,6 +56,11 @@ describe('BluetoothLowEnergyService', () => {
return key === 'bluetoothLowEnergy' ? mockConfig : c.get(key);
})
};
const loggerService = {
log: jest.fn(),
error: jest.fn(),
warn: jest.fn()
};

const iBeaconData = Buffer.from([
76,
Expand Down Expand Up @@ -106,6 +110,7 @@ describe('BluetoothLowEnergyService', () => {
.overrideProvider(ConfigService)
.useValue(configService)
.compile();
module.useLogger(loggerService);

service = module.get<BluetoothLowEnergyService>(BluetoothLowEnergyService);
});
Expand All @@ -119,6 +124,16 @@ describe('BluetoothLowEnergyService', () => {
expect(mockNoble.on).toHaveBeenCalledWith('discover', expect.any(Function));
});

it('should warn if no whitelist has been configured', () => {
mockConfig.whitelist = ['abcd'];
service.onModuleInit();
expect(loggerService.warn).not.toHaveBeenCalled();

mockConfig.whitelist = [];
service.onModuleInit();
expect(loggerService.warn).toHaveBeenCalled();
});

it('should setup cluster listeners on bootstrap', () => {
service.onApplicationBootstrap();
expect(clusterService.on).toHaveBeenCalledWith(
Expand Down Expand Up @@ -149,6 +164,7 @@ describe('BluetoothLowEnergyService', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);

mockConfig.onlyIBeacon = true;
mockConfig.processIBeacon = true;
Expand Down Expand Up @@ -180,6 +196,7 @@ describe('BluetoothLowEnergyService', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);

mockConfig.processIBeacon = false;
service.handleDiscovery({
Expand Down Expand Up @@ -209,6 +226,7 @@ describe('BluetoothLowEnergyService', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);

service.handleDiscovery({
id: '123-123',
Expand All @@ -231,10 +249,41 @@ describe('BluetoothLowEnergyService', () => {
);
});

it('should ignore devices that are not on the whitelist', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
mockConfig.whitelist = ['123-1-1'];

service.handleDiscovery({
id: '123-1-2',
rssi: -82,
advertisement: {}
} as Peripheral);
expect(handleDistanceSpy).not.toHaveBeenCalled();
expect(clusterService.publish).not.toHaveBeenCalled();
});

it('should not publish anything if the whitelist is empty', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
mockConfig.whitelist = [];

service.handleDiscovery({
id: '89:47:65',
rssi: -82,
advertisement: {}
} as Peripheral);
expect(handleDistanceSpy).not.toHaveBeenCalled();
expect(clusterService.publish).not.toHaveBeenCalled();
});

it('should apply tag distance override if it exists', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);
mockConfig.tagOverrides = {
abcd: {
measuredPower: -80
Expand Down Expand Up @@ -282,6 +331,7 @@ describe('BluetoothLowEnergyService', () => {
const handleDistanceSpy = jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);
mockConfig.tagOverrides = {
abcd: {
name: 'better name'
Expand Down Expand Up @@ -339,6 +389,7 @@ describe('BluetoothLowEnergyService', () => {
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
const filterSpy = jest.spyOn(service, 'filterRssi').mockReturnValue(-50);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);

service.handleDiscovery({
id: '12:ab:cd:12:cd',
Expand All @@ -363,6 +414,7 @@ describe('BluetoothLowEnergyService', () => {
jest
.spyOn(service, 'handleNewDistance')
.mockImplementation(() => undefined);
jest.spyOn(service, 'isOnWhitelist').mockReturnValue(true);

service.handleDiscovery({
id: 'id1',
Expand Down
@@ -1,5 +1,6 @@
import {
Injectable,
Logger,
OnApplicationBootstrap,
OnModuleInit
} from '@nestjs/common';
Expand All @@ -24,6 +25,7 @@ export const NEW_DISTANCE_CHANNEL = 'bluetooth-low-energy.new-distance';
export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
implements OnModuleInit, OnApplicationBootstrap {
private readonly config: BluetoothLowEnergyConfig;
private readonly logger: Logger;

constructor(
private readonly entitiesService: EntitiesService,
Expand All @@ -33,6 +35,7 @@ export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
) {
super();
this.config = this.configService.get('bluetoothLowEnergy');
this.logger = new Logger(BluetoothLowEnergyService.name);
}

/**
Expand All @@ -41,6 +44,10 @@ export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
onModuleInit(): void {
noble.on('stateChange', BluetoothLowEnergyService.handleStateChange);
noble.on('discover', this.handleDiscovery.bind(this));

if (!this.isWhitelistEnabled()) {
this.logger.warn('The whitelist is empty, no sensors will be created!');
}
}

/**
Expand Down Expand Up @@ -78,6 +85,10 @@ export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
);
this.handleNewDistance(event);
this.clusterService.publish(NEW_DISTANCE_CHANNEL, event);
} else {
this.logger.debug(
`Ignoring tag with id ${tag.id} and signal strength ${tag.rssi}`
);
}
}

Expand Down Expand Up @@ -114,6 +125,15 @@ export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
); // expected ibeacon data length
}

/**
* Determines whether a whitelist has been configured or not.
*
* @returns Whitelist status
*/
isWhitelistEnabled(): boolean {
return this.config.whitelist?.length > 0;
}

/**
* Checks if an id is on the whitelist of this component.
* Always returns true if the whitelist is empty.
Expand All @@ -124,7 +144,7 @@ export class BluetoothLowEnergyService extends KalmanFilterable(Object, 0.8, 15)
isOnWhitelist(id: string): boolean {
const whitelist = this.config.whitelist;
if (whitelist === undefined || whitelist.length === 0) {
return true;
return false;
}

return this.config.whitelistRegex
Expand Down

0 comments on commit 8807e89

Please sign in to comment.