From 8a144d16f58e452c100bcd8acb602176eaaf5d16 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 17 Mar 2024 23:39:59 -1000 Subject: [PATCH] Move legacy device_tracker setup to a tracked task (#113715) * Move legacy device_tracker setup to a tracked task Legacy platforms are now loaded in a tracked task which allows anything waiting on device_tracker (such as a config entry that uses the device_tracker platform) to proceed. This also allows us to remove the workaround of adding device_tracker to hass.config.components in its setup * tweak * tweak * fix tests --- .../components/device_tracker/__init__.py | 9 ---- .../components/device_tracker/legacy.py | 42 +++++++++++++------ .../test_device_tracker.py | 4 ++ tests/components/device_tracker/test_init.py | 1 + .../components/meraki/test_device_tracker.py | 9 ++-- .../mqtt_json/test_device_tracker.py | 14 +++++++ 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index e16712acf95..346459e324e 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -69,16 +69,7 @@ def is_on(hass: HomeAssistant, entity_id: str) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the device tracker.""" - - # We need to add the component here break the deadlock - # when setting up integrations from config entries as - # they would otherwise wait for the device tracker to be - # setup and thus the config entries would not be able to - # setup their platforms. - hass.config.components.add(DOMAIN) - await async_setup_legacy_integration(hass, config) - return True diff --git a/homeassistant/components/device_tracker/legacy.py b/homeassistant/components/device_tracker/legacy.py index 9ca9567b896..344443cd878 100644 --- a/homeassistant/components/device_tracker/legacy.py +++ b/homeassistant/components/device_tracker/legacy.py @@ -55,6 +55,7 @@ from homeassistant.setup import ( async_start_setup, ) from homeassistant.util import dt as dt_util +from homeassistant.util.async_ import create_eager_task from homeassistant.util.yaml import dump from .const import ( @@ -203,10 +204,37 @@ async def async_setup_integration(hass: HomeAssistant, config: ConfigType) -> No """Set up the legacy integration.""" tracker = await get_tracker(hass, config) + async def async_see_service(call: ServiceCall) -> None: + """Service to see a device.""" + # Temp workaround for iOS, introduced in 0.65 + data = dict(call.data) + data.pop("hostname", None) + data.pop("battery_status", None) + await tracker.async_see(**data) + + hass.services.async_register( + DOMAIN, SERVICE_SEE, async_see_service, SERVICE_SEE_PAYLOAD_SCHEMA + ) + + # + # The platforms load in a non-awaited tracked task + # to ensure device tracker setup can continue and config + # entry integrations are not waiting for legacy device + # tracker platforms to be set up. + # + hass.async_create_task( + _async_setup_legacy_integration(hass, config, tracker), eager_start=True + ) + + +async def _async_setup_legacy_integration( + hass: HomeAssistant, config: ConfigType, tracker: DeviceTracker +) -> None: + """Set up the legacy integration.""" legacy_platforms = await async_extract_config(hass, config) setup_tasks = [ - asyncio.create_task(legacy_platform.async_setup_legacy(hass, tracker)) + create_eager_task(legacy_platform.async_setup_legacy(hass, tracker)) for legacy_platform in legacy_platforms ] @@ -231,18 +259,6 @@ async def async_setup_integration(hass: HomeAssistant, config: ConfigType) -> No hass, tracker.async_update_stale, second=range(0, 60, 5) ) - async def async_see_service(call: ServiceCall) -> None: - """Service to see a device.""" - # Temp workaround for iOS, introduced in 0.65 - data = dict(call.data) - data.pop("hostname", None) - data.pop("battery_status", None) - await tracker.async_see(**data) - - hass.services.async_register( - DOMAIN, SERVICE_SEE, async_see_service, SERVICE_SEE_PAYLOAD_SCHEMA - ) - # restore await tracker.async_setup_tracked_device() diff --git a/tests/components/bluetooth_le_tracker/test_device_tracker.py b/tests/components/bluetooth_le_tracker/test_device_tracker.py index cbf823e4cee..627f2ffadcc 100644 --- a/tests/components/bluetooth_le_tracker/test_device_tracker.py +++ b/tests/components/bluetooth_le_tracker/test_device_tracker.py @@ -103,6 +103,7 @@ async def test_do_not_see_device_if_time_not_updated( CONF_CONSIDER_HOME: timedelta(minutes=10), } result = await async_setup_component(hass, DOMAIN, {DOMAIN: config}) + await hass.async_block_till_done() assert result # Tick until device seen enough times for to be registered for tracking @@ -246,6 +247,7 @@ async def test_preserve_new_tracked_device_name( CONF_TRACK_NEW: True, } assert await async_setup_component(hass, DOMAIN, {DOMAIN: config}) + await hass.async_block_till_done() # Seen once here; return without name when seen subsequent times device = BluetoothServiceInfoBleak( @@ -315,6 +317,7 @@ async def test_tracking_battery_times_out( CONF_TRACK_NEW: True, } result = await async_setup_component(hass, DOMAIN, {DOMAIN: config}) + await hass.async_block_till_done() assert result # Tick until device seen enough times for to be registered for tracking @@ -449,6 +452,7 @@ async def test_tracking_battery_successful( CONF_TRACK_NEW: True, } result = await async_setup_component(hass, DOMAIN, {DOMAIN: config}) + await hass.async_block_till_done() assert result # Tick until device seen enough times for to be registered for tracking diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 520e13e4c83..3166354260b 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -310,6 +310,7 @@ async def test_entity_attributes( with assert_setup_component(1, device_tracker.DOMAIN): assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM) + await hass.async_block_till_done() attrs = hass.states.get(entity_id).attributes diff --git a/tests/components/meraki/test_device_tracker.py b/tests/components/meraki/test_device_tracker.py index 02b9ba06b72..d0cd2cf8c5a 100644 --- a/tests/components/meraki/test_device_tracker.py +++ b/tests/components/meraki/test_device_tracker.py @@ -21,8 +21,9 @@ from homeassistant.setup import async_setup_component def meraki_client(event_loop, hass, hass_client): """Meraki mock client.""" loop = event_loop - assert loop.run_until_complete( - async_setup_component( + + async def setup_and_wait(): + result = await async_setup_component( hass, device_tracker.DOMAIN, { @@ -33,8 +34,10 @@ def meraki_client(event_loop, hass, hass_client): } }, ) - ) + await hass.async_block_till_done() + return result + assert loop.run_until_complete(setup_and_wait()) return loop.run_until_complete(hass_client()) diff --git a/tests/components/mqtt_json/test_device_tracker.py b/tests/components/mqtt_json/test_device_tracker.py index 91f40e2e4df..f150f5c86c9 100644 --- a/tests/components/mqtt_json/test_device_tracker.py +++ b/tests/components/mqtt_json/test_device_tracker.py @@ -61,6 +61,8 @@ async def test_setup_fails_without_mqtt_being_setup( DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}}, ) + await hass.async_block_till_done() + assert "MQTT integration is not available" in caplog.text @@ -83,6 +85,7 @@ async def test_ensure_device_tracker_platform_validation(hass: HomeAssistant) -> DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}}, ) + await hass.async_block_till_done() assert mock_sp.call_count == 1 @@ -97,6 +100,7 @@ async def test_json_message(hass: HomeAssistant) -> None: DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}}, ) + await hass.async_block_till_done() async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() state = hass.states.get("device_tracker.zanzito") @@ -117,6 +121,7 @@ async def test_non_json_message( DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}}, ) + await hass.async_block_till_done() caplog.set_level(logging.ERROR) caplog.clear() @@ -138,6 +143,7 @@ async def test_incomplete_message( DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: topic}}}, ) + await hass.async_block_till_done() caplog.set_level(logging.ERROR) caplog.clear() @@ -161,6 +167,8 @@ async def test_single_level_wildcard_topic(hass: HomeAssistant) -> None: DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}}, ) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() state = hass.states.get("device_tracker.zanzito") @@ -180,6 +188,8 @@ async def test_multi_level_wildcard_topic(hass: HomeAssistant) -> None: DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}}, ) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() state = hass.states.get("device_tracker.zanzito") @@ -200,6 +210,8 @@ async def test_single_level_wildcard_topic_not_matching(hass: HomeAssistant) -> DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}}, ) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() assert hass.states.get(entity_id) is None @@ -218,6 +230,8 @@ async def test_multi_level_wildcard_topic_not_matching(hass: HomeAssistant) -> N DT_DOMAIN, {DT_DOMAIN: {CONF_PLATFORM: "mqtt_json", "devices": {dev_id: subscription}}}, ) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, topic, location) await hass.async_block_till_done() assert hass.states.get(entity_id) is None