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
This commit is contained in:
J. Nick Koston 2024-03-17 23:39:59 -10:00 committed by GitHub
parent 3844ade572
commit 8a144d16f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 54 additions and 25 deletions

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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())

View File

@ -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