Require zeroconf >=0.36 (#118)
This commit is contained in:
parent
dec73b8e29
commit
dd9d716c10
|
@ -1,23 +1,16 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import contextlib
|
import contextlib
|
||||||
import functools
|
|
||||||
import socket
|
import socket
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from ipaddress import IPv4Address, IPv6Address
|
from ipaddress import IPv4Address, IPv6Address
|
||||||
from typing import List, Optional, Tuple, Union, cast
|
from typing import List, Optional, Tuple, Union, cast
|
||||||
|
|
||||||
import zeroconf
|
import zeroconf
|
||||||
|
import zeroconf.asyncio
|
||||||
try:
|
|
||||||
import zeroconf.asyncio
|
|
||||||
|
|
||||||
ZC_ASYNCIO = True
|
|
||||||
except ImportError:
|
|
||||||
ZC_ASYNCIO = False
|
|
||||||
|
|
||||||
from .core import APIConnectionError, ResolveAPIError
|
from .core import APIConnectionError, ResolveAPIError
|
||||||
|
|
||||||
ZeroconfInstanceType = Union[zeroconf.Zeroconf, "zeroconf.asyncio.AsyncZeroconf", None]
|
ZeroconfInstanceType = Union[zeroconf.Zeroconf, zeroconf.asyncio.AsyncZeroconf, None]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
@ -47,60 +40,12 @@ class AddrInfo:
|
||||||
sockaddr: Sockaddr
|
sockaddr: Sockaddr
|
||||||
|
|
||||||
|
|
||||||
def _sync_zeroconf_get_service_info(
|
|
||||||
zeroconf_instance: ZeroconfInstanceType,
|
|
||||||
service_type: str,
|
|
||||||
service_name: str,
|
|
||||||
timeout: float,
|
|
||||||
) -> Optional["zeroconf.ServiceInfo"]:
|
|
||||||
# Use or create zeroconf instance, ensure it's an AsyncZeroconf
|
|
||||||
if zeroconf_instance is None:
|
|
||||||
try:
|
|
||||||
zc = zeroconf.Zeroconf()
|
|
||||||
except Exception:
|
|
||||||
raise ResolveAPIError(
|
|
||||||
"Cannot start mDNS sockets, is this a docker container without "
|
|
||||||
"host network mode?"
|
|
||||||
)
|
|
||||||
do_close = True
|
|
||||||
elif isinstance(zeroconf_instance, zeroconf.Zeroconf):
|
|
||||||
zc = zeroconf_instance
|
|
||||||
do_close = False
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid type passed for zeroconf_instance: {type(zeroconf_instance)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
info = zc.get_service_info(service_type, service_name, int(timeout * 1000))
|
|
||||||
except Exception as exc:
|
|
||||||
raise ResolveAPIError(
|
|
||||||
f"Error resolving mDNS {service_name} via mDNS: {exc}"
|
|
||||||
) from exc
|
|
||||||
finally:
|
|
||||||
if do_close:
|
|
||||||
zc.close()
|
|
||||||
return info
|
|
||||||
|
|
||||||
|
|
||||||
async def _async_zeroconf_get_service_info(
|
async def _async_zeroconf_get_service_info(
|
||||||
zeroconf_instance: ZeroconfInstanceType,
|
zeroconf_instance: ZeroconfInstanceType,
|
||||||
service_type: str,
|
service_type: str,
|
||||||
service_name: str,
|
service_name: str,
|
||||||
timeout: float,
|
timeout: float,
|
||||||
) -> Optional["zeroconf.ServiceInfo"]:
|
) -> Optional["zeroconf.ServiceInfo"]:
|
||||||
if not ZC_ASYNCIO:
|
|
||||||
return await asyncio.get_event_loop().run_in_executor(
|
|
||||||
None,
|
|
||||||
functools.partial(
|
|
||||||
_sync_zeroconf_get_service_info,
|
|
||||||
zeroconf_instance,
|
|
||||||
service_type,
|
|
||||||
service_name,
|
|
||||||
timeout,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Use or create zeroconf instance, ensure it's an AsyncZeroconf
|
# Use or create zeroconf instance, ensure it's an AsyncZeroconf
|
||||||
if zeroconf_instance is None:
|
if zeroconf_instance is None:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -58,9 +58,6 @@ class ReconnectLogic(zeroconf.RecordUpdateListener): # type: ignore[misc,name-d
|
||||||
# Event for tracking when logic should stop
|
# Event for tracking when logic should stop
|
||||||
self._stop_event = asyncio.Event()
|
self._stop_event = asyncio.Event()
|
||||||
|
|
||||||
# Store the caller's event loop so the zeroconf can schedule it in the right one
|
|
||||||
self._event_loop: Optional[asyncio.AbstractEventLoop] = None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _is_stopped(self) -> bool:
|
def _is_stopped(self) -> bool:
|
||||||
return self._stop_event.is_set()
|
return self._stop_event.is_set()
|
||||||
|
@ -181,7 +178,6 @@ class ReconnectLogic(zeroconf.RecordUpdateListener): # type: ignore[misc,name-d
|
||||||
"""Start the reconnecting logic background task."""
|
"""Start the reconnecting logic background task."""
|
||||||
# Create reconnection loop outside of HA's tracked tasks in order
|
# Create reconnection loop outside of HA's tracked tasks in order
|
||||||
# not to delay startup.
|
# not to delay startup.
|
||||||
self._event_loop = asyncio.get_event_loop()
|
|
||||||
self._loop_task = asyncio.create_task(self._reconnect_loop())
|
self._loop_task = asyncio.create_task(self._reconnect_loop())
|
||||||
|
|
||||||
async with self._connected_lock:
|
async with self._connected_lock:
|
||||||
|
@ -243,7 +239,6 @@ class ReconnectLogic(zeroconf.RecordUpdateListener): # type: ignore[misc,name-d
|
||||||
)
|
)
|
||||||
self._reconnect_event.set()
|
self._reconnect_event.set()
|
||||||
|
|
||||||
# From RecordUpdateListener for zeroconf>=0.32
|
|
||||||
def async_update_records(
|
def async_update_records(
|
||||||
self,
|
self,
|
||||||
zc: "zeroconf.Zeroconf", # pylint: disable=unused-argument
|
zc: "zeroconf.Zeroconf", # pylint: disable=unused-argument
|
||||||
|
@ -253,18 +248,3 @@ class ReconnectLogic(zeroconf.RecordUpdateListener): # type: ignore[misc,name-d
|
||||||
"""Listen to zeroconf updated mDNS records. This must be called from the eventloop."""
|
"""Listen to zeroconf updated mDNS records. This must be called from the eventloop."""
|
||||||
for update in records:
|
for update in records:
|
||||||
self._async_on_record(update.new)
|
self._async_on_record(update.new)
|
||||||
|
|
||||||
# From RecordUpdateListener for zeroconf<0.32
|
|
||||||
def update_record(
|
|
||||||
self,
|
|
||||||
zc: "zeroconf.Zeroconf", # pylint: disable=unused-argument
|
|
||||||
now: float, # pylint: disable=unused-argument
|
|
||||||
record: "zeroconf.DNSRecord", # type: ignore[name-defined]
|
|
||||||
) -> None:
|
|
||||||
assert self._event_loop is not None
|
|
||||||
|
|
||||||
async def corofunc() -> None:
|
|
||||||
self._async_on_record(record)
|
|
||||||
|
|
||||||
# Dispatch in event loop
|
|
||||||
asyncio.run_coroutine_threadsafe(corofunc(), self._event_loop)
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
protobuf>=3.12.2,<4.0
|
protobuf>=3.12.2,<4.0
|
||||||
zeroconf>=0.28.0,<1.0
|
zeroconf>=0.36.0,<1.0
|
||||||
noiseprotocol>=0.3.1,<1.0
|
noiseprotocol>=0.3.1,<1.0
|
||||||
|
|
Loading…
Reference in New Issue