Skip to content

Conversation

@arcaartem
Copy link
Contributor

@arcaartem arcaartem commented Jul 24, 2025

Summary

This PR introduces an alternative method for querying paired Bluetooth devices using the public system_profiler command instead of private IOBluetooth framework APIs. This provides the same functionality while using only documented system interfaces and resolves connection status issues with some multi-point devices.

New Features

  • --use-system-profiler command-line flag: Use system_profiler instead of IOBluetooth APIs
  • BLUEUTIL_USE_SYSTEM_PROFILER=1 environment variable: Set default behaviour to use system_profiler
  • Backwards compatibility: Original IOBluetooth API method remains the default

Benefits

  1. Fixes multi-point device issues: Resolves connection status reporting problems for devices that can connect to
    multiple devices simultaneously
  2. Flexible usage: Can be enabled per-command or globally via environment variable

Implementation Details

  • Created get_paired_devices_subprocess() that calls system_profiler SPBluetoothDataType -xml
  • Implemented MockBluetoothDevice class that provides the same interface as IOBluetoothDevice
  • Added wrapper function get_paired_devices() to choose between methods based on flag
  • Updated all paired device queries (--paired, --connected, device search) to use the new system

Usage Examples

  # Use IOBluetooth APIs (default)
  blueutil --paired

  # Use system_profiler via command-line flag
  blueutil --use-system-profiler --paired

  # Use system_profiler via environment variable
  export BLUEUTIL_USE_SYSTEM_PROFILER=1
  blueutil --paired
  blueutil --connected

Backward Compatibility

This change is fully backward compatible. The default behaviour remains unchanged, using IOBluetooth APIs. The new functionality is opt-in only.

@arcaartem arcaartem force-pushed the multi-point-workaround branch 2 times, most recently from 0c83667 to 88b16c8 Compare July 25, 2025 08:53
@arcaartem arcaartem changed the title A working solution Add system_profiler alternative method for paired device queries Jul 25, 2025
@arcaartem arcaartem marked this pull request as ready for review July 25, 2025 08:59
@arcaartem
Copy link
Contributor Author

This change with the use of the new method should address #87

Unfortunately I don't have many devices with multi-point connection so I can't test it thoroughly.

@toy
Copy link
Owner

toy commented Jul 25, 2025

Right away noticed the point about usage of private APIs, but IOBluetoothDevice pairedDevices is public. blueutil uses only few private functions for power and discoverability and one method to unpair, everything else is public.

@arcaartem
Copy link
Contributor Author

Right away noticed the point about usage of private APIs, but IOBluetoothDevice pairedDevices is public. blueutil uses only few private functions for power and discoverability and one method to unpair, everything else is public.

Ah soz, I'll update the copy.

@arcaartem arcaartem force-pushed the multi-point-workaround branch 2 times, most recently from ad87836 to ee001a3 Compare July 25, 2025 21:07
@arcaartem
Copy link
Contributor Author

@toy updated the PR with the correct wording, could you take another look when you have a moment, please?

Copy link
Owner

@toy toy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First thank you for creating the PR

I think it is better at first to add only environment variable way of switching to system_profile method of listing devices. Also it maybe better (not sure about this) to use variable like BLUEUTIL_DEVICE_LISTING=system_profile, what do you think?

And please run clang-format, I'll add a runner for that

This allows the correct connection reporting for some devices with multi-point connection capability (e.g. Bose QC Ultra)
@arcaartem
Copy link
Contributor Author

arcaartem commented Aug 2, 2025

First thank you for creating the PR

My pleasure and thanks for writing the utility, it's an indispensable part of my workflow, such convenience!

I think it is better at first to add only environment variable way of switching to system_profile method of listing devices.

No worries, I'll remove the argument.

Also it maybe better (not sure about this) to use variable like BLUEUTIL_DEVICE_LISTING=system_profile, what do you think?

I think, if we had 3 or more options for device listings, this would have worked really well but since we only have one alternative, I'm leaning towards BLUEUTIL_USE_SYSTEM_PROFILER. If that's ok with you, I'd like to keep it as is and we can revisit if there are other alternatives in the future.

@arcaartem arcaartem force-pushed the multi-point-workaround branch from ee001a3 to a166dee Compare August 2, 2025 20:22
@toy
Copy link
Owner

toy commented Aug 2, 2025

I remembered also few things that I thought about: please mark the option in readme and help output as experimental and I think enabling the feature will break part of other calls, so the mock object should probably use [IOBluetoothDevice deviceWithAddress] and delegate other messages to it

@arcaartem arcaartem force-pushed the multi-point-workaround branch from a166dee to 91b30c3 Compare August 2, 2025 20:25
@arcaartem arcaartem force-pushed the multi-point-workaround branch from 91b30c3 to 84af1da Compare August 2, 2025 20:56
@arcaartem
Copy link
Contributor Author

Thanks for the feedback @toy , I've addressed them to the best of my ability (I'm fairly new to Objective-C), please let me know if you have any other feedback for me.

I remembered also few things that I thought about: please mark the option in readme and help output as experimental and I think enabling the feature will break part of other calls, so the mock object should probably use [IOBluetoothDevice deviceWithAddress] and delegate other messages to it

Good call. I've mark the new method experimental and mock now falls back to the actual object.

Copy link
Owner

@toy toy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to check how it behaves in the next few days

Copy link
Owner

@toy toy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use system_profiler way and few methods I checked seem to work well. And I looked over the code again

@arcaartem
Copy link
Contributor Author

Thank you so much for reviewing it again and your continued support!

I have addressed your most recent feedback, let me know what you think!

Copy link
Owner

@toy toy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added a changelog entry and did a bit of cleanup

Maybe check how it works for you and I can merge'n'release

@arcaartem
Copy link
Contributor Author

I just added a changelog entry and did a bit of cleanup

Maybe check how it works for you and I can merge'n'release

Thanks @toy , I've been using the modified version for the past week, works fine from my end; my headphone connection is detected correctly with this approach

@toy toy merged commit b1f173b into toy:main Aug 18, 2025
1 check passed
@toy
Copy link
Owner

toy commented Aug 18, 2025

Released as v2.13.0

@arcaartem arcaartem deleted the multi-point-workaround branch August 18, 2025 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants