Netgear try all ports (#64170)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
pull/64374/head
starkillerOG 2022-01-18 20:02:43 +01:00 committed by GitHub
parent 88261c6c14
commit 37caa22a36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 24 deletions

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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."""

View File

@ -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%]"
}

View File

@ -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

View File

@ -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

View File

@ -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,