Skip to content

feat: auto-reconnect peripherals that drop while held on this Mac#36

Merged
MegaManSec merged 1 commit into
mainfrom
feat/auto-reconnect-dropped-peripherals
Jun 2, 2026
Merged

feat: auto-reconnect peripherals that drop while held on this Mac#36
MegaManSec merged 1 commit into
mainfrom
feat/auto-reconnect-dropped-peripherals

Conversation

@MegaManSec
Copy link
Copy Markdown
Owner

What & why

Apple's Magic peripherals sometimes fail to reconnect after the Bluetooth radio sleeps (a lid-close, say) and stay stuck until they're physically powered off and on — a macOS-side bug we can't fix, but can recover from. Today the only reconnect attempt is a single shot on wake, so if the device is still stuck at that moment nothing brings it back and the user has to reconnect by hand after power-cycling it.

This adds a persistent auto-reconnect watcher: a peripheral that drops while it should be on this Mac is retried until it's back in range (or a 1 h window expires), so reconnection just happens once the device reappears.

How it works

  • Armed on a genuine local disconnect (handlePeripheralDisconnected) and, on wake, for everything this Mac was connected to right before sleep (connectedBeforeSleep) — not just the peripherals we actively handed off — so a device that dropped on a lid-close with no peer to take it is covered too. Deliberate releases (handoff, "Remove from PC", sleep) are flagged so they never arm it.
  • Reclaimed only once the device is visible again (RSSI ≠ 127) and a read-only HOLDS_ONE query confirms the peer isn't using it (peer "yes" → leave it; "no" or unreachable → take it). It never tells the peer to disconnect, so an in-use peripheral on the other Mac is never stolen.
  • Probes are a cheap RSSI read on a timer that runs only while something is armed; watcher retries pair silently (the "Pairing Timed Out" notification stays for interactive connects); a live isConnected() check means a device macOS reconnected on its own is dropped from the watchlist rather than needlessly re-paired.
  • Gated by a default-on "Reconnect peripherals if they drop" toggle in Settings → Other. With it off, wake behavior is exactly as before (one-shot reclaim of the released subset).

Safety

Two independent gates keep it from grabbing a peripheral the other Mac is actively using: the RSSI visibility gate (a peripheral connected to the peer isn't advertising → invisible to us → never attempted), and the 5 s wake delay plus HOLDS_ONE's own 5 s connect window, which absorb Wi-Fi reassociation lag on wake.

Testing

Builds clean (CODE_SIGNING_ALLOWED=NO); swift format applied. No test target in this project. The sleep/wake/power-cycle recovery and the RSSI "is it back?" timing want a real two-Mac + Magic-device check — IOBluetooth sleep behavior can't be exercised locally.

Apple's Magic peripherals sometimes fail to reconnect after the Bluetooth
radio sleeps (e.g. a lid-close) and stay stuck until they're physically
powered off and on — a macOS-side bug we can't fix, but can recover from. A
persistent watcher now retries a peripheral that drops while it should be on
this Mac until it's back in range (or a 1h window expires), so the user no
longer has to manually reconnect after flipping the device off and on.

- Armed on a genuine disconnect (handlePeripheralDisconnected) and, on wake,
  for everything this Mac was connected to right before sleep
  (connectedBeforeSleep) -- not just the peripherals we actively handed off --
  so a device that dropped on a peerless lid-close is covered too. Deliberate
  releases (handoff, "Remove from PC", sleep) are flagged so they never arm it.
- Each probe is a cheap RSSI read; only once the device is visible again does
  it consult the peer with a read-only HOLDS_ONE query and reclaim it unless
  the peer is actively using it. It never tells the peer to disconnect, so an
  in-use peripheral on the other Mac is never stolen.
- Watcher retries pair silently; the "Pairing Timed Out" notification stays
  reserved for interactive connects.
- Gated by a default-on "Reconnect peripherals if they drop" toggle in
  Settings -> Other.
@MegaManSec MegaManSec merged commit e16e879 into main Jun 2, 2026
2 checks passed
@MegaManSec MegaManSec deleted the feat/auto-reconnect-dropped-peripherals branch June 2, 2026 22:04
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 2, 2026

🎉 This PR is included in version 2.11.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant