Inside asyncio's coroutine you can use any function if it runs relatively fast (< 0.05 sec.), otherwise you risk to block event loop and through this get significant performance degradation.
If function runs relatively slow it still can be used inside asycnio's coroutine without side-effects, but only inside executor.
If function returns fast and later calls callback - it's a good situation. Such function/callback can be cast to nice asyncio's awaitable using asyncio.Future.
Note also that many asyncio's objects are not thread-safe by default.
Long story short, I don't see reason why asyncio can't be safely used with third-party library that uses threads if everything is implemented right.
But before you rewrite something in large code base you should know for certain why you need asyncio and, IMHO, have some experience with it on lesser code base.
Try to take single callback-based function and cast it to coroutine with asyncio.Future
. Try to execute multiple such coroutines simultaneously. See if you achieve what you want and if everything is going smoothly. Keep going this way.
Wrapping existing code to use asyncio instead of rewriting sounds like a good idea: you can do it iteratively and only for parts you use.