Skip to content

Commit 9be8f1d

Browse files
authored
Merge pull request #84 from quaxalber/development
Fix long device connection times (#59)
2 parents 6ff5b10 + 6896322 commit 9be8f1d

File tree

3 files changed

+75
-71
lines changed

3 files changed

+75
-71
lines changed

README.md

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

153153
Dec 02 23:16:37 pi0w systemd[1]: Started bluetooth_2_usb.service - Bluetooth to USB HID relay.
154-
Dec 02 23:16:39 pi0w bluetooth_2_usb[1326]: 23-12-02 23:16:39 [INFO] Launching Bluetooth 2 USB v0.6.6
154+
Dec 02 23:16:39 pi0w bluetooth_2_usb[1326]: 23-12-02 23:16:39 [INFO] Launching Bluetooth 2 USB v0.6.7
155155
Dec 02 23:16:39 pi0w bluetooth_2_usb[1326]: 23-12-02 23:16:39 [INFO] Discovering input devices...
156156
Dec 02 23:16:42 pi0w bluetooth_2_usb[1326]: 23-12-02 23:16:42 [INFO] Activated relay for device /dev/input/event2, name "AceRK Mouse", phys "a1:b2:c3:d4:e5:f6"
157157
Dec 02 23:16:45 pi0w bluetooth_2_usb[1326]: 23-12-02 23:16:45 [INFO] Activated relay for device /dev/input/event1, name "AceRK Keyboard", phys "a1:b2:c3:d4:e5:f6"
@@ -368,7 +368,7 @@ Here's a few things you could try:
368368
user@pi0w:~ $ sudo service bluetooth_2_usb stop && sudo bluetooth_2_usb -i logi,a1:b2:c3:d4:e5:f6,/dev/input/event3 -d ; sudo service bluetooth_2_usb start
369369
23-12-06 14:19:36 [DEBUG] CLI args: Namespace(device_ids=['logi', 'a1:b2:c3:d4:e5:f6', '/dev/input/event3'], auto_discover=False, debug=True, log_to_file=False, log_path='/var/log/bluetooth_2_usb/bluetooth_2_usb.log', version=False, list_devices=False)
370370
23-12-06 14:19:36 [DEBUG] Logging to stdout
371-
23-12-06 14:19:36 [INFO] Launching Bluetooth 2 USB v0.6.6
371+
23-12-06 14:19:36 [INFO] Launching Bluetooth 2 USB v0.6.7
372372
23-12-06 14:19:36 [DEBUG] Available USB devices: [Mouse gadget (/dev/hidg0), Keyboard gadget (/dev/hidg1), Consumer control gadget (/dev/hidg2)]
373373
23-12-06 14:19:36 [INFO] Discovering input devices...
374374
23-12-06 14:19:36 [DEBUG] Relaying devices with matching name "logi" or MAC "a1:b2:c3:d4:e5:f6" or path "/dev/input/event3"

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.6.6"
16+
_VERSION = "0.6.7"
1717
_VERSIONED_NAME = f"Bluetooth 2 USB v{_VERSION}"
1818

1919

src/bluetooth_2_usb/relay.py

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,7 @@
66
from adafruit_hid.consumer_control import ConsumerControl
77
from adafruit_hid.keyboard import Keyboard
88
from adafruit_hid.mouse import Mouse
9-
from evdev import (
10-
InputDevice,
11-
InputEvent,
12-
KeyEvent,
13-
RelEvent,
14-
categorize,
15-
list_devices,
16-
)
9+
from evdev import InputDevice, InputEvent, KeyEvent, RelEvent, categorize, list_devices
1710
import usb_hid
1811
from usb_hid import Device
1912

@@ -27,12 +20,34 @@
2720

2821

2922
_logger = get_logger()
23+
_keyboard_gadget: Keyboard = None
24+
_mouse_gadget: Mouse = None
25+
_consumer_gadget: ConsumerControl = None
3026

3127
PATH = "path"
3228
MAC = "MAC"
3329
NAME = "name"
3430

3531

32+
def list_readable_devices() -> list[InputDevice]:
33+
return [InputDevice(path) for path in list_devices()]
34+
35+
36+
def init_usb_devices() -> None:
37+
usb_hid.enable(
38+
[
39+
Device.MOUSE,
40+
Device.KEYBOARD,
41+
Device.CONSUMER_CONTROL,
42+
]
43+
)
44+
_logger.debug(f"Available USB devices: {usb_hid.devices}")
45+
global _keyboard_gadget, _mouse_gadget, _consumer_gadget
46+
_keyboard_gadget = Keyboard(usb_hid.devices)
47+
_mouse_gadget = Mouse(usb_hid.devices)
48+
_consumer_gadget = ConsumerControl(usb_hid.devices)
49+
50+
3651
class DeviceIdentifier:
3752
def __init__(self, device_identifier: str) -> None:
3853
self._value = device_identifier
@@ -86,9 +101,6 @@ def matches(self, device: InputDevice) -> bool:
86101
class DeviceRelay:
87102
def __init__(self, input_device: InputDevice):
88103
self._input_device = input_device
89-
self._keyboard_gadget = Keyboard(usb_hid.devices)
90-
self._mouse_gadget = Mouse(usb_hid.devices)
91-
self._consumer_gadget = ConsumerControl(usb_hid.devices)
92104

93105
@property
94106
def input_device(self) -> InputDevice:
@@ -107,45 +119,48 @@ async def async_relay_events_loop(self) -> NoReturn:
107119
async def _async_relay_event(self, input_event: InputEvent) -> None:
108120
event = categorize(input_event)
109121
_logger.debug(f"Received {event} from {self.input_device.name}")
110-
method = None
122+
func = None
111123
if isinstance(event, RelEvent):
112-
method = self._move_mouse
124+
func = _move_mouse
113125
elif isinstance(event, KeyEvent):
114-
method = self._send_key
115-
if method:
126+
func = _send_key
127+
if func:
116128
loop = asyncio.get_running_loop()
117-
await loop.run_in_executor(None, method, event)
118-
119-
def _send_key(self, event: KeyEvent) -> None:
120-
key_id, key_name = evdev_to_usb_hid(event)
121-
if key_id is None:
122-
return
123-
device_out = self._get_output_device(event)
124-
try:
125-
if event.keystate == KeyEvent.key_down:
126-
_logger.debug(f"Pressing {key_name} (0x{key_id:02X}) on {device_out}")
127-
device_out.press(key_id)
128-
elif event.keystate == KeyEvent.key_up:
129-
_logger.debug(f"Releasing {key_name} (0x{key_id:02X}) on {device_out}")
130-
device_out.release(key_id)
131-
except Exception:
132-
_logger.exception(f"Failed sending 0x{key_id:02X} to {device_out}")
133-
134-
def _get_output_device(self, event: KeyEvent) -> ConsumerControl | Keyboard | Mouse:
135-
if is_consumer_key(event):
136-
return self._consumer_gadget
137-
elif is_mouse_button(event):
138-
return self._mouse_gadget
139-
return self._keyboard_gadget
140-
141-
def _move_mouse(self, event: RelEvent) -> None:
142-
x, y, mwheel = get_mouse_movement(event)
143-
coordinates = f"(x={x}, y={y}, mwheel={mwheel})"
144-
try:
145-
_logger.debug(f"Moving mouse {self._mouse_gadget} {coordinates}")
146-
self._mouse_gadget.move(x, y, mwheel)
147-
except Exception:
148-
_logger.exception(f"Failed moving mouse {self._mouse_gadget} {coordinates}")
129+
await loop.run_in_executor(None, func, event)
130+
131+
132+
def _move_mouse(event: RelEvent) -> None:
133+
x, y, mwheel = get_mouse_movement(event)
134+
coordinates = f"(x={x}, y={y}, mwheel={mwheel})"
135+
try:
136+
_logger.debug(f"Moving mouse {_mouse_gadget} {coordinates}")
137+
_mouse_gadget.move(x, y, mwheel)
138+
except Exception:
139+
_logger.exception(f"Failed moving mouse {_mouse_gadget} {coordinates}")
140+
141+
142+
def _send_key(event: KeyEvent) -> None:
143+
key_id, key_name = evdev_to_usb_hid(event)
144+
if key_id is None:
145+
return
146+
device_out = _get_output_device(event)
147+
try:
148+
if event.keystate == KeyEvent.key_down:
149+
_logger.debug(f"Pressing {key_name} (0x{key_id:02X}) on {device_out}")
150+
device_out.press(key_id)
151+
elif event.keystate == KeyEvent.key_up:
152+
_logger.debug(f"Releasing {key_name} (0x{key_id:02X}) on {device_out}")
153+
device_out.release(key_id)
154+
except Exception:
155+
_logger.exception(f"Failed sending 0x{key_id:02X} to {device_out}")
156+
157+
158+
def _get_output_device(event: KeyEvent) -> ConsumerControl | Keyboard | Mouse:
159+
if is_consumer_key(event):
160+
return _consumer_gadget
161+
elif is_mouse_button(event):
162+
return _mouse_gadget
163+
return _keyboard_gadget
149164

150165

151166
class RelayController:
@@ -156,12 +171,13 @@ class RelayController:
156171
def __init__(
157172
self, device_identifiers: list[str] = None, auto_discover: bool = False
158173
) -> None:
159-
_enable_usb_devices()
174+
init_usb_devices()
160175
if not device_identifiers:
161176
device_identifiers = []
162177
self._device_ids = [DeviceIdentifier(id) for id in device_identifiers]
163178
self._auto_discover = auto_discover
164179
self._cancelled = False
180+
self._device_relay_paths: list[str] = []
165181

166182
async def async_relay_devices(self) -> NoReturn:
167183
try:
@@ -187,13 +203,13 @@ async def _async_discover_devices_loop(self) -> AsyncGenerator[InputDevice, None
187203
for device in list_readable_devices():
188204
if self._should_relay(device):
189205
yield device
190-
await asyncio.sleep(1)
206+
await asyncio.sleep(0.1)
191207

192208
def _should_relay(self, device: InputDevice) -> bool:
193-
return not self._has_task(device) and self._matches_criteria(device)
209+
return not self._has_relay(device) and self._matches_criteria(device)
194210

195-
def _has_task(self, device: InputDevice) -> bool:
196-
return device.path in [task.get_name() for task in asyncio.all_tasks()]
211+
def _has_relay(self, device: InputDevice) -> bool:
212+
return device.path in self._device_relay_paths
197213

198214
def _matches_criteria(self, device: InputDevice) -> bool:
199215
return self._auto_discover or self._matches_any_identifier(device)
@@ -207,6 +223,7 @@ def _create_task(self, device: InputDevice, task_group: TaskGroup) -> None:
207223
async def _async_relay_events(self, device: InputDevice) -> NoReturn:
208224
try:
209225
relay = DeviceRelay(device)
226+
self._device_relay_paths.append(device.path)
210227
_logger.info(f"Activated {repr(relay)}")
211228
await relay.async_relay_events_loop()
212229
except CancelledError:
@@ -216,19 +233,6 @@ async def _async_relay_events(self, device: InputDevice) -> NoReturn:
216233
_logger.critical(f"Connection to {device.name} lost [{repr(ex)}]")
217234
except Exception:
218235
_logger.exception(f"{device.name} failed!")
219-
await asyncio.sleep(2)
220-
221-
222-
def list_readable_devices() -> list[InputDevice]:
223-
return [InputDevice(path) for path in list_devices()]
224-
225-
226-
def _enable_usb_devices():
227-
usb_hid.enable(
228-
[
229-
Device.MOUSE,
230-
Device.KEYBOARD,
231-
Device.CONSUMER_CONTROL,
232-
]
233-
)
234-
_logger.debug(f"Available USB devices: {usb_hid.devices}")
236+
await asyncio.sleep(1)
237+
finally:
238+
self._device_relay_paths.remove(device.path)

0 commit comments

Comments
 (0)