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):
"""