From c5e0642b0b38b5b7bee01e492ed93a72c879c09e Mon Sep 17 00:00:00 2001 From: Jake Ob Date: Fri, 18 Oct 2024 19:50:51 +0300 Subject: [PATCH] Fix broken controller regexp scheme in the bluetoothctl parser (#599) This commit fixes the controller parser scheme in order to take care and extract some extra attributes, the manufacturer and version. The order of the attributes appearing in the regexp scheme must follow the order they appear in the `bluetoothctl show` outputs. A new test has been added to test outputs with these extra attrs. Co-authored-by: Kelly Brazil --- jc/parsers/bluetoothctl.py | 16 +++++- ...etoothctl_controller_with_manufacturer.out | 37 +++++++++++++ tests/test_bluetoothctl.py | 52 +++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out diff --git a/jc/parsers/bluetoothctl.py b/jc/parsers/bluetoothctl.py index 84de1528..2fabd41b 100644 --- a/jc/parsers/bluetoothctl.py +++ b/jc/parsers/bluetoothctl.py @@ -28,6 +28,8 @@ Controller: [ { + "manufacturer": string, + "version": string, "name": string, "is_default": boolean, "is_public": boolean, @@ -127,6 +129,8 @@ class info(): Controller = TypedDict( "Controller", { + "manufacturer": str, + "version": str, "name": str, "is_default": bool, "is_public": bool, @@ -175,7 +179,9 @@ class info(): _controller_head_pattern = r"Controller (?P
([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P.+)" _controller_line_pattern = ( - r"(\s*Name:\s*(?P.+)" + r"(\s*Manufacturer:\s*(?P.+)" + + r"|\s*Version:\s*(?P.+)" + + r"|\s*Name:\s*(?P.+)" + r"|\s*Alias:\s*(?P.+)" + r"|\s*Class:\s*(?P.+)" + r"|\s*Powered:\s*(?P.+)" @@ -203,6 +209,8 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]: return None controller: Controller = { + "manufacturer": '', + "version": '', "name": '', "is_default": False, "is_public": False, @@ -241,7 +249,11 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]: matches = result.groupdict() - if matches["name"]: + if matches["manufacturer"]: + controller["manufacturer"] = matches["manufacturer"] + elif matches["version"]: + controller["version"] = matches["version"] + elif matches["name"]: controller["name"] = matches["name"] elif matches["alias"]: controller["alias"] = matches["alias"] diff --git a/tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out b/tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out new file mode 100644 index 00000000..6b46fd96 --- /dev/null +++ b/tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out @@ -0,0 +1,37 @@ +Controller 48:A4:72:3C:96:63 (public) + Manufacturer: 0x0002 (2) + Version: 0x08 (8) + Name: ubuntu + Alias: ubuntu + Class: 0x007c0104 (8126724) + Powered: yes + Discoverable: yes + DiscoverableTimeout: 0x000000b4 (180) + Pairable: yes + UUID: Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb) + UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb) + UUID: OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb) + UUID: Message Access Server (00001132-0000-1000-8000-00805f9b34fb) + UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) + UUID: IrMC Sync (00001104-0000-1000-8000-00805f9b34fb) + UUID: Vendor specific (00005005-0000-1000-8000-0002ee000001) + UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb) + UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) + UUID: Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb) + UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb) + UUID: Device Information (0000180a-0000-1000-8000-00805f9b34fb) + UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) + UUID: Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb) + UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb) + UUID: OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb) + UUID: Handsfree (0000111e-0000-1000-8000-00805f9b34fb) + Modalias: usb:v1D6Bp0246d0548 + Discovering: yes + Roles: central + Roles: peripheral +Advertising Features: + ActiveInstances: 0x00 (0) + SupportedInstances: 0x05 (5) + SupportedIncludes: tx-power + SupportedIncludes: appearance + SupportedIncludes: local-name diff --git a/tests/test_bluetoothctl.py b/tests/test_bluetoothctl.py index f573f950..1df04213 100644 --- a/tests/test_bluetoothctl.py +++ b/tests/test_bluetoothctl.py @@ -104,6 +104,58 @@ def test_bluetoothctl_controller(self): if actual: for k, v in expected.items(): self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}") + + def test_bluetoothctl_controller_with_manufacturer(self): + """ + Test 'bluetoothctl' with controller having manufacturer attr + """ + + with open("tests/fixtures/generic/bluetoothctl_controller_with_manufacturer.out", "r") as f: + output = f.read() + + actual = parse(output, quiet=True) + + self.assertIsNotNone(actual) + self.assertIsNotNone(actual[0]) + + expected = { + "manufacturer": "0x0002 (2)", + "version": "0x08 (8)", + "address": "48:A4:72:3C:96:63", + "is_public": True, + "name": "ubuntu", + "alias": "ubuntu", + "class": "0x007c0104 (8126724)", + "powered": "yes", + "discoverable": "yes", + "discoverable_timeout": "0x000000b4 (180)", + "pairable": "yes", + "uuids": [ + "Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb)", + "A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)", + "OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb)", + "Message Access Server (00001132-0000-1000-8000-00805f9b34fb)", + "PnP Information (00001200-0000-1000-8000-00805f9b34fb)", + "IrMC Sync (00001104-0000-1000-8000-00805f9b34fb)", + "Vendor specific (00005005-0000-1000-8000-0002ee000001)", + "A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)", + "Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)", + "Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb)", + "Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)", + "Device Information (0000180a-0000-1000-8000-00805f9b34fb)", + "Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)", + "Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)", + "Audio Source (0000110a-0000-1000-8000-00805f9b34fb)", + "OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb)", + "Handsfree (0000111e-0000-1000-8000-00805f9b34fb)" + ], + "modalias": "usb:v1D6Bp0246d0548", + "discovering": "yes" + } + + if actual: + for k, v in expected.items(): + self.assertEqual(v, actual[0][k], f"Controller regex failed on {k}") def test_bluetoothctl_controllers(self): """