Start ServiceBrowser as soon as possible in zeroconf (#50784)
Co-authored-by: Ruslan Sayfutdinov <ruslan@sayfutdinov.com>
This commit is contained in:
parent
7a60d0eae4
commit
3cc3cacd08
6 changed files with 227 additions and 150 deletions
|
@ -1,7 +1,8 @@
|
||||||
"""Support for exposing Home Assistant via Zeroconf."""
|
"""Support for exposing Home Assistant via Zeroconf."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Iterable
|
import asyncio
|
||||||
|
from collections.abc import Coroutine, Iterable
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
@ -13,7 +14,6 @@ from typing import Any, TypedDict, cast
|
||||||
from pyroute2 import IPRoute
|
from pyroute2 import IPRoute
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from zeroconf import (
|
from zeroconf import (
|
||||||
Error as ZeroconfError,
|
|
||||||
InterfaceChoice,
|
InterfaceChoice,
|
||||||
IPVersion,
|
IPVersion,
|
||||||
NonUniqueNameException,
|
NonUniqueNameException,
|
||||||
|
@ -29,7 +29,8 @@ from homeassistant.const import (
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
__version__,
|
__version__,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Event, HomeAssistant
|
from homeassistant.core import Event, HomeAssistant, callback
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||||
from homeassistant.loader import async_get_homekit, async_get_zeroconf, bind_hass
|
from homeassistant.loader import async_get_homekit, async_get_zeroconf, bind_hass
|
||||||
|
@ -90,6 +91,14 @@ class HaServiceInfo(TypedDict):
|
||||||
properties: dict[str, Any]
|
properties: dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class ZeroconfFlow(TypedDict):
|
||||||
|
"""A queued zeroconf discovery flow."""
|
||||||
|
|
||||||
|
domain: str
|
||||||
|
context: dict[str, Any]
|
||||||
|
data: HaServiceInfo
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
async def async_get_instance(hass: HomeAssistant) -> HaZeroconf:
|
async def async_get_instance(hass: HomeAssistant) -> HaZeroconf:
|
||||||
"""Zeroconf instance to be shared with other integrations that use it."""
|
"""Zeroconf instance to be shared with other integrations that use it."""
|
||||||
|
@ -183,6 +192,12 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||||
aio_zc = await _async_get_instance(hass, **zc_args)
|
aio_zc = await _async_get_instance(hass, **zc_args)
|
||||||
zeroconf = aio_zc.zeroconf
|
zeroconf = aio_zc.zeroconf
|
||||||
|
|
||||||
|
zeroconf_types, homekit_models = await asyncio.gather(
|
||||||
|
async_get_zeroconf(hass), async_get_homekit(hass)
|
||||||
|
)
|
||||||
|
discovery = ZeroconfDiscovery(hass, zeroconf, zeroconf_types, homekit_models)
|
||||||
|
await discovery.async_setup()
|
||||||
|
|
||||||
async def _async_zeroconf_hass_start(_event: Event) -> None:
|
async def _async_zeroconf_hass_start(_event: Event) -> None:
|
||||||
"""Expose Home Assistant on zeroconf when it starts.
|
"""Expose Home Assistant on zeroconf when it starts.
|
||||||
|
|
||||||
|
@ -191,15 +206,17 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||||
uuid = await hass.helpers.instance_id.async_get()
|
uuid = await hass.helpers.instance_id.async_get()
|
||||||
await _async_register_hass_zc_service(hass, aio_zc, uuid)
|
await _async_register_hass_zc_service(hass, aio_zc, uuid)
|
||||||
|
|
||||||
async def _async_zeroconf_hass_started(_event: Event) -> None:
|
@callback
|
||||||
"""Start the service browser."""
|
def _async_start_discovery(_event: Event) -> None:
|
||||||
|
"""Start processing flows."""
|
||||||
|
discovery.async_start()
|
||||||
|
|
||||||
await _async_start_zeroconf_browser(hass, zeroconf)
|
async def _async_zeroconf_hass_stop(_event: Event) -> None:
|
||||||
|
await discovery.async_stop()
|
||||||
|
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_zeroconf_hass_stop)
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_zeroconf_hass_start)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_zeroconf_hass_start)
|
||||||
hass.bus.async_listen_once(
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_start_discovery)
|
||||||
EVENT_HOMEASSISTANT_STARTED, _async_zeroconf_hass_started
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -259,44 +276,98 @@ async def _async_register_hass_zc_service(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async_start_zeroconf_browser(
|
class FlowDispatcher:
|
||||||
hass: HomeAssistant, zeroconf: Zeroconf
|
"""Dispatch discovery flows."""
|
||||||
) -> None:
|
|
||||||
"""Start the zeroconf browser."""
|
|
||||||
|
|
||||||
zeroconf_types = await async_get_zeroconf(hass)
|
def __init__(self, hass: HomeAssistant):
|
||||||
homekit_models = await async_get_homekit(hass)
|
"""Init the discovery dispatcher."""
|
||||||
|
self.hass = hass
|
||||||
|
self.pending_flows: list[ZeroconfFlow] = []
|
||||||
|
self.started = False
|
||||||
|
|
||||||
types = list(zeroconf_types)
|
@callback
|
||||||
|
def async_start(self) -> None:
|
||||||
|
"""Start processing pending flows."""
|
||||||
|
self.started = True
|
||||||
|
self.hass.loop.call_soon(self._async_process_pending_flows)
|
||||||
|
|
||||||
for hk_type in HOMEKIT_TYPES:
|
def _async_process_pending_flows(self) -> None:
|
||||||
if hk_type not in zeroconf_types:
|
for flow in self.pending_flows:
|
||||||
types.append(hk_type)
|
self.hass.async_create_task(self._init_flow(flow))
|
||||||
|
self.pending_flows = []
|
||||||
|
|
||||||
|
def create(self, flow: ZeroconfFlow) -> None:
|
||||||
|
"""Create and add or queue a flow."""
|
||||||
|
if self.started:
|
||||||
|
self.hass.create_task(self._init_flow(flow))
|
||||||
|
else:
|
||||||
|
self.pending_flows.append(flow)
|
||||||
|
|
||||||
|
def _init_flow(self, flow: ZeroconfFlow) -> Coroutine[None, None, FlowResult]:
|
||||||
|
"""Create a flow."""
|
||||||
|
return self.hass.config_entries.flow.async_init(
|
||||||
|
flow["domain"], context=flow["context"], data=flow["data"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ZeroconfDiscovery:
|
||||||
|
"""Discovery via zeroconf."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
zeroconf: Zeroconf,
|
||||||
|
zeroconf_types: dict[str, list[dict[str, str]]],
|
||||||
|
homekit_models: dict[str, str],
|
||||||
|
) -> None:
|
||||||
|
"""Init discovery."""
|
||||||
|
self.hass = hass
|
||||||
|
self.zeroconf = zeroconf
|
||||||
|
self.zeroconf_types = zeroconf_types
|
||||||
|
self.homekit_models = homekit_models
|
||||||
|
|
||||||
|
self.flow_dispatcher: FlowDispatcher | None = None
|
||||||
|
self.service_browser: HaServiceBrowser | None = None
|
||||||
|
|
||||||
|
async def async_setup(self) -> None:
|
||||||
|
"""Start discovery."""
|
||||||
|
self.flow_dispatcher = FlowDispatcher(self.hass)
|
||||||
|
types = list(self.zeroconf_types)
|
||||||
|
# We want to make sure we know about other HomeAssistant
|
||||||
|
# instances as soon as possible to avoid name conflicts
|
||||||
|
# so we always browse for ZEROCONF_TYPE
|
||||||
|
for hk_type in (ZEROCONF_TYPE, *HOMEKIT_TYPES):
|
||||||
|
if hk_type not in self.zeroconf_types:
|
||||||
|
types.append(hk_type)
|
||||||
|
_LOGGER.debug("Starting Zeroconf browser")
|
||||||
|
self.service_browser = HaServiceBrowser(
|
||||||
|
self.zeroconf, types, handlers=[self.service_update]
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_stop(self) -> None:
|
||||||
|
"""Cancel the service browser and stop processing the queue."""
|
||||||
|
if self.service_browser:
|
||||||
|
await self.hass.async_add_executor_job(self.service_browser.cancel)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_start(self) -> None:
|
||||||
|
"""Start processing discovery flows."""
|
||||||
|
assert self.flow_dispatcher is not None
|
||||||
|
self.flow_dispatcher.async_start()
|
||||||
|
|
||||||
def service_update(
|
def service_update(
|
||||||
|
self,
|
||||||
zeroconf: Zeroconf,
|
zeroconf: Zeroconf,
|
||||||
service_type: str,
|
service_type: str,
|
||||||
name: str,
|
name: str,
|
||||||
state_change: ServiceStateChange,
|
state_change: ServiceStateChange,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Service state changed."""
|
"""Service state changed."""
|
||||||
nonlocal zeroconf_types
|
|
||||||
nonlocal homekit_models
|
|
||||||
|
|
||||||
if state_change == ServiceStateChange.Removed:
|
if state_change == ServiceStateChange.Removed:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
service_info = ServiceInfo(service_type, name)
|
||||||
service_info = zeroconf.get_service_info(service_type, name)
|
service_info.load_from_cache(zeroconf)
|
||||||
except ZeroconfError:
|
|
||||||
_LOGGER.exception("Failed to get info for device %s", name)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not service_info:
|
|
||||||
# Prevent the browser thread from collapsing as
|
|
||||||
# service_info can be None
|
|
||||||
_LOGGER.debug("Failed to get info for device %s", name)
|
|
||||||
return
|
|
||||||
|
|
||||||
info = info_from_service(service_info)
|
info = info_from_service(service_info)
|
||||||
if not info:
|
if not info:
|
||||||
|
@ -305,10 +376,12 @@ async def _async_start_zeroconf_browser(
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.debug("Discovered new device %s %s", name, info)
|
_LOGGER.debug("Discovered new device %s %s", name, info)
|
||||||
|
assert self.flow_dispatcher is not None
|
||||||
|
|
||||||
# If we can handle it as a HomeKit discovery, we do that here.
|
# If we can handle it as a HomeKit discovery, we do that here.
|
||||||
if service_type in HOMEKIT_TYPES:
|
if service_type in HOMEKIT_TYPES:
|
||||||
discovery_was_forwarded = handle_homekit(hass, homekit_models, info)
|
if pending_flow := handle_homekit(self.hass, self.homekit_models, info):
|
||||||
|
self.flow_dispatcher.create(pending_flow)
|
||||||
# Continue on here as homekit_controller
|
# Continue on here as homekit_controller
|
||||||
# still needs to get updates on devices
|
# still needs to get updates on devices
|
||||||
# so it can see when the 'c#' field is updated.
|
# so it can see when the 'c#' field is updated.
|
||||||
|
@ -316,10 +389,7 @@ async def _async_start_zeroconf_browser(
|
||||||
# We only send updates to homekit_controller
|
# We only send updates to homekit_controller
|
||||||
# if the device is already paired in order to avoid
|
# if the device is already paired in order to avoid
|
||||||
# offering a second discovery for the same device
|
# offering a second discovery for the same device
|
||||||
if (
|
if pending_flow and HOMEKIT_PAIRED_STATUS_FLAG in info["properties"]:
|
||||||
discovery_was_forwarded
|
|
||||||
and HOMEKIT_PAIRED_STATUS_FLAG in info["properties"]
|
|
||||||
):
|
|
||||||
try:
|
try:
|
||||||
# 0 means paired and not discoverable by iOS clients)
|
# 0 means paired and not discoverable by iOS clients)
|
||||||
if int(info["properties"][HOMEKIT_PAIRED_STATUS_FLAG]):
|
if int(info["properties"][HOMEKIT_PAIRED_STATUS_FLAG]):
|
||||||
|
@ -348,7 +418,7 @@ async def _async_start_zeroconf_browser(
|
||||||
|
|
||||||
# Not all homekit types are currently used for discovery
|
# Not all homekit types are currently used for discovery
|
||||||
# so not all service type exist in zeroconf_types
|
# so not all service type exist in zeroconf_types
|
||||||
for matcher in zeroconf_types.get(service_type, []):
|
for matcher in self.zeroconf_types.get(service_type, []):
|
||||||
if len(matcher) > 1:
|
if len(matcher) > 1:
|
||||||
if "macaddress" in matcher and (
|
if "macaddress" in matcher and (
|
||||||
uppercase_mac is None
|
uppercase_mac is None
|
||||||
|
@ -368,19 +438,17 @@ async def _async_start_zeroconf_browser(
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
hass.add_job(
|
flow: ZeroconfFlow = {
|
||||||
hass.config_entries.flow.async_init(
|
"domain": matcher["domain"],
|
||||||
matcher["domain"], context={"source": DOMAIN}, data=info
|
"context": {"source": config_entries.SOURCE_ZEROCONF},
|
||||||
) # type: ignore
|
"data": info,
|
||||||
)
|
}
|
||||||
|
self.flow_dispatcher.create(flow)
|
||||||
_LOGGER.debug("Starting Zeroconf browser")
|
|
||||||
HaServiceBrowser(zeroconf, types, handlers=[service_update])
|
|
||||||
|
|
||||||
|
|
||||||
def handle_homekit(
|
def handle_homekit(
|
||||||
hass: HomeAssistant, homekit_models: dict[str, str], info: HaServiceInfo
|
hass: HomeAssistant, homekit_models: dict[str, str], info: HaServiceInfo
|
||||||
) -> bool:
|
) -> ZeroconfFlow | None:
|
||||||
"""Handle a HomeKit discovery.
|
"""Handle a HomeKit discovery.
|
||||||
|
|
||||||
Return if discovery was forwarded.
|
Return if discovery was forwarded.
|
||||||
|
@ -394,7 +462,7 @@ def handle_homekit(
|
||||||
break
|
break
|
||||||
|
|
||||||
if model is None:
|
if model is None:
|
||||||
return False
|
return None
|
||||||
|
|
||||||
for test_model in homekit_models:
|
for test_model in homekit_models:
|
||||||
if (
|
if (
|
||||||
|
@ -404,16 +472,13 @@ def handle_homekit(
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
hass.add_job(
|
return {
|
||||||
hass.config_entries.flow.async_init(
|
"domain": homekit_models[test_model],
|
||||||
homekit_models[test_model],
|
"context": {"source": config_entries.SOURCE_HOMEKIT},
|
||||||
context={"source": config_entries.SOURCE_HOMEKIT},
|
"data": info,
|
||||||
data=info,
|
}
|
||||||
) # type: ignore
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
return None
|
||||||
|
|
||||||
|
|
||||||
def info_from_service(service: ServiceInfo) -> HaServiceInfo | None:
|
def info_from_service(service: ServiceInfo) -> HaServiceInfo | None:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "zeroconf",
|
"domain": "zeroconf",
|
||||||
"name": "Zero-configuration networking (zeroconf)",
|
"name": "Zero-configuration networking (zeroconf)",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/zeroconf",
|
"documentation": "https://www.home-assistant.io/integrations/zeroconf",
|
||||||
"requirements": ["zeroconf==0.30.0","pyroute2==0.5.18"],
|
"requirements": ["zeroconf==0.31.0","pyroute2==0.5.18"],
|
||||||
"dependencies": ["api"],
|
"dependencies": ["api"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
|
|
|
@ -34,7 +34,7 @@ sqlalchemy==1.4.13
|
||||||
voluptuous-serialize==2.4.0
|
voluptuous-serialize==2.4.0
|
||||||
voluptuous==0.12.1
|
voluptuous==0.12.1
|
||||||
yarl==1.6.3
|
yarl==1.6.3
|
||||||
zeroconf==0.30.0
|
zeroconf==0.31.0
|
||||||
|
|
||||||
pycryptodome>=3.6.6
|
pycryptodome>=3.6.6
|
||||||
|
|
||||||
|
|
|
@ -2402,7 +2402,7 @@ zeep[async]==4.0.0
|
||||||
zengge==0.2
|
zengge==0.2
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.30.0
|
zeroconf==0.31.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zha-quirks==0.0.57
|
zha-quirks==0.0.57
|
||||||
|
|
|
@ -1290,7 +1290,7 @@ yeelight==0.6.2
|
||||||
zeep[async]==4.0.0
|
zeep[async]==4.0.0
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.30.0
|
zeroconf==0.31.0
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zha-quirks==0.0.57
|
zha-quirks==0.0.57
|
||||||
|
|
|
@ -1,14 +1,7 @@
|
||||||
"""Test Zeroconf component setup process."""
|
"""Test Zeroconf component setup process."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from zeroconf import (
|
from zeroconf import InterfaceChoice, IPVersion, ServiceInfo, ServiceStateChange
|
||||||
BadTypeInNameException,
|
|
||||||
Error as ZeroconfError,
|
|
||||||
InterfaceChoice,
|
|
||||||
IPVersion,
|
|
||||||
ServiceInfo,
|
|
||||||
ServiceStateChange,
|
|
||||||
)
|
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
from homeassistant.components.zeroconf import CONF_DEFAULT_INTERFACE, CONF_IPV6
|
from homeassistant.components.zeroconf import CONF_DEFAULT_INTERFACE, CONF_IPV6
|
||||||
|
@ -149,8 +142,10 @@ async def test_setup(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -181,8 +176,9 @@ async def test_setup_with_overly_long_url_and_name(hass, mock_zeroconf, caplog):
|
||||||
hass.config,
|
hass.config,
|
||||||
"location_name",
|
"location_name",
|
||||||
"\u00dcBER \u00dcber German Umlaut long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string",
|
"\u00dcBER \u00dcber German Umlaut long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string long string",
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo.request",
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -195,8 +191,10 @@ async def test_setup_with_default_interface(hass, mock_zeroconf):
|
||||||
"""Test default interface config."""
|
"""Test default interface config."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_DEFAULT_INTERFACE: True}}
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_DEFAULT_INTERFACE: True}}
|
||||||
)
|
)
|
||||||
|
@ -210,8 +208,10 @@ async def test_setup_without_default_interface(hass, mock_zeroconf):
|
||||||
"""Test without default interface config."""
|
"""Test without default interface config."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_DEFAULT_INTERFACE: False}}
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_DEFAULT_INTERFACE: False}}
|
||||||
)
|
)
|
||||||
|
@ -223,8 +223,10 @@ async def test_setup_without_ipv6(hass, mock_zeroconf):
|
||||||
"""Test without ipv6."""
|
"""Test without ipv6."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: False}}
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: False}}
|
||||||
)
|
)
|
||||||
|
@ -238,8 +240,10 @@ async def test_setup_with_ipv6(hass, mock_zeroconf):
|
||||||
"""Test without ipv6."""
|
"""Test without ipv6."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(
|
assert await async_setup_component(
|
||||||
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: True}}
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: True}}
|
||||||
)
|
)
|
||||||
|
@ -253,8 +257,10 @@ async def test_setup_with_ipv6_default(hass, mock_zeroconf):
|
||||||
"""Test without ipv6 as default."""
|
"""Test without ipv6 as default."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -262,20 +268,6 @@ async def test_setup_with_ipv6_default(hass, mock_zeroconf):
|
||||||
assert mock_zeroconf.called_with()
|
assert mock_zeroconf.called_with()
|
||||||
|
|
||||||
|
|
||||||
async def test_service_with_invalid_name(hass, mock_zeroconf, caplog):
|
|
||||||
"""Test we do not crash on service with an invalid name."""
|
|
||||||
with patch.object(
|
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
|
||||||
) as mock_service_browser:
|
|
||||||
mock_zeroconf.get_service_info.side_effect = BadTypeInNameException
|
|
||||||
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 "Failed to get info for device" in caplog.text
|
|
||||||
|
|
||||||
|
|
||||||
async def test_zeroconf_match_macaddress(hass, mock_zeroconf):
|
async def test_zeroconf_match_macaddress(hass, mock_zeroconf):
|
||||||
"""Test configured options for a device are loaded via config entry."""
|
"""Test configured options for a device are loaded via config entry."""
|
||||||
|
|
||||||
|
@ -300,10 +292,10 @@ async def test_zeroconf_match_macaddress(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_zeroconf_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"FFAADDCC11DD"
|
side_effect=get_zeroconf_info_mock("FFAADDCC11DD"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -333,10 +325,10 @@ async def test_zeroconf_match_manufacturer(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = (
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
get_zeroconf_info_mock_manufacturer("Samsung Electronics")
|
side_effect=get_zeroconf_info_mock_manufacturer("Samsung Electronics"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -366,10 +358,10 @@ async def test_zeroconf_match_manufacturer_not_present(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_zeroconf_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"aa:bb:cc:dd:ee:ff"
|
side_effect=get_zeroconf_info_mock("aabbccddeeff"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -398,10 +390,10 @@ async def test_zeroconf_no_match(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_zeroconf_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"FFAADDCC11DD"
|
side_effect=get_zeroconf_info_mock("FFAADDCC11DD"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -430,10 +422,10 @@ async def test_zeroconf_no_match_manufacturer(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=http_only_service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = (
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
get_zeroconf_info_mock_manufacturer("Not Samsung Electronics")
|
side_effect=get_zeroconf_info_mock_manufacturer("Not Samsung Electronics"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -456,10 +448,10 @@ async def test_homekit_match_partial_space(hass, mock_zeroconf):
|
||||||
side_effect=lambda *args, **kwargs: service_update_mock(
|
side_effect=lambda *args, **kwargs: service_update_mock(
|
||||||
*args, **kwargs, limit_service="_hap._tcp.local."
|
*args, **kwargs, limit_service="_hap._tcp.local."
|
||||||
),
|
),
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"LIFX bulb", HOMEKIT_STATUS_UNPAIRED
|
side_effect=get_homekit_info_mock("LIFX bulb", HOMEKIT_STATUS_UNPAIRED),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -483,10 +475,10 @@ async def test_homekit_match_partial_dash(hass, mock_zeroconf):
|
||||||
side_effect=lambda *args, **kwargs: service_update_mock(
|
side_effect=lambda *args, **kwargs: service_update_mock(
|
||||||
*args, **kwargs, limit_service="_hap._udp.local."
|
*args, **kwargs, limit_service="_hap._udp.local."
|
||||||
),
|
),
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"Rachio-fa46ba", HOMEKIT_STATUS_UNPAIRED
|
side_effect=get_homekit_info_mock("Rachio-fa46ba", HOMEKIT_STATUS_UNPAIRED),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -510,10 +502,10 @@ async def test_homekit_match_full(hass, mock_zeroconf):
|
||||||
side_effect=lambda *args, **kwargs: service_update_mock(
|
side_effect=lambda *args, **kwargs: service_update_mock(
|
||||||
*args, **kwargs, limit_service="_hap._udp.local."
|
*args, **kwargs, limit_service="_hap._udp.local."
|
||||||
),
|
),
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"BSB002", HOMEKIT_STATUS_UNPAIRED
|
side_effect=get_homekit_info_mock("BSB002", HOMEKIT_STATUS_UNPAIRED),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -537,10 +529,10 @@ async def test_homekit_already_paired(hass, mock_zeroconf):
|
||||||
side_effect=lambda *args, **kwargs: service_update_mock(
|
side_effect=lambda *args, **kwargs: service_update_mock(
|
||||||
*args, **kwargs, limit_service="_hap._tcp.local."
|
*args, **kwargs, limit_service="_hap._tcp.local."
|
||||||
),
|
),
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"tado", HOMEKIT_STATUS_PAIRED
|
side_effect=get_homekit_info_mock("tado", HOMEKIT_STATUS_PAIRED),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -565,10 +557,10 @@ async def test_homekit_invalid_paring_status(hass, mock_zeroconf):
|
||||||
side_effect=lambda *args, **kwargs: service_update_mock(
|
side_effect=lambda *args, **kwargs: service_update_mock(
|
||||||
*args, **kwargs, limit_service="_hap._tcp.local."
|
*args, **kwargs, limit_service="_hap._tcp.local."
|
||||||
),
|
),
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
"tado", b"invalid"
|
side_effect=get_homekit_info_mock("tado", b"invalid"),
|
||||||
)
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -588,10 +580,12 @@ async def test_homekit_not_paired(hass, mock_zeroconf):
|
||||||
hass.config_entries.flow, "async_init"
|
hass.config_entries.flow, "async_init"
|
||||||
) as mock_config_flow, patch.object(
|
) as mock_config_flow, patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
) as mock_service_browser:
|
) as mock_service_browser, patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock(
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_homekit_info_mock(
|
||||||
"this_will_not_match_any_integration", HOMEKIT_STATUS_UNPAIRED
|
"this_will_not_match_any_integration", HOMEKIT_STATUS_UNPAIRED
|
||||||
)
|
),
|
||||||
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -636,34 +630,44 @@ async def test_get_instance(hass, mock_zeroconf):
|
||||||
|
|
||||||
async def test_removed_ignored(hass, mock_zeroconf):
|
async def test_removed_ignored(hass, mock_zeroconf):
|
||||||
"""Test we remove it when a zeroconf entry is removed."""
|
"""Test we remove it when a zeroconf entry is removed."""
|
||||||
mock_zeroconf.get_service_info.side_effect = ZeroconfError
|
|
||||||
|
|
||||||
def service_update_mock(zeroconf, services, handlers):
|
def service_update_mock(zeroconf, services, handlers):
|
||||||
"""Call service update handler."""
|
"""Call service update handler."""
|
||||||
handlers[0](
|
handlers[0](
|
||||||
zeroconf, "_service.added", "name._service.added", ServiceStateChange.Added
|
zeroconf,
|
||||||
|
"_service.added.local.",
|
||||||
|
"name._service.added.local.",
|
||||||
|
ServiceStateChange.Added,
|
||||||
)
|
)
|
||||||
handlers[0](
|
handlers[0](
|
||||||
zeroconf,
|
zeroconf,
|
||||||
"_service.updated",
|
"_service.updated.local.",
|
||||||
"name._service.updated",
|
"name._service.updated.local.",
|
||||||
ServiceStateChange.Updated,
|
ServiceStateChange.Updated,
|
||||||
)
|
)
|
||||||
handlers[0](
|
handlers[0](
|
||||||
zeroconf,
|
zeroconf,
|
||||||
"_service.removed",
|
"_service.removed.local.",
|
||||||
"name._service.removed",
|
"name._service.removed.local.",
|
||||||
ServiceStateChange.Removed,
|
ServiceStateChange.Removed,
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch.object(zeroconf, "HaServiceBrowser", side_effect=service_update_mock):
|
with patch.object(
|
||||||
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
|
) as mock_service_info:
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(mock_zeroconf.get_service_info.mock_calls) == 2
|
assert len(mock_service_info.mock_calls) == 2
|
||||||
assert mock_zeroconf.get_service_info.mock_calls[0][1][0] == "_service.added"
|
import pprint
|
||||||
assert mock_zeroconf.get_service_info.mock_calls[1][1][0] == "_service.updated"
|
|
||||||
|
pprint.pprint(mock_service_info.mock_calls[0][1])
|
||||||
|
assert mock_service_info.mock_calls[0][1][0] == "_service.added.local."
|
||||||
|
assert mock_service_info.mock_calls[1][1][0] == "_service.updated.local."
|
||||||
|
|
||||||
|
|
||||||
async def test_async_detect_interfaces_setting_non_loopback_route(hass, mock_zeroconf):
|
async def test_async_detect_interfaces_setting_non_loopback_route(hass, mock_zeroconf):
|
||||||
|
@ -673,8 +677,10 @@ async def test_async_detect_interfaces_setting_non_loopback_route(hass, mock_zer
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.zeroconf.IPRoute.route",
|
"homeassistant.components.zeroconf.IPRoute.route",
|
||||||
return_value=_ROUTE_NO_LOOPBACK,
|
return_value=_ROUTE_NO_LOOPBACK,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -688,8 +694,10 @@ async def test_async_detect_interfaces_setting_loopback_route(hass, mock_zerocon
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.zeroconf.IPRoute.route", return_value=_ROUTE_LOOPBACK
|
"homeassistant.components.zeroconf.IPRoute.route", return_value=_ROUTE_LOOPBACK
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -701,8 +709,10 @@ async def test_async_detect_interfaces_setting_empty_route(hass, mock_zeroconf):
|
||||||
"""Test without default interface config and the route returns nothing."""
|
"""Test without default interface config and the route returns nothing."""
|
||||||
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
), patch("homeassistant.components.zeroconf.IPRoute.route", return_value=[]):
|
), patch("homeassistant.components.zeroconf.IPRoute.route", return_value=[]), patch(
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
|
):
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -716,8 +726,10 @@ async def test_async_detect_interfaces_setting_exception(hass, mock_zeroconf):
|
||||||
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.zeroconf.IPRoute.route", side_effect=AttributeError
|
"homeassistant.components.zeroconf.IPRoute.route", side_effect=AttributeError
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.zeroconf.ServiceInfo",
|
||||||
|
side_effect=get_service_info_mock,
|
||||||
):
|
):
|
||||||
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
Loading…
Add table
Reference in a new issue