Rework config flow in AVM Fritz!Tools (#67767)

This commit is contained in:
Michael 2022-03-30 01:53:53 +02:00 committed by GitHub
parent 603601b32e
commit 94c5dbfd16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 66 deletions

View File

@ -1,7 +1,7 @@
"""Support for AVM Fritz!Box functions.""" """Support for AVM Fritz!Box functions."""
import logging import logging
from fritzconnection.core.exceptions import FritzSecurityError from fritzconnection.core.exceptions import FritzConnectionException
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
@ -28,10 +28,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
try: try:
await avm_wrapper.async_setup(entry.options) await avm_wrapper.async_setup(entry.options)
except FritzSecurityError as ex:
raise ConfigEntryAuthFailed from ex
except FRITZ_EXCEPTIONS as ex: except FRITZ_EXCEPTIONS as ex:
raise ConfigEntryNotReady from ex raise ConfigEntryNotReady from ex
except FritzConnectionException as ex:
raise ConfigEntryAuthFailed from ex
if ( if (
"X_AVM-DE_UPnP1" in avm_wrapper.connection.services "X_AVM-DE_UPnP1" in avm_wrapper.connection.services

View File

@ -6,6 +6,7 @@ import socket
from typing import Any from typing import Any
from urllib.parse import ParseResult, urlparse from urllib.parse import ParseResult, urlparse
from fritzconnection import FritzConnection
from fritzconnection.core.exceptions import FritzConnectionException, FritzSecurityError from fritzconnection.core.exceptions import FritzConnectionException, FritzSecurityError
import voluptuous as vol import voluptuous as vol
@ -19,7 +20,6 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNA
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from .common import AvmWrapper
from .const import ( from .const import (
CONF_OLD_DISCOVERY, CONF_OLD_DISCOVERY,
DEFAULT_CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY,
@ -49,29 +49,25 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize FRITZ!Box Tools flow.""" """Initialize FRITZ!Box Tools flow."""
self._host: str | None = None self._host: str | None = None
self._entry: ConfigEntry self._entry: ConfigEntry | None = None
self._name: str self._name: str = ""
self._password: str self._password: str = ""
self._port: int | None = None self._port: int | None = None
self._username: str self._username: str = ""
self.avm_wrapper: AvmWrapper self._model: str = ""
async def fritz_tools_init(self) -> str | None: def fritz_tools_init(self) -> str | None:
"""Initialize FRITZ!Box Tools class.""" """Initialize FRITZ!Box Tools class."""
if not self._host or not self._port:
return None
self.avm_wrapper = AvmWrapper(
hass=self.hass,
host=self._host,
port=self._port,
username=self._username,
password=self._password,
)
try: try:
await self.avm_wrapper.async_setup() connection = FritzConnection(
address=self._host,
port=self._port,
user=self._username,
password=self._password,
timeout=60.0,
pool_maxsize=30,
)
except FritzSecurityError: except FritzSecurityError:
return ERROR_AUTH_INVALID return ERROR_AUTH_INVALID
except FritzConnectionException: except FritzConnectionException:
@ -80,9 +76,11 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")
return ERROR_UNKNOWN return ERROR_UNKNOWN
self._model = connection.call_action("DeviceInfo:1", "GetInfo")["NewModelName"]
if ( if (
"X_AVM-DE_UPnP1" in self.avm_wrapper.connection.services "X_AVM-DE_UPnP1" in connection.services
and not (await self.avm_wrapper.async_get_upnp_configuration())["NewEnable"] and not connection.call_action("X_AVM-DE_UPnP1", "GetInfo")["NewEnable"]
): ):
return ERROR_UPNP_NOT_CONFIGURED return ERROR_UPNP_NOT_CONFIGURED
@ -109,10 +107,10 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_create_entry( return self.async_create_entry(
title=self._name, title=self._name,
data={ data={
CONF_HOST: self.avm_wrapper.host, CONF_HOST: self._host,
CONF_PASSWORD: self.avm_wrapper.password, CONF_PASSWORD: self._password,
CONF_PORT: self.avm_wrapper.port, CONF_PORT: self._port,
CONF_USERNAME: self.avm_wrapper.username, CONF_USERNAME: self._username,
}, },
options={ options={
CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds(), CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds(),
@ -163,7 +161,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_USERNAME] self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD] self._password = user_input[CONF_PASSWORD]
error = await self.fritz_tools_init() error = await self.hass.async_add_executor_job(self.fritz_tools_init)
if error: if error:
errors["base"] = error errors["base"] = error
@ -213,8 +211,8 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_USERNAME] self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD] self._password = user_input[CONF_PASSWORD]
if not (error := await self.fritz_tools_init()): if not (error := await self.hass.async_add_executor_job(self.fritz_tools_init)):
self._name = self.avm_wrapper.model self._name = self._model
if await self.async_check_configured_entry(): if await self.async_check_configured_entry():
error = "already_configured" error = "already_configured"
@ -226,10 +224,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_reauth(self, data: dict[str, Any]) -> FlowResult: async def async_step_reauth(self, data: dict[str, Any]) -> FlowResult:
"""Handle flow upon an API authentication error.""" """Handle flow upon an API authentication error."""
if cfg_entry := self.hass.config_entries.async_get_entry( self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
self.context["entry_id"]
):
self._entry = cfg_entry
self._host = data[CONF_HOST] self._host = data[CONF_HOST]
self._port = data[CONF_PORT] self._port = data[CONF_PORT]
self._username = data[CONF_USERNAME] self._username = data[CONF_USERNAME]
@ -265,11 +260,12 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_USERNAME] self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD] self._password = user_input[CONF_PASSWORD]
if error := await self.fritz_tools_init(): if error := await self.hass.async_add_executor_job(self.fritz_tools_init):
return self._show_setup_form_reauth_confirm( return self._show_setup_form_reauth_confirm(
user_input=user_input, errors={"base": error} user_input=user_input, errors={"base": error}
) )
assert isinstance(self._entry, ConfigEntry)
self.hass.config_entries.async_update_entry( self.hass.config_entries.async_update_entry(
self._entry, self._entry,
data={ data={

View File

@ -38,9 +38,9 @@ from tests.common import MockConfigEntry
async def test_user(hass: HomeAssistant, fc_class_mock, mock_get_source_ip): async def test_user(hass: HomeAssistant, fc_class_mock, mock_get_source_ip):
"""Test starting a flow by user.""" """Test starting a flow by user."""
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info", "homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=MOCK_FIRMWARE_INFO, return_value=MOCK_FIRMWARE_INFO,
), patch( ), patch(
@ -91,9 +91,9 @@ async def test_user_already_configured(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info", "homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=MOCK_FIRMWARE_INFO, return_value=MOCK_FIRMWARE_INFO,
), patch( ), patch(
@ -134,9 +134,9 @@ async def test_exception_security(hass: HomeAssistant, mock_get_source_ip):
assert result["step_id"] == "user" assert result["step_id"] == "user"
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=FritzSecurityError, side_effect=FritzSecurityError,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=MOCK_USER_DATA result["flow_id"], user_input=MOCK_USER_DATA
@ -157,9 +157,9 @@ async def test_exception_connection(hass: HomeAssistant, mock_get_source_ip):
assert result["step_id"] == "user" assert result["step_id"] == "user"
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=FritzConnectionException, side_effect=FritzConnectionException,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=MOCK_USER_DATA result["flow_id"], user_input=MOCK_USER_DATA
@ -180,9 +180,9 @@ async def test_exception_unknown(hass: HomeAssistant, mock_get_source_ip):
assert result["step_id"] == "user" assert result["step_id"] == "user"
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=OSError, side_effect=OSError,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input=MOCK_USER_DATA result["flow_id"], user_input=MOCK_USER_DATA
@ -202,9 +202,9 @@ async def test_reauth_successful(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info", "homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=MOCK_FIRMWARE_INFO, return_value=MOCK_FIRMWARE_INFO,
), patch( ), patch(
@ -252,9 +252,9 @@ async def test_reauth_not_successful(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=FritzConnectionException, side_effect=FritzConnectionException,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
@ -291,9 +291,9 @@ async def test_ssdp_already_configured(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IPS["fritz.box"], return_value=MOCK_IPS["fritz.box"],
): ):
@ -318,9 +318,9 @@ async def test_ssdp_already_configured_host(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IPS["fritz.box"], return_value=MOCK_IPS["fritz.box"],
): ):
@ -345,9 +345,9 @@ async def test_ssdp_already_configured_host_uuid(
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.config_flow.socket.gethostbyname", "homeassistant.components.fritz.config_flow.socket.gethostbyname",
return_value=MOCK_IPS["fritz.box"], return_value=MOCK_IPS["fritz.box"],
): ):
@ -364,9 +364,9 @@ async def test_ssdp_already_in_progress_host(
): ):
"""Test starting a flow from discovery twice.""" """Test starting a flow from discovery twice."""
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_SSDP}, data=MOCK_SSDP_DATA DOMAIN, context={"source": SOURCE_SSDP}, data=MOCK_SSDP_DATA
@ -387,9 +387,9 @@ async def test_ssdp_already_in_progress_host(
async def test_ssdp(hass: HomeAssistant, fc_class_mock, mock_get_source_ip): async def test_ssdp(hass: HomeAssistant, fc_class_mock, mock_get_source_ip):
"""Test starting a flow from discovery.""" """Test starting a flow from discovery."""
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info", "homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=MOCK_FIRMWARE_INFO, return_value=MOCK_FIRMWARE_INFO,
), patch( ), patch(
@ -430,9 +430,9 @@ async def test_ssdp(hass: HomeAssistant, fc_class_mock, mock_get_source_ip):
async def test_ssdp_exception(hass: HomeAssistant, mock_get_source_ip): async def test_ssdp_exception(hass: HomeAssistant, mock_get_source_ip):
"""Test starting a flow from discovery but no device found.""" """Test starting a flow from discovery but no device found."""
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=FritzConnectionException, side_effect=FritzConnectionException,
), patch("homeassistant.components.fritz.common.FritzStatus"): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_SSDP}, data=MOCK_SSDP_DATA DOMAIN, context={"source": SOURCE_SSDP}, data=MOCK_SSDP_DATA
@ -459,11 +459,9 @@ async def test_options_flow(hass: HomeAssistant, fc_class_mock, mock_get_source_
mock_config.add_to_hass(hass) mock_config.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.fritz.common.FritzConnection", "homeassistant.components.fritz.config_flow.FritzConnection",
side_effect=fc_class_mock, side_effect=fc_class_mock,
), patch("homeassistant.components.fritz.common.FritzStatus"), patch( ), patch("homeassistant.components.fritz.common.FritzBoxTools"):
"homeassistant.components.fritz.common.FritzBoxTools"
):
result = await hass.config_entries.options.async_init(mock_config.entry_id) result = await hass.config_entries.options.async_init(mock_config.entry_id)
assert result["type"] == RESULT_TYPE_FORM assert result["type"] == RESULT_TYPE_FORM
assert result["step_id"] == "init" assert result["step_id"] == "init"