mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix totalconnect config flow (#59461)
* update total_connect_client to 2021.10 * update for total_connect_client changes * remove unused return value * bump total_connect_client to 2021.11.1 * bump total_connect_client to 2021.11.2 * Move to public ResultCode * load locations to prevent 'unknown error occurred' * add test for zero locations * put error message in strings * test for abort and message from strings * handle AuthenticationError in step_user * update tests with exceptions * update reauth with exceptions * use try except else per suggestion * only create schema if necessary * catch auth error in async_setup_entry * one more fix in test_init
This commit is contained in:
parent
5fc51130ea
commit
49c4886f40
@ -34,12 +34,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
temp_codes = conf[CONF_USERCODES]
|
temp_codes = conf[CONF_USERCODES]
|
||||||
usercodes = {int(code): temp_codes[code] for code in temp_codes}
|
usercodes = {int(code): temp_codes[code] for code in temp_codes}
|
||||||
client = await hass.async_add_executor_job(
|
|
||||||
TotalConnectClient, username, password, usercodes
|
|
||||||
)
|
|
||||||
|
|
||||||
if not client.is_logged_in():
|
try:
|
||||||
raise ConfigEntryAuthFailed("TotalConnect authentication failed")
|
client = await hass.async_add_executor_job(
|
||||||
|
TotalConnectClient, username, password, usercodes
|
||||||
|
)
|
||||||
|
except AuthenticationError as exception:
|
||||||
|
raise ConfigEntryAuthFailed("TotalConnect authentication failed") from exception
|
||||||
|
|
||||||
coordinator = TotalConnectDataUpdateCoordinator(hass, client)
|
coordinator = TotalConnectDataUpdateCoordinator(hass, client)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Config flow for the Total Connect component."""
|
"""Config flow for the Total Connect component."""
|
||||||
from total_connect_client.client import TotalConnectClient
|
from total_connect_client.client import TotalConnectClient
|
||||||
|
from total_connect_client.exceptions import AuthenticationError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
@ -36,18 +37,18 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
await self.async_set_unique_id(username)
|
await self.async_set_unique_id(username)
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
client = await self.hass.async_add_executor_job(
|
try:
|
||||||
TotalConnectClient, username, password, None
|
client = await self.hass.async_add_executor_job(
|
||||||
)
|
TotalConnectClient, username, password, None
|
||||||
|
)
|
||||||
if client.is_logged_in():
|
except AuthenticationError:
|
||||||
|
errors["base"] = "invalid_auth"
|
||||||
|
else:
|
||||||
# username/password valid so show user locations
|
# username/password valid so show user locations
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.client = client
|
self.client = client
|
||||||
return await self.async_step_locations()
|
return await self.async_step_locations()
|
||||||
# authentication failed / invalid
|
|
||||||
errors["base"] = "invalid_auth"
|
|
||||||
|
|
||||||
data_schema = vol.Schema(
|
data_schema = vol.Schema(
|
||||||
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
|
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
|
||||||
@ -88,6 +89,12 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
# Force the loading of locations using I/O
|
||||||
|
number_locations = await self.hass.async_add_executor_job(
|
||||||
|
self.client.get_number_locations,
|
||||||
|
)
|
||||||
|
if number_locations < 1:
|
||||||
|
return self.async_abort(reason="no_locations")
|
||||||
for location_id in self.client.locations:
|
for location_id in self.client.locations:
|
||||||
self.usercodes[location_id] = None
|
self.usercodes[location_id] = None
|
||||||
|
|
||||||
@ -129,14 +136,14 @@ class TotalConnectConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
data_schema=PASSWORD_DATA_SCHEMA,
|
data_schema=PASSWORD_DATA_SCHEMA,
|
||||||
)
|
)
|
||||||
|
|
||||||
client = await self.hass.async_add_executor_job(
|
try:
|
||||||
TotalConnectClient,
|
await self.hass.async_add_executor_job(
|
||||||
self.username,
|
TotalConnectClient,
|
||||||
user_input[CONF_PASSWORD],
|
self.username,
|
||||||
self.usercodes,
|
user_input[CONF_PASSWORD],
|
||||||
)
|
self.usercodes,
|
||||||
|
)
|
||||||
if not client.is_logged_in():
|
except AuthenticationError:
|
||||||
errors["base"] = "invalid_auth"
|
errors["base"] = "invalid_auth"
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="reauth_confirm",
|
step_id="reauth_confirm",
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
|
"no_locations": "No locations are available for this user, check TotalConnect settings"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Tests for the TotalConnect config flow."""
|
"""Tests for the TotalConnect config flow."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from total_connect_client.exceptions import AuthenticationError
|
||||||
|
|
||||||
from homeassistant import data_entry_flow
|
from homeassistant import data_entry_flow
|
||||||
from homeassistant.components.totalconnect.const import CONF_USERCODES, DOMAIN
|
from homeassistant.components.totalconnect.const import CONF_USERCODES, DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
|
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
|
||||||
@ -92,10 +94,7 @@ async def test_abort_if_already_setup(hass):
|
|||||||
).add_to_hass(hass)
|
).add_to_hass(hass)
|
||||||
|
|
||||||
# Should fail, same USERNAME (flow)
|
# Should fail, same USERNAME (flow)
|
||||||
with patch(
|
with patch("homeassistant.components.totalconnect.config_flow.TotalConnectClient"):
|
||||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
|
||||||
) as client_mock:
|
|
||||||
client_mock.return_value.is_logged_in.return_value = True
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_USER},
|
context={"source": SOURCE_USER},
|
||||||
@ -111,7 +110,7 @@ async def test_login_failed(hass):
|
|||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
"homeassistant.components.totalconnect.config_flow.TotalConnectClient"
|
||||||
) as client_mock:
|
) as client_mock:
|
||||||
client_mock.return_value.is_logged_in.return_value = False
|
client_mock.side_effect = AuthenticationError()
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": SOURCE_USER},
|
context={"source": SOURCE_USER},
|
||||||
@ -143,7 +142,7 @@ async def test_reauth(hass):
|
|||||||
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||||
):
|
):
|
||||||
# first test with an invalid password
|
# first test with an invalid password
|
||||||
client_mock.return_value.is_logged_in.return_value = False
|
client_mock.side_effect = AuthenticationError()
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||||
@ -153,7 +152,7 @@ async def test_reauth(hass):
|
|||||||
assert result["errors"] == {"base": "invalid_auth"}
|
assert result["errors"] == {"base": "invalid_auth"}
|
||||||
|
|
||||||
# now test with the password valid
|
# now test with the password valid
|
||||||
client_mock.return_value.is_logged_in.return_value = True
|
client_mock.side_effect = None
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
result["flow_id"], user_input={CONF_PASSWORD: "password"}
|
||||||
@ -163,3 +162,31 @@ async def test_reauth(hass):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(hass.config_entries.async_entries()) == 1
|
assert len(hass.config_entries.async_entries()) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_locations(hass):
|
||||||
|
"""Test with no user locations."""
|
||||||
|
responses = [
|
||||||
|
RESPONSE_AUTHENTICATE,
|
||||||
|
RESPONSE_PARTITION_DETAILS,
|
||||||
|
RESPONSE_GET_ZONE_DETAILS_SUCCESS,
|
||||||
|
RESPONSE_DISARMED,
|
||||||
|
]
|
||||||
|
|
||||||
|
with patch(TOTALCONNECT_REQUEST, side_effect=responses,) as mock_request, patch(
|
||||||
|
"homeassistant.components.totalconnect.async_setup_entry", return_value=True
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.totalconnect.TotalConnectClient.get_number_locations",
|
||||||
|
return_value=0,
|
||||||
|
):
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data=CONFIG_DATA_NO_USERCODES,
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "no_locations"
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert mock_request.call_count == 1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Tests for the TotalConnect init process."""
|
"""Tests for the TotalConnect init process."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from total_connect_client.exceptions import AuthenticationError
|
||||||
|
|
||||||
from homeassistant.components.totalconnect.const import DOMAIN
|
from homeassistant.components.totalconnect.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -20,9 +22,8 @@ async def test_reauth_started(hass):
|
|||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.totalconnect.TotalConnectClient",
|
"homeassistant.components.totalconnect.TotalConnectClient",
|
||||||
autospec=True,
|
|
||||||
) as mock_client:
|
) as mock_client:
|
||||||
mock_client.return_value.is_logged_in.return_value = False
|
mock_client.side_effect = AuthenticationError()
|
||||||
assert await async_setup_component(hass, DOMAIN, {})
|
assert await async_setup_component(hass, DOMAIN, {})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user