mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Small cleanups to Yale Access Bluetooth (#76691)
- Abort the discovery flow if the user has already started interacting with a user flow or bluetooth discovery - Remove docs_url from the flow - Fix useless return
This commit is contained in:
parent
6e03b12a93
commit
58883feaf6
@ -16,7 +16,7 @@ from yalexs_ble import (
|
||||
)
|
||||
from yalexs_ble.const import YALE_MFR_ID
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant import config_entries, data_entry_flow
|
||||
from homeassistant.components.bluetooth import (
|
||||
BluetoothServiceInfoBleak,
|
||||
async_discovered_service_info,
|
||||
@ -25,7 +25,6 @@ from homeassistant.const import CONF_ADDRESS
|
||||
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
from homeassistant.loader import async_get_integration
|
||||
|
||||
from .const import CONF_KEY, CONF_LOCAL_NAME, CONF_SLOT, DOMAIN
|
||||
from .util import async_get_service_info, human_readable_name
|
||||
@ -85,39 +84,52 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
discovery_info["key"],
|
||||
discovery_info["slot"],
|
||||
)
|
||||
|
||||
address = lock_cfg.address
|
||||
local_name = lock_cfg.local_name
|
||||
hass = self.hass
|
||||
|
||||
# We do not want to raise on progress as integration_discovery takes
|
||||
# precedence over other discovery flows since we already have the keys.
|
||||
await self.async_set_unique_id(lock_cfg.address, raise_on_progress=False)
|
||||
#
|
||||
# After we do discovery we will abort the flows that do not have the keys
|
||||
# below unless the user is already setting them up.
|
||||
await self.async_set_unique_id(address, raise_on_progress=False)
|
||||
new_data = {CONF_KEY: lock_cfg.key, CONF_SLOT: lock_cfg.slot}
|
||||
self._abort_if_unique_id_configured(updates=new_data)
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data.get(CONF_LOCAL_NAME) == lock_cfg.local_name:
|
||||
if self.hass.config_entries.async_update_entry(
|
||||
if hass.config_entries.async_update_entry(
|
||||
entry, data={**entry.data, **new_data}
|
||||
):
|
||||
self.hass.async_create_task(
|
||||
self.hass.config_entries.async_reload(entry.entry_id)
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_reload(entry.entry_id)
|
||||
)
|
||||
raise AbortFlow(reason="already_configured")
|
||||
|
||||
try:
|
||||
self._discovery_info = await async_get_service_info(
|
||||
self.hass, lock_cfg.local_name, lock_cfg.address
|
||||
hass, local_name, address
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
return self.async_abort(reason="no_devices_found")
|
||||
|
||||
# Integration discovery should abort other flows unless they
|
||||
# are already in the process of being set up since this discovery
|
||||
# will already have all the keys and the user can simply confirm.
|
||||
for progress in self._async_in_progress(include_uninitialized=True):
|
||||
# Integration discovery should abort other discovery types
|
||||
# since it already has the keys and slots, and the other
|
||||
# discovery types do not.
|
||||
context = progress["context"]
|
||||
if (
|
||||
not context.get("active")
|
||||
and context.get("local_name") == lock_cfg.local_name
|
||||
or context.get("unique_id") == lock_cfg.address
|
||||
):
|
||||
self.hass.config_entries.flow.async_abort(progress["flow_id"])
|
||||
local_name_is_unique(local_name)
|
||||
and context.get("local_name") == local_name
|
||||
) or context.get("unique_id") == address:
|
||||
if context.get("active"):
|
||||
# The user has already started interacting with this flow
|
||||
# and entered the keys. We abort the discovery flow since
|
||||
# we assume they do not want to use the discovered keys for
|
||||
# some reason.
|
||||
raise data_entry_flow.AbortFlow("already_in_progress")
|
||||
hass.config_entries.flow.async_abort(progress["flow_id"])
|
||||
|
||||
self._lock_cfg = lock_cfg
|
||||
self.context["title_placeholders"] = {
|
||||
@ -228,12 +240,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
vol.Required(CONF_SLOT): int,
|
||||
}
|
||||
)
|
||||
integration = await async_get_integration(self.hass, DOMAIN)
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=data_schema,
|
||||
errors=errors,
|
||||
description_placeholders={"docs_url": integration.documentation},
|
||||
)
|
||||
|
||||
|
||||
|
@ -55,8 +55,8 @@ class YaleXSBLELock(YALEXSBLEEntity, LockEntity):
|
||||
|
||||
async def async_unlock(self, **kwargs: Any) -> None:
|
||||
"""Unlock the lock."""
|
||||
return await self._device.unlock()
|
||||
await self._device.unlock()
|
||||
|
||||
async def async_lock(self, **kwargs: Any) -> None:
|
||||
"""Lock the lock."""
|
||||
return await self._device.lock()
|
||||
await self._device.lock()
|
||||
|
@ -3,7 +3,7 @@
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Check the documentation at {docs_url} for how to find the offline key.",
|
||||
"description": "Check the documentation for how to find the offline key.",
|
||||
"data": {
|
||||
"address": "Bluetooth address",
|
||||
"key": "Offline Key (32-byte hex string)",
|
||||
@ -22,6 +22,7 @@
|
||||
"invalid_key_index": "The offline key slot must be an integer between 0 and 255."
|
||||
},
|
||||
"abort": {
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"no_unconfigured_devices": "No unconfigured devices found.",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||
|
@ -2,6 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured",
|
||||
"already_in_progress": "Configuration flow is already in progress",
|
||||
"no_devices_found": "No devices found on the network",
|
||||
"no_unconfigured_devices": "No unconfigured devices found."
|
||||
},
|
||||
@ -23,7 +24,7 @@
|
||||
"key": "Offline Key (32-byte hex string)",
|
||||
"slot": "Offline Key Slot (Integer between 0 and 255)"
|
||||
},
|
||||
"description": "Check the documentation at {docs_url} for how to find the offline key."
|
||||
"description": "Check the documentation for how to find the offline key."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -752,3 +752,83 @@ async def test_integration_discovery_takes_precedence_over_bluetooth_non_unique_
|
||||
if flow["handler"] == DOMAIN
|
||||
]
|
||||
assert len(flows) == 1
|
||||
|
||||
|
||||
async def test_user_is_setting_up_lock_and_discovery_happens_in_the_middle(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test that the user is setting up the lock and waiting for validation and the keys get discovered.
|
||||
|
||||
In this case the integration discovery should abort and let the user continue setting up the lock.
|
||||
"""
|
||||
with patch(
|
||||
"homeassistant.components.yalexs_ble.config_flow.async_discovered_service_info",
|
||||
return_value=[NOT_YALE_DISCOVERY_INFO, YALE_ACCESS_LOCK_DISCOVERY_INFO],
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
user_flow_event = asyncio.Event()
|
||||
valdidate_started = asyncio.Event()
|
||||
|
||||
async def _wait_for_user_flow():
|
||||
valdidate_started.set()
|
||||
await user_flow_event.wait()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.yalexs_ble.config_flow.PushLock.validate",
|
||||
side_effect=_wait_for_user_flow,
|
||||
), patch(
|
||||
"homeassistant.components.yalexs_ble.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
user_flow_task = asyncio.create_task(
|
||||
hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_ADDRESS: YALE_ACCESS_LOCK_DISCOVERY_INFO.address,
|
||||
CONF_KEY: "2fd51b8621c6a139eaffbedcb846b60f",
|
||||
CONF_SLOT: 66,
|
||||
},
|
||||
)
|
||||
)
|
||||
await valdidate_started.wait()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.yalexs_ble.util.async_process_advertisements",
|
||||
return_value=LOCK_DISCOVERY_INFO_UUID_ADDRESS,
|
||||
):
|
||||
discovery_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
|
||||
data={
|
||||
"name": "Front Door",
|
||||
"address": OLD_FIRMWARE_LOCK_DISCOVERY_INFO.address,
|
||||
"key": "2fd51b8621c6a139eaffbedcb846b60f",
|
||||
"slot": 66,
|
||||
"serial": "M1XXX012LU",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert discovery_result["type"] == FlowResultType.ABORT
|
||||
assert discovery_result["reason"] == "already_in_progress"
|
||||
|
||||
user_flow_event.set()
|
||||
user_flow_result = await user_flow_task
|
||||
|
||||
assert user_flow_result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert user_flow_result["title"] == YALE_ACCESS_LOCK_DISCOVERY_INFO.name
|
||||
assert user_flow_result["data"] == {
|
||||
CONF_LOCAL_NAME: YALE_ACCESS_LOCK_DISCOVERY_INFO.name,
|
||||
CONF_ADDRESS: YALE_ACCESS_LOCK_DISCOVERY_INFO.address,
|
||||
CONF_KEY: "2fd51b8621c6a139eaffbedcb846b60f",
|
||||
CONF_SLOT: 66,
|
||||
}
|
||||
assert (
|
||||
user_flow_result["result"].unique_id == YALE_ACCESS_LOCK_DISCOVERY_INFO.address
|
||||
)
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
Loading…
x
Reference in New Issue
Block a user