mirror of
https://github.com/esphome/esphome.git
synced 2025-09-15 17:52:19 +01:00
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""DNS resolver for ESPHome using aioesphomeapi."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import threading
|
|
|
|
from aioesphomeapi.core import ResolveAPIError, ResolveTimeoutAPIError
|
|
import aioesphomeapi.host_resolver as hr
|
|
|
|
from esphome.core import EsphomeError
|
|
|
|
RESOLVE_TIMEOUT = 10.0 # seconds
|
|
|
|
|
|
class AsyncResolver(threading.Thread):
|
|
"""Resolver using aioesphomeapi that runs in a thread for faster results.
|
|
|
|
This resolver uses aioesphomeapi's async_resolve_host to handle DNS resolution,
|
|
including proper .local domain fallback. Running in a thread allows us to get
|
|
the result immediately without waiting for asyncio.run() to complete its
|
|
cleanup cycle, which can take significant time.
|
|
"""
|
|
|
|
def __init__(self, hosts: list[str], port: int) -> None:
|
|
"""Initialize the resolver."""
|
|
super().__init__(daemon=True)
|
|
self.hosts = hosts
|
|
self.port = port
|
|
self.result: list[hr.AddrInfo] | None = None
|
|
self.exception: Exception | None = None
|
|
self.event = threading.Event()
|
|
|
|
async def _resolve(self) -> None:
|
|
"""Resolve hostnames to IP addresses."""
|
|
try:
|
|
self.result = await hr.async_resolve_host(
|
|
self.hosts, self.port, timeout=RESOLVE_TIMEOUT
|
|
)
|
|
except Exception as e: # pylint: disable=broad-except
|
|
# We need to catch all exceptions to ensure the event is set
|
|
# Otherwise the thread could hang forever
|
|
self.exception = e
|
|
finally:
|
|
self.event.set()
|
|
|
|
def run(self) -> None:
|
|
"""Run the DNS resolution."""
|
|
asyncio.run(self._resolve())
|
|
|
|
def resolve(self) -> list[hr.AddrInfo]:
|
|
"""Start the thread and wait for the result."""
|
|
self.start()
|
|
|
|
if not self.event.wait(
|
|
timeout=RESOLVE_TIMEOUT + 1.0
|
|
): # Give it 1 second more than the resolver timeout
|
|
raise EsphomeError("Timeout resolving IP address")
|
|
|
|
if exc := self.exception:
|
|
if isinstance(exc, ResolveTimeoutAPIError):
|
|
raise EsphomeError(f"Timeout resolving IP address: {exc}") from exc
|
|
if isinstance(exc, ResolveAPIError):
|
|
raise EsphomeError(f"Error resolving IP address: {exc}") from exc
|
|
raise exc
|
|
|
|
return self.result
|