Add Re-Auth Flow to vesync (#137398)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>pull/139252/head
parent
2509353221
commit
befed910da
|
@ -12,6 +12,7 @@ from homeassistant.const import (
|
|||
Platform,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, ServiceCall, callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
|
@ -59,8 +60,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||
login = await hass.async_add_executor_job(manager.login)
|
||||
|
||||
if not login:
|
||||
_LOGGER.error("Unable to login to the VeSync server")
|
||||
return False
|
||||
raise ConfigEntryAuthFailed
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][VS_MANAGER] = manager
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""Config flow utilities."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from pyvesync import VeSync
|
||||
|
@ -57,3 +58,36 @@ class VeSyncFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
title=username,
|
||||
data={CONF_USERNAME: username, CONF_PASSWORD: password},
|
||||
)
|
||||
|
||||
async def async_step_reauth(
|
||||
self, entry_data: Mapping[str, Any]
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle re-authentication with vesync."""
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Confirm re-authentication with vesync."""
|
||||
|
||||
if user_input:
|
||||
username = user_input[CONF_USERNAME]
|
||||
password = user_input[CONF_PASSWORD]
|
||||
|
||||
manager = VeSync(username, password)
|
||||
login = await self.hass.async_add_executor_job(manager.login)
|
||||
if login:
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reauth_entry(),
|
||||
data_updates={
|
||||
CONF_USERNAME: username,
|
||||
CONF_PASSWORD: password,
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm",
|
||||
data_schema=DATA_SCHEMA,
|
||||
description_placeholders={"name": "VeSync"},
|
||||
errors={"base": "invalid_auth"},
|
||||
)
|
||||
|
|
|
@ -7,13 +7,22 @@
|
|||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "[%key:common::config_flow::title::reauth%]",
|
||||
"description": "The vesync integration needs to re-authenticate your account",
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]"
|
||||
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
|
|
|
@ -48,3 +48,59 @@ async def test_config_flow_user_input(hass: HomeAssistant) -> None:
|
|||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"][CONF_USERNAME] == "user"
|
||||
assert result["data"][CONF_PASSWORD] == "pass"
|
||||
|
||||
|
||||
async def test_reauth_flow(hass: HomeAssistant) -> None:
|
||||
"""Test a successful reauth flow."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await mock_entry.start_reauth_flow(hass)
|
||||
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
with patch("pyvesync.vesync.VeSync.login", return_value=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "new-username", CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert mock_entry.data == {
|
||||
CONF_USERNAME: "new-username",
|
||||
CONF_PASSWORD: "new-password",
|
||||
}
|
||||
|
||||
|
||||
async def test_reauth_flow_invalid_auth(hass: HomeAssistant) -> None:
|
||||
"""Test an authorization error reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await mock_entry.start_reauth_flow(hass)
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
|
||||
with patch("pyvesync.vesync.VeSync.login", return_value=False):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "new-username", CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
with patch("pyvesync.vesync.VeSync.login", return_value=True):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "new-username", CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from pyvesync import VeSync
|
||||
|
||||
from homeassistant.components.vesync import SERVICE_UPDATE_DEVS, async_setup_entry
|
||||
|
@ -19,25 +18,17 @@ async def test_async_setup_entry__not_login(
|
|||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
manager: VeSync,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test setup does not create config entry when not logged in."""
|
||||
manager.login = Mock(return_value=False)
|
||||
|
||||
with (
|
||||
patch.object(hass.config_entries, "async_forward_entry_setups") as setups_mock,
|
||||
patch(
|
||||
"homeassistant.components.vesync.async_generate_device_list"
|
||||
) as process_mock,
|
||||
):
|
||||
assert not await async_setup_entry(hass, config_entry)
|
||||
await hass.async_block_till_done()
|
||||
assert setups_mock.call_count == 0
|
||||
assert process_mock.call_count == 0
|
||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert manager.login.call_count == 1
|
||||
assert DOMAIN not in hass.data
|
||||
assert "Unable to login to the VeSync server" in caplog.text
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert config_entry.state is ConfigEntryState.SETUP_ERROR
|
||||
assert not hass.data.get(DOMAIN)
|
||||
|
||||
|
||||
async def test_async_setup_entry__no_devices(
|
||||
|
|
Loading…
Reference in New Issue