mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +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 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 (
|
from homeassistant.components.bluetooth import (
|
||||||
BluetoothServiceInfoBleak,
|
BluetoothServiceInfoBleak,
|
||||||
async_discovered_service_info,
|
async_discovered_service_info,
|
||||||
@ -25,7 +25,6 @@ from homeassistant.const import CONF_ADDRESS
|
|||||||
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
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 .const import CONF_KEY, CONF_LOCAL_NAME, CONF_SLOT, DOMAIN
|
||||||
from .util import async_get_service_info, human_readable_name
|
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["key"],
|
||||||
discovery_info["slot"],
|
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
|
# We do not want to raise on progress as integration_discovery takes
|
||||||
# precedence over other discovery flows since we already have the keys.
|
# 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}
|
new_data = {CONF_KEY: lock_cfg.key, CONF_SLOT: lock_cfg.slot}
|
||||||
self._abort_if_unique_id_configured(updates=new_data)
|
self._abort_if_unique_id_configured(updates=new_data)
|
||||||
for entry in self._async_current_entries():
|
for entry in self._async_current_entries():
|
||||||
if entry.data.get(CONF_LOCAL_NAME) == lock_cfg.local_name:
|
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}
|
entry, data={**entry.data, **new_data}
|
||||||
):
|
):
|
||||||
self.hass.async_create_task(
|
hass.async_create_task(
|
||||||
self.hass.config_entries.async_reload(entry.entry_id)
|
hass.config_entries.async_reload(entry.entry_id)
|
||||||
)
|
)
|
||||||
raise AbortFlow(reason="already_configured")
|
raise AbortFlow(reason="already_configured")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._discovery_info = await async_get_service_info(
|
self._discovery_info = await async_get_service_info(
|
||||||
self.hass, lock_cfg.local_name, lock_cfg.address
|
hass, local_name, address
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
return self.async_abort(reason="no_devices_found")
|
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):
|
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"]
|
context = progress["context"]
|
||||||
if (
|
if (
|
||||||
not context.get("active")
|
local_name_is_unique(local_name)
|
||||||
and context.get("local_name") == lock_cfg.local_name
|
and context.get("local_name") == local_name
|
||||||
or context.get("unique_id") == lock_cfg.address
|
) or context.get("unique_id") == address:
|
||||||
):
|
if context.get("active"):
|
||||||
self.hass.config_entries.flow.async_abort(progress["flow_id"])
|
# 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._lock_cfg = lock_cfg
|
||||||
self.context["title_placeholders"] = {
|
self.context["title_placeholders"] = {
|
||||||
@ -228,12 +240,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
vol.Required(CONF_SLOT): int,
|
vol.Required(CONF_SLOT): int,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
integration = await async_get_integration(self.hass, DOMAIN)
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=data_schema,
|
data_schema=data_schema,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
description_placeholders={"docs_url": integration.documentation},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ class YaleXSBLELock(YALEXSBLEEntity, LockEntity):
|
|||||||
|
|
||||||
async def async_unlock(self, **kwargs: Any) -> None:
|
async def async_unlock(self, **kwargs: Any) -> None:
|
||||||
"""Unlock the lock."""
|
"""Unlock the lock."""
|
||||||
return await self._device.unlock()
|
await self._device.unlock()
|
||||||
|
|
||||||
async def async_lock(self, **kwargs: Any) -> None:
|
async def async_lock(self, **kwargs: Any) -> None:
|
||||||
"""Lock the lock."""
|
"""Lock the lock."""
|
||||||
return await self._device.lock()
|
await self._device.lock()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"flow_title": "{name}",
|
"flow_title": "{name}",
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"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": {
|
"data": {
|
||||||
"address": "Bluetooth address",
|
"address": "Bluetooth address",
|
||||||
"key": "Offline Key (32-byte hex string)",
|
"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."
|
"invalid_key_index": "The offline key slot must be an integer between 0 and 255."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"no_unconfigured_devices": "No unconfigured devices found.",
|
"no_unconfigured_devices": "No unconfigured devices found.",
|
||||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Device is already configured",
|
"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_devices_found": "No devices found on the network",
|
||||||
"no_unconfigured_devices": "No unconfigured devices found."
|
"no_unconfigured_devices": "No unconfigured devices found."
|
||||||
},
|
},
|
||||||
@ -23,7 +24,7 @@
|
|||||||
"key": "Offline Key (32-byte hex string)",
|
"key": "Offline Key (32-byte hex string)",
|
||||||
"slot": "Offline Key Slot (Integer between 0 and 255)"
|
"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
|
if flow["handler"] == DOMAIN
|
||||||
]
|
]
|
||||||
assert len(flows) == 1
|
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