From 03be0d7eec5d9d61e04e5ab2873cfeb35cd77098 Mon Sep 17 00:00:00 2001 From: mpeter Date: Fri, 8 Aug 2025 19:46:28 +0200 Subject: [PATCH] refactored BluetoothHID.py, added comments for future maintainability --- BluetoothHID.py | 52 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/BluetoothHID.py b/BluetoothHID.py index fd851b5..3986eaf 100644 --- a/BluetoothHID.py +++ b/BluetoothHID.py @@ -5,6 +5,15 @@ import os import socket +# see section "2.5 PSMs and SPSMs": https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf +BLUETOOTH_PORT_HID_CONTROL = 0x0011 +BLUETOOTH_PORT_HID_INTERRUPT = 0x0013 + +# see section "3.3 SDP Service Class and Profile Identifiers" of https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf +# and Vol 3, Part B, section 2.5.1 of https://www.bluetooth.com/specifications/specs/core-specification-5-3/ +BLUETOOT_PROFILE_HID = "00001124-0000-1000-8000-00805f9b34fb" + + class BluetoothHIDProfile(dbus.service.Object): def __init__(self, bus, path): super(BluetoothHIDProfile, self).__init__(bus, path) @@ -43,40 +52,51 @@ def error_handler(e): class BluetoothHIDService(object): - PROFILE_PATH = "/org/bluez/bthid_profile" + PROFILE_PATH = "/org/bluez/emubthid_profile" HOST = 0 PORT = 1 def __init__(self, service_record, MAC): - self.P_CTRL = 0x0011 - self.P_INTR = 0x0013 self.SELFMAC = MAC + bus = dbus.SystemBus() bluez_obj = bus.get_object("org.bluez", "/org/bluez") manager = dbus.Interface(bluez_obj, "org.bluez.ProfileManager1") BluetoothHIDProfile(bus, self.PROFILE_PATH) - opts = { - "ServiceRecord": service_record, - "Name": "BTKeyboardProfile", - "RequireAuthentication": False, - "RequireAuthorization": False, - "Service": "MY BTKBD", - "Role": "server" - } sock_control = L2CAPSocket(True) sock_inter = L2CAPSocket(True) - sock_control.bind((self.SELFMAC, self.P_CTRL)) - sock_inter.bind((self.SELFMAC, self.P_INTR)) + # The PSM parameter sets the bluetooth "port" for L2CAP connections + # "Valid values in the first range are assigned by the Bluetooth SIG and indicate protocols." + # https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host/logical-link-control-and-adaptation-protocol-specification.html + sock_control.bind((self.SELFMAC, BLUETOOTH_PORT_HID_CONTROL)) + sock_inter.bind((self.SELFMAC, BLUETOOTH_PORT_HID_INTERRUPT)) - manager.RegisterProfile(self.PROFILE_PATH, "00001124-0000-1000-8000-00805f9b34fb", opts) - print("Registered") + # IDEs dont see this function because bluetooth.BluetoothSocket imports them dynamically sock_control.listen(1) sock_inter.listen(1) - print(f"waiting for connection at controller {MAC}, please double check with the MAC in bluetoothctl") + + manager.RegisterProfile(self.PROFILE_PATH, BLUETOOT_PROFILE_HID, { + "Name": "Emulated Bluetooth keyboard", + # "Service": "MY BTKBD", # this refers to the bluetooth spec service class uuid + "Role": "server", + "RequireAuthentication": True, + "RequireAuthorization": True, + "ServiceRecord": service_record, + # note: it seems as if socket creation could be replaced with working through DBus (see PSM option and NewConnection function). + # maybe currently the conenction is somehow handled at 2 places at the same time? + # + # https://www.bluez.org/bluez-5-api-introduction-and-porting-guide/ + # "The new Profile1 interface (and removal of org.bluez.Service)" + }) + + print("Registered Bluetooth HID profile in Bluez") + + print(f"Waiting for connection at controller {MAC}, please double check with the MAC in bluetoothctl") + self.ccontrol, cinfo = sock_control.accept() print("Control channel connected to " + cinfo[self.HOST]) self.cinter, cinfo = sock_inter.accept()