From ab3018b06f9bc550ea667cf3cc51d6c986f8fdb8 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:59:26 -0400 Subject: [PATCH 1/4] Automatically correct IP addresses surrounded by brackets --- homeassistant/components/zha/__init__.py | 13 +++++++++---- tests/components/zha/test_init.py | 9 +++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 5607cabffea19..fafc7fcb320dc 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -3,6 +3,7 @@ import copy import logging import os +import re import voluptuous as vol from zhaquirks import setup as setup_quirks @@ -40,6 +41,10 @@ ) from .core.discovery import GROUP_PROBE +BRACKETED_IP_ADDRESS_REGEX = re.compile( + r"^socket://\[(?P\d+\.\d+\.\d+\.\d+)\]:(?P\d+)$" +) + DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({vol.Optional(CONF_TYPE): cv.string}) ZHA_CONFIG_SCHEMA = { vol.Optional(CONF_BAUDRATE): cv.positive_int, @@ -91,13 +96,13 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b Will automatically load components to support devices found on the network. """ - # Strip whitespace around `socket://` URIs, this is no longer accepted by zigpy - # This will be removed in 2023.7.0 + # Remove brackets around IP addresses, this no longer works in CPython 3.11.4 + # This will be removed in 2023.11.0 path = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] data = copy.deepcopy(dict(config_entry.data)) - if path.startswith("socket://") and path != path.strip(): - data[CONF_DEVICE][CONF_DEVICE_PATH] = path.strip() + if BRACKETED_IP_ADDRESS_REGEX.match(path): + data[CONF_DEVICE][CONF_DEVICE_PATH] = path.replace("[", "").replace("]", "") hass.config_entries.async_update_entry(config_entry, data=data) zha_data = hass.data.setdefault(DATA_ZHA, {}) diff --git a/tests/components/zha/test_init.py b/tests/components/zha/test_init.py index 23a76de4c2504..abee39c2b3294 100644 --- a/tests/components/zha/test_init.py +++ b/tests/components/zha/test_init.py @@ -115,18 +115,19 @@ async def test_config_depreciation(hass: HomeAssistant, zha_config) -> None: ("path", "cleaned_path"), [ ("/dev/path1", "/dev/path1"), - ("/dev/path1 ", "/dev/path1 "), - ("socket://dev/path1 ", "socket://dev/path1"), + ("/dev/path1[asd]", "/dev/path1[asd]"), + ("socket://1.2.3.4:5678", "socket://1.2.3.4:5678"), + ("socket://[1.2.3.4]:5678", "socket://1.2.3.4:5678"), ], ) @patch("homeassistant.components.zha.setup_quirks", Mock(return_value=True)) @patch( "homeassistant.components.zha.websocket_api.async_load_api", Mock(return_value=True) ) -async def test_setup_with_v3_spaces_in_uri( +async def test_setup_with_v3_brackets_in_uri( hass: HomeAssistant, path: str, cleaned_path: str ) -> None: - """Test migration of config entry from v3 with spaces after `socket://` URI.""" + """Test migration of config entry from v3 with brackets around the IP address.""" config_entry_v3 = MockConfigEntry( domain=DOMAIN, data={ From b01df3932d91d9f3d78de9111cd26162209858e5 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:05:09 -0400 Subject: [PATCH 2/4] Simplify regex --- homeassistant/components/zha/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index fafc7fcb320dc..7f05e30ac0df0 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -41,9 +41,7 @@ ) from .core.discovery import GROUP_PROBE -BRACKETED_IP_ADDRESS_REGEX = re.compile( - r"^socket://\[(?P\d+\.\d+\.\d+\.\d+)\]:(?P\d+)$" -) +BRACKETED_IP_ADDRESS_REGEX = re.compile(r"^(socket|tcp)://\[\d+\.\d+\.\d+\.\d+\]:\d+$") DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({vol.Optional(CONF_TYPE): cv.string}) ZHA_CONFIG_SCHEMA = { From a12b389fb2655cb0a27fa9c3dd3d315177ce37ad Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Wed, 5 Jul 2023 14:08:52 -0400 Subject: [PATCH 3/4] Move pattern inline --- homeassistant/components/zha/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 7f05e30ac0df0..27f8f6cce0d47 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -41,8 +41,6 @@ ) from .core.discovery import GROUP_PROBE -BRACKETED_IP_ADDRESS_REGEX = re.compile(r"^(socket|tcp)://\[\d+\.\d+\.\d+\.\d+\]:\d+$") - DEVICE_CONFIG_SCHEMA_ENTRY = vol.Schema({vol.Optional(CONF_TYPE): cv.string}) ZHA_CONFIG_SCHEMA = { vol.Optional(CONF_BAUDRATE): cv.positive_int, @@ -99,7 +97,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b path = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] data = copy.deepcopy(dict(config_entry.data)) - if BRACKETED_IP_ADDRESS_REGEX.match(path): + if re.match(r"^(socket|tcp)://\[\d+\.\d+\.\d+\.\d+\]:\d+$", path): data[CONF_DEVICE][CONF_DEVICE_PATH] = path.replace("[", "").replace("]", "") hass.config_entries.async_update_entry(config_entry, data=data) From 90a957106d49b9b68b549418e71c98d8836020af Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Thu, 6 Jul 2023 11:36:22 -0400 Subject: [PATCH 4/4] Maintain old behavior of stripping whitespace --- homeassistant/components/zha/__init__.py | 19 +++++++++++++++++-- tests/components/zha/test_init.py | 11 +++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 27f8f6cce0d47..8a81648b580ae 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -86,6 +86,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: return True +def _clean_serial_port_path(path: str) -> str: + """Clean the serial port path, applying corrections where necessary.""" + + if path.startswith("socket://"): + path = path.strip() + + # Removes extraneous brackets from IP addresses (they don't parse in CPython 3.11.4) + if re.match(r"^socket://\[\d+\.\d+\.\d+\.\d+\]:\d+$", path): + path = path.replace("[", "").replace("]", "") + + return path + + async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up ZHA. @@ -95,10 +108,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b # Remove brackets around IP addresses, this no longer works in CPython 3.11.4 # This will be removed in 2023.11.0 path = config_entry.data[CONF_DEVICE][CONF_DEVICE_PATH] + cleaned_path = _clean_serial_port_path(path) data = copy.deepcopy(dict(config_entry.data)) - if re.match(r"^(socket|tcp)://\[\d+\.\d+\.\d+\.\d+\]:\d+$", path): - data[CONF_DEVICE][CONF_DEVICE_PATH] = path.replace("[", "").replace("]", "") + if path != cleaned_path: + _LOGGER.debug("Cleaned serial port path %r -> %r", path, cleaned_path) + data[CONF_DEVICE][CONF_DEVICE_PATH] = cleaned_path hass.config_entries.async_update_entry(config_entry, data=data) zha_data = hass.data.setdefault(DATA_ZHA, {}) diff --git a/tests/components/zha/test_init.py b/tests/components/zha/test_init.py index abee39c2b3294..24ee63fb3d506 100644 --- a/tests/components/zha/test_init.py +++ b/tests/components/zha/test_init.py @@ -114,20 +114,27 @@ async def test_config_depreciation(hass: HomeAssistant, zha_config) -> None: @pytest.mark.parametrize( ("path", "cleaned_path"), [ + # No corrections ("/dev/path1", "/dev/path1"), ("/dev/path1[asd]", "/dev/path1[asd]"), + ("/dev/path1 ", "/dev/path1 "), ("socket://1.2.3.4:5678", "socket://1.2.3.4:5678"), + # Brackets around URI ("socket://[1.2.3.4]:5678", "socket://1.2.3.4:5678"), + # Spaces + ("socket://dev/path1 ", "socket://dev/path1"), + # Both + ("socket://[1.2.3.4]:5678 ", "socket://1.2.3.4:5678"), ], ) @patch("homeassistant.components.zha.setup_quirks", Mock(return_value=True)) @patch( "homeassistant.components.zha.websocket_api.async_load_api", Mock(return_value=True) ) -async def test_setup_with_v3_brackets_in_uri( +async def test_setup_with_v3_cleaning_uri( hass: HomeAssistant, path: str, cleaned_path: str ) -> None: - """Test migration of config entry from v3 with brackets around the IP address.""" + """Test migration of config entry from v3, applying corrections to the port path.""" config_entry_v3 = MockConfigEntry( domain=DOMAIN, data={