makcu 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (36) hide show
  1. makcu-0.1.2/MANIFEST.in +7 -0
  2. {makcu-0.1.1 → makcu-0.1.2}/PKG-INFO +7 -12
  3. {makcu-0.1.1 → makcu-0.1.2}/README.md +7 -12
  4. {makcu-0.1.1 → makcu-0.1.2}/makcu/__init__.py +2 -2
  5. {makcu-0.1.1 → makcu-0.1.2}/makcu/__main__.py +24 -9
  6. makcu-0.1.2/makcu/conftest.py +22 -0
  7. {makcu-0.1.1 → makcu-0.1.2}/makcu/connection.py +43 -49
  8. {makcu-0.1.1 → makcu-0.1.2}/makcu/controller.py +4 -37
  9. {makcu-0.1.1 → makcu-0.1.2}/makcu/mouse.py +0 -10
  10. makcu-0.1.2/makcu/test_suite.py +98 -0
  11. {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/PKG-INFO +7 -12
  12. makcu-0.1.2/makcu.egg-info/SOURCES.txt +18 -0
  13. {makcu-0.1.1 → makcu-0.1.2}/pyproject.toml +6 -2
  14. makcu-0.1.2/pytest.ini +4 -0
  15. makcu-0.1.1/makcu/utils.py +0 -7
  16. makcu-0.1.1/makcu.egg-info/SOURCES.txt +0 -29
  17. makcu-0.1.1/tests/test_button_mask.py +0 -7
  18. makcu-0.1.1/tests/test_capture_right_clicks.py +0 -21
  19. makcu-0.1.1/tests/test_controller_behavior.py +0 -7
  20. makcu-0.1.1/tests/test_device_info.py +0 -8
  21. makcu-0.1.1/tests/test_firmware_version.py +0 -7
  22. makcu-0.1.1/tests/test_get_button_states.py +0 -16
  23. makcu-0.1.1/tests/test_get_raw_mask.py +0 -14
  24. makcu-0.1.1/tests/test_is_button_pressed.py +0 -13
  25. makcu-0.1.1/tests/test_lock_state.py +0 -22
  26. makcu-0.1.1/tests/test_middle_click.py +0 -6
  27. makcu-0.1.1/tests/test_port_connection.py +0 -4
  28. makcu-0.1.1/tests/test_press_and_release.py +0 -14
  29. makcu-0.1.1/tests/test_reset_all.py +0 -20
  30. makcu-0.1.1/tests/test_set_fallback_port.py +0 -3
  31. {makcu-0.1.1 → makcu-0.1.2}/LICENSE +0 -0
  32. {makcu-0.1.1 → makcu-0.1.2}/makcu/enums.py +0 -0
  33. {makcu-0.1.1 → makcu-0.1.2}/makcu/errors.py +0 -0
  34. {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/dependency_links.txt +0 -0
  35. {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/top_level.txt +0 -0
  36. {makcu-0.1.1 → makcu-0.1.2}/setup.cfg +0 -0
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ include pytest.ini
5
+
6
+ recursive-include makcu *.py
7
+ prune makcu/tests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makcu
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Python library to interact with Makcu devices.
5
5
  Author: SleepyTotem
6
6
  License: GPL
@@ -65,7 +65,7 @@ python -m makcu --runtest
65
65
  ```python
66
66
  from makcu import create_controller, MouseButton
67
67
 
68
- makcu = create_controller()
68
+ makcu = create_controller("COM1") # Fallback port
69
69
  makcu.click(MouseButton.LEFT)
70
70
  makcu.move(100, 50)
71
71
  makcu.scroll(-1)
@@ -85,7 +85,7 @@ makcu = create_controller(debug=True, send_init=True)
85
85
  #### Set fallback port manually
86
86
 
87
87
  ```python
88
- makcu.set_port("COM4") # use before create_controller()
88
+ makcu = create_controller("COM4") # Optional fallback com port
89
89
  ```
90
90
 
91
91
  ---
@@ -189,17 +189,9 @@ def on_button_event(button, pressed):
189
189
  makcu.set_button_callback(on_button_event)
190
190
  ```
191
191
 
192
- ### Configure Debounce
193
-
194
- ```python
195
- makcu.set_callback_debounce_time(100) # milliseconds
196
- ```
197
-
198
192
  ---
199
193
 
200
- ## Click Capturing
201
-
202
- ### ❌ **Currently broken (Pending Firmware Update)**
194
+ ## Click Capturing (Pending Firmware Update)
203
195
 
204
196
  Click capturing will allow you to detect and count click events in software.
205
197
 
@@ -248,6 +240,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
248
240
  ### Send raw serial commands
249
241
 
250
242
  ```python
243
+ from makcu import create_controller
244
+ makcu = create_controller()
251
245
  response = makcu.transport.send_command("km.version()", expect_response=True)
252
246
  print(response)
253
247
  ```
@@ -313,3 +307,4 @@ Please open an issue on the project repository and I will get to it asap
313
307
  ## 🌐 Links
314
308
 
315
309
  - 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
310
+ - 🔗 [PyPi Homepage](https://github.com/SleepyTotem/makcu-py-lib)
@@ -53,7 +53,7 @@ python -m makcu --runtest
53
53
  ```python
54
54
  from makcu import create_controller, MouseButton
55
55
 
56
- makcu = create_controller()
56
+ makcu = create_controller("COM1") # Fallback port
57
57
  makcu.click(MouseButton.LEFT)
58
58
  makcu.move(100, 50)
59
59
  makcu.scroll(-1)
@@ -73,7 +73,7 @@ makcu = create_controller(debug=True, send_init=True)
73
73
  #### Set fallback port manually
74
74
 
75
75
  ```python
76
- makcu.set_port("COM4") # use before create_controller()
76
+ makcu = create_controller("COM4") # Optional fallback com port
77
77
  ```
78
78
 
79
79
  ---
@@ -177,17 +177,9 @@ def on_button_event(button, pressed):
177
177
  makcu.set_button_callback(on_button_event)
178
178
  ```
179
179
 
180
- ### Configure Debounce
181
-
182
- ```python
183
- makcu.set_callback_debounce_time(100) # milliseconds
184
- ```
185
-
186
180
  ---
187
181
 
188
- ## Click Capturing
189
-
190
- ### ❌ **Currently broken (Pending Firmware Update)**
182
+ ## Click Capturing (Pending Firmware Update)
191
183
 
192
184
  Click capturing will allow you to detect and count click events in software.
193
185
 
@@ -236,6 +228,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
236
228
  ### Send raw serial commands
237
229
 
238
230
  ```python
231
+ from makcu import create_controller
232
+ makcu = create_controller()
239
233
  response = makcu.transport.send_command("km.version()", expect_response=True)
240
234
  print(response)
241
235
  ```
@@ -300,4 +294,5 @@ Please open an issue on the project repository and I will get to it asap
300
294
 
301
295
  ## 🌐 Links
302
296
 
303
- - 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
297
+ - 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
298
+ - 🔗 [PyPi Homepage](https://github.com/SleepyTotem/makcu-py-lib)
@@ -2,8 +2,8 @@ from .controller import MakcuController
2
2
  from .enums import MouseButton
3
3
  from .errors import MakcuError, MakcuConnectionError
4
4
 
5
- def create_controller(debug=False, send_init=True):
6
- makcu = MakcuController(debug=debug, send_init=send_init)
5
+ def create_controller(fallback_com_port="", debug=False, send_init=True):
6
+ makcu = MakcuController(fallback_com_port, debug=debug, send_init=send_init)
7
7
  makcu.connect()
8
8
  return makcu
9
9
 
@@ -1,9 +1,8 @@
1
- # makcu/__main__.py
2
-
3
1
  import sys
4
- import subprocess
5
2
  import webbrowser
6
3
  import os
4
+ from pathlib import Path
5
+ import pytest
7
6
  from makcu import create_controller, MakcuConnectionError
8
7
 
9
8
  def debug_console():
@@ -44,14 +43,30 @@ def test_port(port):
44
43
 
45
44
  def run_tests():
46
45
  print("🧪 Running Pytest Suite...")
47
- subprocess.run([
48
- sys.executable, "-m", "pytest",
49
- "--html=latest_pytest.html", "--self-contained-html"
50
- ])
46
+
47
+ package_dir = Path(__file__).resolve().parent
48
+ test_file = package_dir / "test_suite.py"
49
+
50
+ sys.exit(pytest.main([
51
+ str(test_file),
52
+ "--rootdir", str(package_dir),
53
+ "-v", "--tb=short",
54
+ "--capture=tee-sys",
55
+ "--html=latest_pytest.html",
56
+ "--self-contained-html"
57
+ ]))
51
58
 
52
59
  report_path = os.path.abspath("latest_pytest.html")
53
- print(f"📄 Opening test report: {report_path}")
54
- webbrowser.open(f"file://{report_path}")
60
+ if os.path.exists(report_path):
61
+ print(f"📄 Opening test report: {report_path}")
62
+ webbrowser.open(f"file://{report_path}")
63
+ else:
64
+ print("❌ Report not found. Something went wrong.")
65
+
66
+ if result.returncode != 0:
67
+ print("❌ Some tests failed.")
68
+ else:
69
+ print("✅ All tests passed.")
55
70
 
56
71
  def main():
57
72
  args = sys.argv[1:]
@@ -0,0 +1,22 @@
1
+ import pytest
2
+ from makcu import create_controller
3
+ import time
4
+
5
+ @pytest.fixture(scope="session")
6
+ def makcu():
7
+ ctrl = create_controller()
8
+ yield ctrl
9
+ ctrl.disconnect()
10
+ time.sleep(0.2)
11
+
12
+ @pytest.fixture(autouse=True)
13
+ def ensure_clean_exit(makcu):
14
+ yield
15
+ makcu.mouse.lock_left(False)
16
+ makcu.mouse.lock_right(False)
17
+ makcu.mouse.lock_middle(False)
18
+ makcu.mouse.lock_side1(False)
19
+ makcu.mouse.lock_side2(False)
20
+ makcu.mouse.lock_x(False)
21
+ makcu.mouse.lock_y(False)
22
+ makcu.enable_button_monitoring(False)
@@ -6,7 +6,6 @@ from .errors import MakcuConnectionError, MakcuTimeoutError
6
6
  from .enums import MouseButton
7
7
 
8
8
  class SerialTransport:
9
- global fallback_com_port
10
9
  baud_change_command = bytearray([0xDE, 0xAD, 0x05, 0x00, 0xA5, 0x00, 0x09, 0x3D, 0x00])
11
10
 
12
11
  button_map = {
@@ -17,7 +16,8 @@ class SerialTransport:
17
16
  4: 'mouse5'
18
17
  }
19
18
 
20
- def __init__(self, debug=False, send_init=True):
19
+ def __init__(self, fallback, debug=False, send_init=True):
20
+ self._fallback_com_port = fallback
21
21
  self._log_messages = []
22
22
  self.debug = debug
23
23
  self.send_init = send_init
@@ -28,7 +28,6 @@ class SerialTransport:
28
28
  self._stop_event = threading.Event()
29
29
  self._listener_thread = None
30
30
  self._button_states = {btn: False for btn in self.button_map.values()}
31
- self._debounce_time_ms = 50
32
31
  self._last_callback_time = {bit: 0 for bit in self.button_map}
33
32
  self._pause_listener = False
34
33
 
@@ -48,6 +47,7 @@ class SerialTransport:
48
47
  self.serial = None
49
48
  self._current_baud = None
50
49
 
50
+
51
51
  def receive_response(self, max_bytes=1024, max_lines=3, sent_command: str = "") -> str:
52
52
  lines = []
53
53
  try:
@@ -69,9 +69,6 @@ class SerialTransport:
69
69
  lines.remove(command_clean)
70
70
  return "\n".join(lines)
71
71
 
72
- def set_debounce_time(self, ms: int):
73
- self._debounce_time_ms = ms
74
-
75
72
  def set_button_callback(self, callback):
76
73
  self._button_callback = callback
77
74
 
@@ -84,22 +81,20 @@ class SerialTransport:
84
81
  print(entry, flush=True)
85
82
 
86
83
  def find_com_port(self):
87
- global fallback_com_port
88
84
  self._log("Searching for CH343 device...")
89
85
 
90
86
  for port in list_ports.comports():
91
- if "USB-Enhanced-SERIAL CH343" in port.description:
87
+ if "VID:PID=1A86:55D3" in port.hwid.upper():
92
88
  self._log(f"Device found: {port.device}")
93
89
  return port.device
94
90
 
95
- if fallback_com_port and "COM" in fallback_com_port:
96
- self._log(f"CH343 not found. Falling back to specified port: {fallback_com_port}")
97
- return fallback_com_port
91
+ if self._fallback_com_port:
92
+ self._log(f"Device not found. Falling back to specified port: {self._fallback_com_port}")
93
+ return self._fallback_com_port
98
94
  else:
99
- self._log("Fallback port is not valid.")
95
+ self._log("Fallback port not specified or invalid.")
100
96
  return None
101
97
 
102
-
103
98
  def _open_serial_port(self, port, baud_rate):
104
99
  try:
105
100
  self._log(f"Trying to open {port} at {baud_rate} baud.")
@@ -113,18 +108,14 @@ class SerialTransport:
113
108
  self._log("Sending baud rate switch command to 4M.")
114
109
  self.serial.write(self.baud_change_command)
115
110
  self.serial.flush()
116
- port = self.serial.name
117
- self.serial.close()
118
- time.sleep(0.1)
119
- self.serial = self._open_serial_port(port, 4000000)
120
- if self.serial:
121
- self._current_baud = 4000000
122
- self._log("Switched to 4M baud successfully.")
123
- return True
124
- else:
125
- self._log("Failed to reopen port at 4M baud.")
111
+ time.sleep(0.05)
112
+ self.serial.baudrate = 4000000
113
+ self._current_baud = 4000000
114
+ self._log("Switched to 4M baud successfully.")
115
+ return True
126
116
  return False
127
117
 
118
+
128
119
  def connect(self):
129
120
  if self._is_connected:
130
121
  self._log("Already connected.")
@@ -224,54 +215,57 @@ class SerialTransport:
224
215
 
225
216
  def _listen(self, debug=False):
226
217
  self._log("Started listener thread")
227
- last_value = None
228
218
  button_states = {i: False for i in self.button_map}
229
219
  self._last_mask = 0
220
+ self._last_callback_time = {bit: 0 for bit in self.button_map}
230
221
 
231
222
  while self._is_connected and not self._stop_event.is_set():
232
223
  if self._pause_listener:
233
224
  time.sleep(0.001)
234
225
  continue
226
+
235
227
  try:
236
228
  byte = self.serial.read(1)
237
229
  if byte:
238
230
  value = byte[0]
239
- mask = 0
240
- for bit, name in self.button_map.items():
241
- is_pressed = bool(value & (1 << bit))
242
- if is_pressed != button_states[bit]:
243
- button_states[bit] = is_pressed
244
- if is_pressed:
245
- mask |= (1 << bit)
246
-
247
- for bit, name in self.button_map.items():
248
- self._button_states[name] = button_states[bit]
249
-
250
- now = time.time() * 1000
251
- if self._button_callback and mask != self._last_mask:
231
+
232
+ if value != self._last_mask:
233
+ byte_str = str(byte)
234
+
235
+ if "b'\\x00" in byte_str:
236
+ for bit in self.button_map:
237
+ button_states[bit] = False
238
+
239
+ elif "b'\\x" in byte_str:
240
+ for bit, name in self.button_map.items():
241
+ is_pressed = bool(value & (1 << bit))
242
+ if is_pressed != button_states[bit]:
243
+ button_states[bit] = is_pressed
244
+
252
245
  for bit, name in self.button_map.items():
253
- previous = bool(self._last_mask & (1 << bit))
254
- current = bool(mask & (1 << bit))
255
- if previous != current:
256
- last_time = self._last_callback_time.get(bit, 0)
257
- if now - last_time >= self._debounce_time_ms:
246
+ self._button_states[name] = button_states[bit]
247
+
248
+ if self._button_callback:
249
+ for bit, name in self.button_map.items():
250
+ previous = bool(self._last_mask & (1 << bit))
251
+ current = bool(value & (1 << bit))
252
+ if previous != current:
258
253
  button_enum = self._button_enum_map.get(bit)
259
254
  if button_enum:
260
255
  self._button_callback(button_enum, current)
261
- self._last_callback_time[bit] = now
262
256
 
263
- self._last_mask = mask
257
+ self._last_mask = value
264
258
 
265
- if debug:
266
- pressed = [name for bit, name in self.button_map.items() if button_states[bit]]
267
- button_str = ", ".join(pressed) if pressed else "No buttons pressed"
268
- self._log(f"Byte: {value} (0x{value:02X}) -> {button_str}")
259
+ if debug:
260
+ pressed = [name for bit, name in self.button_map.items() if button_states[bit]]
261
+ button_str = ", ".join(pressed) if pressed else "No buttons pressed"
262
+ self._log(f"Byte: {value} (0x{value:02X}) -> {button_str}")
269
263
 
270
- last_value = value
271
264
  except serial.SerialException as e:
272
265
  if "ClearCommError failed" not in str(e):
273
266
  self._log(f"Serial error during listening: {e}")
274
267
  break
268
+
275
269
  time.sleep(0.0001)
276
270
 
277
271
  self._log("Listener thread exiting")
@@ -6,8 +6,8 @@ from .errors import MakcuConnectionError
6
6
  from .enums import MouseButton
7
7
 
8
8
  class MakcuController:
9
- def __init__(self, debug=False, send_init=True):
10
- self.transport = SerialTransport(debug=debug, send_init=send_init)
9
+ def __init__(self, fallback_com_port, debug=False, send_init=True):
10
+ self.transport = SerialTransport(fallback_com_port, debug=debug, send_init=send_init)
11
11
  self.mouse = Mouse(self.transport)
12
12
 
13
13
  def connect(self):
@@ -92,10 +92,6 @@ class MakcuController:
92
92
  self._check_connection()
93
93
  return self.transport.get_button_mask()
94
94
 
95
- def is_locked(self, target: str) -> bool:
96
- self._check_connection()
97
- return self.mouse.is_locked(target)
98
-
99
95
  def is_button_locked(self, button: MouseButton) -> bool:
100
96
  self._check_connection()
101
97
  return self.mouse.is_button_locked(button)
@@ -146,21 +142,9 @@ class MakcuController:
146
142
  def get_all_lock_states(self) -> dict:
147
143
  self._check_connection()
148
144
  return self.mouse.get_all_lock_states()
149
-
150
- def set_callback_debounce_time(self, ms: int):
151
- self._check_connection()
152
- self.transport.set_debounce_time(ms)
153
-
154
- def start_capturing_clicks(self, button: str):
155
- self._check_connection()
156
- self.transport._capture_counts[button] = 0
157
- self.transport._capture_active[button] = True
158
- self.transport._capture_last_state[button] = None
159
- self.mouse.begin_capture(button)
160
145
 
161
146
  def stop_capturing_clicks(self, button: str) -> int:
162
147
  self._check_connection()
163
- self.transport._capture_active[button] = False
164
148
  return self.mouse.stop_capturing_clicks(button)
165
149
 
166
150
  def press(self, button: MouseButton):
@@ -170,28 +154,11 @@ class MakcuController:
170
154
  def release(self, button: MouseButton):
171
155
  self._check_connection()
172
156
  self.mouse.release(button)
173
-
174
- def set_port(self, port: str):
175
- import makcu.connection
176
- makcu.connection.fallback_com_port = port
177
-
157
+
178
158
  def get_button_states(self) -> dict:
179
159
  self._check_connection()
180
160
  return self.transport.get_button_states()
181
161
 
182
162
  def is_button_pressed(self, button: MouseButton) -> bool:
183
163
  self._check_connection()
184
- return self.transport.get_button_states().get(button.name.lower(), False)
185
-
186
- def get_raw_mask(self) -> int:
187
- self._check_connection()
188
- return self.transport.get_button_mask()
189
-
190
- def get_virtual_bit_mapping(self) -> dict:
191
- return {
192
- "LEFT": 0,
193
- "RIGHT": 1,
194
- "MIDDLE": 2,
195
- "MOUSE4": 3,
196
- "MOUSE5": 4
197
- }
164
+ return self.transport.get_button_states().get(button.name.lower(), False)
@@ -117,16 +117,6 @@ class Mouse:
117
117
  """
118
118
  return self.transport.read_captured_clicks(button)
119
119
 
120
- def get_captured_clicks_enum(self, button: MouseButton) -> int:
121
- mapping = {
122
- MouseButton.LEFT: "LEFT",
123
- MouseButton.RIGHT: "RIGHT",
124
- MouseButton.MIDDLE: "MIDDLE",
125
- MouseButton.MOUSE4: "MOUSE4",
126
- MouseButton.MOUSE5: "MOUSE5",
127
- }
128
- return self.stop_capturing_clicks(mapping[button])
129
-
130
120
  def get_all_lock_states(self) -> dict:
131
121
  return {
132
122
  target: self.is_locked(target)
@@ -0,0 +1,98 @@
1
+ import pytest
2
+ from makcu import MouseButton
3
+
4
+ def test_is_button_pressed(makcu):
5
+ assert makcu.is_button_pressed(MouseButton.LEFT) in [True, False]
6
+
7
+ def test_press_and_release(makcu):
8
+ makcu.press(MouseButton.LEFT)
9
+ makcu.release(MouseButton.LEFT)
10
+
11
+ def test_firmware_version(makcu):
12
+ print("Getting firmware version...")
13
+ version = makcu.mouse.get_firmware_version()
14
+ print(f"Firmware version: {version}")
15
+ assert version and len(version.strip()) > 0
16
+
17
+ def test_middle_click(makcu):
18
+ makcu.mouse.press(MouseButton.MIDDLE)
19
+ makcu.mouse.release(MouseButton.MIDDLE)
20
+
21
+ def test_device_info(makcu):
22
+ print("Fetching device info...")
23
+ info = makcu.mouse.get_device_info()
24
+ print(f"Device Info: {info}")
25
+ assert info.get("port")
26
+ assert info.get("isConnected") is True
27
+
28
+ def test_port_connection(makcu):
29
+ assert makcu.is_connected()
30
+
31
+ @pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
32
+ def test_capture_right_clicks(makcu):
33
+ makcu.mouse.lock_right(True)
34
+ assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
35
+
36
+ makcu.mouse.begin_capture("RIGHT")
37
+ makcu.mouse.press(MouseButton.RIGHT)
38
+ makcu.mouse.release(MouseButton.RIGHT)
39
+ makcu.mouse.press(MouseButton.RIGHT)
40
+ makcu.mouse.release(MouseButton.RIGHT)
41
+
42
+ makcu.mouse.lock_right(False)
43
+ assert not makcu.mouse.is_button_locked(MouseButton.RIGHT)
44
+
45
+ count = makcu.mouse.stop_capturing_clicks("RIGHT")
46
+ assert count >= 2, f"Expected >=2 captured clicks, got {count}"
47
+
48
+ def test_button_mask(makcu):
49
+ print("Getting button mask...")
50
+ mask = makcu.get_button_mask()
51
+ print(f"Mask value: {mask}")
52
+ assert isinstance(mask, int)
53
+
54
+ def test_get_button_states(makcu):
55
+ states = makcu.get_button_states()
56
+ assert isinstance(states, dict)
57
+ for key in ['left', 'right', 'middle', 'mouse4', 'mouse5']:
58
+ assert key in states
59
+
60
+ def test_lock_state(makcu):
61
+ print("Locking LEFT button...")
62
+ makcu.lock_left(True)
63
+
64
+ print("Querying lock state while LEFT is locked...")
65
+ assert makcu.is_button_locked(MouseButton.LEFT)
66
+
67
+ print("Querying all lock states...")
68
+ all_states = makcu.get_all_lock_states()
69
+ print(f"All lock states: {all_states}")
70
+
71
+ assert all_states["LEFT"] is True
72
+ assert isinstance(all_states["RIGHT"], bool)
73
+
74
+ print("Unlocking LEFT button...")
75
+ makcu.lock_left(False)
76
+
77
+ print("Rechecking LEFT lock state after unlock...")
78
+ assert not makcu.is_button_locked(MouseButton.LEFT)
79
+
80
+ def test_makcu_behavior(makcu):
81
+ makcu.move(25, 25)
82
+ makcu.click(MouseButton.LEFT)
83
+ makcu.scroll(-2)
84
+
85
+ def test_reset_all(makcu):
86
+ makcu.mouse.lock_left(False)
87
+ makcu.mouse.lock_right(False)
88
+ makcu.mouse.lock_middle(False)
89
+ makcu.mouse.lock_side1(False)
90
+ makcu.mouse.lock_side2(False)
91
+ makcu.mouse.lock_x(False)
92
+ makcu.mouse.lock_y(False)
93
+
94
+ states = makcu.mouse.get_all_lock_states()
95
+ assert all(state is False for state in states.values() if state is not None), \
96
+ f"Expected all unlocked, got: {states}"
97
+
98
+ makcu.enable_button_monitoring(False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makcu
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Python library to interact with Makcu devices.
5
5
  Author: SleepyTotem
6
6
  License: GPL
@@ -65,7 +65,7 @@ python -m makcu --runtest
65
65
  ```python
66
66
  from makcu import create_controller, MouseButton
67
67
 
68
- makcu = create_controller()
68
+ makcu = create_controller("COM1") # Fallback port
69
69
  makcu.click(MouseButton.LEFT)
70
70
  makcu.move(100, 50)
71
71
  makcu.scroll(-1)
@@ -85,7 +85,7 @@ makcu = create_controller(debug=True, send_init=True)
85
85
  #### Set fallback port manually
86
86
 
87
87
  ```python
88
- makcu.set_port("COM4") # use before create_controller()
88
+ makcu = create_controller("COM4") # Optional fallback com port
89
89
  ```
90
90
 
91
91
  ---
@@ -189,17 +189,9 @@ def on_button_event(button, pressed):
189
189
  makcu.set_button_callback(on_button_event)
190
190
  ```
191
191
 
192
- ### Configure Debounce
193
-
194
- ```python
195
- makcu.set_callback_debounce_time(100) # milliseconds
196
- ```
197
-
198
192
  ---
199
193
 
200
- ## Click Capturing
201
-
202
- ### ❌ **Currently broken (Pending Firmware Update)**
194
+ ## Click Capturing (Pending Firmware Update)
203
195
 
204
196
  Click capturing will allow you to detect and count click events in software.
205
197
 
@@ -248,6 +240,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
248
240
  ### Send raw serial commands
249
241
 
250
242
  ```python
243
+ from makcu import create_controller
244
+ makcu = create_controller()
251
245
  response = makcu.transport.send_command("km.version()", expect_response=True)
252
246
  print(response)
253
247
  ```
@@ -313,3 +307,4 @@ Please open an issue on the project repository and I will get to it asap
313
307
  ## 🌐 Links
314
308
 
315
309
  - 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
310
+ - 🔗 [PyPi Homepage](https://github.com/SleepyTotem/makcu-py-lib)
@@ -0,0 +1,18 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ pytest.ini
6
+ makcu/__init__.py
7
+ makcu/__main__.py
8
+ makcu/conftest.py
9
+ makcu/connection.py
10
+ makcu/controller.py
11
+ makcu/enums.py
12
+ makcu/errors.py
13
+ makcu/mouse.py
14
+ makcu/test_suite.py
15
+ makcu.egg-info/PKG-INFO
16
+ makcu.egg-info/SOURCES.txt
17
+ makcu.egg-info/dependency_links.txt
18
+ makcu.egg-info/top_level.txt
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "makcu"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "Python library to interact with Makcu devices."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.7"
@@ -14,7 +14,11 @@ license = { text = "GPL" }
14
14
 
15
15
  [tool.setuptools]
16
16
  packages = ["makcu"]
17
+ include-package-data = true
17
18
 
18
19
  [build-system]
19
20
  requires = ["setuptools>=61.0"]
20
- build-backend = "setuptools.build_meta"
21
+ build-backend = "setuptools.build_meta"
22
+
23
+ [tool.setuptools.data-files]
24
+ "." = ["tests/*.py"]
makcu-0.1.2/pytest.ini ADDED
@@ -0,0 +1,4 @@
1
+ [pytest]
2
+ python_files = test_suite.py
3
+ testpaths = makcu
4
+ addopts = -v --tb=short --capture=tee-sys --html=latest_pytest.html --self-contained-html
@@ -1,7 +0,0 @@
1
- from serial.tools import list_ports
2
-
3
- def find_makcu_port():
4
- for port in list_ports.comports():
5
- if "CH343" in port.description:
6
- return port.device
7
- return None
@@ -1,29 +0,0 @@
1
- LICENSE
2
- README.md
3
- pyproject.toml
4
- makcu/__init__.py
5
- makcu/__main__.py
6
- makcu/connection.py
7
- makcu/controller.py
8
- makcu/enums.py
9
- makcu/errors.py
10
- makcu/mouse.py
11
- makcu/utils.py
12
- makcu.egg-info/PKG-INFO
13
- makcu.egg-info/SOURCES.txt
14
- makcu.egg-info/dependency_links.txt
15
- makcu.egg-info/top_level.txt
16
- tests/test_button_mask.py
17
- tests/test_capture_right_clicks.py
18
- tests/test_controller_behavior.py
19
- tests/test_device_info.py
20
- tests/test_firmware_version.py
21
- tests/test_get_button_states.py
22
- tests/test_get_raw_mask.py
23
- tests/test_is_button_pressed.py
24
- tests/test_lock_state.py
25
- tests/test_middle_click.py
26
- tests/test_port_connection.py
27
- tests/test_press_and_release.py
28
- tests/test_reset_all.py
29
- tests/test_set_fallback_port.py
@@ -1,7 +0,0 @@
1
- # test_button_mask.py
2
-
3
- def test_button_mask(makcu):
4
- print("Getting button mask...")
5
- mask = makcu.get_button_mask()
6
- print(f"Mask value: {mask}")
7
- assert isinstance(mask, int)
@@ -1,21 +0,0 @@
1
- # test_capture_right_clicks.py
2
- import pytest
3
- from makcu import MouseButton
4
-
5
- @pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
6
- def test_capture_right_clicks(makcu):
7
- makcu.mouse.lock_right(True)
8
- assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
9
-
10
- makcu.mouse.begin_capture("RIGHT")
11
-
12
- makcu.mouse.press(MouseButton.RIGHT)
13
- makcu.mouse.release(MouseButton.RIGHT)
14
- makcu.mouse.press(MouseButton.RIGHT)
15
- makcu.mouse.release(MouseButton.RIGHT)
16
-
17
- makcu.mouse.lock_right(False)
18
- assert not makcu.mouse.is_button_locked(MouseButton.RIGHT)
19
-
20
- count = makcu.mouse.stop_capturing_clicks("RIGHT")
21
- assert count >= 2, f"Expected >=2 captured clicks, got {count}"
@@ -1,7 +0,0 @@
1
- # test_makcu_behavior.py
2
- from makcu import MouseButton
3
-
4
- def test_makcu_behavior(makcu):
5
- makcu.move(25, 25)
6
- makcu.click(MouseButton.LEFT)
7
- makcu.scroll(-2)
@@ -1,8 +0,0 @@
1
- # tests/test_device_info.py
2
-
3
- def test_device_info(makcu):
4
- print("Fetching device info...")
5
- info = makcu.mouse.get_device_info()
6
- print(f"Device Info: {info}")
7
- assert info.get("port")
8
- assert info.get("isConnected") is True
@@ -1,7 +0,0 @@
1
- # tests/test_firmware_version.py
2
-
3
- def test_firmware_version(makcu):
4
- print("Getting firmware version...")
5
- version = makcu.mouse.get_firmware_version()
6
- print(f"Firmware version: {version}")
7
- assert version and len(version.strip()) > 0
@@ -1,16 +0,0 @@
1
- # test_get_button_states.py
2
-
3
- import pytest
4
- from makcu import create_controller
5
-
6
- @pytest.fixture(scope="module")
7
- def makcu():
8
- ctrl = create_controller()
9
- yield ctrl
10
- ctrl.disconnect()
11
-
12
- def test_get_button_states(makcu):
13
- states = makcu.get_button_states()
14
- assert isinstance(states, dict)
15
- for key in ['left', 'right', 'middle', 'mouse4', 'mouse5']:
16
- assert key in states
@@ -1,14 +0,0 @@
1
- # test_get_raw_mask.py
2
-
3
- import pytest
4
- from makcu import create_controller
5
-
6
- @pytest.fixture(scope="module")
7
- def makcu():
8
- ctrl = create_controller()
9
- yield ctrl
10
- ctrl.disconnect()
11
-
12
- def test_get_raw_mask(makcu):
13
- mask = makcu.get_raw_mask()
14
- assert isinstance(mask, int)
@@ -1,13 +0,0 @@
1
- # test_is_button_pressed.py
2
-
3
- import pytest
4
- from makcu import create_controller, MouseButton
5
-
6
- @pytest.fixture(scope="module")
7
- def makcu():
8
- ctrl = create_controller()
9
- yield ctrl
10
- ctrl.disconnect()
11
-
12
- def test_is_button_pressed(makcu):
13
- assert makcu.is_button_pressed(MouseButton.LEFT) in [True, False]
@@ -1,22 +0,0 @@
1
- # tests/test_lock_state.py
2
- from makcu import MouseButton
3
-
4
- def test_lock_state(makcu):
5
- print("Locking LEFT button...")
6
- makcu.lock_left(True)
7
-
8
- print("Querying lock state while LEFT is locked...")
9
- assert makcu.is_button_locked(MouseButton.LEFT)
10
-
11
- print("Querying all lock states...")
12
- all_states = makcu.get_all_lock_states()
13
- print(f"All lock states: {all_states}")
14
-
15
- assert all_states["LEFT"] is True
16
- assert isinstance(all_states["RIGHT"], bool)
17
-
18
- print("Unlocking LEFT button...")
19
- makcu.lock_left(False)
20
-
21
- print("Rechecking LEFT lock state after unlock...")
22
- assert not makcu.is_button_locked(MouseButton.LEFT)
@@ -1,6 +0,0 @@
1
- # test_middle_click.py
2
- from makcu import MouseButton
3
-
4
- def test_middle_click(makcu):
5
- makcu.mouse.press(MouseButton.MIDDLE)
6
- makcu.mouse.release(MouseButton.MIDDLE)
@@ -1,4 +0,0 @@
1
- # test_port_connection.py
2
-
3
- def test_port_connection(makcu):
4
- assert makcu.is_connected()
@@ -1,14 +0,0 @@
1
- # test_press_and_release.py
2
-
3
- import pytest
4
- from makcu import create_controller, MouseButton
5
-
6
- @pytest.fixture(scope="module")
7
- def makcu():
8
- ctrl = create_controller()
9
- yield ctrl
10
- ctrl.disconnect()
11
-
12
- def test_press_and_release(makcu):
13
- makcu.press(MouseButton.LEFT)
14
- makcu.release(MouseButton.LEFT)
@@ -1,20 +0,0 @@
1
- # test_reset_all.py
2
-
3
- def test_reset_all(makcu):
4
- # Unlock all buttons
5
- makcu.mouse.lock_left(False)
6
- makcu.mouse.lock_right(False)
7
- makcu.mouse.lock_middle(False)
8
- makcu.mouse.lock_side1(False)
9
- makcu.mouse.lock_side2(False)
10
- makcu.mouse.lock_x(False)
11
- makcu.mouse.lock_y(False)
12
-
13
- # Check lock states to confirm everything is clean
14
- states = makcu.mouse.get_all_lock_states()
15
-
16
- assert all(state is False for state in states.values() if state is not None), \
17
- f"Expected all unlocked, got: {states}"
18
-
19
- # Optionally disable monitoring
20
- makcu.enable_button_monitoring(False)
@@ -1,3 +0,0 @@
1
- def test_set_fallback_port(makcu):
2
- makcu.set_port("COM5")
3
- assert True
File without changes
File without changes
File without changes
File without changes