diff --git a/tests/components/shelly/test_config_flow.py b/tests/components/shelly/test_config_flow.py index 2b77140e97cce0..d1cb61dd8dc63c 100644 --- a/tests/components/shelly/test_config_flow.py +++ b/tests/components/shelly/test_config_flow.py @@ -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() + + DISCOVERY_INFO = ZeroconfServiceInfo( ip_address=ip_address("1.1.1.1"), ip_addresses=[ip_address("1.1.1.1")], @@ -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} @@ -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 @@ -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} ) @@ -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} @@ -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, @@ -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", @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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( @@ -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( @@ -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( @@ -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,