mirror of
https://github.com/Electric-Special/ha-core.git
synced 2026-03-21 06:05:26 +01:00
Filter devices with active discovery flows from Shelly user step (#157201)
This commit is contained in:
@@ -42,7 +42,13 @@ from homeassistant.components.bluetooth import (
|
||||
async_clear_address_from_match_history,
|
||||
async_discovered_service_info,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult, OptionsFlow
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_BLUETOOTH,
|
||||
SOURCE_ZEROCONF,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE,
|
||||
CONF_HOST,
|
||||
@@ -109,6 +115,7 @@ BLE_SCANNER_OPTIONS = [
|
||||
|
||||
INTERNAL_WIFI_AP_IP = "192.168.33.1"
|
||||
MANUAL_ENTRY_STRING = "manual"
|
||||
DISCOVERY_SOURCES = {SOURCE_BLUETOOTH, SOURCE_ZEROCONF}
|
||||
|
||||
|
||||
async def async_get_ip_from_ble(ble_device: BLEDevice) -> str | None:
|
||||
@@ -445,11 +452,13 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
discovered_devices.update(await self._async_discover_zeroconf_devices())
|
||||
|
||||
# Filter out already-configured devices (excluding ignored)
|
||||
# and devices with active discovery flows (already being offered to user)
|
||||
current_ids = self._async_current_ids(include_ignore=False)
|
||||
in_progress_macs = self._async_get_in_progress_discovery_macs()
|
||||
discovered_devices = {
|
||||
mac: device
|
||||
for mac, device in discovered_devices.items()
|
||||
if mac not in current_ids
|
||||
if mac not in current_ids and mac not in in_progress_macs
|
||||
}
|
||||
|
||||
# Store discovered devices for use in selection
|
||||
@@ -575,6 +584,22 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
step_id="credentials", data_schema=vol.Schema(schema), errors=errors
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_get_in_progress_discovery_macs(self) -> set[str]:
|
||||
"""Get MAC addresses of devices with active discovery flows.
|
||||
|
||||
Returns MAC addresses from bluetooth and zeroconf discovery flows
|
||||
that are already in progress, so they can be filtered from the
|
||||
user step device list (since they're already being offered).
|
||||
"""
|
||||
return {
|
||||
mac
|
||||
for flow in self._async_in_progress(include_uninitialized=True)
|
||||
if flow["flow_id"] != self.flow_id
|
||||
and flow["context"].get("source") in DISCOVERY_SOURCES
|
||||
and (mac := flow["context"].get("unique_id"))
|
||||
}
|
||||
|
||||
def _abort_idle_ble_flows(self, mac: str) -> None:
|
||||
"""Abort idle BLE provisioning flows for this device.
|
||||
|
||||
|
||||
@@ -1805,20 +1805,12 @@ async def test_user_flow_select_ble_device(
|
||||
# Mock empty zeroconf discovery
|
||||
mock_discovery.return_value = []
|
||||
|
||||
# Inject BLE device with RPC-over-BLE enabled
|
||||
# Inject BLE device with RPC-over-BLE enabled (no discovery flow created)
|
||||
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3)
|
||||
|
||||
# Wait for bluetooth discovery to process
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Start a bluetooth discovery flow manually to simulate auto-discovery
|
||||
ble_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
data=BLE_DISCOVERY_INFO_GEN3,
|
||||
)
|
||||
ble_flow_id = ble_result["flow_id"]
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
@@ -1826,7 +1818,7 @@ async def test_user_flow_select_ble_device(
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
# Select the BLE device - should take over from the discovery flow
|
||||
# Select the BLE device
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_DEVICE: "CCBA97C2D670"}, # MAC from manufacturer data
|
||||
@@ -1891,9 +1883,59 @@ async def test_user_flow_select_ble_device(
|
||||
assert result["result"].unique_id == "CCBA97C2D670"
|
||||
assert result["title"] == "Test BLE Device"
|
||||
|
||||
# Verify the original bluetooth discovery flow no longer exists
|
||||
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
|
||||
assert not any(f["flow_id"] == ble_flow_id for f in flows)
|
||||
|
||||
async def test_user_flow_filters_devices_with_active_discovery_flows(
|
||||
hass: HomeAssistant,
|
||||
mock_discovery: AsyncMock,
|
||||
mock_rpc_device: Mock,
|
||||
) -> None:
|
||||
"""Test user flow filters out devices that already have discovery flows."""
|
||||
# Mock empty zeroconf discovery
|
||||
mock_discovery.return_value = []
|
||||
|
||||
# Inject BLE device with RPC-over-BLE enabled
|
||||
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3)
|
||||
|
||||
# Wait for bluetooth discovery to process
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Start a bluetooth discovery flow to simulate auto-discovery
|
||||
await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_BLUETOOTH},
|
||||
data=BLE_DISCOVERY_INFO_GEN3,
|
||||
)
|
||||
|
||||
# Start a user flow - should go to manual entry since the only
|
||||
# discovered device already has an active discovery flow
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
# Should go directly to manual entry since the BLE device is filtered
|
||||
# out (it already has an active discovery flow being offered to the user)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "user_manual"
|
||||
|
||||
# Complete the manual entry flow to reach terminal state
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
return_value={"mac": "aabbccddeeff", "model": MODEL_PLUS_2PM, "gen": 2},
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_HOST: "10.10.10.10"},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Test name"
|
||||
assert result["data"] == {
|
||||
CONF_HOST: "10.10.10.10",
|
||||
CONF_PORT: DEFAULT_HTTP_PORT,
|
||||
CONF_SLEEP_PERIOD: 0,
|
||||
CONF_MODEL: MODEL_PLUS_2PM,
|
||||
CONF_GEN: 2,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user