My current goal is to add an SDP record to the Bluetooth service running on a MacBook Pro, such that I can advertise this service to other Bluetooth devices. Ideally, I would like to accomplish this task in Python.
At this point in time, I have successfully created, added, and advertised with an SDP record on Linux under the BlueZ Bluetooth stack, however, I'm having a bit of trouble getting a similar process to work on MacOS.
After a bit of research, my understanding is that interacting with the Bluetooth API on MacOS involves using the IOBluetooth Objective-C framework, which can be accomplished in Python through pyobjc. On a side-note, I also understand that the CoreBluetooth framework exists, however, the functionality of this framework isn't a suitable fit since it lacks SDP record add/remove functionality.
I'm currently dealing with two problems:
1. Creating a MacOS suitable SDP record
MacOS uses the PLIST format for loading/specifying SDP records. I've got an existing record in XML format that's loadable by BlueZ. Would it be possible to convert this record to a PLIST format or should I be looking into rewriting the record from scratch?
2. Adding the SDP record through pyobjc
I'm able to interact/query basic Bluetooth functionality through pyobjc/IOBluetooth. The problem occurs when I attempt to use the IOBluetoothSDPServiceRecord class. From some examples I've seen elsewhere (one example here), this class seems to be the one you would use to add a new record? When I attempt to load/use this class, I'm not able to access any of its functions.
I apologize in advance if this seems like a trivial/or amateur problem to those familiar with Objective-C! Python is where my expertise lies, so interfacing with this language is a bit out of my wheelhouse.
I'll also provide a minimal working example of what I've been working at below:
IOBluetooth.py
import objc as _objc
_objc.loadBundle('IOBluetooth', globals(),\
bundle_path=u'/System/Library/Frameworks/IOBluetooth.framework')
bluetooth_test.py
from IOBluetooth import *
sdp = None
with open("record.plist", "r") as f:
sdp = f.read()
# This functions correctly
devs = IOBluetoothDevice.recentDevices_(0)
print(devs[0].getNameOrAddress())
# This does not
sdp_sr = IOBluetoothSDPServiceRecord.alloc().init()
sdp_sr.publishedServiceRecord(sdp)
Output of bluetooth_test.py
Apple Watch
Traceback (most recent call last):
File "bluetooth_test.py", line 12, in <module>
sdp_sr.publishedServiceRecord(sdp)
AttributeError: 'IOBluetoothSDPServiceRecord' object has no attribute 'publishedServiceRecord'
Any input on this would be much appreciated!
After working on this a bit more, I was able to come up with an answer. For those who happen to be working on something similar, I'll post how I solved this:
After a bit of searching, I deemed it better to rewrite the existing XML SDP record by hand. I couldn't find any utility that would handle the conversion of 16 bit Bluetooth UUIDs from XML to PLIST format.
The above IOBluetooth.py
file was unchanged from my question.
bluetooth_test.py
from IOBluetooth import *
from Cocoa import NSDictionary
import time
plist = NSDictionary.dictionaryWithContentsOfFile_("service.plist")
sdp_sr = IOBluetoothSDPServiceRecord.publishedServiceRecordWithDictionary_(plist)
time.sleep(10)
sdp_sr.removeServiceRecord()
print("Removed Service Record")
What really helped with exploring the IOBluetooth API was using the dir()
function in Python. You can actually toss a pyobjc Instance or Class at it and you'll receive a list of all the available methods.
Hope this helps someone in the future!