Ensure we do not start discovered flows until after the started event has fired (#38047)

* Ensure we do not start discovered flows until after the start event has fired

This change makes zeroconf and ssdp match discovery behavior of not
creating config flows until the start event has been fired.  This
prevents config flow creation/dependency installs for discovered
config flows from competing for cpu time during startup.

* Start discovery/service browser/ssdp when EVENT_HOMEASSISTANT_STARTED is fired instead of EVENT_HOMEASSISTANT_START
This commit is contained in:
J. Nick Koston 2020-07-21 14:18:43 -10:00 committed by GitHub
parent e766a119d2
commit 5cf7b1b1bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 10 deletions

View file

@ -15,7 +15,7 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import async_discover, async_load_platform
@ -209,7 +209,7 @@ async def async_setup(hass, config):
"""Schedule the first discovery when Home Assistant starts up."""
async_track_point_in_utc_time(hass, scan_devices, dt_util.utcnow())
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, schedule_first)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, schedule_first)
return True

View file

@ -7,6 +7,7 @@ import aiohttp
from defusedxml import ElementTree
from netdisco import ssdp, util
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.generated.ssdp import SSDP
from homeassistant.helpers.event import async_track_time_interval
@ -33,12 +34,12 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup(hass, config):
"""Set up the SSDP integration."""
async def initialize():
async def initialize(_):
scanner = Scanner(hass)
await scanner.async_scan(None)
async_track_time_interval(hass, scanner.async_scan, SCAN_INTERVAL)
hass.loop.create_task(initialize())
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, initialize)
return True

View file

@ -21,6 +21,7 @@ from homeassistant import util
from homeassistant.const import (
ATTR_NAME,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STARTED,
EVENT_HOMEASSISTANT_STOP,
__version__,
)
@ -247,7 +248,13 @@ def setup(hass, config):
if HOMEKIT_TYPE not in ZEROCONF:
types.append(HOMEKIT_TYPE)
HaServiceBrowser(zeroconf, types, handlers=[service_update])
def zeroconf_hass_started(_event):
"""Start the service browser."""
_LOGGER.debug("Starting Zeroconf browser")
HaServiceBrowser(zeroconf, types, handlers=[service_update])
hass.bus.listen_once(EVENT_HOMEASSISTANT_STARTED, zeroconf_hass_started)
return True

View file

@ -6,6 +6,7 @@ import pytest
from homeassistant import config_entries
from homeassistant.bootstrap import async_setup_component
from homeassistant.components import discovery
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.util.dt import utcnow
from tests.async_mock import patch
@ -36,10 +37,12 @@ def netdisco_mock():
async def mock_discovery(hass, discoveries, config=BASE_CONFIG):
"""Mock discoveries."""
result = await async_setup_component(hass, "discovery", config)
assert result
await hass.async_start()
with patch("homeassistant.components.zeroconf.async_get_instance"):
assert await async_setup_component(hass, "discovery", config)
await hass.async_block_till_done()
await hass.async_start()
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
with patch.object(discovery, "_discover", discoveries), patch(
"homeassistant.components.discovery.async_discover", return_value=mock_coro()

View file

@ -4,7 +4,7 @@ from zeroconf import InterfaceChoice, IPVersion, ServiceInfo, ServiceStateChange
from homeassistant.components import zeroconf
from homeassistant.components.zeroconf import CONF_DEFAULT_INTERFACE, CONF_IPV6
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
from homeassistant.generated import zeroconf as zc_gen
from homeassistant.setup import async_setup_component
@ -76,6 +76,8 @@ async def test_setup(hass, mock_zeroconf):
) as mock_service_browser:
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
expected_flow_calls = 0
@ -97,6 +99,8 @@ async def test_setup_with_default_interface(hass, mock_zeroconf):
assert await async_setup_component(
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_DEFAULT_INTERFACE: True}}
)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert mock_zeroconf.called_with(interface_choice=InterfaceChoice.Default)
@ -123,6 +127,8 @@ async def test_setup_without_ipv6(hass, mock_zeroconf):
assert await async_setup_component(
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: False}}
)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert mock_zeroconf.called_with(ip_version=IPVersion.V4Only)
@ -136,6 +142,8 @@ async def test_setup_with_ipv6(hass, mock_zeroconf):
assert await async_setup_component(
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: True}}
)
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert mock_zeroconf.called_with()
@ -147,6 +155,8 @@ async def test_setup_with_ipv6_default(hass, mock_zeroconf):
):
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert mock_zeroconf.called_with()
@ -164,6 +174,8 @@ async def test_homekit_match_partial_space(hass, mock_zeroconf):
"LIFX bulb", HOMEKIT_STATUS_UNPAIRED
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 1
@ -183,6 +195,8 @@ async def test_homekit_match_partial_dash(hass, mock_zeroconf):
"Rachio-fa46ba", HOMEKIT_STATUS_UNPAIRED
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 1
@ -202,6 +216,8 @@ async def test_homekit_match_full(hass, mock_zeroconf):
"BSB002", HOMEKIT_STATUS_UNPAIRED
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
homekit_mock = get_homekit_info_mock("BSB002", HOMEKIT_STATUS_UNPAIRED)
info = homekit_mock("_hap._tcp.local.", "BSB002._hap._tcp.local.")
@ -226,6 +242,8 @@ async def test_homekit_already_paired(hass, mock_zeroconf):
"tado", HOMEKIT_STATUS_PAIRED
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 2
@ -246,6 +264,8 @@ async def test_homekit_invalid_paring_status(hass, mock_zeroconf):
"tado", b"invalid"
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 1
@ -265,6 +285,8 @@ async def test_homekit_not_paired(hass, mock_zeroconf):
"this_will_not_match_any_integration", HOMEKIT_STATUS_UNPAIRED
)
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
await hass.async_block_till_done()
assert len(mock_service_browser.mock_calls) == 1
assert len(mock_config_flow.mock_calls) == 1