Netgear try all ports (#64170)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>pull/64374/head
parent
88261c6c14
commit
37caa22a36
|
@ -1,6 +1,8 @@
|
||||||
"""Support for Netgear routers."""
|
"""Support for Netgear routers."""
|
||||||
|
import logging
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
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.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
@ -9,6 +11,8 @@ from .const import DOMAIN, PLATFORMS
|
||||||
from .errors import CannotLoginException
|
from .errors import CannotLoginException
|
||||||
from .router import NetgearRouter
|
from .router import NetgearRouter
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Netgear component."""
|
"""Set up Netgear component."""
|
||||||
|
@ -18,6 +22,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
except CannotLoginException as ex:
|
except CannotLoginException as ex:
|
||||||
raise ConfigEntryNotReady from 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.setdefault(DOMAIN, {})
|
||||||
hass.data[DOMAIN][entry.unique_id] = router
|
hass.data[DOMAIN][entry.unique_id] = router
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,7 @@ def _discovery_schema_with_defaults(discovery_info):
|
||||||
|
|
||||||
|
|
||||||
def _user_schema_with_defaults(user_input):
|
def _user_schema_with_defaults(user_input):
|
||||||
user_schema = {
|
user_schema = {vol.Optional(CONF_HOST, default=user_input.get(CONF_HOST, "")): str}
|
||||||
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.update(_ordered_shared_schema(user_input))
|
user_schema.update(_ordered_shared_schema(user_input))
|
||||||
|
|
||||||
return vol.Schema(user_schema)
|
return vol.Schema(user_schema)
|
||||||
|
@ -169,8 +165,8 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
return await self._show_setup_form()
|
return await self._show_setup_form()
|
||||||
|
|
||||||
host = user_input.get(CONF_HOST, self.placeholders[CONF_HOST])
|
host = user_input.get(CONF_HOST, self.placeholders[CONF_HOST])
|
||||||
port = user_input.get(CONF_PORT, self.placeholders[CONF_PORT])
|
port = self.placeholders[CONF_PORT]
|
||||||
ssl = user_input.get(CONF_SSL, self.placeholders[CONF_SSL])
|
ssl = self.placeholders[CONF_SSL]
|
||||||
username = user_input.get(CONF_USERNAME, self.placeholders[CONF_USERNAME])
|
username = user_input.get(CONF_USERNAME, self.placeholders[CONF_USERNAME])
|
||||||
password = user_input[CONF_PASSWORD]
|
password = user_input[CONF_PASSWORD]
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -196,8 +192,8 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
CONF_USERNAME: username,
|
CONF_USERNAME: username,
|
||||||
CONF_PASSWORD: password,
|
CONF_PASSWORD: password,
|
||||||
CONF_HOST: host,
|
CONF_HOST: host,
|
||||||
CONF_PORT: port,
|
CONF_PORT: api.port,
|
||||||
CONF_SSL: ssl,
|
CONF_SSL: api.ssl,
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.get("ModelName") is not None and info.get("DeviceName") is not None:
|
if info.get("ModelName") is not None and info.get("DeviceName") is not None:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"domain": "netgear",
|
"domain": "netgear",
|
||||||
"name": "NETGEAR",
|
"name": "NETGEAR",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/netgear",
|
"documentation": "https://www.home-assistant.io/integrations/netgear",
|
||||||
"requirements": ["pynetgear==0.8.0"],
|
"requirements": ["pynetgear==0.9.0"],
|
||||||
"codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"],
|
"codeowners": ["@hacf-fr", "@Quentame", "@starkillerOG"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
|
|
@ -52,7 +52,7 @@ def get_api(
|
||||||
"""Get the Netgear API and login to it."""
|
"""Get the Netgear API and login to it."""
|
||||||
api: Netgear = Netgear(password, host, username, port, ssl)
|
api: Netgear = Netgear(password, host, username, port, ssl)
|
||||||
|
|
||||||
if not api.login():
|
if not api.login_try_port():
|
||||||
raise CannotLoginException
|
raise CannotLoginException
|
||||||
|
|
||||||
return api
|
return api
|
||||||
|
@ -244,6 +244,16 @@ class NetgearRouter:
|
||||||
"""Event specific per Netgear entry to signal updates in devices."""
|
"""Event specific per Netgear entry to signal updates in devices."""
|
||||||
return f"{DOMAIN}-{self._host}-device-update"
|
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):
|
class NetgearDeviceEntity(Entity):
|
||||||
"""Base class for a device connected to a Netgear router."""
|
"""Base class for a device connected to a Netgear router."""
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
"description": "Default host: {host}\nDefault port: {port}\nDefault username: {username}",
|
"description": "Default host: {host}\nDefault port: {port}\nDefault username: {username}",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "[%key:common::config_flow::data::host%] (Optional)",
|
"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)",
|
"username": "[%key:common::config_flow::data::username%] (Optional)",
|
||||||
"password": "[%key:common::config_flow::data::password%]"
|
"password": "[%key:common::config_flow::data::password%]"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1690,7 +1690,7 @@ pymyq==3.1.4
|
||||||
pymysensors==0.22.1
|
pymysensors==0.22.1
|
||||||
|
|
||||||
# homeassistant.components.netgear
|
# homeassistant.components.netgear
|
||||||
pynetgear==0.8.0
|
pynetgear==0.9.0
|
||||||
|
|
||||||
# homeassistant.components.netio
|
# homeassistant.components.netio
|
||||||
pynetio==0.1.9.1
|
pynetio==0.1.9.1
|
||||||
|
|
|
@ -1059,7 +1059,7 @@ pymyq==3.1.4
|
||||||
pymysensors==0.22.1
|
pymysensors==0.22.1
|
||||||
|
|
||||||
# homeassistant.components.netgear
|
# homeassistant.components.netgear
|
||||||
pynetgear==0.8.0
|
pynetgear==0.9.0
|
||||||
|
|
||||||
# homeassistant.components.nina
|
# homeassistant.components.nina
|
||||||
pynina==0.1.4
|
pynina==0.1.4
|
||||||
|
|
|
@ -69,6 +69,20 @@ def mock_controller_service():
|
||||||
"homeassistant.components.netgear.async_setup_entry", return_value=True
|
"homeassistant.components.netgear.async_setup_entry", return_value=True
|
||||||
), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
|
), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
|
||||||
service_mock.return_value.get_info = Mock(return_value=ROUTER_INFOS)
|
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
|
yield service_mock
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,6 +95,8 @@ def mock_controller_service_incomplete():
|
||||||
"homeassistant.components.netgear.async_setup_entry", return_value=True
|
"homeassistant.components.netgear.async_setup_entry", return_value=True
|
||||||
), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
|
), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
|
||||||
service_mock.return_value.get_info = Mock(return_value=router_infos)
|
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
|
yield service_mock
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +104,7 @@ def mock_controller_service_incomplete():
|
||||||
def mock_controller_service_failed():
|
def mock_controller_service_failed():
|
||||||
"""Mock a failed service."""
|
"""Mock a failed service."""
|
||||||
with patch("homeassistant.components.netgear.router.Netgear") as service_mock:
|
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)
|
service_mock.return_value.get_info = Mock(return_value=None)
|
||||||
yield service_mock
|
yield service_mock
|
||||||
|
|
||||||
|
@ -106,8 +122,6 @@ async def test_user(hass, service):
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
CONF_HOST: HOST,
|
CONF_HOST: HOST,
|
||||||
CONF_PORT: PORT,
|
|
||||||
CONF_SSL: SSL,
|
|
||||||
CONF_USERNAME: USERNAME,
|
CONF_USERNAME: USERNAME,
|
||||||
CONF_PASSWORD: PASSWORD,
|
CONF_PASSWORD: PASSWORD,
|
||||||
},
|
},
|
||||||
|
@ -135,8 +149,6 @@ async def test_user_connect_error(hass, service_failed):
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
CONF_HOST: HOST,
|
CONF_HOST: HOST,
|
||||||
CONF_PORT: PORT,
|
|
||||||
CONF_SSL: SSL,
|
|
||||||
CONF_USERNAME: USERNAME,
|
CONF_USERNAME: USERNAME,
|
||||||
CONF_PASSWORD: PASSWORD,
|
CONF_PASSWORD: PASSWORD,
|
||||||
},
|
},
|
||||||
|
@ -159,8 +171,6 @@ async def test_user_incomplete_info(hass, service_incomplete):
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{
|
{
|
||||||
CONF_HOST: HOST,
|
CONF_HOST: HOST,
|
||||||
CONF_PORT: PORT,
|
|
||||||
CONF_SSL: SSL,
|
|
||||||
CONF_USERNAME: USERNAME,
|
CONF_USERNAME: USERNAME,
|
||||||
CONF_PASSWORD: PASSWORD,
|
CONF_PASSWORD: PASSWORD,
|
||||||
},
|
},
|
||||||
|
@ -256,7 +266,7 @@ async def test_ssdp(hass, service):
|
||||||
assert result["data"][CONF_PASSWORD] == PASSWORD
|
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."""
|
"""Test ssdp step with port 5555."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
|
Loading…
Reference in New Issue