From 37caa22a36854790b7aabe92aed755f6f7a7e023 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Tue, 18 Jan 2022 20:02:43 +0100 Subject: [PATCH] Netgear try all ports (#64170) Co-authored-by: Paulus Schoutsen --- homeassistant/components/netgear/__init__.py | 21 ++++++++++++++- .../components/netgear/config_flow.py | 14 ++++------ .../components/netgear/manifest.json | 2 +- homeassistant/components/netgear/router.py | 12 ++++++++- homeassistant/components/netgear/strings.json | 2 -- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/netgear/test_config_flow.py | 26 +++++++++++++------ 8 files changed, 57 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/netgear/__init__.py b/homeassistant/components/netgear/__init__.py index 954658a5bef..089469acef2 100644 --- a/homeassistant/components/netgear/__init__.py +++ b/homeassistant/components/netgear/__init__.py @@ -1,6 +1,8 @@ """Support for Netgear routers.""" +import logging + from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_HOST +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SSL from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import device_registry as dr @@ -9,6 +11,8 @@ from .const import DOMAIN, PLATFORMS from .errors import CannotLoginException from .router import NetgearRouter +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Netgear component.""" @@ -18,6 +22,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except CannotLoginException as ex: raise ConfigEntryNotReady from ex + port = entry.data.get(CONF_PORT) + ssl = entry.data.get(CONF_SSL) + if port != router.port or ssl != router.ssl: + data = entry.data.copy() + data[CONF_PORT] = router.port + data[CONF_SSL] = router.ssl + hass.config_entries.async_update_entry(entry, data=data) + _LOGGER.info( + "Netgear port-SSL combination updated from (%i, %r) to (%i, %r), this should only occur after a firmware update", + port, + ssl, + router.port, + router.ssl, + ) + hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.unique_id] = router diff --git a/homeassistant/components/netgear/config_flow.py b/homeassistant/components/netgear/config_flow.py index efc9fdec553..791fbd26bd3 100644 --- a/homeassistant/components/netgear/config_flow.py +++ b/homeassistant/components/netgear/config_flow.py @@ -38,11 +38,7 @@ def _discovery_schema_with_defaults(discovery_info): def _user_schema_with_defaults(user_input): - user_schema = { - vol.Optional(CONF_HOST, default=user_input.get(CONF_HOST, "")): str, - vol.Optional(CONF_PORT, default=user_input.get(CONF_PORT, DEFAULT_PORT)): int, - vol.Optional(CONF_SSL, default=user_input.get(CONF_SSL, False)): bool, - } + user_schema = {vol.Optional(CONF_HOST, default=user_input.get(CONF_HOST, "")): str} user_schema.update(_ordered_shared_schema(user_input)) return vol.Schema(user_schema) @@ -169,8 +165,8 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): return await self._show_setup_form() host = user_input.get(CONF_HOST, self.placeholders[CONF_HOST]) - port = user_input.get(CONF_PORT, self.placeholders[CONF_PORT]) - ssl = user_input.get(CONF_SSL, self.placeholders[CONF_SSL]) + port = self.placeholders[CONF_PORT] + ssl = self.placeholders[CONF_SSL] username = user_input.get(CONF_USERNAME, self.placeholders[CONF_USERNAME]) password = user_input[CONF_PASSWORD] if not username: @@ -196,8 +192,8 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): CONF_USERNAME: username, CONF_PASSWORD: password, CONF_HOST: host, - CONF_PORT: port, - CONF_SSL: ssl, + CONF_PORT: api.port, + CONF_SSL: api.ssl, } if info.get("ModelName") is not None and info.get("DeviceName") is not None: diff --git a/homeassistant/components/netgear/manifest.json b/homeassistant/components/netgear/manifest.json index 1f4a15f1de1..e8af27cb3b6 100644 --- a/homeassistant/components/netgear/manifest.json +++ b/homeassistant/components/netgear/manifest.json @@ -2,7 +2,7 @@ "domain": "netgear", "name": "NETGEAR", "documentation": "https://www.home-assistant.io/integrations/netgear", - "requirements": ["pynetgear==0.8.0"], + "requirements": ["pynetgear==0.9.0"], "codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"], "iot_class": "local_polling", "config_flow": true, diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index dbfd0439a85..5226218a623 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -52,7 +52,7 @@ def get_api( """Get the Netgear API and login to it.""" api: Netgear = Netgear(password, host, username, port, ssl) - if not api.login(): + if not api.login_try_port(): raise CannotLoginException return api @@ -244,6 +244,16 @@ class NetgearRouter: """Event specific per Netgear entry to signal updates in devices.""" return f"{DOMAIN}-{self._host}-device-update" + @property + def port(self) -> int: + """Port used by the API.""" + return self._api.port + + @property + def ssl(self) -> bool: + """SSL used by the API.""" + return self._api.ssl + class NetgearDeviceEntity(Entity): """Base class for a device connected to a Netgear router.""" diff --git a/homeassistant/components/netgear/strings.json b/homeassistant/components/netgear/strings.json index 318864291f1..ce575277dad 100644 --- a/homeassistant/components/netgear/strings.json +++ b/homeassistant/components/netgear/strings.json @@ -5,8 +5,6 @@ "description": "Default host: {host}\nDefault port: {port}\nDefault username: {username}", "data": { "host": "[%key:common::config_flow::data::host%] (Optional)", - "port": "[%key:common::config_flow::data::port%] (Optional)", - "ssl": "[%key:common::config_flow::data::ssl%]", "username": "[%key:common::config_flow::data::username%] (Optional)", "password": "[%key:common::config_flow::data::password%]" } diff --git a/requirements_all.txt b/requirements_all.txt index 26ac0903b29..ec95ded24f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1690,7 +1690,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.8.0 +pynetgear==0.9.0 # homeassistant.components.netio pynetio==0.1.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cccfcb7b29d..3d06f7de7ad 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1059,7 +1059,7 @@ pymyq==3.1.4 pymysensors==0.22.1 # homeassistant.components.netgear -pynetgear==0.8.0 +pynetgear==0.9.0 # homeassistant.components.nina pynina==0.1.4 diff --git a/tests/components/netgear/test_config_flow.py b/tests/components/netgear/test_config_flow.py index d1109af2d9a..33c634e250a 100644 --- a/tests/components/netgear/test_config_flow.py +++ b/tests/components/netgear/test_config_flow.py @@ -69,6 +69,20 @@ def mock_controller_service(): "homeassistant.components.netgear.async_setup_entry", return_value=True ), patch("homeassistant.components.netgear.router.Netgear") as service_mock: service_mock.return_value.get_info = Mock(return_value=ROUTER_INFOS) + service_mock.return_value.port = 80 + service_mock.return_value.ssl = False + yield service_mock + + +@pytest.fixture(name="service_5555") +def mock_controller_service_5555(): + """Mock a successful service.""" + with patch( + "homeassistant.components.netgear.async_setup_entry", return_value=True + ), patch("homeassistant.components.netgear.router.Netgear") as service_mock: + service_mock.return_value.get_info = Mock(return_value=ROUTER_INFOS) + service_mock.return_value.port = 5555 + service_mock.return_value.ssl = True yield service_mock @@ -81,6 +95,8 @@ def mock_controller_service_incomplete(): "homeassistant.components.netgear.async_setup_entry", return_value=True ), patch("homeassistant.components.netgear.router.Netgear") as service_mock: service_mock.return_value.get_info = Mock(return_value=router_infos) + service_mock.return_value.port = 80 + service_mock.return_value.ssl = False yield service_mock @@ -88,7 +104,7 @@ def mock_controller_service_incomplete(): def mock_controller_service_failed(): """Mock a failed service.""" with patch("homeassistant.components.netgear.router.Netgear") as service_mock: - service_mock.return_value.login = Mock(return_value=None) + service_mock.return_value.login_try_port = Mock(return_value=None) service_mock.return_value.get_info = Mock(return_value=None) yield service_mock @@ -106,8 +122,6 @@ async def test_user(hass, service): result["flow_id"], { CONF_HOST: HOST, - CONF_PORT: PORT, - CONF_SSL: SSL, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD, }, @@ -135,8 +149,6 @@ async def test_user_connect_error(hass, service_failed): result["flow_id"], { CONF_HOST: HOST, - CONF_PORT: PORT, - CONF_SSL: SSL, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD, }, @@ -159,8 +171,6 @@ async def test_user_incomplete_info(hass, service_incomplete): result["flow_id"], { CONF_HOST: HOST, - CONF_PORT: PORT, - CONF_SSL: SSL, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD, }, @@ -256,7 +266,7 @@ async def test_ssdp(hass, service): assert result["data"][CONF_PASSWORD] == PASSWORD -async def test_ssdp_port_5555(hass, service): +async def test_ssdp_port_5555(hass, service_5555): """Test ssdp step with port 5555.""" result = await hass.config_entries.flow.async_init( DOMAIN,