From 651e44e0b6ad37825c244015e76a3d3c03b11c2d Mon Sep 17 00:00:00 2001 From: skarnataki Date: Tue, 10 Oct 2023 11:43:57 +0000 Subject: [PATCH] Submitting review comment fix: header function and extra lines. Executed formatter on file. --- bumble/hid.py | 9 +- examples/hid_key_map.py | 464 +++++++++++++++++----------------- examples/hid_report_parser.py | 67 +++-- examples/run_hid_host.py | 254 +++++++++++++------ 4 files changed, 452 insertions(+), 342 deletions(-) diff --git a/bumble/hid.py b/bumble/hid.py index 41d9d8f..1a8e5f9 100644 --- a/bumble/hid.py +++ b/bumble/hid.py @@ -208,11 +208,11 @@ class Host(EventEmitter): def on_l2cap_channel_open(self, l2cap_channel: l2cap.Channel) -> None: if l2cap_channel.psm == HID_CONTROL_PSM: - self.l2cap_ctrl_channel = l2cap_channel - self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu + self.l2cap_ctrl_channel = l2cap_channel # type: ignore + self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu # type: ignore else: - self.l2cap_intr_channel = l2cap_channel - self.l2cap_intr_channel.sink = self.on_intr_pdu + self.l2cap_intr_channel = l2cap_channel # type: ignore + self.l2cap_intr_channel.sink = self.on_intr_pdu # type: ignore logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}') def on_ctrl_pdu(self, pdu: bytes) -> None: @@ -300,4 +300,3 @@ class Host(EventEmitter): msg = bytearray([header]) logger.debug(f'>>> HID CONTROL VIRTUAL CABLE UNPLUG, PDU: {msg.hex()}') self.l2cap_ctrl_channel.send_pdu(msg) # type: ignore - diff --git a/examples/hid_key_map.py b/examples/hid_key_map.py index aa919fd..5f61e7f 100644 --- a/examples/hid_key_map.py +++ b/examples/hid_key_map.py @@ -2,247 +2,247 @@ # letters shift_map = { - 'a' : 'A', - 'b' : 'B', - 'c' : 'C', - 'd' : 'D', - 'e' : 'E', - 'f' : 'F', - 'g' : 'G', - 'h' : 'H', - 'i' : 'I', - 'j' : 'J', - 'k' : 'K', - 'l' : 'L', - 'm' : 'M', - 'n' : 'N', - 'o' : 'O', - 'p' : 'P', - 'q' : 'Q', - 'r' : 'R', - 's' : 'S', - 't' : 'T', - 'u' : 'U', - 'v' : 'V', - 'w' : 'W', - 'x' : 'X', - 'y' : 'Y', - 'z' : 'Z', - # numbers - '1' : '!', - '2' : '@', - '3' : '#', - '4' : '$', - '5' : '%', - '6' : '^', - '7' : '&', - '8' : '*', - '9' : '(', - '0' : ')', - # symbols - '-' : '_', - '=' : '+', - '[' : '{', - ']' : '}', - '\\' : '|', - ';' : ':', - '\'' : '"', - ',' : '<', - '.' : '>', - '/' : '?', - '`' : '~' + 'a': 'A', + 'b': 'B', + 'c': 'C', + 'd': 'D', + 'e': 'E', + 'f': 'F', + 'g': 'G', + 'h': 'H', + 'i': 'I', + 'j': 'J', + 'k': 'K', + 'l': 'L', + 'm': 'M', + 'n': 'N', + 'o': 'O', + 'p': 'P', + 'q': 'Q', + 'r': 'R', + 's': 'S', + 't': 'T', + 'u': 'U', + 'v': 'V', + 'w': 'W', + 'x': 'X', + 'y': 'Y', + 'z': 'Z', + # numbers + '1': '!', + '2': '@', + '3': '#', + '4': '$', + '5': '%', + '6': '^', + '7': '&', + '8': '*', + '9': '(', + '0': ')', + # symbols + '-': '_', + '=': '+', + '[': '{', + ']': '}', + '\\': '|', + ';': ':', + '\'': '"', + ',': '<', + '.': '>', + '/': '?', + '`': '~', } # hex map # modifier keys mod_keys = { - '00' : '', - '01' : 'left_ctrl', - '02' : 'left_shift', - '04' : 'left_alt', - '08' : 'left_meta', - '10' : 'right_ctrl', - '20' : 'right_shift', - '40' : 'right_alt', - '80' : 'right_meta' + '00': '', + '01': 'left_ctrl', + '02': 'left_shift', + '04': 'left_alt', + '08': 'left_meta', + '10': 'right_ctrl', + '20': 'right_shift', + '40': 'right_alt', + '80': 'right_meta', } # base keys base_keys = { - # meta - '00' : '', # none - '01' : 'error_ovf', - # letters - '04' : 'a', - '05' : 'b', - '06' : 'c', - '07' : 'd', - '08' : 'e', - '09' : 'f', - '0a' : 'g', - '0b' : 'h', - '0c' : 'i', - '0d' : 'j', - '0e' : 'k', - '0f' : 'l', - '10' : 'm', - '11' : 'n', - '12' : 'o', - '13' : 'p', - '14' : 'q', - '15' : 'r', - '16' : 's', - '17' : 't', - '18' : 'u', - '19' : 'v', - '1a' : 'w', - '1b' : 'x', - '1c' : 'y', - '1d' : 'z', - # numbers - '1e' : '1', - '1f' : '2', - '20' : '3', - '21' : '4', - '22' : '5', - '23' : '6', - '24' : '7', - '25' : '8', - '26' : '9', - '27' : '0', - # misc - '28' : 'enter', #enter \n - '29' : 'esc', - '2a' : 'backspace', - '2b' : 'tab', - '2c' : 'spacebar', #space - '2d' : '-', - '2e' : '=', - '2f' : '[', - '30' : ']', - '31' : '\\', - '32' : '=', - '33' : '_SEMICOLON', - '34' : 'KEY_APOSTROPHE', - '35' : 'KEY_GRAVE', - '36' : 'KEY_COMMA', - '37' : 'KEY_DOT', - '38' : 'KEY_SLASH', - '39' : 'KEY_CAPSLOCK', - '3a' : 'KEY_F1', - '3b' : 'KEY_F2', - '3c' : 'KEY_F3', - '3d' : 'KEY_F4', - '3e' : 'KEY_F5', - '3f' : 'KEY_F6', - '40' : 'KEY_F7', - '41' : 'KEY_F8', - '42' : 'KEY_F9', - '43' : 'KEY_F10', - '44' : 'KEY_F11', - '45' : 'KEY_F12', - '46' : 'KEY_SYSRQ', - '47' : 'KEY_SCROLLLOCK', - '48' : 'KEY_PAUSE', - '49' : 'KEY_INSERT', - '4a' : 'KEY_HOME', - '4b' : 'KEY_PAGEUP', - '4c' : 'KEY_DELETE', - '4d' : 'KEY_END', - '4e' : 'KEY_PAGEDOWN', - '4f' : 'KEY_RIGHT', - '50' : 'KEY_LEFT', - '51' : 'KEY_DOWN', - '52' : 'KEY_UP', - '53' : 'KEY_NUMLOCK', - '54' : 'KEY_KPSLASH', - '55' : 'KEY_KPASTERISK', - '56' : 'KEY_KPMINUS', - '57' : 'KEY_KPPLUS', - '58' : 'KEY_KPENTER', - '59' : 'KEY_KP1', - '5a' : 'KEY_KP2', - '5b' : 'KEY_KP3', - '5c' : 'KEY_KP4', - '5d' : 'KEY_KP5', - '5e' : 'KEY_KP6', - '5f' : 'KEY_KP7', - '60' : 'KEY_KP8', - '61' : 'KEY_KP9', - '62' : 'KEY_KP0', - '63' : 'KEY_KPDOT', - '64' : 'KEY_102ND', - '65' : 'KEY_COMPOSE', - '66' : 'KEY_POWER', - '67' : 'KEY_KPEQUAL', - '68' : 'KEY_F13', - '69' : 'KEY_F14', - '6a' : 'KEY_F15', - '6b' : 'KEY_F16', - '6c' : 'KEY_F17', - '6d' : 'KEY_F18', - '6e' : 'KEY_F19', - '6f' : 'KEY_F20', - '70' : 'KEY_F21', - '71' : 'KEY_F22', - '72' : 'KEY_F23', - '73' : 'KEY_F24', - '74' : 'KEY_OPEN', - '75' : 'KEY_HELP', - '76' : 'KEY_PROPS', - '77' : 'KEY_FRONT', - '78' : 'KEY_STOP', - '79' : 'KEY_AGAIN', - '7a' : 'KEY_UNDO', - '7b' : 'KEY_CUT', - '7c' : 'KEY_COPY', - '7d' : 'KEY_PASTE', - '7e' : 'KEY_FIND', - '7f' : 'KEY_MUTE', - '80' : 'KEY_VOLUMEUP', - '81' : 'KEY_VOLUMEDOWN', - '85' : 'KEY_KPCOMMA', - '87' : 'KEY_RO', - '88' : 'KEY_KATAKANAHIRAGANA', - '89' : 'KEY_YEN', - '8a' : 'KEY_HENKAN', - '8b' : 'KEY_MUHENKAN', - '8c' : 'KEY_KPJPCOMMA', - '90' : 'KEY_HANGEUL', - '91' : 'KEY_HANJA', - '92' : 'KEY_KATAKANA', - '93' : 'KEY_HIRAGANA', - '94' : 'KEY_ZENKAKUHANKAKU', - 'b6' : 'KEY_KPLEFTPAREN', - 'b7' : 'KEY_KPRIGHTPAREN', - 'e0' : 'KEY_LEFTCTRL', - 'e1' : 'KEY_LEFTSHIFT', - 'e2' : 'KEY_LEFTALT', - 'e3' : 'KEY_LEFTMETA', - 'e4' : 'KEY_RIGHTCTRL', - 'e5' : 'KEY_RIGHTSHIFT', - 'e6' : 'KEY_RIGHTALT', - 'e7' : 'KEY_RIGHTMETA', - 'e8' : 'KEY_MEDIA_PLAYPAUSE', - 'e9' : 'KEY_MEDIA_STOPCD', - 'ea' : 'KEY_MEDIA_PREVIOUSSONG', - 'eb' : 'KEY_MEDIA_NEXTSONG', - 'ec' : 'KEY_MEDIA_EJECTCD', - 'ed' : 'KEY_MEDIA_VOLUMEUP', - 'ee' : 'KEY_MEDIA_VOLUMEDOWN', - 'ef' : 'KEY_MEDIA_MUTE', - 'f0' : 'KEY_MEDIA_WWW', - 'f1' : 'KEY_MEDIA_BACK', - 'f2' : 'KEY_MEDIA_FORWARD', - 'f3' : 'KEY_MEDIA_STOP', - 'f4' : 'KEY_MEDIA_FIND', - 'f5' : 'KEY_MEDIA_SCROLLUP', - 'f6' : 'KEY_MEDIA_SCROLLDOWN', - 'f7' : 'KEY_MEDIA_EDIT', - 'f8' : 'KEY_MEDIA_SLEEP', - 'f9' : 'KEY_MEDIA_COFFEE', - 'fa' : 'KEY_MEDIA_REFRESH', - 'fb' : 'KEY_MEDIA_CALC' + # meta + '00': '', # none + '01': 'error_ovf', + # letters + '04': 'a', + '05': 'b', + '06': 'c', + '07': 'd', + '08': 'e', + '09': 'f', + '0a': 'g', + '0b': 'h', + '0c': 'i', + '0d': 'j', + '0e': 'k', + '0f': 'l', + '10': 'm', + '11': 'n', + '12': 'o', + '13': 'p', + '14': 'q', + '15': 'r', + '16': 's', + '17': 't', + '18': 'u', + '19': 'v', + '1a': 'w', + '1b': 'x', + '1c': 'y', + '1d': 'z', + # numbers + '1e': '1', + '1f': '2', + '20': '3', + '21': '4', + '22': '5', + '23': '6', + '24': '7', + '25': '8', + '26': '9', + '27': '0', + # misc + '28': 'enter', # enter \n + '29': 'esc', + '2a': 'backspace', + '2b': 'tab', + '2c': 'spacebar', # space + '2d': '-', + '2e': '=', + '2f': '[', + '30': ']', + '31': '\\', + '32': '=', + '33': '_SEMICOLON', + '34': 'KEY_APOSTROPHE', + '35': 'KEY_GRAVE', + '36': 'KEY_COMMA', + '37': 'KEY_DOT', + '38': 'KEY_SLASH', + '39': 'KEY_CAPSLOCK', + '3a': 'KEY_F1', + '3b': 'KEY_F2', + '3c': 'KEY_F3', + '3d': 'KEY_F4', + '3e': 'KEY_F5', + '3f': 'KEY_F6', + '40': 'KEY_F7', + '41': 'KEY_F8', + '42': 'KEY_F9', + '43': 'KEY_F10', + '44': 'KEY_F11', + '45': 'KEY_F12', + '46': 'KEY_SYSRQ', + '47': 'KEY_SCROLLLOCK', + '48': 'KEY_PAUSE', + '49': 'KEY_INSERT', + '4a': 'KEY_HOME', + '4b': 'KEY_PAGEUP', + '4c': 'KEY_DELETE', + '4d': 'KEY_END', + '4e': 'KEY_PAGEDOWN', + '4f': 'KEY_RIGHT', + '50': 'KEY_LEFT', + '51': 'KEY_DOWN', + '52': 'KEY_UP', + '53': 'KEY_NUMLOCK', + '54': 'KEY_KPSLASH', + '55': 'KEY_KPASTERISK', + '56': 'KEY_KPMINUS', + '57': 'KEY_KPPLUS', + '58': 'KEY_KPENTER', + '59': 'KEY_KP1', + '5a': 'KEY_KP2', + '5b': 'KEY_KP3', + '5c': 'KEY_KP4', + '5d': 'KEY_KP5', + '5e': 'KEY_KP6', + '5f': 'KEY_KP7', + '60': 'KEY_KP8', + '61': 'KEY_KP9', + '62': 'KEY_KP0', + '63': 'KEY_KPDOT', + '64': 'KEY_102ND', + '65': 'KEY_COMPOSE', + '66': 'KEY_POWER', + '67': 'KEY_KPEQUAL', + '68': 'KEY_F13', + '69': 'KEY_F14', + '6a': 'KEY_F15', + '6b': 'KEY_F16', + '6c': 'KEY_F17', + '6d': 'KEY_F18', + '6e': 'KEY_F19', + '6f': 'KEY_F20', + '70': 'KEY_F21', + '71': 'KEY_F22', + '72': 'KEY_F23', + '73': 'KEY_F24', + '74': 'KEY_OPEN', + '75': 'KEY_HELP', + '76': 'KEY_PROPS', + '77': 'KEY_FRONT', + '78': 'KEY_STOP', + '79': 'KEY_AGAIN', + '7a': 'KEY_UNDO', + '7b': 'KEY_CUT', + '7c': 'KEY_COPY', + '7d': 'KEY_PASTE', + '7e': 'KEY_FIND', + '7f': 'KEY_MUTE', + '80': 'KEY_VOLUMEUP', + '81': 'KEY_VOLUMEDOWN', + '85': 'KEY_KPCOMMA', + '87': 'KEY_RO', + '88': 'KEY_KATAKANAHIRAGANA', + '89': 'KEY_YEN', + '8a': 'KEY_HENKAN', + '8b': 'KEY_MUHENKAN', + '8c': 'KEY_KPJPCOMMA', + '90': 'KEY_HANGEUL', + '91': 'KEY_HANJA', + '92': 'KEY_KATAKANA', + '93': 'KEY_HIRAGANA', + '94': 'KEY_ZENKAKUHANKAKU', + 'b6': 'KEY_KPLEFTPAREN', + 'b7': 'KEY_KPRIGHTPAREN', + 'e0': 'KEY_LEFTCTRL', + 'e1': 'KEY_LEFTSHIFT', + 'e2': 'KEY_LEFTALT', + 'e3': 'KEY_LEFTMETA', + 'e4': 'KEY_RIGHTCTRL', + 'e5': 'KEY_RIGHTSHIFT', + 'e6': 'KEY_RIGHTALT', + 'e7': 'KEY_RIGHTMETA', + 'e8': 'KEY_MEDIA_PLAYPAUSE', + 'e9': 'KEY_MEDIA_STOPCD', + 'ea': 'KEY_MEDIA_PREVIOUSSONG', + 'eb': 'KEY_MEDIA_NEXTSONG', + 'ec': 'KEY_MEDIA_EJECTCD', + 'ed': 'KEY_MEDIA_VOLUMEUP', + 'ee': 'KEY_MEDIA_VOLUMEDOWN', + 'ef': 'KEY_MEDIA_MUTE', + 'f0': 'KEY_MEDIA_WWW', + 'f1': 'KEY_MEDIA_BACK', + 'f2': 'KEY_MEDIA_FORWARD', + 'f3': 'KEY_MEDIA_STOP', + 'f4': 'KEY_MEDIA_FIND', + 'f5': 'KEY_MEDIA_SCROLLUP', + 'f6': 'KEY_MEDIA_SCROLLDOWN', + 'f7': 'KEY_MEDIA_EDIT', + 'f8': 'KEY_MEDIA_SLEEP', + 'f9': 'KEY_MEDIA_COFFEE', + 'fa': 'KEY_MEDIA_REFRESH', + 'fb': 'KEY_MEDIA_CALC', } diff --git a/examples/hid_report_parser.py b/examples/hid_report_parser.py index f53288f..1c891c6 100644 --- a/examples/hid_report_parser.py +++ b/examples/hid_report_parser.py @@ -23,7 +23,7 @@ def get_key(modifier: str, key: str) -> str: class Keyboard: def __init__(self): # type: ignore self.report = [ - [ # Bit array for Modifier keys + [ # Bit array for Modifier keys 0, # Right GUI - (usually the Windows key) 0, # Right ALT 0, # Right Shift @@ -33,8 +33,8 @@ class Keyboard: 0, # Left Shift 0, # Left Control ], - 0x00, # Vendor reserved - '', # Rest is space for 6 keys + 0x00, # Vendor reserved + '', # Rest is space for 6 keys '', '', '', @@ -60,17 +60,27 @@ class Keyboard: print(color('\tKeyboard Input Received', 'green', None, 'bold')) print(color(f'Keys:', 'white', None, 'bold')) for i in range(1, 7): - print(color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1]) + print( + color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1] + ) print(color(f'\nModifier Keys:', 'white', None, 'bold')) print( - color(f' Left Ctrl : ', 'cyan'), f'{self.report[0][0] == 1!s:<5}', # type: ignore - color(f' Left Shift : ', 'cyan'), f'{self.report[0][1] == 1!s:<5}', # type: ignore - color(f' Left ALT : ', 'cyan'), f'{self.report[0][2] == 1!s:<5}', # type: ignore - color(f' Left GUI : ', 'cyan'), f'{self.report[0][3] == 1!s:<5}\n', # type: ignore - color(f' Right Ctrl : ', 'cyan'), f'{self.report[0][4] == 1!s:<5}', # type: ignore - color(f' Right Shift : ', 'cyan'), f'{self.report[0][5] == 1!s:<5}', # type: ignore - color(f' Right ALT : ', 'cyan'), f'{self.report[0][6] == 1!s:<5}', # type: ignore - color(f' Right GUI : ', 'cyan'), f'{self.report[0][7] == 1!s:<5}', # type: ignore + color(f' Left Ctrl : ', 'cyan'), + f'{self.report[0][0] == 1!s:<5}', # type: ignore + color(f' Left Shift : ', 'cyan'), + f'{self.report[0][1] == 1!s:<5}', # type: ignore + color(f' Left ALT : ', 'cyan'), + f'{self.report[0][2] == 1!s:<5}', # type: ignore + color(f' Left GUI : ', 'cyan'), + f'{self.report[0][3] == 1!s:<5}\n', # type: ignore + color(f' Right Ctrl : ', 'cyan'), + f'{self.report[0][4] == 1!s:<5}', # type: ignore + color(f' Right Shift : ', 'cyan'), + f'{self.report[0][5] == 1!s:<5}', # type: ignore + color(f' Right ALT : ', 'cyan'), + f'{self.report[0][6] == 1!s:<5}', # type: ignore + color(f' Right GUI : ', 'cyan'), + f'{self.report[0][7] == 1!s:<5}', # type: ignore ) @@ -78,7 +88,7 @@ class Keyboard: class Mouse: def __init__(self): # type: ignore self.report = [ - [ # Bit array for Buttons + [ # Bit array for Buttons 0, # Button 1 (primary/trigger 0, # Button 2 (secondary) 0, # Button 3 (tertiary) @@ -106,23 +116,32 @@ class Mouse: def print_mouse_report(self) -> None: print(color('\tMouse Input Received', 'green', None, 'bold')) print( - color(f' Button 1 (primary/trigger) = ', 'cyan'), self.report[0][0] == 1, # type: ignore - color(f'\n Button 2 (secondary) = ', 'cyan'), self.report[0][1] == 1, # type: ignore - color(f'\n Button 3 (tertiary) = ', 'cyan'), self.report[0][2] == 1, # type: ignore - color(f'\n Button4 = ', 'cyan'), self.report[0][3] == 1, # type: ignore - color(f'\n Button5 = ', 'cyan'), self.report[0][4] == 1, # type: ignore - color(f'\n X (X-axis displacement) = ', 'cyan'), self.report[1], - color(f'\n Y (Y-axis displacement) = ', 'cyan'), self.report[2], - color(f'\n Wheel = ', 'cyan'), self.report[3], - color(f'\n AC PAN = ', 'cyan'), self.report[4], + color(f' Button 1 (primary/trigger) = ', 'cyan'), + self.report[0][0] == 1, # type: ignore + color(f'\n Button 2 (secondary) = ', 'cyan'), + self.report[0][1] == 1, # type: ignore + color(f'\n Button 3 (tertiary) = ', 'cyan'), + self.report[0][2] == 1, # type: ignore + color(f'\n Button4 = ', 'cyan'), + self.report[0][3] == 1, # type: ignore + color(f'\n Button5 = ', 'cyan'), + self.report[0][4] == 1, # type: ignore + color(f'\n X (X-axis displacement) = ', 'cyan'), + self.report[1], + color(f'\n Y (Y-axis displacement) = ', 'cyan'), + self.report[2], + color(f'\n Wheel = ', 'cyan'), + self.report[3], + color(f'\n AC PAN = ', 'cyan'), + self.report[4], ) # ------------------------------------------------------------------------------ class ReportParser: - def parse_input_report(input_report: bytes) -> None: # type: ignore + def parse_input_report(self, input_report: bytes) -> None: # type: ignore - report_id = input_report[0] + report_id = input_report[0] # pylint: disable=unsubscriptable-object report_length = len(input_report) # Keyboard input report (report id = 1) diff --git a/examples/run_hid_host.py b/examples/run_hid_host.py index d5faf8a..a174444 100644 --- a/examples/run_hid_host.py +++ b/examples/run_hid_host.py @@ -53,7 +53,7 @@ from hid_report_parser import ReportParser SDP_HID_SERVICE_NAME_ATTRIBUTE_ID = 0x0100 SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID = 0x0101 SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID = 0x0102 -SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID = 0x0200 # [DEPRECATED] +SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID = 0x0200 # [DEPRECATED] SDP_HID_PARSER_VERSION_ATTRIBUTE_ID = 0x0201 SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID = 0x0202 SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID = 0x0203 @@ -61,12 +61,12 @@ SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID = 0x0204 SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID = 0x0205 SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID = 0x0206 SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID = 0x0207 -SDP_HID_SDP_DISABLE_ATTRIBUTE_ID = 0x0208 # [DEPRECATED] +SDP_HID_SDP_DISABLE_ATTRIBUTE_ID = 0x0208 # [DEPRECATED] SDP_HID_BATTERY_POWER_ATTRIBUTE_ID = 0x0209 SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID = 0x020A -SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID = 0x020B # DEPRECATED] +SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID = 0x020B # DEPRECATED] SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID = 0x020C -SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID = 0x020D +SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID = 0x020D SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID = 0x020E SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID = 0x020F SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210 @@ -74,6 +74,7 @@ SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210 # ----------------------------------------------------------------------------- + async def get_hid_device_sdp_record(device, connection): # Connect to the SDP Server @@ -84,114 +85,187 @@ async def get_hid_device_sdp_record(device, connection): else: print(color('Failed to connect to SDP Server', 'red')) - # List BT HID Device service in the root browse group + # List BT HID Device service in the root browse group service_record_handles = await sdp_client.search_services( [BT_HUMAN_INTERFACE_DEVICE_SERVICE] ) - if (len(service_record_handles) < 1): + if len(service_record_handles) < 1: await sdp_client.disconnect() - raise Exception(color(f'BT HID Device service not found on peer device!!!!','red')) + raise Exception( + color(f'BT HID Device service not found on peer device!!!!', 'red') + ) # For BT_HUMAN_INTERFACE_DEVICE_SERVICE service, get all its attributes for service_record_handle in service_record_handles: attributes = await sdp_client.get_attributes( service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE] ) - print( - color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow') - ) - print(color(f'SDP attributes for HID device','magenta')) + print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow')) + print(color(f'SDP attributes for HID device', 'magenta')) for attribute in attributes: - if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID : - print(color(' Service Record Handle : ', 'cyan'), hex(attribute.value.value)) + if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID: + print( + color(' Service Record Handle : ', 'cyan'), + hex(attribute.value.value), + ) - elif attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID : - print(color(' Service Class : ', 'cyan'), attribute.value.value[0].value) + elif attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID: + print( + color(' Service Class : ', 'cyan'), attribute.value.value[0].value + ) - elif attribute.id == SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID : - print(color(' SDP Browse Group List : ', 'cyan'), attribute.value.value[0].value) + elif attribute.id == SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID: + print( + color(' SDP Browse Group List : ', 'cyan'), + attribute.value.value[0].value, + ) - elif attribute.id == SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID : - print(color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[0].value) - print(color(' PSM for Bluetooth HID Control channel : ', 'cyan'), hex(attribute.value.value[0].value[1].value)) - print(color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[1].value[0].value) + elif attribute.id == SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID: + print( + color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), + attribute.value.value[0].value[0].value, + ) + print( + color(' PSM for Bluetooth HID Control channel : ', 'cyan'), + hex(attribute.value.value[0].value[1].value), + ) + print( + color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), + attribute.value.value[1].value[0].value, + ) - elif attribute.id == SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID : - print(color(' Lanugage : ', 'cyan'), hex(attribute.value.value[0].value)) - print(color(' Encoding : ', 'cyan'), hex(attribute.value.value[1].value)) - print(color(' PrimaryLanguageBaseID : ', 'cyan'), hex(attribute.value.value[2].value)) + elif attribute.id == SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID: + print( + color(' Lanugage : ', 'cyan'), hex(attribute.value.value[0].value) + ) + print( + color(' Encoding : ', 'cyan'), hex(attribute.value.value[1].value) + ) + print( + color(' PrimaryLanguageBaseID : ', 'cyan'), + hex(attribute.value.value[2].value), + ) - elif attribute.id == SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID : - print(color(' BT_HUMAN_INTERFACE_DEVICE_SERVICE ', 'cyan'), attribute.value.value[0].value[0].value) - print(color(' HID Profileversion number : ', 'cyan'), hex(attribute.value.value[0].value[1].value)) + elif attribute.id == SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID: + print( + color(' BT_HUMAN_INTERFACE_DEVICE_SERVICE ', 'cyan'), + attribute.value.value[0].value[0].value, + ) + print( + color(' HID Profileversion number : ', 'cyan'), + hex(attribute.value.value[0].value[1].value), + ) - elif attribute.id == SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID : - print(color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[0].value[0].value) - print(color(' PSM for Bluetooth HID Interrupt channel : ', 'cyan'), hex(attribute.value.value[0].value[0].value[1].value)) - print(color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), attribute.value.value[0].value[1].value[0].value) + elif attribute.id == SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID: + print( + color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'), + attribute.value.value[0].value[0].value[0].value, + ) + print( + color(' PSM for Bluetooth HID Interrupt channel : ', 'cyan'), + hex(attribute.value.value[0].value[0].value[1].value), + ) + print( + color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'), + attribute.value.value[0].value[1].value[0].value, + ) - elif attribute.id == SDP_HID_SERVICE_NAME_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_SERVICE_NAME_ATTRIBUTE_ID: print(color(' Service Name: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID: print(color(' Service Description: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID: print(color(' Provider Name: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID: print(color(' Release Number: ', 'cyan'), hex(attribute.value.value)) - elif attribute.id == SDP_HID_PARSER_VERSION_ATTRIBUTE_ID : - print(color(' HID Parser Version: ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_PARSER_VERSION_ATTRIBUTE_ID: + print( + color(' HID Parser Version: ', 'cyan'), hex(attribute.value.value) + ) - elif attribute.id == SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID : - print(color(' HIDDeviceSubclass: ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID: + print( + color(' HIDDeviceSubclass: ', 'cyan'), hex(attribute.value.value) + ) - elif attribute.id == SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID: print(color(' HIDCountryCode: ', 'cyan'), hex(attribute.value.value)) - elif attribute.id == SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID: print(color(' HIDVirtualCable: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID: print(color(' HIDReconnectInitiate: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID : - print(color(' HID Report Descriptor type: ', 'cyan'), hex(attribute.value.value[0].value[0].value)) - print(color(' HID Report DescriptorList: ', 'cyan'), attribute.value.value[0].value[1].value) + elif attribute.id == SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID: + print( + color(' HID Report Descriptor type: ', 'cyan'), + hex(attribute.value.value[0].value[0].value), + ) + print( + color(' HID Report DescriptorList: ', 'cyan'), + attribute.value.value[0].value[1].value, + ) - elif attribute.id == SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID : - print(color(' HID LANGID Base Language: ', 'cyan'), hex(attribute.value.value[0].value[0].value)) - print(color(' HID LANGID Base Bluetooth String Offset: ', 'cyan'), hex(attribute.value.value[0].value[1].value)) + elif attribute.id == SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID: + print( + color(' HID LANGID Base Language: ', 'cyan'), + hex(attribute.value.value[0].value[0].value), + ) + print( + color(' HID LANGID Base Bluetooth String Offset: ', 'cyan'), + hex(attribute.value.value[0].value[1].value), + ) - elif attribute.id == SDP_HID_BATTERY_POWER_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_BATTERY_POWER_ATTRIBUTE_ID: print(color(' HIDBatteryPower: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID: print(color(' HIDRemoteWake: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID : - print(color(' HIDProfileVersion : ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID: + print( + color(' HIDProfileVersion : ', 'cyan'), hex(attribute.value.value) + ) - elif attribute.id == SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID : - print(color(' HIDSupervisionTimeout: ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID: + print( + color(' HIDSupervisionTimeout: ', 'cyan'), + hex(attribute.value.value), + ) - elif attribute.id == SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID : - print(color(' HIDNormallyConnectable: ', 'cyan'), attribute.value.value) + elif attribute.id == SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID: + print( + color(' HIDNormallyConnectable: ', 'cyan'), attribute.value.value + ) - elif attribute.id == SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID : + elif attribute.id == SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID: print(color(' HIDBootDevice: ', 'cyan'), attribute.value.value) - elif attribute.id == SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID : - print(color(' HIDSSRHostMaxLatency: ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID: + print( + color(' HIDSSRHostMaxLatency: ', 'cyan'), + hex(attribute.value.value), + ) - elif attribute.id == SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID : - print(color(' HIDSSRHostMinTimeout: ', 'cyan'), hex(attribute.value.value)) + elif attribute.id == SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID: + print( + color(' HIDSSRHostMinTimeout: ', 'cyan'), + hex(attribute.value.value), + ) else: - print(color(f' Warning: Attribute ID: {attribute.id} match not found.\n Attribute Info: {attribute}', 'yellow')) + print( + color( + f' Warning: Attribute ID: {attribute.id} match not found.\n Attribute Info: {attribute}', + 'yellow', + ) + ) await sdp_client.disconnect() @@ -217,26 +291,33 @@ async def main(): return def on_hid_data_cb(pdu): - report_type = pdu[0] & 0x0f - if (len(pdu) == 1): - print(color(f'Warning: No report received','yellow')) + report_type = pdu[0] & 0x0F + if len(pdu) == 1: + print(color(f'Warning: No report received', 'yellow')) return report_length = len(pdu[1:]) report_id = pdu[1] - if (report_type != Message.ReportType.OTHER_REPORT): - print(color(f' Report type = {report_type}, Report length = {report_length}, Report id = {report_id}', 'blue', None, 'bold')) + if report_type != Message.ReportType.OTHER_REPORT: + print( + color( + f' Report type = {report_type}, Report length = {report_length}, Report id = {report_id}', + 'blue', + None, + 'bold', + ) + ) - if ((report_length <= 1) or (report_id == 0)): + if (report_length <= 1) or (report_id == 0): return if report_type == Message.ReportType.INPUT_REPORT: - ReportParser.parse_input_report(pdu[1:]) #type: ignore + ReportParser.parse_input_report(pdu[1:]) # type: ignore async def handle_virtual_cable_unplug(): await hid_host.disconnect_interrupt_channel() await hid_host.disconnect_control_channel() - await device.keystore.delete(target_address) #type: ignore + await device.keystore.delete(target_address) # type: ignore await connection.disconnect() def on_hid_virtual_cable_unplug_cb(): @@ -282,7 +363,9 @@ async def main(): async def menu(): reader = await get_stream_reader(sys.stdin) while True: - print("\n************************ HID Host Menu *****************************\n") + print( + "\n************************ HID Host Menu *****************************\n" + ) print(" 1. Connect Control Channel") print(" 2. Connect Interrupt Channel") print(" 3. Disconnect Control Channel") @@ -343,15 +426,17 @@ async def main(): if choice1 == '1': # data includes first octet as report id - data = bytearray([0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]) + data = bytearray( + [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01] + ) hid_host.set_report(1, data) elif choice1 == '2': - data = bytearray([0x03, 0x01, 0x01]) + data = bytearray([0x03, 0x01, 0x01]) hid_host.set_report(2, data) elif choice1 == '3': - data = bytearray([0x05, 0x01, 0x01, 0x01]) + data = bytearray([0x05, 0x01, 0x01, 0x01]) hid_host.set_report(3, data) else: @@ -382,11 +467,13 @@ async def main(): choice1 = choice1.decode('utf-8').strip() if choice1 == '1': - data = bytearray([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + data = bytearray( + [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + ) hid_host.send_data(data) elif choice1 == '2': - data = bytearray([0x03, 0x00, 0x0d, 0xfd, 0x00, 0x00]) + data = bytearray([0x03, 0x00, 0x0D, 0xFD, 0x00, 0x00]) hid_host.send_data(data) else: @@ -406,8 +493,12 @@ async def main(): print('Device not found or Device already unpaired.') elif choice == '13': - peer_address = Address.from_string_for_transport(target_address, transport=BT_BR_EDR_TRANSPORT) - connection = device.find_connection_by_bd_addr(peer_address, transport=BT_BR_EDR_TRANSPORT) + peer_address = Address.from_string_for_transport( + target_address, transport=BT_BR_EDR_TRANSPORT + ) + connection = device.find_connection_by_bd_addr( + peer_address, transport=BT_BR_EDR_TRANSPORT + ) if connection is not None: await connection.disconnect() else: @@ -421,7 +512,9 @@ async def main(): print('Device not found or Device already unpaired.') elif choice == '15': - connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT) + connection = await device.connect( + target_address, transport=BT_BR_EDR_TRANSPORT + ) await connection.authenticate() await connection.encrypt() @@ -445,4 +538,3 @@ async def main(): logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) -