Skip to content

Commit

Permalink
feat(bluetooth-classic): add options for interval and timeout
Browse files Browse the repository at this point in the history
The behavior of Bluetooth Classic scanning can now be customized a bit
more. This potentially addresses #147 and #142.
  • Loading branch information
mKeRix committed Mar 22, 2020
1 parent 69316da commit c5b181f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 13 deletions.
24 changes: 18 additions & 6 deletions docs/integrations/bluetooth-classic.md
Expand Up @@ -53,15 +53,27 @@ Each configured device will create a sensor that has the name of the closest roo

Each instance running this integration will also create a switch for enabling or disabling Bluetooth inquiries. A disabled switch blocks all Bluetooth requests of the instance, which essentially means that your Bluetooth device won't be discovered by this instance anymore. It does not take the instance out of the rotation however, so the time to detection stays the same.

You could use this to reduce the resources used by room-assistant when you are certain nobody is home. Another example would be disabling the inquiries when you are asleep to save the batteries of your Bluetooth devices at night.
You could use this to reduce the resources used by room-assistant when you are certain nobody is home. Another example would be disabling the inquiries when you are asleep to save the batteries of your Bluetooth devices at night.

## Troubleshooting

### Random incorrect not_home states

This happens when no measurements were collected for the device recently. To make the timeout a bit more lax and get rid of these incorrect states you can raise the `timeoutCycles` value. Note that this will also delay your device being reported as `not_home` when it really has gone away.

### WiFi/Bluetooth interference

Bluetooth uses the 2.4 GHz band, which may also be used by some of your other WiFi devices. This can cause interferences. Some Bluetooth devices may also handle a too aggressive refresh interval badly. If you are noticing any issues (e.g. WiFi devices dropping off the network, Bluetooth headphones disconnecting randomly) try raising the `interval` a bit. Note that this will also make the state updates a bit slower.

## Settings

| Name | Type | Default | Description |
| ------------- | ------ | ------- | ------------------------------------------------------------ |
| `addresses` | Array | | List of Bluetooth MAC addresses that should be tracked. You can usually find them in the device settings. |
| `minRssi` | Number | | Limits the RSSI at which a device is still reported if configured. Remember, the RSSI is the inverse of the sensor attribute distance, so for a cutoff at 10 you would configure -10. |
| `hciDeviceId` | Number | 0 | ID of the Bluetooth device to use for the inquiries, e.g. `0` to use `hci0`. |
| Name | Type | Default | Description |
| --------------- | ------ | ------- | ------------------------------------------------------------ |
| `addresses` | Array | | List of Bluetooth MAC addresses that should be tracked. You can usually find them in the device settings. |
| `minRssi` | Number | | Limits the RSSI at which a device is still reported if configured. Remember, the RSSI is the inverse of the sensor attribute distance, so for a cutoff at 10 you would configure -10. |
| `hciDeviceId` | Number | 0 | ID of the Bluetooth device to use for the inquiries, e.g. `0` to use `hci0`. |
| `interval` | Number | 6 | The interval at which the Bluetooth devices are queried in seconds. |
| `timeoutCycles` | Number | 2 | The number of completed query cycles after which collected measurements are considered obsolete. The timeout in seconds is calculated as `max(addresses, clusterDevices) * interval * timeoutCycles`. |

::: details Example Config
```yaml
Expand Down
Expand Up @@ -2,4 +2,6 @@ export class BluetoothClassicConfig {
addresses: string[] = [];
minRssi?: number;
hciDeviceId = 0;
interval = 6;
timeoutCycles = 2;
}
Expand Up @@ -57,7 +57,9 @@ describe('BluetoothClassicService', () => {
};
const config: Partial<BluetoothClassicConfig> = {
addresses: ['8d:ad:e3:e2:7a:01', 'f7:6c:e3:10:55:b5'],
hciDeviceId: 0
hciDeviceId: 0,
interval: 6,
timeoutCycles: 2
};
const configService = {
get: jest.fn().mockImplementation((key: string) => {
Expand Down
26 changes: 20 additions & 6 deletions src/integrations/bluetooth-classic/bluetooth-classic.service.ts
Expand Up @@ -13,7 +13,7 @@ import { exec } from 'child_process';
import { Node } from 'democracy';
import { NewRssiEvent } from './new-rssi.event';
import _ from 'lodash';
import { Interval, SchedulerRegistry } from '@nestjs/schedule';
import { SchedulerRegistry } from '@nestjs/schedule';
import { EntityCustomization } from '../../entities/entity-customization.interface';
import { SensorConfig } from '../home-assistant/sensor-config';
import {
Expand All @@ -28,7 +28,6 @@ import { DISTRIBUTED_DEVICE_ID } from '../home-assistant/home-assistant.const';
import { Switch } from '../../entities/switch';
import { SwitchConfig } from '../home-assistant/switch-config';

const INTERVAL = 6 * 1000;
const execPromise = util.promisify(exec);

@Injectable()
Expand Down Expand Up @@ -74,6 +73,15 @@ export class BluetoothClassicService extends KalmanFilterable(Object, 1.4, 1)
);
this.clusterService.on(NEW_RSSI_CHANNEL, this.handleNewRssi.bind(this));
this.clusterService.subscribe(NEW_RSSI_CHANNEL);

const interval = setInterval(
this.distributeInquiries.bind(this),
this.config.interval * 1000
);
this.schedulerRegistry.addInterval(
`bluetooth_classic_distribute_inquiries`,
interval
);
}

/**
Expand Down Expand Up @@ -140,7 +148,6 @@ export class BluetoothClassicService extends KalmanFilterable(Object, 1.4, 1)
* Sends out RSSI requests to the connected nodes in the cluster by matching each one with a MAC address.
* Rotates the mapping on each call.
*/
@Interval(INTERVAL)
distributeInquiries(): void {
if (this.clusterService.isMajorityLeader()) {
const nodes = this.getParticipatingNodes();
Expand Down Expand Up @@ -185,7 +192,7 @@ export class BluetoothClassicService extends KalmanFilterable(Object, 1.4, 1)
const output = await execPromise(
`hcitool -i hci${this.config.hciDeviceId} cc "${address}" && hcitool -i hci${this.config.hciDeviceId} rssi "${address}"`,
{
timeout: 5.5 * 1000,
timeout: (this.config.interval - 0.5) * 1000,
killSignal: 'SIGKILL'
}
);
Expand Down Expand Up @@ -343,7 +350,10 @@ export class BluetoothClassicService extends KalmanFilterable(Object, 1.4, 1)
customizations
) as RoomPresenceDistanceSensor;

const interval = setInterval(sensor.updateState.bind(sensor), INTERVAL);
const interval = setInterval(
sensor.updateState.bind(sensor),
this.config.interval * 1000
);
this.schedulerRegistry.addInterval(`${sensorId}_timeout_check`, interval);

return sensor;
Expand All @@ -357,7 +367,11 @@ export class BluetoothClassicService extends KalmanFilterable(Object, 1.4, 1)
protected calculateCurrentTimeout(): number {
const nodes = this.getParticipatingNodes();
const addresses = this.config.addresses;
return (Math.max(nodes.length, addresses.length) * 2 * INTERVAL) / 1000;
return (
Math.max(nodes.length, addresses.length) *
this.config.timeoutCycles *
this.config.interval
);
}

/**
Expand Down

0 comments on commit c5b181f

Please sign in to comment.