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:
J. Nick Koston 2022-08-12 21:55:48 -10:00 committed by GitHub
parent 6e03b12a93
commit 58883feaf6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 21 deletions

View File

@ -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},
)

View File

@ -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()

View File

@ -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%]"

View File

@ -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."
}
}
}

View File

@ -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