mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Melcloud add reconfigure flow (#115999)
This commit is contained in:
@ -841,6 +841,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/media_source/ @hunterjm
|
||||
/tests/components/media_source/ @hunterjm
|
||||
/homeassistant/components/mediaroom/ @dgomes
|
||||
/homeassistant/components/melcloud/ @erwindouna
|
||||
/tests/components/melcloud/ @erwindouna
|
||||
/homeassistant/components/melissa/ @kennedyshead
|
||||
/tests/components/melissa/ @kennedyshead
|
||||
/homeassistant/components/melnor/ @vanstinator
|
||||
|
@ -25,7 +25,6 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
entry: ConfigEntry | None = None
|
||||
|
||||
async def _create_entry(self, username: str, token: str) -> ConfigFlowResult:
|
||||
@ -148,3 +147,66 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
return acquired_token, errors
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a reconfiguration flow initialized by the user."""
|
||||
self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
|
||||
return await self.async_step_reconfigure_confirm()
|
||||
|
||||
async def async_step_reconfigure_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle a reconfiguration flow initialized by the user."""
|
||||
errors: dict[str, str] = {}
|
||||
acquired_token = None
|
||||
assert self.entry
|
||||
|
||||
if user_input is not None:
|
||||
user_input[CONF_USERNAME] = self.entry.data[CONF_USERNAME]
|
||||
try:
|
||||
async with asyncio.timeout(10):
|
||||
acquired_token = await pymelcloud.login(
|
||||
user_input[CONF_USERNAME],
|
||||
user_input[CONF_PASSWORD],
|
||||
async_get_clientsession(self.hass),
|
||||
)
|
||||
except (ClientResponseError, AttributeError) as err:
|
||||
if (
|
||||
isinstance(err, ClientResponseError)
|
||||
and err.status
|
||||
in (
|
||||
HTTPStatus.UNAUTHORIZED,
|
||||
HTTPStatus.FORBIDDEN,
|
||||
)
|
||||
or isinstance(err, AttributeError)
|
||||
and err.name == "get"
|
||||
):
|
||||
errors["base"] = "invalid_auth"
|
||||
else:
|
||||
errors["base"] = "cannot_connect"
|
||||
except (
|
||||
TimeoutError,
|
||||
ClientError,
|
||||
):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
if not errors:
|
||||
user_input[CONF_TOKEN] = acquired_token
|
||||
return self.async_update_reload_and_abort(
|
||||
self.entry,
|
||||
data={**self.entry.data, **user_input},
|
||||
reason="reconfigure_successful",
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reconfigure_confirm",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
description_placeholders={CONF_USERNAME: self.entry.data[CONF_USERNAME]},
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "melcloud",
|
||||
"name": "MELCloud",
|
||||
"codeowners": [],
|
||||
"codeowners": ["@erwindouna"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/melcloud",
|
||||
"iot_class": "cloud_polling",
|
||||
|
@ -16,6 +16,16 @@
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"reconfigure_confirm": {
|
||||
"title": "Reconfigure your MelCloud",
|
||||
"description": "Reconfigure the entry to obtain a new token, for your account: `{username}`.",
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "Enter the (new) password for MelCloud."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@ -25,7 +35,8 @@
|
||||
},
|
||||
"abort": {
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"already_configured": "MELCloud integration already configured for this email. Access token has been refreshed."
|
||||
"already_configured": "MELCloud integration already configured for this email. Access token has been refreshed.",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
@ -9,7 +9,8 @@ import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.melcloud.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_RECONFIGURE
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
@ -305,3 +306,136 @@ async def test_client_errors_reauthentication(
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "reason"),
|
||||
[
|
||||
(HTTPStatus.UNAUTHORIZED, "invalid_auth"),
|
||||
(HTTPStatus.FORBIDDEN, "invalid_auth"),
|
||||
(HTTPStatus.INTERNAL_SERVER_ERROR, "cannot_connect"),
|
||||
],
|
||||
)
|
||||
async def test_reconfigure_flow(
|
||||
hass: HomeAssistant, mock_login, mock_request_info, error, reason
|
||||
) -> None:
|
||||
"""Test re-configuration flow."""
|
||||
mock_login.side_effect = ClientResponseError(mock_request_info(), (), status=error)
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-email@test-domain.com", "token": "test-original-token"},
|
||||
unique_id="test-email@test-domain.com",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": SOURCE_RECONFIGURE,
|
||||
"unique_id": mock_entry.unique_id,
|
||||
"entry_id": mock_entry.entry_id,
|
||||
},
|
||||
data=mock_entry.data,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.melcloud.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["errors"]["base"] == reason
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
||||
mock_login.side_effect = None
|
||||
with patch(
|
||||
"homeassistant.components.melcloud.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
entry = hass.config_entries.async_get_entry(mock_entry.entry_id)
|
||||
assert entry
|
||||
assert entry.title == "Mock Title"
|
||||
assert entry.data == {
|
||||
"username": "test-email@test-domain.com",
|
||||
"token": "test-token",
|
||||
"password": "test-password",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "reason"),
|
||||
[
|
||||
(TimeoutError(), "cannot_connect"),
|
||||
(AttributeError(name="get"), "invalid_auth"),
|
||||
],
|
||||
)
|
||||
async def test_form_errors_reconfigure(
|
||||
hass: HomeAssistant, mock_login, error, reason
|
||||
) -> None:
|
||||
"""Test we handle cannot connect error."""
|
||||
mock_login.side_effect = error
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-email@test-domain.com", "token": "test-original-token"},
|
||||
unique_id="test-email@test-domain.com",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": SOURCE_RECONFIGURE,
|
||||
"unique_id": mock_entry.unique_id,
|
||||
"entry_id": mock_entry.entry_id,
|
||||
},
|
||||
data=mock_entry.data,
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.melcloud.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"]["base"] == reason
|
||||
|
||||
mock_login.side_effect = None
|
||||
with patch(
|
||||
"homeassistant.components.melcloud.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "test-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
entry = hass.config_entries.async_get_entry(mock_entry.entry_id)
|
||||
assert entry
|
||||
assert entry.title == "Mock Title"
|
||||
assert entry.data == {
|
||||
"username": "test-email@test-domain.com",
|
||||
"token": "test-token",
|
||||
"password": "test-password",
|
||||
}
|
||||
|
Reference in New Issue
Block a user