README.md
Rendering markdown...
#!/usr/bin/env python3
# Written by Kevin Mitchell
# Released as open source under GPLv3
import pexpect
import time
import re
class BluetoothCtl:
def __init__(self):
self.child = pexpect.spawn("bluetoothctl", encoding='utf-8')
self.child.expect("#")
def send_command(self, command, timeout=5):
self.child.sendline(command)
self.child.expect("#", timeout=timeout)
return self.child.before
def power_on(self):
self.send_command("power on")
print("BLE dongle powered on")
def power_off(self):
self.send_command("power off")
print("BLE dongle powered off")
def scan(self, timeout:int=2):
self.send_command("scan on")
time.sleep(timeout)
scan_output = self.child.before
self.send_command("scan off")
print("Scan turned off")
print("Scan output:\n", scan_output)
if self.check_and_handle_error(scan_output):
print("Retrying scan after handling error...")
self.send_command("scan on")
time.sleep(10) # Adjust the sleep time if needed
scan_output = self.child.before
self.send_command("scan off")
print("Retry scan output:\n", scan_output)
return scan_output
def check_and_handle_error(self, scan_output):
if "Failed to stop discovery: org.bluez.Error.InProgress" in scan_output:
print("Error encountered: Failed to stop discovery. Power cycling the BLE dongle.")
self.power_off()
time.sleep(2)
self.power_on()
return True
return False
def find_device(self, device_name):
scan_output = self.scan()
pattern = re.compile(r"((?:[0-9A-Fa-f]{{2}}:){{5}}[0-9A-Fa-f]{{2}})\s+{}".format(re.escape(device_name)), re.IGNORECASE)
match = pattern.search(scan_output)
if match:
return match.group(1) # Return the MAC address
return None
def send_pairing_request(self, mac_address):
print(f"Sending Pairing Request to {mac_address}")
pairing_request_data = (
"000084008000060001000004100000010000041000000100000410000001000004100000010000041000000100000410"
"000001000004100000010000041000000100000410000001000004100000010000041000000100000410000001000004"
"100000010000041000000100000410000001000004100000010000041000000100000410000001000004100000010000"
"0410000001"
)
self.send_command(f"pair {mac_address} {pairing_request_data}", timeout=5)
def send_pairing_confirm(self, mac_address):
print(f"Sending Pairing Confirm to {mac_address}")
pairing_confirm_data = (
"0000150011000600031d9adfe958dd809adcbd7c227274"
)
self.send_command(f"pairing confirm {mac_address} {pairing_confirm_data}", timeout=5)
def send_pairing_random(self, mac_address):
print(f"Sending Pairing Random to {mac_address}")
pairing_random_data = (
"000015001100060004c0c0c0c0c0c0c0c0c0c0c0c0c0c0"
)
self.send_command(f"pairing random {mac_address} {pairing_random_data}", timeout=5)
def send_le_start_encryption(self, mac_address):
print(f"Sending LE Start Encryption to {mac_address}")
long_term_key = "bfb74c1f07f748a671e841deef3a05c2"
self.send_command(f"le start-encryption {mac_address} {long_term_key}", timeout=5)
def main():
bt = BluetoothCtl()
bt.power_on()
time.sleep(1)
target_device_name = "Multi Role"
def send_sequence():
bt.send_pairing_request(mac_address)
time.sleep(2)
bt.send_pairing_confirm(mac_address)
time.sleep(2)
bt.send_pairing_random(mac_address)
time.sleep(2)
bt.send_le_start_encryption(mac_address)
time.sleep(2)
print("Sequence of pairing messages and LE Start Encryption sent successfully.")
try:
found_device = True
while True:
mac_address = bt.find_device(target_device_name)
print("Searching for device: Multi Role")
if mac_address:
if not found_device:
print(f"Device '{target_device_name}' found with MAC address {mac_address}.")
found_device = True
send_sequence(mac_address)
print(f"Connected to {mac_address}.")
time.sleep(2)
else:
if found_device:
print(f"Device '{target_device_name}' no longer found.")
found_device = False
print(f"Device '{target_device_name}' not found. Retrying...")
time.sleep(2)
bt.power_on()
time.sleep(2)
except KeyboardInterrupt:
print("Exiting...")
finally:
bt.power_off()
if __name__ == "__main__":
main()