Provide workaround for missing/disabled/broken IPv6 (#37887)
parent
53d6f4948e
commit
abe3e3094e
|
@ -9,6 +9,7 @@ from zeroconf import (
|
||||||
DNSPointer,
|
DNSPointer,
|
||||||
DNSRecord,
|
DNSRecord,
|
||||||
InterfaceChoice,
|
InterfaceChoice,
|
||||||
|
IPVersion,
|
||||||
NonUniqueNameException,
|
NonUniqueNameException,
|
||||||
ServiceBrowser,
|
ServiceBrowser,
|
||||||
ServiceInfo,
|
ServiceInfo,
|
||||||
|
@ -42,7 +43,9 @@ ZEROCONF_TYPE = "_home-assistant._tcp.local."
|
||||||
HOMEKIT_TYPE = "_hap._tcp.local."
|
HOMEKIT_TYPE = "_hap._tcp.local."
|
||||||
|
|
||||||
CONF_DEFAULT_INTERFACE = "default_interface"
|
CONF_DEFAULT_INTERFACE = "default_interface"
|
||||||
|
CONF_IPV6 = "ipv6"
|
||||||
DEFAULT_DEFAULT_INTERFACE = False
|
DEFAULT_DEFAULT_INTERFACE = False
|
||||||
|
DEFAULT_IPV6 = True
|
||||||
|
|
||||||
HOMEKIT_PROPERTIES = "properties"
|
HOMEKIT_PROPERTIES = "properties"
|
||||||
HOMEKIT_PAIRED_STATUS_FLAG = "sf"
|
HOMEKIT_PAIRED_STATUS_FLAG = "sf"
|
||||||
|
@ -54,7 +57,8 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_DEFAULT_INTERFACE, default=DEFAULT_DEFAULT_INTERFACE
|
CONF_DEFAULT_INTERFACE, default=DEFAULT_DEFAULT_INTERFACE
|
||||||
): cv.boolean
|
): cv.boolean,
|
||||||
|
vol.Optional(CONF_IPV6, default=DEFAULT_IPV6): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -68,11 +72,17 @@ async def async_get_instance(hass):
|
||||||
return await hass.async_add_executor_job(_get_instance, hass)
|
return await hass.async_add_executor_job(_get_instance, hass)
|
||||||
|
|
||||||
|
|
||||||
def _get_instance(hass, default_interface=False):
|
def _get_instance(hass, default_interface=False, ipv6=True):
|
||||||
"""Create an instance."""
|
"""Create an instance."""
|
||||||
logging.getLogger("zeroconf").setLevel(logging.NOTSET)
|
logging.getLogger("zeroconf").setLevel(logging.NOTSET)
|
||||||
args = [InterfaceChoice.Default] if default_interface else []
|
|
||||||
zeroconf = HaZeroconf(*args)
|
zc_args = {}
|
||||||
|
if default_interface:
|
||||||
|
zc_args["interfaces"] = InterfaceChoice.Default
|
||||||
|
if not ipv6:
|
||||||
|
zc_args["ip_version"] = IPVersion.V4Only
|
||||||
|
|
||||||
|
zeroconf = HaZeroconf(**zc_args)
|
||||||
|
|
||||||
def stop_zeroconf(_):
|
def stop_zeroconf(_):
|
||||||
"""Stop Zeroconf."""
|
"""Stop Zeroconf."""
|
||||||
|
@ -115,8 +125,13 @@ class HaZeroconf(Zeroconf):
|
||||||
|
|
||||||
def setup(hass, config):
|
def setup(hass, config):
|
||||||
"""Set up Zeroconf and make Home Assistant discoverable."""
|
"""Set up Zeroconf and make Home Assistant discoverable."""
|
||||||
|
zc_config = config.get(DOMAIN, {})
|
||||||
zeroconf = hass.data[DOMAIN] = _get_instance(
|
zeroconf = hass.data[DOMAIN] = _get_instance(
|
||||||
hass, config.get(DOMAIN, {}).get(CONF_DEFAULT_INTERFACE)
|
hass,
|
||||||
|
default_interface=zc_config.get(
|
||||||
|
CONF_DEFAULT_INTERFACE, DEFAULT_DEFAULT_INTERFACE
|
||||||
|
),
|
||||||
|
ipv6=zc_config.get(CONF_IPV6, DEFAULT_IPV6),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get instance UUID
|
# Get instance UUID
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
"""Test Zeroconf component setup process."""
|
"""Test Zeroconf component setup process."""
|
||||||
import pytest
|
import pytest
|
||||||
from zeroconf import InterfaceChoice, ServiceInfo, ServiceStateChange
|
from zeroconf import InterfaceChoice, IPVersion, ServiceInfo, ServiceStateChange
|
||||||
|
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
from homeassistant.components.zeroconf import CONF_DEFAULT_INTERFACE
|
from homeassistant.components.zeroconf import CONF_DEFAULT_INTERFACE, CONF_IPV6
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.generated import zeroconf as zc_gen
|
from homeassistant.generated import zeroconf as zc_gen
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@ -114,6 +114,43 @@ async def test_setup_without_default_interface(hass, mock_zeroconf):
|
||||||
assert mock_zeroconf.called_with()
|
assert mock_zeroconf.called_with()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_without_ipv6(hass, mock_zeroconf):
|
||||||
|
"""Test without ipv6."""
|
||||||
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
):
|
||||||
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: False}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_zeroconf.called_with(ip_version=IPVersion.V4Only)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_with_ipv6(hass, mock_zeroconf):
|
||||||
|
"""Test without ipv6."""
|
||||||
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
):
|
||||||
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {CONF_IPV6: True}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_zeroconf.called_with()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_with_ipv6_default(hass, mock_zeroconf):
|
||||||
|
"""Test without ipv6 as default."""
|
||||||
|
with patch.object(hass.config_entries.flow, "async_init"), patch.object(
|
||||||
|
zeroconf, "HaServiceBrowser", side_effect=service_update_mock
|
||||||
|
):
|
||||||
|
mock_zeroconf.get_service_info.side_effect = get_service_info_mock
|
||||||
|
assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}})
|
||||||
|
|
||||||
|
assert mock_zeroconf.called_with()
|
||||||
|
|
||||||
|
|
||||||
async def test_homekit_match_partial_space(hass, mock_zeroconf):
|
async def test_homekit_match_partial_space(hass, mock_zeroconf):
|
||||||
"""Test configured options for a device are loaded via config entry."""
|
"""Test configured options for a device are loaded via config entry."""
|
||||||
with patch.dict(
|
with patch.dict(
|
||||||
|
|
Loading…
Reference in New Issue