Skip to content

Commit 8de4fc4

Browse files
authored
Reformat help texts (#90)
* Use asyncio.all_tasks() again to check if relay is active * Add missing type hints * Add Arguments class with typed properties * Reformat help texts * Unify punctuation * Print help arg last * Re-add print_help() * Rearrange help args * Update docs to v0.7.1 * Bump version
1 parent f667797 commit 8de4fc4

File tree

4 files changed

+186
-88
lines changed

4 files changed

+186
-88
lines changed

README.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ Follow these steps to install and configure the project:
151151
└─5869 python3.11 /home/user/bluetooth_2_usb/bluetooth_2_usb.py --auto_discover --grab_devices
152152

153153
Dec 13 10:33:00 pi0w systemd[1]: Started bluetooth_2_usb.service - Bluetooth to USB HID relay.
154-
Dec 13 10:33:06 pi0w bluetooth_2_usb[5869]: 23-12-13 10:33:06 [INFO] Launching Bluetooth 2 USB v0.7.0
154+
Dec 13 10:33:06 pi0w bluetooth_2_usb[5869]: 23-12-13 10:33:06 [INFO] Launching Bluetooth 2 USB v0.7.1
155155
Dec 13 10:33:06 pi0w bluetooth_2_usb[5869]: 23-12-13 10:33:06 [INFO] Discovering input devices...
156156
Dec 13 10:33:09 pi0w bluetooth_2_usb[5869]: 23-12-13 10:33:09 [INFO] Activated relay for device /dev/input/event2, name "AceRK Mouse", phys "b8:27:eb:be:dc:81"
157157
Dec 13 10:33:09 pi0w bluetooth_2_usb[5869]: 23-12-13 10:33:09 [INFO] Activated relay for device /dev/input/event1, name "AceRK Keyboard", phys "b8:27:eb:be:dc:81"
@@ -182,26 +182,29 @@ Currently you can provide the following CLI arguments:
182182

183183
```console
184184
user@pi0w:~ $ bluetooth_2_usb -h
185-
usage: bluetooth_2_usb.py [-h] [--device_ids DEVICE_IDS] [--auto_discover] [--debug] [--log_to_file] [--log_path LOG_PATH]
186-
[--version] [--list_devices] [--grab_devices]
185+
usage: bluetooth_2_usb.py [--device_ids DEVICE_IDS] [--auto_discover] [--grab_devices] [--list_devices] [--log_to_file] [--log_path LOG_PATH] [--debug] [--version] [--help]
187186

188-
Bluetooth to USB HID relay. Handles Bluetooth keyboard and mouse events from multiple input devices and translates them to
189-
USB using Linux's gadget mode.
187+
Bluetooth to USB HID relay. Handles Bluetooth keyboard and mouse events from multiple input devices and translates them to USB using Linux's gadget mode.
190188

191189
options:
192-
-h, --help show this help message and exit
193190
--device_ids DEVICE_IDS, -i DEVICE_IDS
194-
Comma-separated list of identifiers for input devices to be relayed. An identifier is either the
195-
input device path, the MAC address or any case-insensitive substring of the device name. Default is
196-
None. Example: --device_ids '/dev/input/event2,a1:b2:c3:d4:e5:f6,0A-1B-2C-3D-4E-5F,logi'
191+
Comma-separated list of identifiers for input devices to be relayed.
192+
An identifier is either the input device path, the MAC address or any case-insensitive substring of the device name.
193+
Example: --device_ids '/dev/input/event2,a1:b2:c3:d4:e5:f6,0A-1B-2C-3D-4E-5F,logi'
194+
Default: None
197195
--auto_discover, -a Enable auto-discovery mode. All readable input devices will be relayed automatically.
198-
--debug, -d Enable debug mode. Increases log verbosity
199-
--log_to_file, -f Add a handler that logs to file additionally to stdout.
196+
Default: disabled
197+
--grab_devices, -g Grab the input devices, i.e., suppress any events on your relay device.
198+
Devices are not grabbed by default.
199+
--list_devices, -l List all available input devices and exit.
200+
--log_to_file, -f Add a handler that logs to file, additionally to stdout.
200201
--log_path LOG_PATH, -p LOG_PATH
201-
The path of the log file. Default is /var/log/bluetooth_2_usb/bluetooth_2_usb.log.
202+
The path of the log file
203+
Default: /var/log/bluetooth_2_usb/bluetooth_2_usb.log
204+
--debug, -d Enable debug mode (Increases log verbosity)
205+
Default: disabled
202206
--version, -v Display the version number of this software and exit.
203-
--list_devices, -l List all available input devices and exit.
204-
--grab_devices, -g Grab the input devices, i.e., suppress any events on your relay device (RPi).
207+
--help, -h Show this help message and exit.
205208
```
206209

207210
### 5.3. Consuming the API from your Python code
@@ -373,7 +376,7 @@ Here's a few things you could try:
373376
user@pi0w:~ $ sudo service bluetooth_2_usb stop && sudo bluetooth_2_usb -ad ; sudo service bluetooth_2_usb start
374377
23-12-12 13:03:28 [DEBUG] CLI args: Namespace(device_ids=None, auto_discover=True, debug=True, log_to_file=False, log_path='/var/log/bluetooth_2_usb/bluetooth_2_usb.log', version=False, list_devices=False)
375378
23-12-12 13:03:28 [DEBUG] Logging to stdout
376-
23-12-12 13:03:28 [INFO] Launching Bluetooth 2 USB v0.7.0
379+
23-12-12 13:03:28 [INFO] Launching Bluetooth 2 USB v0.7.1
377380
23-12-12 13:03:28 [INFO] Discovering input devices...
378381
23-12-12 13:03:28 [DEBUG] Auto-discovery enabled. Relaying all input devices.
379382
23-12-12 13:03:28 [DEBUG] Initializing USB gadgets...

bluetooth_2_usb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
_logger = get_logger()
16-
_VERSION = "0.7.0"
16+
_VERSION = "0.7.1"
1717
_VERSIONED_NAME = f"Bluetooth 2 USB v{_VERSION}"
1818

1919

src/bluetooth_2_usb/args.py

Lines changed: 158 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,84 @@
1-
from argparse import Namespace
21
import argparse
32
import atexit
43
import sys
4+
from typing import Optional, List
55

66
from usb_hid import disable
77

88

99
class CustomArgumentParser(argparse.ArgumentParser):
10+
def __init__(self, *args, **kwargs):
11+
super().__init__(*args, add_help=False, **kwargs)
12+
self.register("action", "help", _HelpAction) # Register the help action
13+
14+
def add_arguments(self):
15+
self.add_argument(
16+
"--device_ids",
17+
"-i",
18+
type=lambda input: [item.strip() for item in input.split(",")],
19+
default=None,
20+
help="Comma-separated list of identifiers for input devices to be relayed.\nAn identifier is either the input device path, the MAC address or any case-insensitive substring of the device name.\nExample: --device_ids '/dev/input/event2,a1:b2:c3:d4:e5:f6,0A-1B-2C-3D-4E-5F,logi'\nDefault: None",
21+
)
22+
self.add_argument(
23+
"--auto_discover",
24+
"-a",
25+
action="store_true",
26+
default=False,
27+
help="Enable auto-discovery mode. All readable input devices will be relayed automatically.\nDefault: disabled",
28+
)
29+
self.add_argument(
30+
"--grab_devices",
31+
"-g",
32+
action="store_true",
33+
default=False,
34+
help="Grab the input devices, i.e., suppress any events on your relay device.\nDevices are not grabbed by default.",
35+
)
36+
self.add_argument(
37+
"--list_devices",
38+
"-l",
39+
action="store_true",
40+
default=False,
41+
help="List all available input devices and exit.",
42+
)
43+
self.add_argument(
44+
"--log_to_file",
45+
"-f",
46+
action="store_true",
47+
default=False,
48+
help="Add a handler that logs to file, additionally to stdout.",
49+
)
50+
self.add_argument(
51+
"--log_path",
52+
"-p",
53+
type=str,
54+
default="/var/log/bluetooth_2_usb/bluetooth_2_usb.log",
55+
help="The path of the log file\nDefault: /var/log/bluetooth_2_usb/bluetooth_2_usb.log",
56+
)
57+
self.add_argument(
58+
"--debug",
59+
"-d",
60+
action="store_true",
61+
default=False,
62+
help="Enable debug mode (Increases log verbosity)\nDefault: disabled",
63+
)
64+
self.add_argument(
65+
"--version",
66+
"-v",
67+
action="store_true",
68+
default=False,
69+
help="Display the version number of this software and exit.",
70+
)
71+
72+
def add_help_argument(self):
73+
# Add the help argument last
74+
self.add_argument(
75+
"--help",
76+
"-h",
77+
action="help",
78+
default=argparse.SUPPRESS,
79+
help="Show this help message and exit.",
80+
)
81+
1082
def print_help(self) -> None:
1183
"""
1284
When the script is run with help or version flag, we need to unregister usb_hid.disable() from atexit
@@ -16,76 +88,99 @@ def print_help(self) -> None:
1688
super().print_help()
1789

1890

19-
def parse_args() -> Namespace:
91+
class _HelpAction(argparse._HelpAction):
92+
def __call__(self, parser, namespace, values, option_string=None):
93+
parser.print_help()
94+
parser.exit()
95+
96+
97+
class Arguments:
98+
__slots__ = [
99+
"_device_ids",
100+
"_auto_discover",
101+
"_grab_devices",
102+
"_list_devices",
103+
"_log_to_file",
104+
"_log_path",
105+
"_debug",
106+
"_version",
107+
]
108+
109+
def __init__(
110+
self,
111+
device_ids: Optional[List[str]],
112+
auto_discover: bool,
113+
grab_devices: bool,
114+
list_devices: bool,
115+
log_to_file: bool,
116+
log_path: str,
117+
debug: bool,
118+
version: bool,
119+
):
120+
self._device_ids = device_ids
121+
self._auto_discover = auto_discover
122+
self._grab_devices = grab_devices
123+
self._list_devices = list_devices
124+
self._log_to_file = log_to_file
125+
self._log_path = log_path
126+
self._debug = debug
127+
self._version = version
128+
129+
@property
130+
def device_ids(self) -> Optional[List[str]]:
131+
return self._device_ids
132+
133+
@property
134+
def auto_discover(self) -> bool:
135+
return self._auto_discover
136+
137+
@property
138+
def grab_devices(self) -> bool:
139+
return self._grab_devices
140+
141+
@property
142+
def list_devices(self) -> bool:
143+
return self._list_devices
144+
145+
@property
146+
def log_to_file(self) -> bool:
147+
return self._log_to_file
148+
149+
@property
150+
def log_path(self) -> str:
151+
return self._log_path
152+
153+
@property
154+
def debug(self) -> bool:
155+
return self._debug
156+
157+
@property
158+
def version(self) -> bool:
159+
return self._version
160+
161+
162+
def parse_args() -> Arguments:
20163
parser = CustomArgumentParser(
21164
description="Bluetooth to USB HID relay. Handles Bluetooth keyboard and mouse events from multiple input devices and translates them to USB using Linux's gadget mode.",
165+
formatter_class=argparse.RawTextHelpFormatter,
22166
)
23167

24-
parser.add_argument(
25-
"--device_ids",
26-
"-i",
27-
type=lambda input: [item.strip() for item in input.split(",")],
28-
default=None,
29-
help="Comma-separated list of identifiers for input devices to be relayed.\n\n \
30-
An identifier is either the input device path, the MAC address or any case-insensitive substring of the device name.\n\n \
31-
Default is None.\n\n \
32-
Example: --device_ids '/dev/input/event2,a1:b2:c3:d4:e5:f6,0A-1B-2C-3D-4E-5F,logi'",
33-
)
34-
parser.add_argument(
35-
"--auto_discover",
36-
"-a",
37-
action="store_true",
38-
default=False,
39-
help="Enable auto-discovery mode. All readable input devices will be relayed automatically.",
40-
)
41-
parser.add_argument(
42-
"--debug",
43-
"-d",
44-
action="store_true",
45-
default=False,
46-
help="Enable debug mode. Increases log verbosity",
47-
)
48-
parser.add_argument(
49-
"--log_to_file",
50-
"-f",
51-
action="store_true",
52-
default=False,
53-
help="Add a handler that logs to file additionally to stdout. ",
54-
)
55-
parser.add_argument(
56-
"--log_path",
57-
"-p",
58-
type=str,
59-
default="/var/log/bluetooth_2_usb/bluetooth_2_usb.log",
60-
help="The path of the log file. Default is /var/log/bluetooth_2_usb/bluetooth_2_usb.log.",
61-
)
62-
parser.add_argument(
63-
"--version",
64-
"-v",
65-
action="store_true",
66-
default=False,
67-
help="Display the version number of this software and exit.",
68-
)
69-
parser.add_argument(
70-
"--list_devices",
71-
"-l",
72-
action="store_true",
73-
default=False,
74-
help="List all available input devices and exit.",
75-
)
76-
parser.add_argument(
77-
"--grab_devices",
78-
"-g",
79-
action="store_true",
80-
default=False,
81-
help="Grab the input devices, i.e., suppress any events on your relay device (RPi).",
82-
)
83-
168+
parser.add_arguments()
169+
parser.add_help_argument()
84170
args = parser.parse_args()
85171

86172
# Check if no arguments were provided
87173
if len(sys.argv) == 1:
88174
parser.print_help()
89175
sys.exit(1)
90176

91-
return args
177+
return Arguments(
178+
device_ids=args.device_ids,
179+
auto_discover=args.auto_discover,
180+
grab_devices=args.grab_devices,
181+
list_devices=args.list_devices,
182+
log_to_file=args.log_to_file,
183+
log_path=args.log_path,
184+
debug=args.debug,
185+
version=args.version,
186+
)

src/bluetooth_2_usb/relay.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
from usb_hid import Device
1212

1313
from .evdev import (
14+
evdev_to_usb_hid,
1415
get_mouse_movement,
1516
is_consumer_key,
1617
is_mouse_button,
17-
evdev_to_usb_hid,
1818
)
1919
from .logging import get_logger
2020

@@ -49,7 +49,7 @@ def init_usb_gadgets() -> None:
4949
_logger.debug(f"Enabled USB gadgets: {usb_hid.devices}")
5050

5151

52-
def all_gadgets_ready():
52+
def all_gadgets_ready() -> bool:
5353
return all(
5454
dev is not None for dev in (_keyboard_gadget, _mouse_gadget, _consumer_gadget)
5555
)
@@ -73,10 +73,10 @@ def normalized_value(self) -> str:
7373
def type(self) -> str:
7474
return self._type
7575

76-
def __str__(self):
76+
def __str__(self) -> str:
7777
return self.value
7878

79-
def __repr__(self):
79+
def __repr__(self) -> str:
8080
return f'{self.type} "{self.value}"'
8181

8282
def _determine_identifier_type(self) -> str:
@@ -106,7 +106,7 @@ def matches(self, device: InputDevice) -> bool:
106106

107107

108108
class DeviceRelay:
109-
def __init__(self, input_device: InputDevice, grab_device: bool = False):
109+
def __init__(self, input_device: InputDevice, grab_device: bool = False)->None:
110110
self._input_device = input_device
111111
if grab_device:
112112
self._input_device.grab()
@@ -117,10 +117,10 @@ def __init__(self, input_device: InputDevice, grab_device: bool = False):
117117
def input_device(self) -> InputDevice:
118118
return self._input_device
119119

120-
def __str__(self):
120+
def __str__(self) -> str:
121121
return f"relay for {self.input_device.name}"
122122

123-
def __repr__(self):
123+
def __repr__(self) -> str:
124124
return f"relay for {self.input_device}"
125125

126126
async def async_relay_events_loop(self) -> NoReturn:
@@ -236,13 +236,13 @@ def _create_task(self, device: InputDevice, task_group: TaskGroup) -> None:
236236
async def _async_relay_events(self, device: InputDevice) -> NoReturn:
237237
try:
238238
relay = DeviceRelay(device, self._grab_devices)
239-
_logger.info(f"Activated {repr(relay)}")
239+
_logger.info(f"Activated {relay!r}")
240240
await relay.async_relay_events_loop()
241241
except CancelledError:
242242
self._cancelled = True
243243
_logger.critical(f"{device.name} was cancelled")
244244
except (OSError, FileNotFoundError) as ex:
245-
_logger.critical(f"Connection to {device.name} lost [{repr(ex)}]")
245+
_logger.critical(f"Connection to {device.name} lost [{ex!r}]")
246246
except Exception:
247247
_logger.exception(f"{device.name} failed!")
248248
await asyncio.sleep(1)

0 commit comments

Comments
 (0)