Submitting review comment fix: header function and extra lines.

Executed formatter on file.
This commit is contained in:
skarnataki
2023-10-10 11:43:57 +00:00
committed by Lucas Abel
parent 963fa41a49
commit 651e44e0b6
4 changed files with 452 additions and 342 deletions

View File

@@ -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

View File

@@ -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',
}

View File

@@ -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)

View File

@@ -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())