mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 20:27:08 +00:00
Ensure zeroconf uses the newest non-link local address in discovery (#58257)
This commit is contained in:
parent
50e0c58310
commit
5958e6a3f9
@ -469,13 +469,15 @@ def info_from_service(service: AsyncServiceInfo) -> HaServiceInfo | None:
|
|||||||
if isinstance(value, bytes):
|
if isinstance(value, bytes):
|
||||||
properties[key] = value.decode("utf-8")
|
properties[key] = value.decode("utf-8")
|
||||||
|
|
||||||
if not service.addresses:
|
addresses = service.addresses
|
||||||
|
|
||||||
|
if not addresses:
|
||||||
|
return None
|
||||||
|
if (host := _first_non_link_local_or_v6_address(addresses)) is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
address = service.addresses[0]
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"host": str(ip_address(address)),
|
"host": str(host),
|
||||||
"port": service.port,
|
"port": service.port,
|
||||||
"hostname": service.server,
|
"hostname": service.server,
|
||||||
"type": service.type,
|
"type": service.type,
|
||||||
@ -484,6 +486,15 @@ def info_from_service(service: AsyncServiceInfo) -> HaServiceInfo | None:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _first_non_link_local_or_v6_address(addresses: list[bytes]) -> str | None:
|
||||||
|
"""Return the first ipv6 or non-link local ipv4 address."""
|
||||||
|
for address in addresses:
|
||||||
|
ip_addr = ip_address(address)
|
||||||
|
if not ip_addr.is_link_local or ip_addr.version == 6:
|
||||||
|
return str(ip_addr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _suppress_invalid_properties(properties: dict) -> None:
|
def _suppress_invalid_properties(properties: dict) -> None:
|
||||||
"""Suppress any properties that will cause zeroconf to fail to startup."""
|
"""Suppress any properties that will cause zeroconf to fail to startup."""
|
||||||
|
|
||||||
|
@ -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.36.8"],
|
"requirements": ["zeroconf==0.36.9"],
|
||||||
"dependencies": ["network", "api"],
|
"dependencies": ["network", "api"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
|
@ -32,7 +32,7 @@ sqlalchemy==1.4.23
|
|||||||
voluptuous-serialize==2.4.0
|
voluptuous-serialize==2.4.0
|
||||||
voluptuous==0.12.2
|
voluptuous==0.12.2
|
||||||
yarl==1.6.3
|
yarl==1.6.3
|
||||||
zeroconf==0.36.8
|
zeroconf==0.36.9
|
||||||
|
|
||||||
pycryptodome>=3.6.6
|
pycryptodome>=3.6.6
|
||||||
|
|
||||||
|
@ -2465,7 +2465,7 @@ youtube_dl==2021.06.06
|
|||||||
zengge==0.2
|
zengge==0.2
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.36.8
|
zeroconf==0.36.9
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zha-quirks==0.0.62
|
zha-quirks==0.0.62
|
||||||
|
@ -1427,7 +1427,7 @@ yeelight==0.7.8
|
|||||||
youless-api==0.14
|
youless-api==0.14
|
||||||
|
|
||||||
# homeassistant.components.zeroconf
|
# homeassistant.components.zeroconf
|
||||||
zeroconf==0.36.8
|
zeroconf==0.36.9
|
||||||
|
|
||||||
# homeassistant.components.zha
|
# homeassistant.components.zha
|
||||||
zha-quirks==0.0.62
|
zha-quirks==0.0.62
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test Zeroconf component setup process."""
|
"""Test Zeroconf component setup process."""
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import call, patch
|
from unittest.mock import call, patch
|
||||||
|
|
||||||
from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange
|
from zeroconf import InterfaceChoice, IPVersion, ServiceStateChange
|
||||||
@ -39,7 +40,9 @@ def service_update_mock(ipv6, zeroconf, services, handlers, *, limit_service=Non
|
|||||||
handlers[0](zeroconf, service, f"_name.{service}", ServiceStateChange.Added)
|
handlers[0](zeroconf, service, f"_name.{service}", ServiceStateChange.Added)
|
||||||
|
|
||||||
|
|
||||||
def get_service_info_mock(service_type, name, *args, **kwargs):
|
def get_service_info_mock(
|
||||||
|
service_type: str, name: str, *args: Any, **kwargs: Any
|
||||||
|
) -> AsyncServiceInfo:
|
||||||
"""Return service info for get_service_info."""
|
"""Return service info for get_service_info."""
|
||||||
return AsyncServiceInfo(
|
return AsyncServiceInfo(
|
||||||
service_type,
|
service_type,
|
||||||
@ -53,7 +56,9 @@ def get_service_info_mock(service_type, name, *args, **kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_service_info_mock_without_an_address(service_type, name):
|
def get_service_info_mock_without_an_address(
|
||||||
|
service_type: str, name: str
|
||||||
|
) -> AsyncServiceInfo:
|
||||||
"""Return service info for get_service_info without any addresses."""
|
"""Return service info for get_service_info without any addresses."""
|
||||||
return AsyncServiceInfo(
|
return AsyncServiceInfo(
|
||||||
service_type,
|
service_type,
|
||||||
@ -633,6 +638,42 @@ async def test_info_from_service_with_addresses(hass):
|
|||||||
assert info is None
|
assert info is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_info_from_service_with_link_local_address_first(hass):
|
||||||
|
"""Test that the link local address is ignored."""
|
||||||
|
service_type = "_test._tcp.local."
|
||||||
|
service_info = get_service_info_mock(service_type, f"test.{service_type}")
|
||||||
|
service_info.addresses = ["169.254.12.3", "192.168.66.12"]
|
||||||
|
info = zeroconf.info_from_service(service_info)
|
||||||
|
assert info["host"] == "192.168.66.12"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_info_from_service_with_link_local_address_second(hass):
|
||||||
|
"""Test that the link local address is ignored."""
|
||||||
|
service_type = "_test._tcp.local."
|
||||||
|
service_info = get_service_info_mock(service_type, f"test.{service_type}")
|
||||||
|
service_info.addresses = ["192.168.66.12", "169.254.12.3"]
|
||||||
|
info = zeroconf.info_from_service(service_info)
|
||||||
|
assert info["host"] == "192.168.66.12"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_info_from_service_with_link_local_address_only(hass):
|
||||||
|
"""Test that the link local address is ignored."""
|
||||||
|
service_type = "_test._tcp.local."
|
||||||
|
service_info = get_service_info_mock(service_type, f"test.{service_type}")
|
||||||
|
service_info.addresses = ["169.254.12.3"]
|
||||||
|
info = zeroconf.info_from_service(service_info)
|
||||||
|
assert info is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_info_from_service_prefers_ipv4(hass):
|
||||||
|
"""Test that ipv4 addresses are preferred."""
|
||||||
|
service_type = "_test._tcp.local."
|
||||||
|
service_info = get_service_info_mock(service_type, f"test.{service_type}")
|
||||||
|
service_info.addresses = ["2001:db8:3333:4444:5555:6666:7777:8888", "192.168.66.12"]
|
||||||
|
info = zeroconf.info_from_service(service_info)
|
||||||
|
assert info["host"] == "192.168.66.12"
|
||||||
|
|
||||||
|
|
||||||
async def test_get_instance(hass, mock_async_zeroconf):
|
async def test_get_instance(hass, mock_async_zeroconf):
|
||||||
"""Test we get an instance."""
|
"""Test we get an instance."""
|
||||||
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user