Skip to content

Commit

Permalink
feat(bluetooth-classic): allow id overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
mKeRix committed Feb 14, 2021
1 parent e41034e commit f5fc88d
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 1 deletion.
12 changes: 12 additions & 0 deletions docs/integrations/bluetooth-classic.md
Expand Up @@ -87,11 +87,20 @@ Try to pair your Apple Watch to a Bluetooth device such as headphones/speakers f
| `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`. |
| `preserveState` | Boolean | `false` | Whether the last recorded distance should be preserved when the inquiries switch is turned off or not. |
| `inquireFromStart` | Boolean | `true` | Whether the [Inquiries Switch](#inquiries-switch) is turned on when room-assistant is started or not. |
| `entityOverrides` | [Entity Overrides](#entity-overrides) | | Allows you to override some properties of the created entities. |

### Minimum RSSI

`minRssi` can either be specified as a number that is applied to all devices like shown in the simple example config, or as a map of addresses and `minRssi` values as shown in the advanced example config. The latter allows you to configure different cutoffs for the devices you are using, as they may end up showing different RSSI levels even when placed at the same distance. The `minRssi.default` setting will be applied to all addresses that have not been configured specifically. If it is not set, all devices that don't have a specific value will always be considered to be in range.

### Entity Overrides

The entity overrides object can be considered as a map with the device MAC as key and an object with some of the following settings as value.

| Name | Type | Default | Description |
| ---- | ------ | ------- | ------------------------------------------------------------ |
| `id` | String | | Changes the ID of the device entities. Useful to hide device addresses from publicly shared home automation configurations. |

::: details Simple Example Config

```yaml
Expand Down Expand Up @@ -122,6 +131,9 @@ bluetoothClassic:
addresses:
- '08:05:90:ed:3b:60'
- '77:50:fb:4d:ab:70'
entityOverrides:
'08:05:90:ed:3b:60':
id: my-phone
```

:::
Expand Up @@ -7,4 +7,9 @@ export class BluetoothClassicConfig {
timeoutCycles = 2;
preserveState = false;
inquireFromStart = true;
entityOverrides: { [key: string]: BluetoothClassicEntityOverride } = {};
}

class BluetoothClassicEntityOverride {
id?: string;
}
Expand Up @@ -80,6 +80,7 @@ describe('BluetoothClassicService', () => {
timeoutCycles: 2,
preserveState: false,
inquireFromStart: true,
entityOverrides: {}
};
const configService = {
get: jest.fn().mockImplementation((key: string) => {
Expand Down Expand Up @@ -468,6 +469,49 @@ describe('BluetoothClassicService', () => {
expect(sensorInstance.timeout).toBe(24);
});

it('should register entities with overridden ID if available', async () => {
const deviceId = '10:36:cf:ca:9a:27';
const overriddenId = 'new-id';
config.entityOverrides[deviceId] = { id: overriddenId }

entitiesService.has.mockReturnValue(false);
entitiesService.add.mockImplementation((entity) => entity);
clusterService.nodes.mockReturnValue({
abcd: { channels: [NEW_RSSI_CHANNEL] },
});
bluetoothService.inquireClassicDeviceInfo.mockResolvedValue({
address: deviceId,
name: 'Test iPhone',
});
jest.useFakeTimers();

const event = new NewRssiEvent(
'test-instance',
new Device(deviceId, 'Test Device'),
-10
);
await service.handleNewRssi(event);

expect(entitiesService.add).toHaveBeenCalledWith(
expect.objectContaining({ id: 'bluetooth-classic-new-id-tracker' }),
[
{
for: DeviceTrackerConfig,
overrides: {
sourceType: 'bluetooth',
device: {
identifiers: deviceId,
name: 'Test Device',
connections: [['mac', deviceId]],
manufacturer: undefined,
viaDevice: 'room-assistant-distributed',
},
},
},
]
);
});

it('should trigger regular sensor state updates if the inquiries switch is on', () => {
jest.spyOn(service, 'shouldInquire').mockReturnValue(true);
config.preserveState = false;
Expand Down
Expand Up @@ -163,7 +163,8 @@ export class BluetoothClassicService
`Received RSSI of ${event.rssi} for ${event.device.address} from ${event.instanceName}`
);

const sensorId = makeId(`bluetooth-classic ${event.device.address}`);
const baseId = this.config.entityOverrides[event.device.address]?.id || event.device.address;
const sensorId = makeId(`bluetooth-classic ${baseId}`);
let sensor: RoomPresenceDistanceSensor;
if (this.entitiesService.has(sensorId)) {
sensor = this.entitiesService.get(sensorId) as RoomPresenceDistanceSensor;
Expand Down

0 comments on commit f5fc88d

Please sign in to comment.