makcu 2.1.3__py3-none-any.whl → 2.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- makcu/README.md +405 -0
- makcu/__init__.py +20 -376
- makcu/__main__.py +3 -2
- makcu/connection.py +144 -50
- makcu/controller.py +23 -1
- makcu/test_suite.py +9 -12
- makcu-2.2.0.dist-info/METADATA +1115 -0
- makcu-2.2.0.dist-info/RECORD +17 -0
- makcu-2.2.0.dist-info/licenses/LICENSE +674 -0
- makcu-2.1.3.dist-info/METADATA +0 -32
- makcu-2.1.3.dist-info/RECORD +0 -15
- {makcu-2.1.3.dist-info → makcu-2.2.0.dist-info}/WHEEL +0 -0
- {makcu-2.1.3.dist-info → makcu-2.2.0.dist-info}/top_level.txt +0 -0
makcu/connection.py
CHANGED
@@ -59,6 +59,19 @@ class SerialTransport:
|
|
59
59
|
self.auto_reconnect = auto_reconnect
|
60
60
|
self.override_port = override_port
|
61
61
|
|
62
|
+
if not hasattr(SerialTransport, '_thread_counter'):
|
63
|
+
SerialTransport._thread_counter = 0
|
64
|
+
SerialTransport._thread_map = {}
|
65
|
+
|
66
|
+
# Log version info during initialization
|
67
|
+
try:
|
68
|
+
from makcu import __version__
|
69
|
+
version = __version__
|
70
|
+
self._log(f"Makcu version: {version}")
|
71
|
+
except ImportError:
|
72
|
+
self._log("Makcu version info not available")
|
73
|
+
|
74
|
+
self._log(f"Initializing SerialTransport with params: fallback='{fallback}', debug={debug}, send_init={send_init}, auto_reconnect={auto_reconnect}, override_port={override_port}")
|
62
75
|
|
63
76
|
self._is_connected = False
|
64
77
|
self._reconnect_attempts = 0
|
@@ -85,48 +98,62 @@ class SerialTransport:
|
|
85
98
|
|
86
99
|
self._stop_event = threading.Event()
|
87
100
|
self._listener_thread: Optional[threading.Thread] = None
|
88
|
-
|
101
|
+
|
89
102
|
|
90
|
-
self.
|
103
|
+
self._ascii_decode_table = bytes(range(128))
|
91
104
|
|
105
|
+
self._log("SerialTransport initialization completed")
|
92
106
|
|
93
|
-
self._ascii_decode_table = bytes(range(128))
|
94
107
|
|
95
108
|
def _log(self, message: str, level: str = "INFO") -> None:
|
96
|
-
if not self.debug
|
109
|
+
if not self.debug:
|
97
110
|
return
|
98
111
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
112
|
+
timestamp = time.strftime("%H:%M:%S", time.localtime())
|
113
|
+
thread_id = threading.get_ident()
|
114
|
+
|
115
|
+
# Map thread ID to a simple number
|
116
|
+
if thread_id not in SerialTransport._thread_map:
|
117
|
+
SerialTransport._thread_counter += 1
|
118
|
+
SerialTransport._thread_map[thread_id] = SerialTransport._thread_counter
|
119
|
+
|
120
|
+
thread_num = SerialTransport._thread_map[thread_id]
|
121
|
+
entry = f"[{timestamp}] [T:{thread_num}] [{level}] {message}"
|
122
|
+
print(entry, flush=True)
|
105
123
|
|
106
124
|
def _generate_command_id(self) -> int:
|
125
|
+
old_counter = self._command_counter
|
107
126
|
self._command_counter = (self._command_counter + 1) & 0x2710
|
108
127
|
return self._command_counter
|
109
128
|
|
110
129
|
def find_com_port(self) -> Optional[str]:
|
130
|
+
self._log("Starting COM port discovery")
|
131
|
+
|
111
132
|
if self.override_port:
|
133
|
+
self._log(f"Override port enabled, using: {self._fallback_com_port}")
|
112
134
|
return self._fallback_com_port
|
113
135
|
|
136
|
+
all_ports = list_ports.comports()
|
137
|
+
self._log(f"Found {len(all_ports)} COM ports total")
|
114
138
|
|
115
139
|
target_hwid = "VID:PID=1A86:55D3"
|
116
140
|
|
117
|
-
for port in
|
141
|
+
for i, port in enumerate(all_ports):
|
142
|
+
self._log(f"Port {i}: {port.device} - HWID: {port.hwid}")
|
118
143
|
if target_hwid in port.hwid.upper():
|
119
|
-
self._log(f"
|
144
|
+
self._log(f"Target device found on port: {port.device}")
|
120
145
|
return port.device
|
121
146
|
|
147
|
+
self._log("Target device not found in COM port scan")
|
148
|
+
|
122
149
|
if self._fallback_com_port:
|
123
|
-
self._log(f"Using fallback: {self._fallback_com_port}")
|
150
|
+
self._log(f"Using fallback COM port: {self._fallback_com_port}")
|
124
151
|
return self._fallback_com_port
|
125
152
|
|
153
|
+
self._log("No fallback port specified, returning None")
|
126
154
|
return None
|
127
155
|
|
128
156
|
def _parse_response_line(self, line: bytes) -> ParsedResponse:
|
129
|
-
|
130
157
|
if line.startswith(b'>>> '):
|
131
158
|
content = line[4:].decode('ascii', 'ignore').strip()
|
132
159
|
return ParsedResponse(None, content, False)
|
@@ -139,12 +166,14 @@ class SerialTransport:
|
|
139
166
|
return
|
140
167
|
|
141
168
|
changed_bits = byte_val ^ self._last_button_mask
|
142
|
-
|
169
|
+
self._log(f"Button state changed: 0x{self._last_button_mask:02X} -> 0x{byte_val:02X}")
|
143
170
|
|
144
171
|
for bit in range(5):
|
145
172
|
if changed_bits & (1 << bit):
|
146
173
|
is_pressed = bool(byte_val & (1 << bit))
|
174
|
+
button_name = self.BUTTON_MAP[bit] if bit < len(self.BUTTON_MAP) else f"bit{bit}"
|
147
175
|
|
176
|
+
self._log(f"Button {button_name}: {'PRESSED' if is_pressed else 'RELEASED'}")
|
148
177
|
|
149
178
|
if is_pressed:
|
150
179
|
self._button_states |= (1 << bit)
|
@@ -154,8 +183,8 @@ class SerialTransport:
|
|
154
183
|
if self._button_callback and bit < len(self.BUTTON_ENUM_MAP):
|
155
184
|
try:
|
156
185
|
self._button_callback(self.BUTTON_ENUM_MAP[bit], is_pressed)
|
157
|
-
except Exception:
|
158
|
-
|
186
|
+
except Exception as e:
|
187
|
+
self._log(f"Button callback failed: {e}", "ERROR")
|
159
188
|
|
160
189
|
self._last_button_mask = byte_val
|
161
190
|
|
@@ -166,7 +195,6 @@ class SerialTransport:
|
|
166
195
|
with self._command_lock:
|
167
196
|
if not self._pending_commands:
|
168
197
|
return
|
169
|
-
|
170
198
|
|
171
199
|
oldest_id = next(iter(self._pending_commands))
|
172
200
|
pending = self._pending_commands[oldest_id]
|
@@ -174,7 +202,6 @@ class SerialTransport:
|
|
174
202
|
if pending.future.done():
|
175
203
|
return
|
176
204
|
|
177
|
-
|
178
205
|
if content == pending.command:
|
179
206
|
if not pending.expect_response:
|
180
207
|
pending.future.set_result(pending.command)
|
@@ -188,24 +215,25 @@ class SerialTransport:
|
|
188
215
|
return
|
189
216
|
|
190
217
|
current_time = time.time()
|
218
|
+
|
191
219
|
with self._command_lock:
|
192
|
-
|
193
220
|
timed_out = [
|
194
221
|
(cmd_id, pending)
|
195
222
|
for cmd_id, pending in self._pending_commands.items()
|
196
223
|
if current_time - pending.timestamp > pending.timeout
|
197
224
|
]
|
198
|
-
|
199
225
|
|
200
226
|
for cmd_id, pending in timed_out:
|
227
|
+
age = current_time - pending.timestamp
|
228
|
+
self._log(f"Command '{pending.command}' timed out after {age:.3f}s", "ERROR")
|
201
229
|
del self._pending_commands[cmd_id]
|
202
230
|
if not pending.future.done():
|
203
231
|
pending.future.set_exception(
|
204
232
|
MakcuTimeoutError(f"Command #{cmd_id} timed out")
|
205
233
|
)
|
206
234
|
|
207
|
-
|
208
235
|
def _listen(self) -> None:
|
236
|
+
self._log("Starting listener thread")
|
209
237
|
|
210
238
|
read_buffer = bytearray(4096)
|
211
239
|
line_buffer = bytearray(256)
|
@@ -223,25 +251,20 @@ class SerialTransport:
|
|
223
251
|
|
224
252
|
while is_connected() and not stop_requested():
|
225
253
|
try:
|
226
|
-
|
227
254
|
bytes_available = serial_in_waiting()
|
228
255
|
if not bytes_available:
|
229
256
|
time.sleep(0.001)
|
230
257
|
continue
|
231
258
|
|
232
|
-
|
233
259
|
bytes_read = serial_read(min(bytes_available, 4096))
|
234
|
-
|
235
260
|
|
236
261
|
for byte_val in bytes_read:
|
237
|
-
|
238
262
|
if byte_val < 32 and byte_val not in (0x0D, 0x0A):
|
263
|
+
# Button data
|
239
264
|
self._handle_button_data(byte_val)
|
240
265
|
else:
|
241
|
-
|
242
|
-
if byte_val == 0x0A:
|
266
|
+
if byte_val == 0x0A: # LF
|
243
267
|
if line_pos > 0:
|
244
|
-
|
245
268
|
line = bytes(line_buffer[:line_pos])
|
246
269
|
line_pos = 0
|
247
270
|
|
@@ -249,27 +272,32 @@ class SerialTransport:
|
|
249
272
|
response = self._parse_response_line(line)
|
250
273
|
if response.content:
|
251
274
|
self._process_pending_commands(response.content)
|
252
|
-
elif byte_val != 0x0D:
|
275
|
+
elif byte_val != 0x0D: # Not CR
|
253
276
|
if line_pos < 256:
|
254
277
|
line_buffer[line_pos] = byte_val
|
255
278
|
line_pos += 1
|
256
|
-
|
257
279
|
|
258
280
|
current_time = time.time()
|
259
281
|
if current_time - last_cleanup > cleanup_interval:
|
260
282
|
self._cleanup_timed_out_commands()
|
261
283
|
last_cleanup = current_time
|
262
284
|
|
263
|
-
except serial.SerialException:
|
285
|
+
except serial.SerialException as e:
|
286
|
+
self._log(f"Serial exception in listener: {e}", "ERROR")
|
264
287
|
if self.auto_reconnect:
|
265
288
|
self._attempt_reconnect()
|
266
289
|
else:
|
267
290
|
break
|
268
|
-
except Exception:
|
269
|
-
|
291
|
+
except Exception as e:
|
292
|
+
self._log(f"Unexpected exception in listener: {e}", "ERROR")
|
293
|
+
|
294
|
+
self._log("Listener thread ending")
|
270
295
|
|
271
296
|
def _attempt_reconnect(self) -> None:
|
297
|
+
self._log(f"Attempting reconnect #{self._reconnect_attempts + 1}/{self.MAX_RECONNECT_ATTEMPTS}")
|
298
|
+
|
272
299
|
if self._reconnect_attempts >= self.MAX_RECONNECT_ATTEMPTS:
|
300
|
+
self._log("Max reconnect attempts reached, giving up", "ERROR")
|
273
301
|
self._is_connected = False
|
274
302
|
return
|
275
303
|
|
@@ -277,44 +305,63 @@ class SerialTransport:
|
|
277
305
|
|
278
306
|
try:
|
279
307
|
if self.serial and self.serial.is_open:
|
308
|
+
self._log("Closing existing serial connection for reconnect")
|
280
309
|
self.serial.close()
|
281
310
|
|
282
311
|
time.sleep(self.RECONNECT_DELAY)
|
283
312
|
|
284
313
|
self.port = self.find_com_port()
|
285
314
|
if not self.port:
|
286
|
-
raise MakcuConnectionError("Device not found")
|
315
|
+
raise MakcuConnectionError("Device not found during reconnect")
|
287
316
|
|
288
|
-
|
317
|
+
self._log(f"Reconnecting to {self.port} at {self.baudrate} baud")
|
289
318
|
self.serial = serial.Serial(
|
290
319
|
self.port,
|
291
320
|
self.baudrate,
|
292
321
|
timeout=0.001,
|
293
322
|
write_timeout=0.01
|
294
323
|
)
|
295
|
-
|
324
|
+
|
325
|
+
if not self._change_baud_to_4M():
|
326
|
+
raise MakcuConnectionError("Failed to change baud during reconnect")
|
296
327
|
|
297
328
|
if self.send_init:
|
329
|
+
self._log("Sending init command during reconnect")
|
298
330
|
self.serial.write(b"km.buttons(1)\r")
|
299
331
|
self.serial.flush()
|
300
332
|
|
301
333
|
self._reconnect_attempts = 0
|
334
|
+
self._log("Reconnect successful")
|
302
335
|
|
303
|
-
except Exception:
|
336
|
+
except Exception as e:
|
337
|
+
self._log(f"Reconnect attempt failed: {e}", "ERROR")
|
304
338
|
time.sleep(self.RECONNECT_DELAY)
|
305
339
|
|
306
340
|
def _change_baud_to_4M(self) -> bool:
|
341
|
+
self._log("Changing baud rate to 4M")
|
342
|
+
|
307
343
|
if self.serial and self.serial.is_open:
|
308
344
|
self.serial.write(self.BAUD_CHANGE_COMMAND)
|
309
345
|
self.serial.flush()
|
346
|
+
|
310
347
|
time.sleep(0.02)
|
348
|
+
|
349
|
+
old_baud = self.serial.baudrate
|
311
350
|
self.serial.baudrate = 4000000
|
312
351
|
self._current_baud = 4000000
|
352
|
+
|
353
|
+
self._log(f"Baud rate changed: {old_baud} -> {self.serial.baudrate}")
|
313
354
|
return True
|
355
|
+
|
356
|
+
self._log("Cannot change baud - serial not open", "ERROR")
|
314
357
|
return False
|
315
358
|
|
316
359
|
def connect(self) -> None:
|
360
|
+
connection_start = time.time()
|
361
|
+
self._log("Starting connection process")
|
362
|
+
|
317
363
|
if self._is_connected:
|
364
|
+
self._log("Already connected")
|
318
365
|
return
|
319
366
|
|
320
367
|
if not self.override_port:
|
@@ -325,8 +372,9 @@ class SerialTransport:
|
|
325
372
|
if not self.port:
|
326
373
|
raise MakcuConnectionError("Makcu device not found")
|
327
374
|
|
375
|
+
self._log(f"Connecting to {self.port}")
|
376
|
+
|
328
377
|
try:
|
329
|
-
|
330
378
|
self.serial = serial.Serial(
|
331
379
|
self.port,
|
332
380
|
115200,
|
@@ -343,10 +391,14 @@ class SerialTransport:
|
|
343
391
|
self._is_connected = True
|
344
392
|
self._reconnect_attempts = 0
|
345
393
|
|
394
|
+
connection_time = time.time() - connection_start
|
395
|
+
self._log(f"Connection established in {connection_time:.3f}s")
|
396
|
+
|
346
397
|
if self.send_init:
|
347
|
-
self.
|
398
|
+
self._log("Sending initialization command")
|
399
|
+
init_cmd = b"km.buttons(1)\r"
|
400
|
+
self.serial.write(init_cmd)
|
348
401
|
self.serial.flush()
|
349
|
-
|
350
402
|
|
351
403
|
self._stop_event.clear()
|
352
404
|
self._listener_thread = threading.Thread(
|
@@ -355,38 +407,61 @@ class SerialTransport:
|
|
355
407
|
name="MakcuListener"
|
356
408
|
)
|
357
409
|
self._listener_thread.start()
|
410
|
+
self._log(f"Listener thread started: {self._listener_thread.name}")
|
358
411
|
|
359
412
|
except Exception as e:
|
413
|
+
self._log(f"Connection failed: {e}", "ERROR")
|
360
414
|
if self.serial:
|
361
|
-
|
415
|
+
try:
|
416
|
+
self.serial.close()
|
417
|
+
except:
|
418
|
+
pass
|
362
419
|
raise MakcuConnectionError(f"Failed to connect: {e}")
|
363
420
|
|
364
421
|
def disconnect(self) -> None:
|
422
|
+
self._log("Starting disconnection process")
|
423
|
+
|
365
424
|
self._is_connected = False
|
366
425
|
|
367
426
|
if self.send_init:
|
368
427
|
self._stop_event.set()
|
369
428
|
if self._listener_thread and self._listener_thread.is_alive():
|
370
429
|
self._listener_thread.join(timeout=0.1)
|
430
|
+
if self._listener_thread.is_alive():
|
431
|
+
self._log("Listener thread did not join within timeout")
|
432
|
+
else:
|
433
|
+
self._log("Listener thread stopped")
|
434
|
+
|
435
|
+
pending_count = len(self._pending_commands)
|
436
|
+
if pending_count > 0:
|
437
|
+
self._log(f"Cancelling {pending_count} pending commands")
|
371
438
|
|
372
439
|
with self._command_lock:
|
373
|
-
for pending in self._pending_commands.
|
440
|
+
for cmd_id, pending in self._pending_commands.items():
|
374
441
|
if not pending.future.done():
|
375
442
|
pending.future.cancel()
|
376
443
|
self._pending_commands.clear()
|
377
444
|
|
378
445
|
if self.serial and self.serial.is_open:
|
446
|
+
self._log(f"Closing serial port: {self.serial.port}")
|
379
447
|
self.serial.close()
|
448
|
+
|
380
449
|
self.serial = None
|
450
|
+
self._log("Disconnection completed")
|
381
451
|
|
382
|
-
def send_command(self, command: str, expect_response: bool =
|
452
|
+
def send_command(self, command: str, expect_response: bool = True,
|
383
453
|
timeout: float = DEFAULT_TIMEOUT) -> Optional[str]:
|
454
|
+
command_start = time.time()
|
455
|
+
|
384
456
|
if not self._is_connected or not self.serial or not self.serial.is_open:
|
385
457
|
raise MakcuConnectionError("Not connected")
|
386
458
|
|
387
459
|
if not expect_response:
|
388
|
-
|
460
|
+
cmd_bytes = f"{command}\r\n".encode('ascii')
|
461
|
+
self.serial.write(cmd_bytes)
|
389
462
|
self.serial.flush()
|
463
|
+
send_time = time.time() - command_start
|
464
|
+
self._log(f"Command '{command}' sent in {send_time:.3f}s (no response expected)")
|
390
465
|
return command
|
391
466
|
|
392
467
|
cmd_id = self._generate_command_id()
|
@@ -405,56 +480,75 @@ class SerialTransport:
|
|
405
480
|
)
|
406
481
|
|
407
482
|
try:
|
408
|
-
|
483
|
+
cmd_bytes = f"{tagged_command}\r\n".encode('ascii')
|
484
|
+
self.serial.write(cmd_bytes)
|
409
485
|
self.serial.flush()
|
410
486
|
|
411
487
|
result = future.result(timeout=timeout)
|
412
|
-
|
488
|
+
|
489
|
+
response = result.split('#')[0] if '#' in result else result
|
490
|
+
total_time = time.time() - command_start
|
491
|
+
self._log(f"Command '{command}' completed in {total_time:.3f}s total")
|
492
|
+
return response
|
413
493
|
|
414
494
|
except TimeoutError:
|
495
|
+
total_time = time.time() - command_start
|
496
|
+
self._log(f"Command '{command}' timed out after {total_time:.3f}s", "ERROR")
|
415
497
|
raise MakcuTimeoutError(f"Command timed out: {command}")
|
416
498
|
except Exception as e:
|
499
|
+
total_time = time.time() - command_start
|
500
|
+
self._log(f"Command '{command}' failed after {total_time:.3f}s: {e}", "ERROR")
|
417
501
|
with self._command_lock:
|
418
502
|
self._pending_commands.pop(cmd_id, None)
|
419
503
|
raise
|
420
504
|
|
421
505
|
async def async_send_command(self, command: str, expect_response: bool = False,
|
422
506
|
timeout: float = DEFAULT_TIMEOUT) -> Optional[str]:
|
507
|
+
self._log(f"Async sending command: '{command}'")
|
423
508
|
loop = asyncio.get_running_loop()
|
424
509
|
return await loop.run_in_executor(
|
425
510
|
None, self.send_command, command, expect_response, timeout
|
426
511
|
)
|
427
512
|
|
428
513
|
def is_connected(self) -> bool:
|
429
|
-
|
514
|
+
connected = self._is_connected and self.serial is not None and self.serial.is_open
|
515
|
+
return connected
|
430
516
|
|
431
517
|
def set_button_callback(self, callback: Optional[Callable[[MouseButton, bool], None]]) -> None:
|
518
|
+
self._log(f"Setting button callback: {callback is not None}")
|
432
519
|
self._button_callback = callback
|
433
520
|
|
434
521
|
def get_button_states(self) -> Dict[str, bool]:
|
435
|
-
|
522
|
+
states = {
|
436
523
|
self.BUTTON_MAP[i]: bool(self._button_states & (1 << i))
|
437
524
|
for i in range(5)
|
438
525
|
}
|
526
|
+
return states
|
439
527
|
|
440
528
|
def get_button_mask(self) -> int:
|
441
529
|
return self._last_button_mask
|
442
530
|
|
443
531
|
def enable_button_monitoring(self, enable: bool = True) -> None:
|
444
|
-
|
532
|
+
cmd = "km.buttons(1)" if enable else "km.buttons(0)"
|
533
|
+
self._log(f"{'Enabling' if enable else 'Disabling'} button monitoring")
|
534
|
+
self.send_command(cmd)
|
445
535
|
|
446
536
|
async def __aenter__(self):
|
537
|
+
self._log("Async context manager enter")
|
447
538
|
loop = asyncio.get_running_loop()
|
448
539
|
await loop.run_in_executor(None, self.connect)
|
449
540
|
return self
|
450
541
|
|
451
542
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
543
|
+
self._log("Async context manager exit")
|
452
544
|
loop = asyncio.get_running_loop()
|
453
545
|
await loop.run_in_executor(None, self.disconnect)
|
454
546
|
|
455
547
|
def __enter__(self):
|
548
|
+
self._log("Sync context manager enter")
|
456
549
|
self.connect()
|
457
550
|
return self
|
458
551
|
|
459
552
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
553
|
+
self._log("Sync context manager exit")
|
460
554
|
self.disconnect()
|
makcu/controller.py
CHANGED
@@ -97,6 +97,29 @@ class MakcuController:
|
|
97
97
|
self._check_connection()
|
98
98
|
self.mouse.move(dx, dy)
|
99
99
|
|
100
|
+
@maybe_async
|
101
|
+
def batch_execute(self, actions: List[Callable[[], None]]) -> None:
|
102
|
+
"""Execute a batch of actions in sequence.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
actions: List of callable functions to execute in order
|
106
|
+
|
107
|
+
Example:
|
108
|
+
makcu.batch_execute([
|
109
|
+
lambda: makcu.move(50, 0),
|
110
|
+
lambda: makcu.click(MouseButton.LEFT),
|
111
|
+
lambda: makcu.move(-50, 0),
|
112
|
+
lambda: makcu.click(MouseButton.RIGHT)
|
113
|
+
])
|
114
|
+
"""
|
115
|
+
self._check_connection()
|
116
|
+
|
117
|
+
for action in actions:
|
118
|
+
try:
|
119
|
+
action()
|
120
|
+
except Exception as e:
|
121
|
+
raise RuntimeError(f"Batch execution failed at action: {e}")
|
122
|
+
|
100
123
|
@maybe_async
|
101
124
|
def scroll(self, delta: int) -> None:
|
102
125
|
self._check_connection()
|
@@ -358,7 +381,6 @@ class MakcuController:
|
|
358
381
|
"""Legacy method - use scroll() instead"""
|
359
382
|
await self.scroll(delta)
|
360
383
|
|
361
|
-
|
362
384
|
def create_controller(fallback_com_port: str = "", debug: bool = False,
|
363
385
|
send_init: bool = True, auto_reconnect: bool = True,
|
364
386
|
override_port: bool = False) -> MakcuController:
|
makcu/test_suite.py
CHANGED
@@ -17,7 +17,7 @@ def test_press_and_release(makcu):
|
|
17
17
|
makcu.release(MouseButton.LEFT)
|
18
18
|
|
19
19
|
def test_firmware_version(makcu):
|
20
|
-
version = makcu.
|
20
|
+
version = makcu.get_firmware_version()
|
21
21
|
assert version and len(version.strip()) > 0
|
22
22
|
|
23
23
|
def test_middle_click(makcu):
|
@@ -63,18 +63,15 @@ def test_batch_commands(makcu):
|
|
63
63
|
print("Testing batch command execution (10 commands)...")
|
64
64
|
|
65
65
|
start_time = time.perf_counter()
|
66
|
-
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
makcu.move(0, -10)
|
77
|
-
makcu.scroll(1)
|
67
|
+
async def combo_actions():
|
68
|
+
await makcu.batch_execute([
|
69
|
+
lambda: makcu.move(5, 5),
|
70
|
+
lambda: makcu.click(MouseButton.LEFT),
|
71
|
+
lambda: makcu.scroll(-1)
|
72
|
+
])
|
73
|
+
|
74
|
+
combo_actions()
|
78
75
|
|
79
76
|
end_time = time.perf_counter()
|
80
77
|
elapsed_ms = (end_time - start_time) * 1000
|