Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 47 additions & 51 deletions tests/components/shelly/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@
)
from tests.typing import WebSocketGenerator


async def _async_inject_ble_discovery(
hass: HomeAssistant, info: BluetoothServiceInfoBleak
) -> None:
"""Inject BLE discovery info and wait for processing without triggering config flows."""
with patch.object(hass.config_entries.flow, "async_init"):
inject_bluetooth_service_info_bleak(hass, info)
await hass.async_block_till_done()
Comment thread
bdraco marked this conversation as resolved.


DISCOVERY_INFO = ZeroconfServiceInfo(
ip_address=ip_address("1.1.1.1"),
ip_addresses=[ip_address("1.1.1.1")],
Expand Down Expand Up @@ -1078,7 +1088,7 @@ async def test_user_flow_both_ble_and_zeroconf_prefers_zeroconf(

# Inject BLE device with same MAC (from manufacturer data)
# The manufacturer data contains WiFi MAC CCBA97C2D670
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_GEN3)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3)

result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
Expand Down Expand Up @@ -1140,7 +1150,7 @@ async def test_user_flow_with_ble_devices(

# Inject BLE device with RPC-over-BLE enabled
# The manufacturer data contains WiFi MAC CCBA97C2D670
inject_bluetooth_service_info_bleak(
await _async_inject_ble_discovery(
hass,
BluetoothServiceInfoBleak(
name="ShellyPlusGen3", # Name without MAC so it uses manufacturer data
Expand All @@ -1163,15 +1173,6 @@ async def test_user_flow_with_ble_devices(
),
)

# Wait for bluetooth discovery to process
await hass.async_block_till_done()

# Abort any auto-discovered bluetooth flows
flows = hass.config_entries.flow.async_progress_by_handler(DOMAIN)
for flow in flows:
if flow["context"]["source"] == config_entries.SOURCE_BLUETOOTH:
hass.config_entries.flow.async_abort(flow["flow_id"])

result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
Expand Down Expand Up @@ -1844,10 +1845,7 @@ async def test_user_flow_select_ble_device(
mock_discovery.return_value = []

# 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()
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_GEN3)

result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
Expand Down Expand Up @@ -3226,7 +3224,7 @@ async def test_bluetooth_discovery(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3293,7 +3291,7 @@ async def test_bluetooth_provisioning_clears_match_history(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_FOR_CLEAR_TEST)

with patch(
"homeassistant.components.shelly.config_flow.async_clear_address_from_match_history",
Expand Down Expand Up @@ -3382,7 +3380,7 @@ async def test_bluetooth_factory_reset_rediscovery(

# First discovery: device is already provisioned (no RPC-over-BLE)
# Inject the device without RPC so it's in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_NO_RPC)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_NO_RPC)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3465,9 +3463,7 @@ async def test_bluetooth_discovery_mac_in_manufacturer_data(
) -> None:
"""Test bluetooth discovery with MAC in manufacturer data (newer devices)."""
# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(
hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA
)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_IN_MANUFACTURER_DATA)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand All @@ -3490,7 +3486,7 @@ async def test_bluetooth_discovery_mac_unknown_model(
) -> None:
"""Test bluetooth discovery with MAC but unknown model ID."""
# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO_MAC_UNKNOWN_MODEL)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand All @@ -3512,7 +3508,7 @@ async def test_bluetooth_discovery_already_configured(
) -> None:
"""Test bluetooth discovery when device is already configured."""
# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

entry = MockConfigEntry(
domain=DOMAIN,
Expand Down Expand Up @@ -3541,7 +3537,7 @@ async def test_bluetooth_discovery_already_configured_clears_match_history(
) -> None:
"""Test bluetooth discovery clears match history when device already configured."""
# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

entry = MockConfigEntry(
domain=DOMAIN,
Expand Down Expand Up @@ -3603,7 +3599,7 @@ async def test_bluetooth_wifi_scan_success(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3673,7 +3669,7 @@ async def test_bluetooth_wifi_scan_failure(
mock_ble_rpc_device.wifi_scan.side_effect = DeviceConnectionError

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3756,7 +3752,7 @@ async def test_bluetooth_wifi_scan_ble_not_permitted(
)

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3790,7 +3786,7 @@ async def test_bluetooth_wifi_credentials_and_provision_success(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3866,7 +3862,7 @@ async def test_bluetooth_wifi_provision_failure(
mock_ble_rpc_device.wifi_setconfig.side_effect = DeviceConnectionError

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3954,7 +3950,7 @@ async def test_bluetooth_wifi_scan_unexpected_exception(
mock_ble_rpc_device.wifi_scan.side_effect = RuntimeError("Unexpected error")

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -3985,7 +3981,7 @@ async def test_bluetooth_provision_unexpected_exception(
mock_ble_rpc_device.wifi_setconfig.side_effect = RuntimeError("Unexpected error")

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4031,7 +4027,7 @@ async def test_bluetooth_provision_device_connection_error_after_wifi(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4092,7 +4088,7 @@ async def test_bluetooth_provision_requires_auth(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4170,7 +4166,7 @@ async def test_bluetooth_provision_validate_input_fails(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4243,7 +4239,7 @@ async def test_bluetooth_provision_firmware_not_fully_provisioned(
]

# Inject BLE device so it's available in the bluetooth scanner
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4299,7 +4295,7 @@ async def test_bluetooth_provision_with_zeroconf_discovery_fast_path(
]

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4387,7 +4383,7 @@ async def test_bluetooth_provision_timeout_active_lookup_fails(
mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}}

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4460,7 +4456,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_succeeds(
mock_ble_rpc_device.status = {"wifi": {"sta_ip": "192.168.1.100"}}

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4540,7 +4536,7 @@ async def test_bluetooth_provision_timeout_ble_fallback_fails(
mock_ble_rpc_device.status = {"wifi": {"sta_ip": None}}

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4600,7 +4596,7 @@ async def test_bluetooth_provision_timeout_ble_exception(
]

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4674,7 +4670,7 @@ async def test_bluetooth_provision_secure_device_both_enabled(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4734,7 +4730,7 @@ async def test_bluetooth_provision_secure_device_both_disabled(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4783,7 +4779,7 @@ async def test_bluetooth_provision_secure_device_only_ap_disabled(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4842,7 +4838,7 @@ async def test_bluetooth_provision_secure_device_only_ble_disabled(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4901,7 +4897,7 @@ async def test_bluetooth_provision_secure_device_with_restart_required(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -4961,7 +4957,7 @@ async def test_bluetooth_provision_secure_device_fails_gracefully(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -5012,7 +5008,7 @@ async def test_zeroconf_aborts_idle_ble_flow(
) -> None:
"""Test zeroconf discovery aborts idle BLE flow (lines 316-321)."""
# Start BLE discovery flow and leave it idle at bluetooth_confirm
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

ble_result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down Expand Up @@ -5070,7 +5066,7 @@ async def test_bluetooth_flow_abort_cleans_up_ble_connection(
{"ssid": "MyNetwork", "rssi": -50, "auth": 2}
]

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

# Start BLE flow
result = await hass.config_entries.flow.async_init(
Expand Down Expand Up @@ -5108,7 +5104,7 @@ async def test_bluetooth_ble_initialize_failure_cleans_up(
mock_device = create_mock_rpc_device()
mock_device.initialize = AsyncMock(side_effect=DeviceConnectionError)

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

# Start BLE flow
result = await hass.config_entries.flow.async_init(
Expand Down Expand Up @@ -5155,7 +5151,7 @@ async def test_bluetooth_ble_shutdown_exception_handled(
# Make shutdown raise an exception
mock_ble_rpc_device.shutdown.side_effect = RuntimeError("Shutdown failed")

inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

# Start BLE flow
result = await hass.config_entries.flow.async_init(
Expand Down Expand Up @@ -5192,7 +5188,7 @@ async def test_bluetooth_provision_ble_reconnect_fails_during_ip_fetch(
]

# Inject BLE device
inject_bluetooth_service_info_bleak(hass, BLE_DISCOVERY_INFO)
await _async_inject_ble_discovery(hass, BLE_DISCOVERY_INFO)

result = await hass.config_entries.flow.async_init(
DOMAIN,
Expand Down