diff --git a/homeassistant/components/elmax/config_flow.py b/homeassistant/components/elmax/config_flow.py index b8697552626..98e49cc8056 100644 --- a/homeassistant/components/elmax/config_flow.py +++ b/homeassistant/components/elmax/config_flow.py @@ -498,7 +498,11 @@ class ElmaxConfigFlow(ConfigFlow, domain=DOMAIN): self, discovery_info: ZeroconfServiceInfo ) -> ConfigFlowResult: """Handle device found via zeroconf.""" - host = discovery_info.host + host = ( + f"[{discovery_info.ip_address}]" + if discovery_info.ip_address.version == 6 + else str(discovery_info.ip_address) + ) https_port = ( int(discovery_info.port) if discovery_info.port is not None diff --git a/tests/components/elmax/__init__.py b/tests/components/elmax/__init__.py index e1a6728f1f5..391c3ccbfb2 100644 --- a/tests/components/elmax/__init__.py +++ b/tests/components/elmax/__init__.py @@ -30,6 +30,7 @@ MOCK_PANEL_PIN = "000000" MOCK_WRONG_PANEL_PIN = "000000" MOCK_PASSWORD = "password" MOCK_DIRECT_HOST = "1.1.1.1" +MOCK_DIRECT_HOST_V6 = "fd00::be2:54:34:2" MOCK_DIRECT_HOST_CHANGED = "2.2.2.2" MOCK_DIRECT_PORT = 443 MOCK_DIRECT_SSL = True diff --git a/tests/components/elmax/conftest.py b/tests/components/elmax/conftest.py index f8cf33ffe1a..02f01036996 100644 --- a/tests/components/elmax/conftest.py +++ b/tests/components/elmax/conftest.py @@ -18,6 +18,7 @@ import respx from . import ( MOCK_DIRECT_HOST, + MOCK_DIRECT_HOST_V6, MOCK_DIRECT_PORT, MOCK_DIRECT_SSL, MOCK_PANEL_ID, @@ -29,6 +30,7 @@ from tests.common import load_fixture MOCK_DIRECT_BASE_URI = ( f"{'https' if MOCK_DIRECT_SSL else 'http'}://{MOCK_DIRECT_HOST}:{MOCK_DIRECT_PORT}" ) +MOCK_DIRECT_BASE_URI_V6 = f"{'https' if MOCK_DIRECT_SSL else 'http'}://[{MOCK_DIRECT_HOST_V6}]:{MOCK_DIRECT_PORT}" @pytest.fixture(autouse=True) @@ -58,12 +60,16 @@ def httpx_mock_cloud_fixture() -> Generator[respx.MockRouter]: yield respx_mock +@pytest.fixture +def base_uri() -> str: + """Configure the base-uri for the respx mock fixtures.""" + return MOCK_DIRECT_BASE_URI + + @pytest.fixture(autouse=True) -def httpx_mock_direct_fixture() -> Generator[respx.MockRouter]: +def httpx_mock_direct_fixture(base_uri: str) -> Generator[respx.MockRouter]: """Configure httpx fixture for direct Panel-API communication.""" - with respx.mock( - base_url=MOCK_DIRECT_BASE_URI, assert_all_called=False - ) as respx_mock: + with respx.mock(base_url=base_uri, assert_all_called=False) as respx_mock: # Mock Login POST. login_route = respx_mock.post(f"/api/v2/{ENDPOINT_LOGIN}", name="login") diff --git a/tests/components/elmax/test_config_flow.py b/tests/components/elmax/test_config_flow.py index be89ee4d5d6..379cfa98bbc 100644 --- a/tests/components/elmax/test_config_flow.py +++ b/tests/components/elmax/test_config_flow.py @@ -1,8 +1,10 @@ """Tests for the Elmax config flow.""" +from ipaddress import IPv4Address, IPv6Address from unittest.mock import patch from elmax_api.exceptions import ElmaxBadLoginError, ElmaxBadPinError, ElmaxNetworkError +import pytest from homeassistant import config_entries from homeassistant.components.elmax.const import ( @@ -28,6 +30,7 @@ from . import ( MOCK_DIRECT_CERT, MOCK_DIRECT_HOST, MOCK_DIRECT_HOST_CHANGED, + MOCK_DIRECT_HOST_V6, MOCK_DIRECT_PORT, MOCK_DIRECT_SSL, MOCK_PANEL_ID, @@ -37,12 +40,27 @@ from . import ( MOCK_USERNAME, MOCK_WRONG_PANEL_PIN, ) +from .conftest import MOCK_DIRECT_BASE_URI_V6 from tests.common import MockConfigEntry MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo( - ip_address=MOCK_DIRECT_HOST, - ip_addresses=[MOCK_DIRECT_HOST], + ip_address=IPv4Address(address=MOCK_DIRECT_HOST), + ip_addresses=[IPv4Address(address=MOCK_DIRECT_HOST)], + hostname="VideoBox.local", + name="VideoBox", + port=443, + properties={ + "idl": MOCK_PANEL_ID, + "idr": MOCK_PANEL_ID, + "v1": "PHANTOM64PRO_GSM 11.9.844", + "v2": "4.9.13", + }, + type="_elmax-ssl._tcp", +) +MOCK_ZEROCONF_DISCOVERY_INFO_V6 = ZeroconfServiceInfo( + ip_address=IPv6Address(address=MOCK_DIRECT_HOST_V6), + ip_addresses=[IPv6Address(address=MOCK_DIRECT_HOST_V6)], hostname="VideoBox.local", name="VideoBox", port=443, @@ -55,8 +73,8 @@ MOCK_ZEROCONF_DISCOVERY_INFO = ZeroconfServiceInfo( type="_elmax-ssl._tcp", ) MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo( - ip_address=MOCK_DIRECT_HOST_CHANGED, - ip_addresses=[MOCK_DIRECT_HOST_CHANGED], + ip_address=IPv4Address(address=MOCK_DIRECT_HOST_CHANGED), + ip_addresses=[IPv4Address(address=MOCK_DIRECT_HOST_CHANGED)], hostname="VideoBox.local", name="VideoBox", port=443, @@ -69,8 +87,8 @@ MOCK_ZEROCONF_DISCOVERY_CHANGED_INFO = ZeroconfServiceInfo( type="_elmax-ssl._tcp", ) MOCK_ZEROCONF_DISCOVERY_INFO_NOT_SUPPORTED = ZeroconfServiceInfo( - ip_address=MOCK_DIRECT_HOST, - ip_addresses=[MOCK_DIRECT_HOST], + ip_address=IPv4Address(MOCK_DIRECT_HOST), + ip_addresses=[IPv4Address(MOCK_DIRECT_HOST)], hostname="VideoBox.local", name="VideoBox", port=443, @@ -194,6 +212,18 @@ async def test_zeroconf_discovery(hass: HomeAssistant) -> None: assert result["errors"] is None +async def test_zeroconf_discovery_ipv6(hass: HomeAssistant) -> None: + """Test discovery of Elmax local api panel.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=MOCK_ZEROCONF_DISCOVERY_INFO_V6, + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "zeroconf_setup" + assert result["errors"] is None + + async def test_zeroconf_setup_show_form(hass: HomeAssistant) -> None: """Test discovery shows a form when activated.""" result = await hass.config_entries.flow.async_init( @@ -230,6 +260,27 @@ async def test_zeroconf_setup(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.CREATE_ENTRY +@pytest.mark.parametrize("base_uri", [MOCK_DIRECT_BASE_URI_V6]) +async def test_zeroconf_ipv6_setup(hass: HomeAssistant) -> None: + """Test the successful creation of config entry via discovery flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=MOCK_ZEROCONF_DISCOVERY_INFO_V6, + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_ELMAX_PANEL_PIN: MOCK_PANEL_PIN, + CONF_ELMAX_MODE_DIRECT_SSL: MOCK_DIRECT_SSL, + }, + ) + + await hass.async_block_till_done() + assert result["type"] is FlowResultType.CREATE_ENTRY + + async def test_zeroconf_already_configured(hass: HomeAssistant) -> None: """Ensure local discovery aborts when same panel is already added to ha.""" MockConfigEntry(