AnalogSensePy 1.0.4__tar.gz → 1.0.6__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.
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: AnalogSensePy
3
+ Version: 1.0.6
4
+ License: MIT
5
+ Project-URL: Homepage, https://github.com/girlglock/AnalogSense-Python-SDK
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: hid; sys_platform == "linux"
10
+ Requires-Dist: hidapi; sys_platform != "linux"
11
+ Dynamic: license-file
12
+
13
+ # AnalogSense Python SDK
14
+ Python port of [AnalogSense.js](https://github.com/AnalogSense/JavaScript-SDK/) for analog keyboard input.
15
+ ## Supported Keyboards/Devices
16
+ - Everything by Wooting
17
+ - Everything by NuPhy
18
+ - Everything by DrunkDeer
19
+ - Razer Huntsman V2 Analog<sup>R</sup>
20
+ - Razer Huntsman Mini Analog<sup>R</sup>
21
+ - Razer Huntsman V3 Pro<sup>R</sup>
22
+ - Razer Huntsman V3 Pro Mini<sup>R</sup>
23
+ - Razer Huntsman V3 Pro Tenkeyless<sup>R</sup>
24
+ - Keychron Q1 HE<sup>P, F</sup>
25
+ - Keychron Q3 HE<sup>P, F</sup>
26
+ - Keychron Q5 HE<sup>P, F</sup>
27
+ - Keychron K2 HE<sup>P, F</sup>
28
+ - Lemokey P1 HE<sup>P, F</sup>
29
+ - Madlions MAD60HE<sup>P</sup>
30
+ - Madlions MAD68HE<sup>P</sup>
31
+ - Madlions MAD68R<sup>P</sup>
32
+ - Redragon K709HE<sup>P</sup>
33
+
34
+ <sup>R</sup> Razer Synapse needs to be installed and running for analogue inputs to be received from this keyboard.
35
+ <sup>P</sup> The official firmware only supports polling, which can lead to lag and missed inputs.
36
+ <sup>F</sup> [Custom firmware with full analog report functionality is available](https://analogsense.org/firmware/).
37
+
38
+ ## Installation
39
+ ```bash
40
+ pip install AnalogSensePy
41
+ ```
42
+
43
+ On Linux you may need udev rules or `sudo` for hid
44
+ ```bash
45
+ printf '%s\n' \
46
+ 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"' \
47
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="31e3", MODE="0660", GROUP="input"' \
48
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", MODE="0660", GROUP="input"' \
49
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1532", MODE="0660", GROUP="input"' \
50
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="19f5", MODE="0660", GROUP="input"' \
51
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="352d", MODE="0660", GROUP="input"' \
52
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="3434", MODE="0660", GROUP="input"' \
53
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="362d", MODE="0660", GROUP="input"' \
54
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="373b", MODE="0660", GROUP="input"' \
55
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="372e", MODE="0660", GROUP="input"' \
56
+ | sudo tee /etc/udev/rules.d/99-analogsense.rules
57
+
58
+ sudo udevadm control --reload-rules && sudo udevadm trigger
59
+ ```
60
+ ## Usage
61
+ ```python
62
+ from analogsense import AnalogSense
63
+
64
+ as_ = AnalogSense()
65
+ devices = as_.get_devices()
66
+
67
+ if not devices:
68
+ print("no supported analog keyboards found.")
69
+ exit()
70
+
71
+ if len(devices) == 1:
72
+ dev = devices[0]
73
+ else:
74
+ for i, d in enumerate(devices):
75
+ print(f" [{i}] {d.product_name}")
76
+ dev = devices[int(input("select device index: "))]
77
+
78
+ def on_keys(active_keys):
79
+ for k in active_keys:
80
+ # converting the scancode to a nice readable string
81
+ name = as_.scancode_to_string(k["scancode"])
82
+
83
+ # some providers can return the digital state of the key
84
+ # (what the system perceives the key as pressed or not)
85
+ # other providers that do not support this will return None
86
+ if k["digital"] is not None:
87
+ digital = " [down]" if k["digital"] else " [up]"
88
+ else:
89
+ digital = ""
90
+
91
+ print(f"{name:20s} {k['value']:.4f}{digital}")
92
+
93
+ dev.start_listening(on_keys)
94
+ input("press keys... enter to stop.\n")
95
+ dev.stop_listening()
96
+ ```
97
+
98
+ The following functions are available on `AnalogSense`:
99
+ - `get_devices() -> list[Device]`
100
+ - `open_device(vendor_id, product_id) -> Device | None`
101
+ - `scancode_to_string(scancode: int) -> str`
102
+
103
+ A device instance has the following members:
104
+ - `start_listening(handler: Callable[[list[{"scancode": int, "value": float}]], None])`
105
+ - `stop_listening()`
106
+ - `product_name: str`
107
+ - `forget()`
108
+ - `dev: DeviceHandle`
109
+
110
+ ### Scancodes
111
+ The scancodes provided by this library are primarily HID scancodes; most keys are mapped as seen on usage page 0x07 (A = 0x04, B = 0x05, ...).
112
+
113
+ Control keys (usage page 0x0C) are mapped in the `0x3__` range, modulo 0x100:
114
+ - `0x3B5` = Next Track
115
+ - `0x3B6` = Previous Track
116
+ - `0x3B7` = Stop Media
117
+ - `0x3CD` = Play/Pause
118
+ - `0x394` = Open File Explorer
119
+ - `0x323` = Open Browser Home Page
120
+
121
+ OEM-specific keys are mapped in the `0x4__` range:
122
+ - `0x401` = Brightness Up
123
+ - `0x402` = Brightness Down
124
+ - `0x403` = Profile 1
125
+ - `0x404` = Profile 2
126
+ - `0x405` = Profile 3
127
+ - `0x408` = Profile Switch
128
+ - `0x409` = Function Key (Fn)
@@ -0,0 +1,6 @@
1
+
2
+ [:sys_platform != "linux"]
3
+ hidapi
4
+
5
+ [:sys_platform == "linux"]
6
+ hid
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: AnalogSensePy
3
+ Version: 1.0.6
4
+ License: MIT
5
+ Project-URL: Homepage, https://github.com/girlglock/AnalogSense-Python-SDK
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: hid; sys_platform == "linux"
10
+ Requires-Dist: hidapi; sys_platform != "linux"
11
+ Dynamic: license-file
12
+
13
+ # AnalogSense Python SDK
14
+ Python port of [AnalogSense.js](https://github.com/AnalogSense/JavaScript-SDK/) for analog keyboard input.
15
+ ## Supported Keyboards/Devices
16
+ - Everything by Wooting
17
+ - Everything by NuPhy
18
+ - Everything by DrunkDeer
19
+ - Razer Huntsman V2 Analog<sup>R</sup>
20
+ - Razer Huntsman Mini Analog<sup>R</sup>
21
+ - Razer Huntsman V3 Pro<sup>R</sup>
22
+ - Razer Huntsman V3 Pro Mini<sup>R</sup>
23
+ - Razer Huntsman V3 Pro Tenkeyless<sup>R</sup>
24
+ - Keychron Q1 HE<sup>P, F</sup>
25
+ - Keychron Q3 HE<sup>P, F</sup>
26
+ - Keychron Q5 HE<sup>P, F</sup>
27
+ - Keychron K2 HE<sup>P, F</sup>
28
+ - Lemokey P1 HE<sup>P, F</sup>
29
+ - Madlions MAD60HE<sup>P</sup>
30
+ - Madlions MAD68HE<sup>P</sup>
31
+ - Madlions MAD68R<sup>P</sup>
32
+ - Redragon K709HE<sup>P</sup>
33
+
34
+ <sup>R</sup> Razer Synapse needs to be installed and running for analogue inputs to be received from this keyboard.
35
+ <sup>P</sup> The official firmware only supports polling, which can lead to lag and missed inputs.
36
+ <sup>F</sup> [Custom firmware with full analog report functionality is available](https://analogsense.org/firmware/).
37
+
38
+ ## Installation
39
+ ```bash
40
+ pip install AnalogSensePy
41
+ ```
42
+
43
+ On Linux you may need udev rules or `sudo` for hid
44
+ ```bash
45
+ printf '%s\n' \
46
+ 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"' \
47
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="31e3", MODE="0660", GROUP="input"' \
48
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", MODE="0660", GROUP="input"' \
49
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1532", MODE="0660", GROUP="input"' \
50
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="19f5", MODE="0660", GROUP="input"' \
51
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="352d", MODE="0660", GROUP="input"' \
52
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="3434", MODE="0660", GROUP="input"' \
53
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="362d", MODE="0660", GROUP="input"' \
54
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="373b", MODE="0660", GROUP="input"' \
55
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="372e", MODE="0660", GROUP="input"' \
56
+ | sudo tee /etc/udev/rules.d/99-analogsense.rules
57
+
58
+ sudo udevadm control --reload-rules && sudo udevadm trigger
59
+ ```
60
+ ## Usage
61
+ ```python
62
+ from analogsense import AnalogSense
63
+
64
+ as_ = AnalogSense()
65
+ devices = as_.get_devices()
66
+
67
+ if not devices:
68
+ print("no supported analog keyboards found.")
69
+ exit()
70
+
71
+ if len(devices) == 1:
72
+ dev = devices[0]
73
+ else:
74
+ for i, d in enumerate(devices):
75
+ print(f" [{i}] {d.product_name}")
76
+ dev = devices[int(input("select device index: "))]
77
+
78
+ def on_keys(active_keys):
79
+ for k in active_keys:
80
+ # converting the scancode to a nice readable string
81
+ name = as_.scancode_to_string(k["scancode"])
82
+
83
+ # some providers can return the digital state of the key
84
+ # (what the system perceives the key as pressed or not)
85
+ # other providers that do not support this will return None
86
+ if k["digital"] is not None:
87
+ digital = " [down]" if k["digital"] else " [up]"
88
+ else:
89
+ digital = ""
90
+
91
+ print(f"{name:20s} {k['value']:.4f}{digital}")
92
+
93
+ dev.start_listening(on_keys)
94
+ input("press keys... enter to stop.\n")
95
+ dev.stop_listening()
96
+ ```
97
+
98
+ The following functions are available on `AnalogSense`:
99
+ - `get_devices() -> list[Device]`
100
+ - `open_device(vendor_id, product_id) -> Device | None`
101
+ - `scancode_to_string(scancode: int) -> str`
102
+
103
+ A device instance has the following members:
104
+ - `start_listening(handler: Callable[[list[{"scancode": int, "value": float}]], None])`
105
+ - `stop_listening()`
106
+ - `product_name: str`
107
+ - `forget()`
108
+ - `dev: DeviceHandle`
109
+
110
+ ### Scancodes
111
+ The scancodes provided by this library are primarily HID scancodes; most keys are mapped as seen on usage page 0x07 (A = 0x04, B = 0x05, ...).
112
+
113
+ Control keys (usage page 0x0C) are mapped in the `0x3__` range, modulo 0x100:
114
+ - `0x3B5` = Next Track
115
+ - `0x3B6` = Previous Track
116
+ - `0x3B7` = Stop Media
117
+ - `0x3CD` = Play/Pause
118
+ - `0x394` = Open File Explorer
119
+ - `0x323` = Open Browser Home Page
120
+
121
+ OEM-specific keys are mapped in the `0x4__` range:
122
+ - `0x401` = Brightness Up
123
+ - `0x402` = Brightness Down
124
+ - `0x403` = Profile 1
125
+ - `0x404` = Profile 2
126
+ - `0x405` = Profile 3
127
+ - `0x408` = Profile Switch
128
+ - `0x409` = Function Key (Fn)
@@ -27,8 +27,24 @@ Python port of [AnalogSense.js](https://github.com/AnalogSense/JavaScript-SDK/)
27
27
  ```bash
28
28
  pip install AnalogSensePy
29
29
  ```
30
+
30
31
  On Linux you may need udev rules or `sudo` for hid
32
+ ```bash
33
+ printf '%s\n' \
34
+ 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"' \
35
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="31e3", MODE="0660", GROUP="input"' \
36
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", MODE="0660", GROUP="input"' \
37
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1532", MODE="0660", GROUP="input"' \
38
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="19f5", MODE="0660", GROUP="input"' \
39
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="352d", MODE="0660", GROUP="input"' \
40
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="3434", MODE="0660", GROUP="input"' \
41
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="362d", MODE="0660", GROUP="input"' \
42
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="373b", MODE="0660", GROUP="input"' \
43
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="372e", MODE="0660", GROUP="input"' \
44
+ | sudo tee /etc/udev/rules.d/99-analogsense.rules
31
45
 
46
+ sudo udevadm control --reload-rules && sudo udevadm trigger
47
+ ```
32
48
  ## Usage
33
49
  ```python
34
50
  from analogsense import AnalogSense
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ import sys
2
3
  import warnings
3
4
 
4
5
  from .providers import ALL_PROVIDERS, AsProvider
@@ -10,7 +11,10 @@ try:
10
11
  _HID_AVAILABLE = True
11
12
  except ImportError:
12
13
  _HID_AVAILABLE = False
13
- warnings.warn("'hidapi' is not installed. you can install it with: pip install hidapi")
14
+ if sys.platform == "linux":
15
+ warnings.warn("'hid' is not installed. you can install it with: pip install hid")
16
+ else:
17
+ warnings.warn("'hidapi' is not installed. you can install it with: pip install hidapi")
14
18
 
15
19
 
16
20
  class DeviceHandle:
@@ -44,14 +48,14 @@ class AnalogSense:
44
48
  seen = set()
45
49
  for provider_cls in self.providers:
46
50
  for f in provider_cls.FILTERS:
47
- kwargs = {}
48
- if "vendor_id" in f: kwargs["vendor_id"] = f["vendor_id"]
49
- if "product_id" in f: kwargs["product_id"] = f["product_id"]
50
51
  try:
51
- for info in _hid.enumerate(**kwargs):
52
+ vendor_id = f.get("vendor_id", 0)
53
+ product_id = f.get("product_id", 0)
54
+ for info in _hid.enumerate(vendor_id, product_id):
52
55
  dedup_key = (info["vendor_id"], info["product_id"], info.get("usage_page", 0), info.get("usage", 0))
53
56
  if dedup_key in seen: continue
54
57
  if "usage_page" in f and info.get("usage_page") != f["usage_page"]: continue
58
+ if "usage" in f and info.get("usage") != f["usage"]: continue
55
59
  seen.add(dedup_key)
56
60
  candidates.append((info, provider_cls))
57
61
  except Exception as e:
@@ -59,13 +63,26 @@ class AnalogSense:
59
63
  return candidates
60
64
 
61
65
  def _open(self, info, provider_cls):
62
- dev = _hid.device()
63
- dev.open_path(info["path"])
64
- return provider_cls(DeviceHandle(dev, info))
66
+ try:
67
+ if sys.platform == "linux":
68
+ dev = _hid.Device(path=info["path"])
69
+ else:
70
+ dev = _hid.device()
71
+ dev.open_path(info["path"])
72
+ return provider_cls(DeviceHandle(dev, info))
73
+ except Exception as e:
74
+ if "Permission denied" in str(e):
75
+ warnings.warn(
76
+ f"Permission denied opening {info.get('product_string', info['path'])}. "
77
+ "On Linux, udev rules are required. See: https://github.com/girlglock/AnalogSense-Python-SDK#installation"
78
+ )
79
+ else:
80
+ warnings.warn(f"Failed to open device: {e}")
81
+ return None
65
82
 
66
83
  def get_devices(self):
67
84
  if not _HID_AVAILABLE: return []
68
- return [self._open(info, cls) for info, cls in self._collect_candidates()]
85
+ return [dev for dev in (self._open(info, cls) for info, cls in self._collect_candidates()) if dev is not None]
69
86
 
70
87
  def open_device(self, vendor_id, product_id):
71
88
  if not _HID_AVAILABLE: return None
@@ -133,10 +133,12 @@ class AsProviderWootingV2(AsProvider):
133
133
 
134
134
  class AsProviderRazerHuntsman(AsProvider):
135
135
  FILTERS = [
136
- {"vendor_id": 0x1532, "product_id": 0x0266},
137
- {"vendor_id": 0x1532, "product_id": 0x0282},
136
+ {"vendor_id": 0x1532, "product_id": 0x0266, "usage_page": 0x0001}, #Razer Huntsman V2
137
+ {"vendor_id": 0x1532, "product_id": 0x0282, "usage_page": 0x0001}, #Razer Huntsman Mini
138
138
  ]
139
139
 
140
+ _REPORT_ID = 7
141
+
140
142
  def start_listening(self, handler: Handler):
141
143
  self._running = True
142
144
  self._prev_scancodes: set[int] = set()
@@ -144,7 +146,10 @@ class AsProviderRazerHuntsman(AsProvider):
144
146
  self._thread.start()
145
147
 
146
148
  def _handle_report(self, data: bytes, handler: Handler):
147
- if data[0] != 7: return
149
+ if data[0] == self._REPORT_ID:
150
+ data = data[1:]
151
+ elif len(data) > 1 and data[0] != 0:
152
+ return
148
153
  active_keys = []
149
154
  current_scancodes = set()
150
155
  i = 1
@@ -164,10 +169,11 @@ class AsProviderRazerHuntsman(AsProvider):
164
169
 
165
170
  class AsProviderRazerHuntsmanV3(AsProvider):
166
171
  FILTERS = [
167
- {"vendor_id": 0x1532, "product_id": 0x02A6},
168
- {"vendor_id": 0x1532, "product_id": 0x02A7},
169
- {"vendor_id": 0x1532, "product_id": 0x02B0},
172
+ {"vendor_id": 0x1532, "product_id": 0x02A6, "usage_page": 0x0001}, #Razer Huntsman V3 Pro
173
+ {"vendor_id": 0x1532, "product_id": 0x02A7, "usage_page": 0x0001}, #Razer Huntsman V3 Pro Tenkeyless
174
+ {"vendor_id": 0x1532, "product_id": 0x02B0, "usage_page": 0x0001}, #Razer Huntsman V3 Pro Mini
170
175
  ]
176
+ _REPORT_ID = 11
171
177
 
172
178
  def start_listening(self, handler: Handler):
173
179
  self._running = True
@@ -176,7 +182,10 @@ class AsProviderRazerHuntsmanV3(AsProvider):
176
182
  self._thread.start()
177
183
 
178
184
  def _handle_report(self, data: bytes, handler: Handler):
179
- if data[0] != 11: return
185
+ if data[0] == self._REPORT_ID:
186
+ data = data[1:]
187
+ elif len(data) > 1 and data[0] != 0:
188
+ return
180
189
  active_keys = []
181
190
  current_scancodes = set()
182
191
  i = 1
@@ -194,10 +203,10 @@ class AsProviderRazerHuntsmanV3(AsProvider):
194
203
  self._prev_scancodes = current_scancodes
195
204
  handler(active_keys)
196
205
 
197
-
206
+ #this one needs testing
198
207
  class AsProviderNuphy(AsProvider):
199
208
  FILTERS = [
200
- {"vendor_id": 0x19F5},
209
+ {"vendor_id": 0x19F5, "usage_page": 0x0001, "usage": 0x0000},
201
210
  ]
202
211
 
203
212
  def start_listening(self, handler: Handler):
@@ -264,15 +273,15 @@ class AsProviderDrunkdeer(AsProvider):
264
273
 
265
274
  class AsProviderKeychron(AsProvider):
266
275
  FILTERS = [
267
- {"vendor_id": 0x3434, "product_id": 0x0B10},
268
- {"vendor_id": 0x3434, "product_id": 0x0B11},
269
- {"vendor_id": 0x3434, "product_id": 0x0B12},
270
- {"vendor_id": 0x3434, "product_id": 0x0B30},
271
- {"vendor_id": 0x3434, "product_id": 0x0B50},
272
- {"vendor_id": 0x3434, "product_id": 0x0E20},
273
- {"vendor_id": 0x3434, "product_id": 0x0E21},
274
- {"vendor_id": 0x3434, "product_id": 0x0E22},
275
- {"vendor_id": 0x362D, "product_id": 0x0610},
276
+ {"vendor_id": 0x3434, "product_id": 0x0B10, "usage_page": 0xFF60, "usage": 0x61}, #Q1 HE ANSI
277
+ {"vendor_id": 0x3434, "product_id": 0x0B11, "usage_page": 0xFF60, "usage": 0x61}, #Q1 HE ISO
278
+ {"vendor_id": 0x3434, "product_id": 0x0B12, "usage_page": 0xFF60, "usage": 0x61}, #Q1 HE JIS
279
+ {"vendor_id": 0x3434, "product_id": 0x0B30, "usage_page": 0xFF60, "usage": 0x61}, #Q3 HE ANSI
280
+ {"vendor_id": 0x3434, "product_id": 0x0B50, "usage_page": 0xFF60, "usage": 0x61}, #Q5 HE ANSI
281
+ {"vendor_id": 0x3434, "product_id": 0x0E20, "usage_page": 0xFF60, "usage": 0x61}, #K2 HE ANSI
282
+ {"vendor_id": 0x3434, "product_id": 0x0E21, "usage_page": 0xFF60, "usage": 0x61}, #K2 HE ISO
283
+ {"vendor_id": 0x3434, "product_id": 0x0E22, "usage_page": 0xFF60, "usage": 0x61}, #K2 HE JIS
284
+ {"vendor_id": 0x362D, "product_id": 0x0610, "usage_page": 0xFF60, "usage": 0x61}, #Lemokey P1 HE ANSI
276
285
  ]
277
286
 
278
287
  _INIT_PAYLOAD = bytes([
@@ -373,15 +382,19 @@ class AsProviderKeychron(AsProvider):
373
382
 
374
383
  class AsProviderMadlions(AsProvider):
375
384
  FILTERS = [
376
- {"vendor_id": 0x373B, "product_id": 0x1053},
377
- {"vendor_id": 0x373B, "product_id": 0x1055},
378
- {"vendor_id": 0x373B, "product_id": 0x1056},
379
- {"vendor_id": 0x373B, "product_id": 0x105D},
380
- {"vendor_id": 0x373B, "product_id": 0x1058},
381
- {"vendor_id": 0x373B, "product_id": 0x1059},
382
- {"vendor_id": 0x373B, "product_id": 0x105A},
383
- {"vendor_id": 0x373B, "product_id": 0x105C},
384
- {"vendor_id": 0x373B, "product_id": 0x10A7},
385
+ #MAD60HE
386
+ {"vendor_id": 0x373B, "product_id": 0x1053, "usage_page": 0xFF60, "usage": 0x61},
387
+ {"vendor_id": 0x373B, "product_id": 0x1054, "usage_page": 0xFF60, "usage": 0x61},
388
+ {"vendor_id": 0x373B, "product_id": 0x1055, "usage_page": 0xFF60, "usage": 0x61},
389
+ {"vendor_id": 0x373B, "product_id": 0x1056, "usage_page": 0xFF60, "usage": 0x61},
390
+ {"vendor_id": 0x373B, "product_id": 0x105D, "usage_page": 0xFF60, "usage": 0x61},
391
+ #MAD68HE
392
+ {"vendor_id": 0x373B, "product_id": 0x1058, "usage_page": 0xFF60, "usage": 0x61},
393
+ {"vendor_id": 0x373B, "product_id": 0x1059, "usage_page": 0xFF60, "usage": 0x61},
394
+ {"vendor_id": 0x373B, "product_id": 0x105A, "usage_page": 0xFF60, "usage": 0x61},
395
+ {"vendor_id": 0x373B, "product_id": 0x105C, "usage_page": 0xFF60, "usage": 0x61},
396
+ #MAD68R
397
+ {"vendor_id": 0x373B, "product_id": 0x10A7, "usage_page": 0xFF60, "usage": 0x61},
385
398
  ]
386
399
 
387
400
  _INIT_PAYLOAD = bytes([
@@ -391,7 +404,7 @@ class AsProviderMadlions(AsProvider):
391
404
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392
405
  ])
393
406
 
394
- _MAD60HE_PIDS = {0x1053, 0x1055, 0x1056, 0x105D}
407
+ _MAD60HE_PIDS = {0x1053, 0x1054, 0x1055, 0x1056, 0x105D}
395
408
 
396
409
  def start_listening(self, handler: Handler):
397
410
  pid = self.dev.product_id
@@ -426,7 +439,7 @@ class AsProviderMadlions(AsProvider):
426
439
 
427
440
  class AsProviderBytech(AsProvider):
428
441
  FILTERS = [
429
- {"vendor_id": 0x372E, "product_id": 0x105B},
442
+ {"vendor_id": 0x372E, "product_id": 0x105B, "usage_page": 0xFF00}, #Redragon K709 HE
430
443
  ]
431
444
 
432
445
  def _build_payload(self, cmd, sub):
@@ -0,0 +1,17 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "AnalogSensePy"
7
+ version = "1.0.6"
8
+ readme = "README.md"
9
+ license = { text = "MIT" }
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "hid; sys_platform == 'linux'",
13
+ "hidapi; sys_platform != 'linux'",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/girlglock/AnalogSense-Python-SDK"
@@ -1,7 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: AnalogSensePy
3
- Version: 1.0.4
4
- Requires-Python: >=3.10
5
- License-File: LICENSE
6
- Requires-Dist: hidapi
7
- Dynamic: license-file
@@ -1 +0,0 @@
1
- hidapi
@@ -1,7 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: AnalogSensePy
3
- Version: 1.0.4
4
- Requires-Python: >=3.10
5
- License-File: LICENSE
6
- Requires-Dist: hidapi
7
- Dynamic: license-file
@@ -1,9 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "AnalogSensePy"
7
- version = "1.0.4"
8
- requires-python = ">=3.10"
9
- dependencies = ["hidapi"]
File without changes
File without changes