From 09f9bb05dc53874691e89e3da761df8c6a1899a3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Dec 2022 05:55:10 -1000 Subject: [PATCH] Add bluetooth diagnostics to shelly (#83438) * Add bluetooth diagnostics to shelly * Add tests Co-authored-by: Shay Levy --- .../components/bluetooth/base_scanner.py | 16 +++++ .../components/esphome/bluetooth/scanner.py | 18 ----- .../components/shelly/diagnostics.py | 10 +++ tests/components/shelly/test_diagnostics.py | 70 ++++++++++++++++++- 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/homeassistant/components/bluetooth/base_scanner.py b/homeassistant/components/bluetooth/base_scanner.py index 103479193b6..3dffff9e153 100644 --- a/homeassistant/components/bluetooth/base_scanner.py +++ b/homeassistant/components/bluetooth/base_scanner.py @@ -226,3 +226,19 @@ class BaseHaRemoteScanner(BaseHaScanner): time=now, ) ) + + 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], + "details": device_adv[0].details, + } + 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 ea44ff45d1c..19d59843746 100644 --- a/homeassistant/components/esphome/bluetooth/scanner.py +++ b/homeassistant/components/esphome/bluetooth/scanner.py @@ -1,8 +1,6 @@ """Bluetooth scanner for esphome.""" from __future__ import annotations -from typing import Any - from aioesphomeapi import BluetoothLEAdvertisement from homeassistant.components.bluetooth import BaseHaRemoteScanner @@ -27,19 +25,3 @@ class ESPHomeScanner(BaseHaRemoteScanner): None, {"address_type": adv.address_type}, ) - - 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], - "details": device_adv[0].details, - } - for device_adv in self.discovered_devices_and_advertisement_data.values() - ], - } diff --git a/homeassistant/components/shelly/diagnostics.py b/homeassistant/components/shelly/diagnostics.py index 6e5f8d139a2..f73de5fb32d 100644 --- a/homeassistant/components/shelly/diagnostics.py +++ b/homeassistant/components/shelly/diagnostics.py @@ -1,10 +1,12 @@ """Diagnostics support for Shelly.""" from __future__ import annotations +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, CONF_USERNAME from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import format_mac from .coordinator import get_entry_data @@ -19,6 +21,7 @@ async def async_get_config_entry_diagnostics( device_settings: str | dict = "not initialized" device_status: str | dict = "not initialized" + bluetooth: str | dict = "not initialized" if shelly_entry_data.block: block_coordinator = shelly_entry_data.block assert block_coordinator @@ -68,6 +71,12 @@ async def async_get_config_entry_diagnostics( if k in ["sys", "wifi"] } + source = format_mac(rpc_coordinator.mac).upper() + if scanner := async_scanner_by_source(hass, source): + bluetooth = { + "scanner": await scanner.async_diagnostics(), + } + if isinstance(device_status, dict): device_status = async_redact_data(device_status, ["ssid"]) @@ -76,4 +85,5 @@ async def async_get_config_entry_diagnostics( "device_info": device_info, "device_settings": device_settings, "device_status": device_status, + "bluetooth": bluetooth, } diff --git a/tests/components/shelly/test_diagnostics.py b/tests/components/shelly/test_diagnostics.py index ccac9bcc1b0..6e917771d30 100644 --- a/tests/components/shelly/test_diagnostics.py +++ b/tests/components/shelly/test_diagnostics.py @@ -1,12 +1,17 @@ """Tests for Shelly diagnostics platform.""" from aiohttp import ClientSession +from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT from homeassistant.components.diagnostics import REDACTED -from homeassistant.components.shelly.const import DOMAIN +from homeassistant.components.shelly.const import ( + CONF_BLE_SCANNER_MODE, + DOMAIN, + BLEScannerMode, +) from homeassistant.components.shelly.diagnostics import TO_REDACT from homeassistant.core import HomeAssistant -from . import init_integration +from . import init_integration, inject_rpc_device_event from .conftest import MOCK_STATUS_COAP from tests.components.diagnostics import get_diagnostics_for_config_entry @@ -30,6 +35,7 @@ async def test_block_config_entry_diagnostics( assert result == { "entry": entry_dict, + "bluetooth": "not initialized", "device_info": { "name": "Test name", "model": "SHSW-25", @@ -44,9 +50,35 @@ async def test_rpc_config_entry_diagnostics( hass: HomeAssistant, hass_client: ClientSession, mock_rpc_device, + monkeypatch, ): """Test config entry diagnostics for rpc device.""" - await init_integration(hass, 2) + await init_integration( + hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} + ) + + inject_rpc_device_event( + monkeypatch, + mock_rpc_device, + { + "events": [ + { + "component": "script:1", + "data": [ + 1, + "aa:bb:cc:dd:ee:ff", + -62, + "AgEGCf9ZANH7O3TIkA==", + "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", + ], + "event": BLE_SCAN_RESULT_EVENT, + "id": 1, + "ts": 1668522399.2, + } + ], + "ts": 1668522399.2, + }, + ) entry = hass.config_entries.async_entries(DOMAIN)[0] entry_dict = entry.as_dict() @@ -58,6 +90,38 @@ async def test_rpc_config_entry_diagnostics( assert result == { "entry": entry_dict, + "bluetooth": { + "scanner": { + "discovered_devices_and_advertisement_data": [ + { + "address": "AA:BB:CC:DD:EE:FF", + "advertisement_data": [ + None, + { + "89": { + "__type": "", + "repr": "b'\\xd1\\xfb;t\\xc8\\x90'", + } + }, + { + "00000d00-0000-1000-8000-00805f9b34fb": { + "__type": "", + "repr": "b'H\\x10a'", + } + }, + ["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + -127, + -62, + [], + ], + "details": {"source": "12:34:56:78:9A:BC"}, + "name": None, + "rssi": -62, + } + ], + "type": "ShellyBLEScanner", + } + }, "device_info": { "name": "Test name", "model": "SHSW-25",