diff --git a/homeassistant/components/esphome/diagnostics.py b/homeassistant/components/esphome/diagnostics.py index a984d057c0c..f270196db50 100644 --- a/homeassistant/components/esphome/diagnostics.py +++ b/homeassistant/components/esphome/diagnostics.py @@ -32,12 +32,13 @@ async def async_get_config_entry_diagnostics( if ( config_entry.unique_id - and (scanner := async_scanner_by_source(hass, config_entry.unique_id)) + and (scanner := async_scanner_by_source(hass, config_entry.unique_id.upper())) and (bluetooth_device := entry_data.bluetooth_device) ): diag["bluetooth"] = { "connections_free": bluetooth_device.ble_connections_free, "connections_limit": bluetooth_device.ble_connections_limit, + "available": bluetooth_device.available, "scanner": await scanner.async_diagnostics(), } diff --git a/tests/components/esphome/conftest.py b/tests/components/esphome/conftest.py index 3b37902fb3d..9182e021a65 100644 --- a/tests/components/esphome/conftest.py +++ b/tests/components/esphome/conftest.py @@ -72,6 +72,7 @@ def mock_config_entry(hass) -> MockConfigEntry: CONF_NOISE_PSK: "12345678123456781234567812345678", CONF_DEVICE_NAME: "test", }, + # ESPHome unique ids are lower case unique_id="11:22:33:44:55:aa", ) config_entry.add_to_hass(hass) @@ -96,7 +97,8 @@ def mock_device_info() -> DeviceInfo: uses_password=False, name="test", legacy_bluetooth_proxy_version=0, - mac_address="11:22:33:44:55:aa", + # ESPHome mac addresses are UPPER case + mac_address="11:22:33:44:55:AA", esphome_version="1.0.0", ) @@ -230,7 +232,7 @@ async def _mock_generic_device_entry( "name": "test", "friendly_name": "Test", "esphome_version": "1.0.0", - "mac_address": "11:22:33:44:55:aa", + "mac_address": "11:22:33:44:55:AA", } device_info = DeviceInfo(**(default_device_info | mock_device_info)) diff --git a/tests/components/esphome/test_bluetooth.py b/tests/components/esphome/test_bluetooth.py index a576c82c944..46858c5826b 100644 --- a/tests/components/esphome/test_bluetooth.py +++ b/tests/components/esphome/test_bluetooth.py @@ -10,7 +10,7 @@ async def test_bluetooth_connect_with_raw_adv( hass: HomeAssistant, mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice ) -> None: """Test bluetooth connect with raw advertisements.""" - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner is not None assert scanner.connectable is True assert scanner.scanning is True @@ -18,11 +18,11 @@ async def test_bluetooth_connect_with_raw_adv( await mock_bluetooth_entry_with_raw_adv.mock_disconnect(True) await hass.async_block_till_done() - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner is None await mock_bluetooth_entry_with_raw_adv.mock_connect() await hass.async_block_till_done() - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner.scanning is True @@ -30,7 +30,7 @@ async def test_bluetooth_connect_with_legacy_adv( hass: HomeAssistant, mock_bluetooth_entry_with_legacy_adv: MockESPHomeDevice ) -> None: """Test bluetooth connect with legacy advertisements.""" - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner is not None assert scanner.connectable is True assert scanner.scanning is True @@ -38,9 +38,9 @@ async def test_bluetooth_connect_with_legacy_adv( await mock_bluetooth_entry_with_legacy_adv.mock_disconnect(True) await hass.async_block_till_done() - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner is None await mock_bluetooth_entry_with_legacy_adv.mock_connect() await hass.async_block_till_done() - scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:aa") + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner.scanning is True diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index 01ba07852d6..4161e69efd0 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -432,7 +432,7 @@ async def test_user_discovers_name_and_gets_key_from_dashboard_fails( DeviceInfo( uses_password=False, name="test", - mac_address="11:22:33:44:55:aa", + mac_address="11:22:33:44:55:AA", ), ] diff --git a/tests/components/esphome/test_diagnostics.py b/tests/components/esphome/test_diagnostics.py index 6000b270d87..d528010af1b 100644 --- a/tests/components/esphome/test_diagnostics.py +++ b/tests/components/esphome/test_diagnostics.py @@ -1,8 +1,13 @@ """Tests for the diagnostics data provided by the ESPHome integration.""" +from unittest.mock import ANY + from syrupy import SnapshotAssertion +from homeassistant.components import bluetooth from homeassistant.core import HomeAssistant +from .conftest import MockESPHomeDevice + from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.typing import ClientSessionGenerator @@ -20,3 +25,77 @@ async def test_diagnostics( result = await get_diagnostics_for_config_entry(hass, hass_client, init_integration) assert result == snapshot + + +async def test_diagnostics_with_bluetooth( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice, +) -> None: + """Test diagnostics for config entry with Bluetooth.""" + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") + assert scanner is not None + assert scanner.connectable is True + entry = mock_bluetooth_entry_with_raw_adv.entry + result = await get_diagnostics_for_config_entry(hass, hass_client, entry) + assert result == { + "bluetooth": { + "available": True, + "connections_free": 0, + "connections_limit": 0, + "scanner": { + "connectable": True, + "discovered_device_timestamps": {}, + "discovered_devices_and_advertisement_data": [], + "last_detection": ANY, + "monotonic_time": ANY, + "name": "test (11:22:33:44:55:AA)", + "scanning": True, + "source": "11:22:33:44:55:AA", + "start_time": ANY, + "time_since_last_device_detection": {}, + "type": "ESPHomeScanner", + }, + }, + "config": { + "data": { + "device_name": "test", + "host": "test.local", + "password": "", + "port": 6053, + }, + "disabled_by": None, + "domain": "esphome", + "entry_id": ANY, + "minor_version": 1, + "options": {"allow_service_calls": False}, + "pref_disable_new_entities": False, + "pref_disable_polling": False, + "source": "user", + "title": "Mock Title", + "unique_id": "11:22:33:44:55:aa", + "version": 1, + }, + "storage_data": { + "api_version": {"major": 99, "minor": 99}, + "device_info": { + "bluetooth_proxy_feature_flags": 63, + "compilation_time": "", + "esphome_version": "1.0.0", + "friendly_name": "Test", + "has_deep_sleep": False, + "legacy_bluetooth_proxy_version": 0, + "mac_address": "**REDACTED**", + "manufacturer": "", + "model": "", + "name": "test", + "project_name": "", + "project_version": "", + "suggested_area": "", + "uses_password": False, + "voice_assistant_version": 0, + "webserver_port": 0, + }, + "services": [], + }, + } diff --git a/tests/components/esphome/test_entry_data.py b/tests/components/esphome/test_entry_data.py index 0ba43092d01..a8535c38224 100644 --- a/tests/components/esphome/test_entry_data.py +++ b/tests/components/esphome/test_entry_data.py @@ -51,7 +51,7 @@ async def test_migrate_entity_unique_id( assert entity_registry.async_get_entity_id("sensor", "esphome", "my_sensor") is None # Note that ESPHome includes the EntityInfo type in the unique id # as this is not a 1:1 mapping to the entity platform (ie. text_sensor) - assert entry.unique_id == "11:22:33:44:55:aa-sensor-mysensor" + assert entry.unique_id == "11:22:33:44:55:AA-sensor-mysensor" async def test_migrate_entity_unique_id_downgrade_upgrade( @@ -71,7 +71,7 @@ async def test_migrate_entity_unique_id_downgrade_upgrade( entity_registry.async_get_or_create( "sensor", "esphome", - "11:22:33:44:55:aa-sensor-mysensor", + "11:22:33:44:55:AA-sensor-mysensor", suggested_object_id="new_sensor", disabled_by=None, ) @@ -108,4 +108,4 @@ async def test_migrate_entity_unique_id_downgrade_upgrade( ) # Note that ESPHome includes the EntityInfo type in the unique id # as this is not a 1:1 mapping to the entity platform (ie. text_sensor) - assert entry.unique_id == "11:22:33:44:55:aa-sensor-mysensor" + assert entry.unique_id == "11:22:33:44:55:AA-sensor-mysensor" diff --git a/tests/components/esphome/test_manager.py b/tests/components/esphome/test_manager.py index 244e7487ed3..69ed653d75b 100644 --- a/tests/components/esphome/test_manager.py +++ b/tests/components/esphome/test_manager.py @@ -45,7 +45,7 @@ async def test_esphome_device_with_old_bluetooth( await hass.async_block_till_done() issue_registry = ir.async_get(hass) issue = issue_registry.async_get_issue( - "esphome", "ble_firmware_outdated-11:22:33:44:55:aa" + "esphome", "ble_firmware_outdated-11:22:33:44:55:AA" ) assert ( issue.learn_more_url @@ -87,7 +87,10 @@ async def test_esphome_device_with_password( issue_registry = ir.async_get(hass) assert ( issue_registry.async_get_issue( - "esphome", "api_password_deprecated-11:22:33:44:55:aa" + # This issue uses the ESPHome mac address which + # is always UPPER case + "esphome", + "api_password_deprecated-11:22:33:44:55:AA", ) is not None ) @@ -118,8 +121,10 @@ async def test_esphome_device_with_current_bluetooth( await hass.async_block_till_done() issue_registry = ir.async_get(hass) assert ( + # This issue uses the ESPHome device info mac address which + # is always UPPER case issue_registry.async_get_issue( - "esphome", "ble_firmware_outdated-11:22:33:44:55:aa" + "esphome", "ble_firmware_outdated-11:22:33:44:55:AA" ) is None ) diff --git a/tests/components/esphome/test_sensor.py b/tests/components/esphome/test_sensor.py index 820ec9ad9c0..080976425f9 100644 --- a/tests/components/esphome/test_sensor.py +++ b/tests/components/esphome/test_sensor.py @@ -118,7 +118,7 @@ async def test_generic_numeric_sensor_with_entity_category_and_icon( assert entry is not None # Note that ESPHome includes the EntityInfo type in the unique id # as this is not a 1:1 mapping to the entity platform (ie. text_sensor) - assert entry.unique_id == "11:22:33:44:55:aa-sensor-mysensor" + assert entry.unique_id == "11:22:33:44:55:AA-sensor-mysensor" assert entry.entity_category is EntityCategory.DIAGNOSTIC @@ -156,7 +156,7 @@ async def test_generic_numeric_sensor_state_class_measurement( assert entry is not None # Note that ESPHome includes the EntityInfo type in the unique id # as this is not a 1:1 mapping to the entity platform (ie. text_sensor) - assert entry.unique_id == "11:22:33:44:55:aa-sensor-mysensor" + assert entry.unique_id == "11:22:33:44:55:AA-sensor-mysensor" assert entry.entity_category is None