mirror of
https://github.com/home-assistant/core.git
synced 2026-08-03 20:24:55 +02:00
Remove name selection (#169446)
Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.components import webhook
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_API_TOKEN, CONF_NAME, CONF_WEBHOOK_ID
|
||||
from homeassistant.const import CONF_API_TOKEN, CONF_WEBHOOK_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
@@ -27,30 +27,43 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
VERSION = 1
|
||||
DOMAIN = DOMAIN
|
||||
_host: str | None = None
|
||||
_locks: list[dict[str, Any]]
|
||||
_api_token: str | None = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
super().__init__()
|
||||
self._locks = []
|
||||
|
||||
async def validate_input(
|
||||
self, hass: HomeAssistant, data: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
"""Validate the user input allows us to connect."""
|
||||
|
||||
# 1. Checking loqed-connection
|
||||
try:
|
||||
session = async_get_clientsession(hass)
|
||||
session = async_get_clientsession(hass)
|
||||
if self._locks and not self._host:
|
||||
# Reuse the lock list already fetched during manual setup to
|
||||
# avoid a duplicate cloud request.
|
||||
lock_data = {"data": self._locks}
|
||||
else:
|
||||
cloud_api_client = cloud_loqed.CloudAPIClient(
|
||||
session,
|
||||
data[CONF_API_TOKEN],
|
||||
)
|
||||
cloud_client = cloud_loqed.LoqedCloudAPI(cloud_api_client)
|
||||
lock_data = await cloud_client.async_get_locks()
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("HTTP Connection error to loqed API")
|
||||
raise CannotConnect from err
|
||||
|
||||
try:
|
||||
lock_data = await cloud_client.async_get_locks()
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("HTTP Connection error to loqed API")
|
||||
raise CannotConnect from err
|
||||
|
||||
try:
|
||||
match_key, match_value = (
|
||||
("bridge_ip", self._host) if self._host else ("id", data.get("lock_id"))
|
||||
)
|
||||
selected_lock = next(
|
||||
lock
|
||||
for lock in lock_data["data"]
|
||||
if lock["bridge_ip"] == self._host or lock["name"] == data.get("name")
|
||||
lock for lock in lock_data["data"] if lock[match_key] == match_value
|
||||
)
|
||||
|
||||
apiclient = loqed.APIClient(session, f"http://{selected_lock['bridge_ip']}")
|
||||
@@ -73,11 +86,11 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"name": selected_lock["name"],
|
||||
"id": selected_lock["id"],
|
||||
}
|
||||
except StopIteration:
|
||||
raise InvalidAuth from StopIteration
|
||||
except aiohttp.ClientError:
|
||||
except StopIteration as err:
|
||||
raise InvalidAuth from err
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("HTTP Connection error to loqed lock")
|
||||
raise CannotConnect from aiohttp.ClientError
|
||||
raise CannotConnect from err
|
||||
|
||||
@override
|
||||
async def async_step_zeroconf(
|
||||
@@ -103,21 +116,10 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Show userform to user."""
|
||||
user_data_schema = (
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_TOKEN): str,
|
||||
}
|
||||
)
|
||||
if self._host
|
||||
else vol.Schema(
|
||||
{
|
||||
# Name field is no longer allowed in config flow schemas
|
||||
# pylint: disable-next=home-assistant-config-flow-name-field
|
||||
vol.Required(CONF_NAME): str,
|
||||
vol.Required(CONF_API_TOKEN): str,
|
||||
}
|
||||
)
|
||||
user_data_schema = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_TOKEN): str,
|
||||
}
|
||||
)
|
||||
|
||||
if user_input is None:
|
||||
@@ -131,6 +133,39 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
errors = {}
|
||||
|
||||
# If no Zeroconf discovery and no selected lock, we need to fetch locks and show picker
|
||||
if not self._host and not user_input.get("lock_id"):
|
||||
session = async_get_clientsession(self.hass)
|
||||
cloud_api_client = cloud_loqed.CloudAPIClient(
|
||||
session,
|
||||
user_input[CONF_API_TOKEN],
|
||||
)
|
||||
cloud_client = cloud_loqed.LoqedCloudAPI(cloud_api_client)
|
||||
|
||||
try:
|
||||
lock_data = await cloud_client.async_get_locks()
|
||||
except aiohttp.ClientError:
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
self._locks = lock_data["data"]
|
||||
self._api_token = user_input[CONF_API_TOKEN]
|
||||
if not self._locks:
|
||||
errors["base"] = "no_locks"
|
||||
elif len(self._locks) == 1:
|
||||
user_input["lock_id"] = self._locks[0]["id"]
|
||||
else:
|
||||
return await self.async_step_pick_lock()
|
||||
|
||||
if errors:
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=user_data_schema,
|
||||
errors=errors,
|
||||
description_placeholders={
|
||||
"config_url": "https://integrations.loqed.com/personal-access-tokens",
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
info = await self.validate_input(self.hass, user_input)
|
||||
except CannotConnect:
|
||||
@@ -147,10 +182,12 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title="LOQED Touch Smart Lock",
|
||||
data=(
|
||||
user_input | {CONF_WEBHOOK_ID: webhook.async_generate_id()} | info
|
||||
),
|
||||
title=info["name"],
|
||||
data={
|
||||
CONF_API_TOKEN: user_input[CONF_API_TOKEN],
|
||||
CONF_WEBHOOK_ID: webhook.async_generate_id(),
|
||||
**info,
|
||||
},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
@@ -162,6 +199,28 @@ class LoqedConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
},
|
||||
)
|
||||
|
||||
async def async_step_pick_lock(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle lock selection when multiple locks are available."""
|
||||
if user_input is not None:
|
||||
if self._api_token is None:
|
||||
return await self.async_step_user()
|
||||
return await self.async_step_user(
|
||||
{**user_input, CONF_API_TOKEN: self._api_token}
|
||||
)
|
||||
|
||||
lock_options = {lock["id"]: lock["name"] for lock in self._locks}
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="pick_lock",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required("lock_id"): vol.In(lock_options),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class CannotConnect(HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
||||
@@ -5,14 +5,20 @@
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"no_locks": "No locks found in your LOQED account."
|
||||
},
|
||||
"flow_title": "LOQED Touch Smartlock setup",
|
||||
"step": {
|
||||
"pick_lock": {
|
||||
"data": {
|
||||
"lock_id": "Select your lock"
|
||||
},
|
||||
"description": "Multiple locks found. Please select the lock you want to configure."
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_token": "[%key:common::config_flow::data::api_token%]",
|
||||
"name": "Name of your lock in the LOQED app."
|
||||
"api_token": "[%key:common::config_flow::data::api_token%]"
|
||||
},
|
||||
"description": "Log in at LOQED's [personal access tokens portal]({config_url}) and: \n* Create an API key by clicking 'Create' \n* Copy the created access token."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Contains fixtures for Loqed tests."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator, Callable
|
||||
from contextlib import contextmanager
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
@@ -15,6 +16,8 @@ from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry, async_load_fixture
|
||||
|
||||
type PatchLockCreationFlow = Callable[[dict[str, Any], loqed.Lock, str], Any]
|
||||
|
||||
|
||||
@pytest.fixture(name="config_entry")
|
||||
async def config_entry_fixture(hass: HomeAssistant) -> MockConfigEntry:
|
||||
@@ -100,3 +103,36 @@ async def integration_fixture(
|
||||
await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.async_block_till_done()
|
||||
yield config_entry
|
||||
|
||||
|
||||
@pytest.fixture(name="patch_lock_creation_flow")
|
||||
def patch_lock_creation_flow_fixture() -> PatchLockCreationFlow:
|
||||
"""Patch config-flow calls used when creating a lock entry."""
|
||||
|
||||
@contextmanager
|
||||
def _patch_lock_creation_flow(
|
||||
all_locks_response: dict[str, Any],
|
||||
lock: loqed.Lock,
|
||||
webhook_id: str,
|
||||
) -> Any:
|
||||
with (
|
||||
patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value=all_locks_response,
|
||||
),
|
||||
patch(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock",
|
||||
return_value=lock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.loqed.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.webhook.async_generate_id",
|
||||
return_value=webhook_id,
|
||||
),
|
||||
):
|
||||
yield
|
||||
|
||||
return _patch_lock_creation_flow
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""Test the Loqed config flow."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from ipaddress import ip_address
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import aiohttp
|
||||
@@ -9,7 +11,7 @@ from loqedAPI import loqed
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.loqed.const import DOMAIN
|
||||
from homeassistant.const import CONF_API_TOKEN, CONF_NAME, CONF_WEBHOOK_ID
|
||||
from homeassistant.const import CONF_API_TOKEN, CONF_WEBHOOK_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
@@ -17,6 +19,9 @@ from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
|
||||
from tests.common import async_load_fixture
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
TEST_API_TOKEN = "eyadiuyfasiuasf"
|
||||
TEST_WEBHOOK_ID = "Webhook_ID"
|
||||
|
||||
zeroconf_data = ZeroconfServiceInfo(
|
||||
ip_address=ip_address("192.168.12.34"),
|
||||
ip_addresses=[ip_address("192.168.12.34")],
|
||||
@@ -28,7 +33,37 @@ zeroconf_data = ZeroconfServiceInfo(
|
||||
)
|
||||
|
||||
|
||||
async def test_create_entry_zeroconf(hass: HomeAssistant) -> None:
|
||||
async def _async_init_zeroconf_flow(hass: HomeAssistant) -> dict[str, Any]:
|
||||
"""Initialize a zeroconf flow and return the form result."""
|
||||
lock_result = json.loads(await async_load_fixture(hass, "status_ok.json", DOMAIN))
|
||||
|
||||
with patch(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock_details",
|
||||
return_value=lock_result,
|
||||
):
|
||||
return await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_ZEROCONF},
|
||||
data=zeroconf_data,
|
||||
)
|
||||
|
||||
|
||||
async def _async_init_user_flow(hass: HomeAssistant) -> dict[str, Any]:
|
||||
"""Initialize a user flow and return the form result."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
return result
|
||||
|
||||
|
||||
async def test_create_entry_zeroconf(
|
||||
hass: HomeAssistant,
|
||||
patch_lock_creation_flow: Callable[[dict[str, Any], loqed.Lock, str], Any],
|
||||
) -> None:
|
||||
"""Test we get can create a lock via zeroconf."""
|
||||
lock_result = json.loads(await async_load_fixture(hass, "status_ok.json", DOMAIN))
|
||||
|
||||
@@ -51,25 +86,8 @@ async def test_create_entry_zeroconf(hass: HomeAssistant) -> None:
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value=all_locks_response,
|
||||
),
|
||||
patch(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock",
|
||||
return_value=mock_lock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.loqed.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.webhook.async_generate_id",
|
||||
return_value=webhook_id,
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
with patch_lock_creation_flow(all_locks_response, mock_lock, webhook_id):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_API_TOKEN: "eyadiuyfasiuasf",
|
||||
@@ -78,9 +96,9 @@ async def test_create_entry_zeroconf(hass: HomeAssistant) -> None:
|
||||
await hass.async_block_till_done()
|
||||
found_lock = all_locks_response["data"][0]
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "LOQED Touch Smart Lock"
|
||||
assert result2["data"] == {
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "MyLock"
|
||||
assert result["data"] == {
|
||||
"id": "Foo",
|
||||
"lock_key_key": found_lock["key_secret"],
|
||||
"bridge_key": found_lock["bridge_key"],
|
||||
@@ -95,55 +113,30 @@ async def test_create_entry_zeroconf(hass: HomeAssistant) -> None:
|
||||
|
||||
|
||||
async def test_create_entry_user(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
patch_lock_creation_flow: Callable[[dict[str, Any], loqed.Lock, str], Any],
|
||||
) -> None:
|
||||
"""Test we can create a lock via manual entry."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
)
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
lock_result = json.loads(await async_load_fixture(hass, "status_ok.json", DOMAIN))
|
||||
mock_lock = Mock(spec=loqed.Lock, id="Foo")
|
||||
webhook_id = "Webhook_ID"
|
||||
webhook_id = TEST_WEBHOOK_ID
|
||||
all_locks_response = json.loads(
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
)
|
||||
found_lock = all_locks_response["data"][0]
|
||||
|
||||
with (
|
||||
patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value=all_locks_response,
|
||||
),
|
||||
patch(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock",
|
||||
return_value=mock_lock,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.loqed.async_setup_entry",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.webhook.async_generate_id",
|
||||
return_value=webhook_id,
|
||||
),
|
||||
patch(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock_details", return_value=lock_result
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
with patch_lock_creation_flow(all_locks_response, mock_lock, webhook_id):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: "eyadiuyfasiuasf", CONF_NAME: "MyLock"},
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "LOQED Touch Smart Lock"
|
||||
assert result2["data"] == {
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "MyLock"
|
||||
assert result["data"] == {
|
||||
"id": "Foo",
|
||||
"lock_key_key": found_lock["key_secret"],
|
||||
"bridge_key": found_lock["bridge_key"],
|
||||
@@ -152,7 +145,56 @@ async def test_create_entry_user(
|
||||
"bridge_ip": found_lock["bridge_ip"],
|
||||
"name": found_lock["name"],
|
||||
CONF_WEBHOOK_ID: webhook_id,
|
||||
CONF_API_TOKEN: "eyadiuyfasiuasf",
|
||||
CONF_API_TOKEN: TEST_API_TOKEN,
|
||||
}
|
||||
mock_lock.getWebhooks.assert_awaited()
|
||||
|
||||
|
||||
async def test_create_entry_user_with_pick_lock(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
patch_lock_creation_flow: Callable[[dict[str, Any], loqed.Lock, str], Any],
|
||||
) -> None:
|
||||
"""Test we can create a lock via manual entry when multiple locks exist."""
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
mock_lock = Mock(spec=loqed.Lock, id="Foo")
|
||||
webhook_id = TEST_WEBHOOK_ID
|
||||
all_locks_response = json.loads(
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
)
|
||||
second_lock = all_locks_response["data"][0].copy()
|
||||
second_lock["id"] = "Bar"
|
||||
second_lock["name"] = "MyOtherLock"
|
||||
all_locks_response["data"].append(second_lock)
|
||||
|
||||
with patch_lock_creation_flow(all_locks_response, mock_lock, webhook_id):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "pick_lock"
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"lock_id": second_lock["id"]},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == second_lock["name"]
|
||||
assert result["data"] == {
|
||||
"id": second_lock["id"],
|
||||
"lock_key_key": second_lock["key_secret"],
|
||||
"bridge_key": second_lock["bridge_key"],
|
||||
"lock_key_local_id": second_lock["local_id"],
|
||||
"bridge_mdns_hostname": second_lock["bridge_hostname"],
|
||||
"bridge_ip": second_lock["bridge_ip"],
|
||||
"name": second_lock["name"],
|
||||
CONF_WEBHOOK_ID: webhook_id,
|
||||
CONF_API_TOKEN: TEST_API_TOKEN,
|
||||
}
|
||||
mock_lock.getWebhooks.assert_awaited()
|
||||
|
||||
@@ -161,10 +203,121 @@ async def test_cannot_connect(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test we handle cannot connect error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
with patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
side_effect=aiohttp.ClientError,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_recover_after_cannot_connect(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
patch_lock_creation_flow: Callable[[dict[str, Any], loqed.Lock, str], Any],
|
||||
) -> None:
|
||||
"""Test we can recover from a connection error and create an entry."""
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
with patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
side_effect=aiohttp.ClientError,
|
||||
):
|
||||
error_result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert error_result["type"] is FlowResultType.FORM
|
||||
assert error_result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
mock_lock = Mock(spec=loqed.Lock, id="Foo")
|
||||
webhook_id = TEST_WEBHOOK_ID
|
||||
all_locks_response = json.loads(
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
)
|
||||
found_lock = all_locks_response["data"][0]
|
||||
|
||||
with patch_lock_creation_flow(all_locks_response, mock_lock, webhook_id):
|
||||
success_result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert success_result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert success_result["title"] == "MyLock"
|
||||
assert success_result["data"] == {
|
||||
"id": "Foo",
|
||||
"lock_key_key": found_lock["key_secret"],
|
||||
"bridge_key": found_lock["bridge_key"],
|
||||
"lock_key_local_id": found_lock["local_id"],
|
||||
"bridge_mdns_hostname": found_lock["bridge_hostname"],
|
||||
"bridge_ip": found_lock["bridge_ip"],
|
||||
"name": found_lock["name"],
|
||||
CONF_WEBHOOK_ID: webhook_id,
|
||||
CONF_API_TOKEN: TEST_API_TOKEN,
|
||||
}
|
||||
mock_lock.getWebhooks.assert_awaited()
|
||||
|
||||
|
||||
async def test_no_locks(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test we handle a situation where the account has no locks."""
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
with patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value={"data": []},
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "no_locks"}
|
||||
|
||||
|
||||
async def test_invalid_auth_when_lock_not_found(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test we handle a situation where the lock is absent from the cloud API response."""
|
||||
result = await _async_init_zeroconf_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value={"data": []},
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_cannot_connect_zeroconf_cloud_api_error(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test we handle a cloud API error during zeroconf validate_input."""
|
||||
result = await _async_init_zeroconf_flow(hass)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
@@ -173,57 +326,21 @@ async def test_cannot_connect(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
side_effect=aiohttp.ClientError,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: "eyadiuyfasiuasf", CONF_NAME: "MyLock"},
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_invalid_auth_when_lock_not_found(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test we handle a situation where the user enters an invalid lock name."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
all_locks_response = json.loads(
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
)
|
||||
|
||||
with patch(
|
||||
"loqedAPI.cloud_loqed.LoqedCloudAPI.async_get_locks",
|
||||
return_value=all_locks_response,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: "eyadiuyfasiuasf", CONF_NAME: "MyLock2"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_cannot_connect_when_lock_not_reachable(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
) -> None:
|
||||
"""Test we handle a situation where the user enters an invalid lock name."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
"""Test we handle a situation where the lock is not reachable."""
|
||||
result = await _async_init_user_flow(hass)
|
||||
|
||||
all_locks_response = json.loads(
|
||||
await async_load_fixture(hass, "get_all_locks.json", DOMAIN)
|
||||
@@ -238,11 +355,11 @@ async def test_cannot_connect_when_lock_not_reachable(
|
||||
"loqedAPI.loqed.LoqedAPI.async_get_lock", side_effect=aiohttp.ClientError
|
||||
),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_API_TOKEN: "eyadiuyfasiuasf", CONF_NAME: "MyLock"},
|
||||
{CONF_API_TOKEN: TEST_API_TOKEN},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
Reference in New Issue
Block a user