diff --git a/esphome/address_cache.py b/esphome/address_cache.py index 6e5881716d..7c20be90f0 100644 --- a/esphome/address_cache.py +++ b/esphome/address_cache.py @@ -40,6 +40,25 @@ class AddressCache: self.mdns_cache = mdns_cache or {} self.dns_cache = dns_cache or {} + def _get_cached_addresses( + self, hostname: str, cache: dict[str, list[str]], cache_type: str + ) -> list[str] | None: + """Get cached addresses from a specific cache. + + Args: + hostname: The hostname to look up + cache: The cache dictionary to check + cache_type: Type of cache for logging ("mDNS" or "DNS") + + Returns: + List of IP addresses if found in cache, None otherwise + """ + normalized = normalize_hostname(hostname) + if addresses := cache.get(normalized): + _LOGGER.debug("Using %s cache for %s: %s", cache_type, hostname, addresses) + return addresses + return None + def get_mdns_addresses(self, hostname: str) -> list[str] | None: """Get cached mDNS addresses for a hostname. @@ -49,11 +68,7 @@ class AddressCache: Returns: List of IP addresses if found in cache, None otherwise """ - normalized = normalize_hostname(hostname) - if addresses := self.mdns_cache.get(normalized): - _LOGGER.debug("Using mDNS cache for %s: %s", hostname, addresses) - return addresses - return None + return self._get_cached_addresses(hostname, self.mdns_cache, "mDNS") def get_dns_addresses(self, hostname: str) -> list[str] | None: """Get cached DNS addresses for a hostname. @@ -64,11 +79,7 @@ class AddressCache: Returns: List of IP addresses if found in cache, None otherwise """ - normalized = normalize_hostname(hostname) - if addresses := self.dns_cache.get(normalized): - _LOGGER.debug("Using DNS cache for %s: %s", hostname, addresses) - return addresses - return None + return self._get_cached_addresses(hostname, self.dns_cache, "DNS") def get_addresses(self, hostname: str) -> list[str] | None: """Get cached addresses for a hostname. diff --git a/esphome/dashboard/status/mdns.py b/esphome/dashboard/status/mdns.py index a5ce69f30c..576bade7cd 100644 --- a/esphome/dashboard/status/mdns.py +++ b/esphome/dashboard/status/mdns.py @@ -6,6 +6,7 @@ import typing from zeroconf import AddressResolver, IPVersion +from esphome.address_cache import normalize_hostname from esphome.zeroconf import ( ESPHOME_SERVICE_TYPE, AsyncEsphomeZeroconf, @@ -60,8 +61,8 @@ class MDNSStatus: if not self.aiozc: return None - # Normalize hostname: remove trailing dots and get the base name - normalized = host_name.rstrip(".").lower() + # Normalize hostname and get the base name + normalized = normalize_hostname(host_name) base_name = normalized.partition(".")[0] # Try to load from zeroconf cache without triggering resolution diff --git a/esphome/helpers.py b/esphome/helpers.py index f4b321b26f..7eb560646b 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -210,28 +210,30 @@ def resolve_ip_address( res.sort(key=addr_preference_) return res - # Check if we have cached addresses for these hosts - cached_hosts: list[str] = [] + # Process hosts + cached_addresses: list[str] = [] uncached_hosts: list[str] = [] + has_cache = address_cache is not None for h in hosts: - # Check if it's already an IP address if is_ip_address(h): - cached_hosts.append(h) - continue + if has_cache: + # If we have a cache, treat IPs as cached + cached_addresses.append(h) + else: + # If no cache, pass IPs through to resolver with hostnames + uncached_hosts.append(h) + elif address_cache and (cached := address_cache.get_addresses(h)): + # Found in cache + cached_addresses.extend(cached) + else: + # Not cached, need to resolve + if address_cache and address_cache.has_cache(): + _LOGGER.info("Host %s not in cache, will need to resolve", h) + uncached_hosts.append(h) - # Check cache if provided - if address_cache and (cached_addresses := address_cache.get_addresses(h)): - cached_hosts.extend(cached_addresses) - continue - - # Not in cache, need to resolve - if address_cache and address_cache.has_cache(): - _LOGGER.info("Host %s not in cache, will need to resolve", h) - uncached_hosts.append(h) - - # Process cached addresses (all should be IP addresses) - for addr in cached_hosts: + # Process cached addresses (includes direct IPs and cached lookups) + for addr in cached_addresses: try: res += socket.getaddrinfo( addr, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST @@ -239,7 +241,7 @@ def resolve_ip_address( except OSError: _LOGGER.debug("Failed to parse IP address '%s'", addr) - # If we have uncached hosts, resolve them + # If we have uncached hosts (only non-IP hostnames), resolve them if uncached_hosts: from esphome.resolver import AsyncResolver