#Active Connections across Multiple Adapters
1 messages Β· Page 1 of 1 (latest)
@remote cipher Ok, I know you don't like this, but I gave it a try anyway since it was the easiest way for me to support multiple adapters and at least not just not working when there are BTProxies with better rssi (device needs pairing, BTProxy wins the rssi competition and communication never happens)
Suddenly, all connected π
oh, can't send screenshots :/
5 to the FSC-BP119 (CSR8510A)
2 to the LM1010 (BCM20702A0)
I registered a callback on discovery and store the ble_devices in a list inside the integration. To connect I pick the first from the list. Each time a connection fails, i rotate the list. π
That is probably just fine for your use case, we just can't do that in the core bluetooth integration since it would have the issues I mentioned above
Maybe I should use MANAGER.async_get_devices_by_address(address, True) instead?
Again, I understand it is not optimal, but in this case it is better to have a lower rssi connection than no communication at all, and forever π
MANAGER isn't meant to be called outside of the bluetooth integration. Anything not called via the api is subject to break in the future without notice https://github.com/home-assistant/core/blob/dev/homeassistant/components/bluetooth/api.py
Oh, ok, thanks
I think it would be fine to add async_get_discovered_devices_and_advertisement_data_by_address to the api though if you need that since its effectively a wrapper around the bleak methods which are already public
And that would have the same devices multiple times (discovered by each adapter)?
That would be useful to me, thanks. I'm not sure if it will end up encouraging ppl to make these kinds of hacks like i did though
If you want to do a PR to add it to the API I think we can get that reviewed pretty quickly
Ok, I'll check it is giving me all devices first
I'm getting 3 ble_devices per mac address.
In the case of no DATA_MANAGER, should I return None like the other methods or an empty list?
if DATA_MANAGER not in hass.data:
return []
That's probably not needed anymore since all integrations that use bluetooth make bluetooth a dep and it will always be there
Oh, just finished opening an issue and filling the PR description.
I left it for consistency with the rest of the file. Also left it as an empty array as I saw another fn that returns false when there is no DATA_MANAGER instead of None (so I assume None is a special case handled somewhere else in a special manner)
Here it is π https://github.com/home-assistant/core/pull/83733
Sorry I can't test it locally because I have a mac and i am very afraid of killing my in-use HA.
I did test the code inside it in my integration.
Thanks for encouraging me for the PR for something you could have done in like 30 seconds π
And thanks for all your answers, and your amazing work on bluetooth!
BTW, I said before I had achieved the same by using the discovery data. That only worked because another adapter had discovered 2 devices first, but then I realised the callback is only called once per device (irrespective of how many adapters see it).
Finally, I just realised that since establish_connection calls freshen_device, my trick only works because the rssi of the device I'm passing is within RSSI_SWITCH_THRESHOLD of the best known one.
I may need to drop using the bleak_retry_connector for this to work reliably. :/
You could construct a fake BLEDevice with a high RSSI
Not 100% sure it won't be too smart though and realize its stale
True. Although if it becomes stale, it will sooner or later pick the next one. I'll experiment a bit more and get a feeling for all of this. Maybe I end up trying to make the better solution you expect in some days π
I've been playing some more, and after much frustration I realised that all objects are wrapped, so it doesn't matter how I tell it which adapter to use, it ends up deciding which adapter to use by iself in
core/homeassistant/components/bluetooth/wrappers.py:192
async def connect(self, **kwargs: Any) -> bool:
...
wrapped_backend = self._async_get_best_available_backend_and_device()
Is this logic not kind of repeated in the get_bluez_device inside the retry connector?
get_bluez_device predates that logic and there is always a chance the bus and bleak are not fully in sync
I think we need to implement connection management for the local adapters. I donβt really see a better solution that is compatible with the existing architecture
I just switched my production system to use the remotes though, so I probably have a few more weeks of testing and tweaking and adjusting before I switch anything to local adapters again
Ok, thank you. Maybe I should buy another small linux computer so I can help.
You will not like this temporary hack. The only way I've found to be able to use multiple local adapters while surviving a BTProxy getting in the way of a pairable device, was to bypass all what HA does.
The variable self._i is increased each time there is a communication error somewhere else in the component, so this does round robin on the possible paths.
MANAGER = cast(BluetoothManager, self._hass.data[DATA_MANAGER])
devices = (
MANAGER.async_get_discovered_devices_and_advertisement_data_by_address(
address=self._mac, connectable=True
)
)
self._i = self._i % len(devices)
ble_device = devices[self._i][0]
UnwrappedBleak = BleakClient.__bases__[0]
self._conn = UnwrappedBleak(
ble_device,
dangerous_use_bleak_cache=True,
)
await self._conn.connect()
Now the integration can use all 3 local adapters.
Seems workable given the constraints. Its far as levels of horrible its not so bad since it doesn't affect the state of an other integration (beyond using up a connection).
MANAGER = cast(BluetoothManager, self._hass.data[DATA_MANAGER])
devices = (
MANAGER.async_get_discovered_devices_and_advertisement_data_by_address(
address=self._mac, connectable=True
)
)
That part is pretty much assured to break in the future though if we don't expose it via an api