-
-
Save pawelszydlo/a7d842b0a118373ed13a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
""" | |
Script for dumping Bluetooth pairings from OS X to a registry file, for Windows | |
import. This will allow you to have your Bluetooth devices paired with both | |
operating systems at the same time. | |
In case of problems with Windows registry entries: pair your device with Windows | |
first, then with OS X, and then do the dump and import. | |
Latest version can be found at: | |
https://gist.github.com/a7d842b0a118373ed13a.git | |
""" | |
__author__ = '[email protected]' | |
import os | |
import plistlib | |
import sys | |
import subprocess | |
BLUED_PLIST = '/private/var/root/Library/Preferences/com.apple.Bluetoothd.plist' | |
def _choose_one(options, what='one'): | |
"""Force user to choose an option if more than one is available.""" | |
chosen = None | |
if not options: | |
return | |
elif len(options) == 1: | |
chosen = 0 | |
else: | |
print 'Choose %s:' % what | |
for number, line in enumerate(options): | |
print '%d. %s' % (number + 1, line) | |
while chosen is None or chosen < 0 or chosen >= len(options): | |
chosen = raw_input('Choose (1 - %d): ' % len(options)) | |
try: | |
chosen = int(chosen) - 1 | |
except ValueError: | |
chosen = None | |
return options[chosen] | |
def _run_command(command): | |
"""Run a shell command.""" | |
p = subprocess.Popen( | |
command, shell=True, | |
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
output = '\n'.join(p.stdout.readlines()) | |
retval = p.wait() | |
return retval, output | |
def _get_pairs(xml_data): | |
"""Extract host device id and pairings from plist xml.""" | |
plist = plistlib.readPlistFromString(xml_data) | |
keys_root = plist.get('LinkKeys') | |
if not keys_root: | |
print 'Key LinkKeys not found in blued.plist.' | |
return None, [] | |
hosts = keys_root.keys() | |
if not hosts: | |
print 'No Bluetooth hosts found in blued.plist.' | |
return None, [] | |
host = _choose_one(hosts, 'Bluetooth host device') | |
print 'Using Bluetooth host device %s...' % host | |
pairs = [] | |
for device_id, device_key in keys_root.get(host, {}).items(): | |
device_key = device_key.data.encode('hex_codec') | |
pairs.append([device_id, device_key]) | |
return host, pairs | |
def _write_reg_file(host_id, pair): | |
"""Write the pairing into a Windows registry file.""" | |
host_id = host_id.replace('-', '') | |
device_id = pair[0].replace('-', '') | |
key = pair[1] | |
# The key needs to be reversed and written as comma separated list of bytes | |
key = ','.join(reversed([key[i:i + 2] for i in range(0, len(key), 2)])) | |
reg_file = open('bt_pair_%s.reg' % device_id, 'w') | |
reg_file.write('Windows Registry Editor Version 5.00\r\n\r\n') | |
reg_file.write('[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\\' | |
'BTHPORT\Parameters\Keys\%s]\r\n' % host_id) | |
reg_file.write('"%s"=hex:%s\r\n' % (device_id, key)) | |
reg_file.close() | |
if __name__ == '__main__': | |
# This script should be run as root | |
if os.geteuid() != 0: | |
print 'You must run this script as root. Try:\nsudo %s' \ | |
% os.path.basename(__file__) | |
sys.exit(1) | |
# Get the keys from blued.plist | |
status, xml_data = _run_command( | |
'plutil -convert xml1 -o - %s' % BLUED_PLIST) | |
if status != 0: | |
print 'Cannot convert binary blued plist into xml.\n"%s"' % xml_data | |
sys.exit(2) | |
host_id, pairs = _get_pairs(xml_data) | |
if not pairs: | |
print 'No pairings found for host device %s.' % host_id | |
sys.exit(3) | |
# Choose which pair to dump | |
chosen = _choose_one(pairs, 'pairing') | |
# Dump the selected pair to registry file | |
print 'Dumping pairing (%s) to registry file...' % ' = '.join(chosen) | |
_write_reg_file(host_id, chosen) | |
print 'Done.' |
I'm not sure about older versions of macOS, but on 10.5.6, the file is /private/var/root/Library/Preferences/com.apple.bluetoothd.plist
, not /private/var/root/Library/Preferences/com.apple.Bluetoothd.plist
(no capital B in bluetoothd).
It likely doesn't matter if your disk is formatted case-insensitive - which is the default - but mine is case-sensitive and it broke.
Also you may want to consider updating your shebang to !#/usr/bin/env python2
Lifesaver! I had to add the permissions as @hmemcpy said. But after that it worked great
import can be done without clicking Regedit, Permissions etc
psexec64 -s reg import full\path\keys.reg
I tried a lot of ways, this one seems to be the best, without unneeded modification ot Registry permissions
psexec is a part of Sysinternals https://docs.microsoft.com/en-us/sysinternals/downloads/
Hi @pawelszydlo, not sure you'll get a notification about a comment on your gist, but a HUGE THANK YOU!!! It helped me share a pairing between my macOS and Windows (dual-boot)!! Amazing!
One comment I'd like to add is that on Windows 10 nowadays you don't have permission to write to that registry key, solved by right-clicking on
Keys
in regedit and adding your user to the permissions, checking both read and write options.After adding yourself, the registry file imports successfully, after which you can remove your permissions from
Keys
. Note that after removing yourself, you'll not see the data inside the key anymore, but it's there.Reboot, and your bluetooth headphones should pair in Windows on their own!
Thank you again!!