BLE: support passkey-protected companion radios (PIN + BlueZ pairing agent)#294
BLE: support passkey-protected companion radios (PIN + BlueZ pairing agent)#294drewmccal wants to merge 2 commits into
Conversation
Radios configured with a Bluetooth PIN couldn't be added: the SDK's MeshCore.create_ble() accepts a `pin=` for pairing, but the integration never passed one, so pairing failed and the connection dropped during GATT service discovery. - const: CONF_BLE_PIN - meshcore_api: MeshCoreAPI accepts ble_pin and forwards it to create_ble(pin=...); blank/whitespace is normalized to None so radios without a PIN don't attempt an empty-passkey pairing - config_flow: optional "Bluetooth PIN" field in the BLE setup and reconfigure steps, persisted in the config entry and threaded through validation - __init__: forward CONF_BLE_PIN into the API kwargs on setup - en.json translations for the new field - tests: tests/test_ble_pin.py (PIN normalization + create_ble forwarding) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Passing pin= to create_ble was necessary but not sufficient: bleak's client.pair() relies on a system BlueZ agent to answer BlueZ's passkey request, and the default Home Assistant OS agent is no-input, so passkey radios still failed with org.bluez.Error.AuthenticationFailed. Add ble_pairing_agent.py: registers a short-lived org.bluez.Agent1 (via dbus_fast, imported lazily) that returns the configured PIN for RequestPasskey/RequestPinCode, makes it the default agent for the duration of the connection attempt, then unregisters it. Once bonded, reconnects don't need the agent. meshcore_api.connect() registers the agent before create_ble and always unregisters it afterward. Everything is best-effort: if dbus_fast is unavailable or registration fails, it logs and proceeds exactly as before (no regression). - tests: agent fallback returns None without dbus_fast; existing connect forwarding tests still pass (agent path degrades gracefully) Note: ideally this agent belongs upstream in meshcore_py/ble_cx; doing it in the integration works because it registers at the system-bus default- agent level, independent of who calls pair(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
nice work, this solves a real gap (#265/#83). but it belongs a layer down in meshcore-py, not here. the SDK already owns this and is just missing the last piece: since it's not urgent, rather than carry a stopgap here and migrate later, let's do it right. filed meshcore-dev/meshcore_py#92 as the target:
happy to review the meshcore-py PR. the config-flow work stays. |
What
Adds support for BLE companion radios that require a passkey/PIN to pair (e.g. Elecrow ThinkNode-M1). Previously these failed at connect time with
org.bluez.Error.AuthenticationFailed.Why
bleak'sclient.pair()needs a BlueZ pairing agent to supply the passkey. HAOS's default agent is no-input/just-works, so a radio that demands a PIN can never complete bonding from the integration — the connection just fails.Changes
CONF_BLE_PINoptional field in the BLE config + reconfigure flows (blank = no PIN, normalized toNone).ble_pairing_agent.py(new): registers anorg.bluez.Agent1viadbus_fast(already present transitively viableak/habluetooth, imported lazily) that answersRequestPasskey/RequestPinCode/RequestConfirmation/etc. with the configured PIN. CapabilityKeyboardDisplay. Registered beforecreate_ble, unregistered infinally.meshcore_api.py: acceptsble_pin, registers/unregisters the agent around the BLE connect.create_bleforwarding, graceful fallback when the agent can't register.If agent registration fails, connection still proceeds (works for already-bonded / just-works radios), so this is non-regressive for existing setups.
🤖 Generated with Claude Code