Add a repair action for unresponsive Bluetooth serial ports on macOS - #2229
Open
edwin-truthsearch-io wants to merge 1 commit into
Open
edwin-truthsearch-io wants to merge 1 commit into
edwin-truthsearch-io wants to merge 1 commit into
Conversation
On macOS a Bluetooth serial port is left behind stale if the machine disappears while it is paired (it is switched off, unplugged, or the host is suspended): /dev/cu.<machine> still exists and opens successfully, but there is no serial connection behind it any longer, so nothing is sent or received. Measured on macOS 26.6.2, neither reopening the port, toggling Bluetooth, power-cycling the machine, nor IOBluetooth closeConnection()/openConnection() rebuilds it, and the Bluetooth daemon cannot be restarted while System Integrity Protection is engaged. Only removing the pairing destroys the stale device, after which pairing the machine again rebuilds a working serial port. Users currently have to do this by hand without any indication that it is what their situation requires. - adds artisanlib/bluetooth_macos.py performing that repair via IOBluetooth: it removes the pairing, rediscovers the machine by an inquiry (pairing a device that was not discovered first fails) and pairs it again - the machine is derived from the configured port by matching the paired devices against the port name macOS derives from their device name, so neither an address nor a device name is configured anywhere - adds a 'Repair' button to the serial port settings, enabled only for a port served by a paired Bluetooth device. As removing a pairing cannot be undone if the machine is unreachable, the repair is only ever triggered explicitly by the user, after a confirmation, and never automatically Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #2228, which recognizes an unresponsive Bluetooth serial port and tells the user to re-pair the machine. This adds the action that does it for them.
Why re-pairing is the only remedy
Measured on macOS 26.6.2 (Apple Silicon) with a Kaleido on Bluetooth SPP, with the port in the stale state —
/dev/cu.<machine>present,open()succeeding instantly, nothing sent or received:IOBluetoothDevice.closeConnection()isConnected()stays trueIOBluetoothDevice.openConnection()bluetoothdOperation not permitted while System Integrity Protection is engagedThe pairing record owns the serial binding, and nothing an application can reach rebuilds it — except removing that pairing.
The change
artisanlib/bluetooth_macos.pyperforms the repair through IOBluetooth: remove the pairing, rediscover the machine by an inquiry, pair it again. The inquiry is not optional — pairing a device built from a bare address fails with error 2; the device has to be discovered first, which is what the system Bluetooth settings do as well.Verified end to end against a real stale port: the repair completed in about 60 seconds, without any system dialog appearing, and the Kaleido transport reconnected on its own immediately afterwards.
No address or device name is configured anywhere. The machine is derived from the port the user already configured: macOS names a Bluetooth serial port after the device name (spaces and invalid characters replaced, optionally suffixed by the serial service name), so the paired devices are matched against the configured port. On a machine with 12 paired devices — keyboard, mouse, AirPods, phones — the lookup matched exactly one and returned
Nonefor a USB serial port and for/dev/cu.Bluetooth-Incoming-Port. That precision is the safety property that matters, since a mismatch would unpair the wrong hardware.Safety
Removing a pairing cannot be undone by the application if the machine is unreachable, so the repair is:
The app already ships
NSBluetoothAlwaysUsageDescription, which IOBluetooth requires; an unbundled process is killed by TCC without it.Notes
ports.pyand a new module only.🤖 Generated with Claude Code