diff --git a/homeassistant/components/esphome/__init__.py b/homeassistant/components/esphome/__init__.py index 13e9496a9fd..5934c9a6f68 100644 --- a/homeassistant/components/esphome/__init__.py +++ b/homeassistant/components/esphome/__init__.py @@ -5,6 +5,7 @@ from __future__ import annotations from aioesphomeapi import APIClient from homeassistant.components import ffmpeg, zeroconf +from homeassistant.components.bluetooth import async_remove_scanner from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, @@ -86,4 +87,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) -> async def async_remove_entry(hass: HomeAssistant, entry: ESPHomeConfigEntry) -> None: """Remove an esphome config entry.""" + if mac_address := entry.unique_id: + async_remove_scanner(hass, mac_address.upper()) await DomainData.get(hass).get_or_create_store(hass, entry).async_remove() diff --git a/homeassistant/components/esphome/manager.py b/homeassistant/components/esphome/manager.py index dfd318c0c74..7fcd859142a 100644 --- a/homeassistant/components/esphome/manager.py +++ b/homeassistant/components/esphome/manager.py @@ -24,7 +24,7 @@ from aioesphomeapi import ( from awesomeversion import AwesomeVersion import voluptuous as vol -from homeassistant.components import tag, zeroconf +from homeassistant.components import bluetooth, tag, zeroconf from homeassistant.const import ( ATTR_DEVICE_ID, CONF_MODE, @@ -425,6 +425,8 @@ class ESPHomeManager: entry_data.disconnect_callbacks.add( async_connect_scanner(hass, entry_data, cli, device_info) ) + else: + bluetooth.async_remove_scanner(hass, device_info.mac_address) if device_info.voice_assistant_feature_flags_compat(api_version) and ( Platform.ASSIST_SATELLITE not in entry_data.loaded_platforms diff --git a/tests/components/esphome/test_bluetooth.py b/tests/components/esphome/test_bluetooth.py index 46858c5826b..31d9fcd34f9 100644 --- a/tests/components/esphome/test_bluetooth.py +++ b/tests/components/esphome/test_bluetooth.py @@ -1,5 +1,7 @@ """Test the ESPHome bluetooth integration.""" +from unittest.mock import patch + from homeassistant.components import bluetooth from homeassistant.core import HomeAssistant @@ -44,3 +46,22 @@ async def test_bluetooth_connect_with_legacy_adv( await hass.async_block_till_done() scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") assert scanner.scanning is True + + +async def test_bluetooth_cleanup_on_remove_entry( + hass: HomeAssistant, mock_bluetooth_entry_with_raw_adv: MockESPHomeDevice +) -> None: + """Test bluetooth is cleaned up on entry removal.""" + scanner = bluetooth.async_scanner_by_source(hass, "11:22:33:44:55:AA") + assert scanner.connectable is True + await hass.config_entries.async_unload( + mock_bluetooth_entry_with_raw_adv.entry.entry_id + ) + + with patch("homeassistant.components.esphome.async_remove_scanner") as remove_mock: + await hass.config_entries.async_remove( + mock_bluetooth_entry_with_raw_adv.entry.entry_id + ) + await hass.async_block_till_done() + + remove_mock.assert_called_once_with(hass, scanner.source)