Show scanner name in the Bluetooth Advertisement Monitor (#23926)

This commit is contained in:
J. Nick Koston 2025-01-28 13:07:16 -10:00 committed by GitHub
parent cbdb7406ad
commit cc8869b9f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 81 additions and 10 deletions

View File

@ -19,6 +19,15 @@ export interface BluetoothDeviceData extends DataTableRowData {
tx_power: number; tx_power: number;
} }
export interface BluetoothScannerDetails {
source: string;
connectable: boolean;
name: string;
adapter: string;
}
export type BluetoothScannersDetails = Record<string, BluetoothScannerDetails>;
interface BluetoothRemoveDeviceData { interface BluetoothRemoveDeviceData {
address: string; address: string;
} }
@ -29,6 +38,11 @@ interface BluetoothAdvertisementSubscriptionMessage {
remove?: BluetoothRemoveDeviceData[]; remove?: BluetoothRemoveDeviceData[];
} }
interface BluetoothScannersDetailsSubscriptionMessage {
add?: BluetoothScannerDetails[];
remove?: BluetoothScannerDetails[];
}
export interface BluetoothAllocationsData { export interface BluetoothAllocationsData {
source: string; source: string;
slots: number; slots: number;
@ -36,6 +50,43 @@ export interface BluetoothAllocationsData {
allocated: string[]; allocated: string[];
} }
export const subscribeBluetoothScannersDetailsUpdates = (
conn: Connection,
store: Store<BluetoothScannersDetails>
): Promise<UnsubscribeFunc> =>
conn.subscribeMessage<BluetoothScannersDetailsSubscriptionMessage>(
(event) => {
const data = { ...(store.state || {}) };
if (event.add) {
for (const device_data of event.add) {
data[device_data.source] = device_data;
}
}
if (event.remove) {
for (const device_data of event.remove) {
delete data[device_data.source];
}
}
store.setState(data, true);
},
{
type: `bluetooth/subscribe_scanner_details`,
}
);
export const subscribeBluetoothScannersDetails = (
conn: Connection,
callbackFunction: (bluetoothScannersDetails: BluetoothScannersDetails) => void
) =>
createCollection<BluetoothScannersDetails>(
"_bluetoothScannerDetails",
() => Promise.resolve<BluetoothScannersDetails>({}), // empty hash as initial state
subscribeBluetoothScannersDetailsUpdates,
conn,
callbackFunction
);
const subscribeBluetoothAdvertisementsUpdates = ( const subscribeBluetoothAdvertisementsUpdates = (
conn: Connection, conn: Connection,
store: Store<BluetoothDeviceData[]> store: Store<BluetoothDeviceData[]>

View File

@ -14,9 +14,14 @@ import "../../../../../components/ha-icon-button";
import "../../../../../layouts/hass-tabs-subpage-data-table"; import "../../../../../layouts/hass-tabs-subpage-data-table";
import { haStyle } from "../../../../../resources/styles"; import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types"; import type { HomeAssistant, Route } from "../../../../../types";
import type { BluetoothDeviceData } from "../../../../../data/bluetooth"; import type {
BluetoothDeviceData,
import { subscribeBluetoothAdvertisements } from "../../../../../data/bluetooth"; BluetoothScannersDetails,
} from "../../../../../data/bluetooth";
import {
subscribeBluetoothAdvertisements,
subscribeBluetoothScannersDetails,
} from "../../../../../data/bluetooth";
import { showBluetoothDeviceInfoDialog } from "./show-dialog-bluetooth-device-info"; import { showBluetoothDeviceInfoDialog } from "./show-dialog-bluetooth-device-info";
@customElement("bluetooth-advertisement-monitor") @customElement("bluetooth-advertisement-monitor")
@ -31,25 +36,39 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement {
@state() private _data: BluetoothDeviceData[] = []; @state() private _data: BluetoothDeviceData[] = [];
private _unsub?: UnsubscribeFunc; @state() private _scanners: BluetoothScannersDetails = {};
private _unsub_advertisements?: UnsubscribeFunc;
private _unsub_scanners?: UnsubscribeFunc;
public connectedCallback(): void { public connectedCallback(): void {
super.connectedCallback(); super.connectedCallback();
if (this.hass) { if (this.hass) {
this._unsub = subscribeBluetoothAdvertisements( this._unsub_advertisements = subscribeBluetoothAdvertisements(
this.hass.connection, this.hass.connection,
(data) => { (data) => {
this._data = data; this._data = data;
} }
); );
this._unsub_scanners = subscribeBluetoothScannersDetails(
this.hass.connection,
(scanners) => {
this._scanners = scanners;
}
);
} }
} }
public disconnectedCallback() { public disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
if (this._unsub) { if (this._unsub_advertisements) {
this._unsub(); this._unsub_advertisements();
this._unsub = undefined; this._unsub_advertisements = undefined;
}
if (this._unsub_scanners) {
this._unsub_scanners();
this._unsub_scanners = undefined;
} }
} }
@ -88,10 +107,11 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement {
} }
); );
private _dataWithIds = memoizeOne((data) => private _dataWithNamedSourceAndIds = memoizeOne((data) =>
data.map((row) => ({ data.map((row) => ({
...row, ...row,
id: row.address, id: row.address,
source: this._scanners[row.source]?.name || row.source,
})) }))
); );
@ -102,7 +122,7 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement {
.narrow=${this.narrow} .narrow=${this.narrow}
.route=${this.route} .route=${this.route}
.columns=${this._columns(this.hass.localize)} .columns=${this._columns(this.hass.localize)}
.data=${this._dataWithIds(this._data)} .data=${this._dataWithNamedSourceAndIds(this._data)}
@row-click=${this._handleRowClicked} @row-click=${this._handleRowClicked}
clickable clickable
></hass-tabs-subpage-data-table> ></hass-tabs-subpage-data-table>