From f0ae1cc6ce7bbd93b4d29b3a6d38135f66825c12 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 27 Nov 2022 09:59:37 -1000 Subject: [PATCH] Add bluetooth diagnostics to esphome (#82761) --- .../components/bluetooth/base_scanner.py | 10 +- .../components/esphome/bluetooth/scanner.py | 16 ++++ .../components/esphome/diagnostics.py | 10 ++ .../components/bluetooth/test_diagnostics.py | 91 ++++++++++++++++--- tests/components/esphome/test_diagnostics.py | 6 +- 5 files changed, 116 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index 88332c79667..b8711b0017f 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -67,12 +67,14 @@ class BaseHaScanner: """Return diagnostic information about the scanner.""" return { "type": self.__class__.__name__, - "discovered_devices": [ + "discovered_devices_and_advertisement_data": [ { - "name": device.name, - "address": device.address, + "name": device_adv[0].name, + "address": device_adv[0].address, + "rssi": device_adv[0].rssi, + "advertisement_data": device_adv[1], } - for device in self.discovered_devices + for device_adv in self.discovered_devices_and_advertisement_data.values() ], } diff --git a/homeassistant/components/esphome/bluetooth/scanner.py b/homeassistant/components/esphome/bluetooth/scanner.py index ab7572fd4ac..3df9a4c6aaa 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -2,6 +2,7 @@ from __future__ import annotations import re +from typing import Any from aioesphomeapi import BluetoothLEAdvertisement @@ -27,3 +28,18 @@ class ESPHomeScanner(BaseHaRemoteScanner): adv.manufacturer_data, None, ) + + async def async_diagnostics(self) -> dict[str, Any]: + """Return diagnostic information about the scanner.""" + return await super().async_diagnostics() | { + "type": self.__class__.__name__, + "discovered_devices_and_advertisement_data": [ + { + "name": device_adv[0].name, + "address": device_adv[0].address, + "rssi": device_adv[0].rssi, + "advertisement_data": device_adv[1], + } + for device_adv in self.discovered_devices_and_advertisement_data.values() + ], + } diff --git a/homeassistant/components/esphome/diagnostics.py b/homeassistant/components/esphome/diagnostics.py index cc82fd536e7..68f195a23fb 100644 --- a/homeassistant/components/esphome/diagnostics.py +++ b/homeassistant/components/esphome/diagnostics.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Any, cast +from homeassistant.components.bluetooth import async_scanner_by_source from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD @@ -29,4 +30,13 @@ async def async_get_config_entry_diagnostics( storage_data = cast("dict[str, Any]", storage_data) diag["storage_data"] = storage_data + if config_entry.unique_id and ( + scanner := async_scanner_by_source(hass, config_entry.unique_id) + ): + diag["bluetooth"] = { + "connections_free": entry_data.ble_connections_free, + "connections_limit": entry_data.ble_connections_limit, + "scanner": await scanner.async_diagnostics(), + } + return async_redact_data(diag, REDACT_KEYS) diff --git a/tests/components/bluetooth/test_diagnostics.py b/tests/components/bluetooth/test_diagnostics.py index bfbefe74151..20ffddf3297 100644 --- a/tests/components/bluetooth/test_diagnostics.py +++ b/tests/components/bluetooth/test_diagnostics.py @@ -24,8 +24,13 @@ async def test_diagnostics( # error if the test is not running on linux since we won't have the correct # deps installed when testing on MacOS. with patch( - "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices", - [BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45")], + "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices_and_advertisement_data", + { + "44:44:33:11:23:45": ( + BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45"), + generate_advertisement_data(local_name="x"), + ) + }, ), patch( "homeassistant.components.bluetooth.diagnostics.platform.system", return_value="Linux", @@ -120,8 +125,21 @@ async def test_diagnostics( "scanners": [ { "adapter": "hci0", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci0 (00:00:00:00:00:01)", @@ -131,8 +149,21 @@ async def test_diagnostics( }, { "adapter": "hci0", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci0 (00:00:00:00:00:01)", @@ -142,8 +173,21 @@ async def test_diagnostics( }, { "adapter": "hci1", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "x", + {}, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "hci1 (00:00:00:00:00:02)", @@ -171,8 +215,13 @@ async def test_diagnostics_macos( ) with patch( - "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices", - [BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45")], + "homeassistant.components.bluetooth.scanner.HaScanner.discovered_devices_and_advertisement_data", + { + "44:44:33:11:23:45": ( + BLEDevice(name="x", rssi=-60, address="44:44:33:11:23:45"), + switchbot_adv, + ) + }, ), patch( "homeassistant.components.bluetooth.diagnostics.platform.system", return_value="Darwin", @@ -274,8 +323,26 @@ async def test_diagnostics_macos( "scanners": [ { "adapter": "Core Bluetooth", - "discovered_devices": [ - {"address": "44:44:33:11:23:45", "name": "x"} + "discovered_devices_and_advertisement_data": [ + { + "address": "44:44:33:11:23:45", + "advertisement_data": [ + "wohand", + { + "1": { + "__type": "", + "repr": "b'\\x01'", + } + }, + {}, + [], + -127, + -127, + [[]], + ], + "name": "x", + "rssi": -60, + } ], "last_detection": ANY, "name": "Core Bluetooth", diff --git a/tests/components/esphome/test_diagnostics.py b/tests/components/esphome/test_diagnostics.py index 319bc2602e1..522ae0c8345 100644 --- a/tests/components/esphome/test_diagnostics.py +++ b/tests/components/esphome/test_diagnostics.py @@ -1,6 +1,7 @@ """Tests for the diagnostics data provided by the ESPHome integration.""" from aiohttp import ClientSession +import pytest from homeassistant.components.esphome import CONF_NOISE_PSK from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT @@ -11,7 +12,10 @@ from tests.components.diagnostics import get_diagnostics_for_config_entry async def test_diagnostics( - hass: HomeAssistant, hass_client: ClientSession, init_integration: MockConfigEntry + hass: HomeAssistant, + hass_client: ClientSession, + init_integration: MockConfigEntry, + enable_bluetooth: pytest.fixture, ): """Test diagnostics for config entry.""" result = await get_diagnostics_for_config_entry(hass, hass_client, init_integration)