Use async zeroconf registration functions (#50168)

pull/50469/head
J. Nick Koston 2021-05-11 11:03:36 -05:00 committed by GitHub
parent e616583bad
commit 909a20b36d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 18 deletions

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from collections.abc import Iterable
from contextlib import suppress
import fnmatch
from functools import partial
import ipaddress
from ipaddress import ip_address
import logging
@ -33,11 +32,10 @@ from homeassistant.const import (
from homeassistant.core import Event, HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.network import NoURLAvailableError, get_url
from homeassistant.helpers.singleton import singleton
from homeassistant.loader import async_get_homekit, async_get_zeroconf
from homeassistant.loader import async_get_homekit, async_get_zeroconf, bind_hass
from homeassistant.util.network import is_loopback
from .models import HaServiceBrowser, HaZeroconf
from .models import HaAsyncZeroconf, HaServiceBrowser, HaZeroconf
from .usage import install_multiple_zeroconf_catcher
_LOGGER = logging.getLogger(__name__)
@ -92,16 +90,26 @@ class HaServiceInfo(TypedDict):
properties: dict[str, Any]
@singleton(DOMAIN)
@bind_hass
async def async_get_instance(hass: HomeAssistant) -> HaZeroconf:
"""Zeroconf instance to be shared with other integrations that use it."""
return cast(HaZeroconf, (await _async_get_instance(hass)).zeroconf)
@bind_hass
async def async_get_async_instance(hass: HomeAssistant) -> HaAsyncZeroconf:
"""Zeroconf instance to be shared with other integrations that use it."""
return await _async_get_instance(hass)
async def _async_get_instance(hass: HomeAssistant, **zcargs: Any) -> HaZeroconf:
async def _async_get_instance(hass: HomeAssistant, **zcargs: Any) -> HaAsyncZeroconf:
if DOMAIN in hass.data:
return cast(HaAsyncZeroconf, hass.data[DOMAIN])
logging.getLogger("zeroconf").setLevel(logging.NOTSET)
zeroconf = await hass.async_add_executor_job(partial(HaZeroconf, **zcargs))
aio_zc = HaAsyncZeroconf(**zcargs)
zeroconf = cast(HaZeroconf, aio_zc.zeroconf)
install_multiple_zeroconf_catcher(zeroconf)
@ -110,8 +118,9 @@ async def _async_get_instance(hass: HomeAssistant, **zcargs: Any) -> HaZeroconf:
zeroconf.ha_close()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _stop_zeroconf)
hass.data[DOMAIN] = aio_zc
return zeroconf
return aio_zc
def _get_ip_route(dst_ip: str) -> Any:
@ -171,7 +180,8 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
if not zc_config.get(CONF_IPV6, DEFAULT_IPV6):
zc_args["ip_version"] = IPVersion.V4Only
zeroconf = hass.data[DOMAIN] = await _async_get_instance(hass, **zc_args)
aio_zc = await _async_get_instance(hass, **zc_args)
zeroconf = aio_zc.zeroconf
async def _async_zeroconf_hass_start(_event: Event) -> None:
"""Expose Home Assistant on zeroconf when it starts.
@ -179,9 +189,7 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
Wait till started or otherwise HTTP is not up and running.
"""
uuid = await hass.helpers.instance_id.async_get()
await hass.async_add_executor_job(
_register_hass_zc_service, hass, zeroconf, uuid
)
await _async_register_hass_zc_service(hass, aio_zc, uuid)
async def _async_zeroconf_hass_started(_event: Event) -> None:
"""Start the service browser."""
@ -196,8 +204,8 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
return True
def _register_hass_zc_service(
hass: HomeAssistant, zeroconf: HaZeroconf, uuid: str
async def _async_register_hass_zc_service(
hass: HomeAssistant, aio_zc: HaAsyncZeroconf, uuid: str
) -> None:
# Get instance UUID
valid_location_name = _truncate_location_name_to_valid(hass.config.location_name)
@ -244,7 +252,7 @@ def _register_hass_zc_service(
_LOGGER.info("Starting Zeroconf broadcast")
try:
zeroconf.register_service(info)
await aio_zc.async_register_service(info)
except NonUniqueNameException:
_LOGGER.error(
"Home Assistant instance with identical name present in the local network"
@ -252,7 +260,7 @@ def _register_hass_zc_service(
async def _async_start_zeroconf_browser(
hass: HomeAssistant, zeroconf: HaZeroconf
hass: HomeAssistant, zeroconf: Zeroconf
) -> None:
"""Start the zeroconf browser."""

View File

@ -1,6 +1,10 @@
"""Models for Zeroconf."""
import asyncio
from typing import Any
from zeroconf import DNSPointer, DNSRecord, ServiceBrowser, Zeroconf
from zeroconf.asyncio import AsyncZeroconf
class HaZeroconf(Zeroconf):
@ -12,6 +16,20 @@ class HaZeroconf(Zeroconf):
ha_close = Zeroconf.close
class HaAsyncZeroconf(AsyncZeroconf):
"""Home Assistant version of AsyncZeroconf."""
def __init__( # pylint: disable=super-init-not-called
self, *args: Any, **kwargs: Any
) -> None:
"""Wrap AsyncZeroconf."""
self.zeroconf = HaZeroconf(*args, **kwargs)
self.loop = asyncio.get_running_loop()
async def async_close(self) -> None:
"""Fake method to avoid integrations closing it."""
class HaServiceBrowser(ServiceBrowser):
"""ServiceBrowser that only consumes DNSPointer records."""

View File

@ -14,7 +14,7 @@ from tests.components.light.conftest import mock_light_profiles # noqa: F401
@pytest.fixture(autouse=True)
def mock_zeroconf():
"""Mock zeroconf."""
with mock.patch("homeassistant.components.zeroconf.HaZeroconf") as mock_zc:
with mock.patch("homeassistant.components.zeroconf.models.HaZeroconf") as mock_zc:
yield mock_zc.return_value

View File

@ -478,7 +478,7 @@ async def mqtt_mock(hass, mqtt_client_mock, mqtt_config):
@pytest.fixture
def mock_zeroconf():
"""Mock zeroconf."""
with patch("homeassistant.components.zeroconf.HaZeroconf") as mock_zc:
with patch("homeassistant.components.zeroconf.models.HaZeroconf") as mock_zc:
yield mock_zc.return_value