AnalogSensePy 1.0.5__tar.gz → 1.0.7__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,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: AnalogSensePy
3
+ Version: 1.0.7
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<sup>T</sup>
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, T</sup>
33
+
34
+ <sup>T</sup> Tested and should work. Feel free to open issues/prs about boards not being picked up properly
35
+ <sup>R</sup> Razer Synapse needs to be installed and running for analogue inputs to be received from this keyboard.
36
+ <sup>P</sup> The official firmware only supports polling, which can lead to lag and missed inputs.
37
+ <sup>F</sup> [Custom firmware with full analog report functionality is available](https://analogsense.org/firmware/).
38
+
39
+ ## Installation
40
+ ```bash
41
+ pip install AnalogSensePy
42
+ ```
43
+
44
+ On Linux you may need udev rules or `sudo` for hid
45
+ ```bash
46
+ printf '%s\n' \
47
+ 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"' \
48
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="31e3", MODE="0660", GROUP="input"' \
49
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", MODE="0660", GROUP="input"' \
50
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1532", MODE="0660", GROUP="input"' \
51
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="19f5", MODE="0660", GROUP="input"' \
52
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="352d", MODE="0660", GROUP="input"' \
53
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="3434", MODE="0660", GROUP="input"' \
54
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="362d", MODE="0660", GROUP="input"' \
55
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="373b", MODE="0660", GROUP="input"' \
56
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="372e", MODE="0660", GROUP="input"' \
57
+ | sudo tee /etc/udev/rules.d/99-analogsense.rules
58
+
59
+ sudo udevadm control --reload-rules && sudo udevadm trigger
60
+ ```
61
+ ## Usage
62
+ ```python
63
+ from analogsense import AnalogSense
64
+
65
+ as_ = AnalogSense()
66
+ devices = as_.get_devices()
67
+
68
+ if not devices:
69
+ print("no supported analog keyboards found.")
70
+ exit()
71
+
72
+ if len(devices) == 1:
73
+ dev = devices[0]
74
+ else:
75
+ for i, d in enumerate(devices):
76
+ print(f" [{i}] {d.product_name}")
77
+ dev = devices[int(input("select device index: "))]
78
+
79
+ def on_keys(active_keys):
80
+ for k in active_keys:
81
+ # converting the scancode to a nice readable string
82
+ name = as_.scancode_to_string(k["scancode"])
83
+
84
+ # some providers can return the digital state of the key
85
+ # (what the system perceives the key as pressed or not)
86
+ # other providers that do not support this will return None
87
+ if k["digital"] is not None:
88
+ digital = " [down]" if k["digital"] else " [up]"
89
+ else:
90
+ digital = ""
91
+
92
+ print(f"{name:20s} {k['value']:.4f}{digital}")
93
+
94
+ dev.start_listening(on_keys)
95
+ input("press keys... enter to stop.\n")
96
+ dev.stop_listening()
97
+ ```
98
+
99
+ The following functions are available on `AnalogSense`:
100
+ - `get_devices() -> list[Device]`
101
+ - `open_device(vendor_id, product_id) -> Device | None`
102
+ - `scancode_to_string(scancode: int) -> str`
103
+
104
+ A device instance has the following members:
105
+ - `start_listening(handler: Callable[[list[{"scancode": int, "value": float}]], None])`
106
+ - `stop_listening()`
107
+ - `product_name: str`
108
+ - `forget()`
109
+ - `dev: DeviceHandle`
110
+
111
+ ### Scancodes
112
+ The scancodes provided by this library are primarily HID scancodes; most keys are mapped as seen on usage page 0x07 (A = 0x04, B = 0x05, ...).
113
+
114
+ Control keys (usage page 0x0C) are mapped in the `0x3__` range, modulo 0x100:
115
+ - `0x3B5` = Next Track
116
+ - `0x3B6` = Previous Track
117
+ - `0x3B7` = Stop Media
118
+ - `0x3CD` = Play/Pause
119
+ - `0x394` = Open File Explorer
120
+ - `0x323` = Open Browser Home Page
121
+
122
+ OEM-specific keys are mapped in the `0x4__` range:
123
+ - `0x401` = Brightness Up
124
+ - `0x402` = Brightness Down
125
+ - `0x403` = Profile 1
126
+ - `0x404` = Profile 2
127
+ - `0x405` = Profile 3
128
+ - `0x408` = Profile Switch
129
+ - `0x409` = Function Key (Fn)
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: AnalogSensePy
3
+ Version: 1.0.7
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<sup>T</sup>
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, T</sup>
33
+
34
+ <sup>T</sup> Tested and should work. Feel free to open issues/prs about boards not being picked up properly
35
+ <sup>R</sup> Razer Synapse needs to be installed and running for analogue inputs to be received from this keyboard.
36
+ <sup>P</sup> The official firmware only supports polling, which can lead to lag and missed inputs.
37
+ <sup>F</sup> [Custom firmware with full analog report functionality is available](https://analogsense.org/firmware/).
38
+
39
+ ## Installation
40
+ ```bash
41
+ pip install AnalogSensePy
42
+ ```
43
+
44
+ On Linux you may need udev rules or `sudo` for hid
45
+ ```bash
46
+ printf '%s\n' \
47
+ 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"' \
48
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="31e3", MODE="0660", GROUP="input"' \
49
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", MODE="0660", GROUP="input"' \
50
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="1532", MODE="0660", GROUP="input"' \
51
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="19f5", MODE="0660", GROUP="input"' \
52
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="352d", MODE="0660", GROUP="input"' \
53
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="3434", MODE="0660", GROUP="input"' \
54
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="362d", MODE="0660", GROUP="input"' \
55
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="373b", MODE="0660", GROUP="input"' \
56
+ 'SUBSYSTEM=="usb", ATTRS{idVendor}=="372e", MODE="0660", GROUP="input"' \
57
+ | sudo tee /etc/udev/rules.d/99-analogsense.rules
58
+
59
+ sudo udevadm control --reload-rules && sudo udevadm trigger
60
+ ```
61
+ ## Usage
62
+ ```python
63
+ from analogsense import AnalogSense
64
+
65
+ as_ = AnalogSense()
66
+ devices = as_.get_devices()
67
+
68
+ if not devices:
69
+ print("no supported analog keyboards found.")
70
+ exit()
71
+
72
+ if len(devices) == 1:
73
+ dev = devices[0]
74
+ else:
75
+ for i, d in enumerate(devices):
76
+ print(f" [{i}] {d.product_name}")
77
+ dev = devices[int(input("select device index: "))]
78
+
79
+ def on_keys(active_keys):
80
+ for k in active_keys:
81
+ # converting the scancode to a nice readable string
82
+ name = as_.scancode_to_string(k["scancode"])
83
+
84
+ # some providers can return the digital state of the key
85
+ # (what the system perceives the key as pressed or not)
86
+ # other providers that do not support this will return None
87
+ if k["digital"] is not None:
88
+ digital = " [down]" if k["digital"] else " [up]"
89
+ else:
90
+ digital = ""
91
+
92
+ print(f"{name:20s} {k['value']:.4f}{digital}")
93
+
94
+ dev.start_listening(on_keys)
95
+ input("press keys... enter to stop.\n")
96
+ dev.stop_listening()
97
+ ```
98
+
99
+ The following functions are available on `AnalogSense`:
100
+ - `get_devices() -> list[Device]`
101
+ - `open_device(vendor_id, product_id) -> Device | None`
102
+ - `scancode_to_string(scancode: int) -> str`
103
+
104
+ A device instance has the following members:
105
+ - `start_listening(handler: Callable[[list[{"scancode": int, "value": float}]], None])`
106
+ - `stop_listening()`
107
+ - `product_name: str`
108
+ - `forget()`
109
+ - `dev: DeviceHandle`
110
+
111
+ ### Scancodes
112
+ The scancodes provided by this library are primarily HID scancodes; most keys are mapped as seen on usage page 0x07 (A = 0x04, B = 0x05, ...).
113
+
114
+ Control keys (usage page 0x0C) are mapped in the `0x3__` range, modulo 0x100:
115
+ - `0x3B5` = Next Track
116
+ - `0x3B6` = Previous Track
117
+ - `0x3B7` = Stop Media
118
+ - `0x3CD` = Play/Pause
119
+ - `0x394` = Open File Explorer
120
+ - `0x323` = Open Browser Home Page
121
+
122
+ OEM-specific keys are mapped in the `0x4__` range:
123
+ - `0x401` = Brightness Up
124
+ - `0x402` = Brightness Down
125
+ - `0x403` = Profile 1
126
+ - `0x404` = Profile 2
127
+ - `0x405` = Profile 3
128
+ - `0x408` = Profile Switch
129
+ - `0x409` = Function Key (Fn)
@@ -1,7 +1,7 @@
1
1
  # AnalogSense Python SDK
2
2
  Python port of [AnalogSense.js](https://github.com/AnalogSense/JavaScript-SDK/) for analog keyboard input.
3
3
  ## Supported Keyboards/Devices
4
- - Everything by Wooting
4
+ - Everything by Wooting<sup>T</sup>
5
5
  - Everything by NuPhy
6
6
  - Everything by DrunkDeer
7
7
  - Razer Huntsman V2 Analog<sup>R</sup>
@@ -17,8 +17,9 @@ Python port of [AnalogSense.js](https://github.com/AnalogSense/JavaScript-SDK/)
17
17
  - Madlions MAD60HE<sup>P</sup>
18
18
  - Madlions MAD68HE<sup>P</sup>
19
19
  - Madlions MAD68R<sup>P</sup>
20
- - Redragon K709HE<sup>P</sup>
20
+ - Redragon K709HE<sup>P, T</sup>
21
21
 
22
+ <sup>T</sup> Tested and should work. Feel free to open issues/prs about boards not being picked up properly
22
23
  <sup>R</sup> Razer Synapse needs to be installed and running for analogue inputs to be received from this keyboard.
23
24
  <sup>P</sup> The official firmware only supports polling, which can lead to lag and missed inputs.
24
25
  <sup>F</sup> [Custom firmware with full analog report functionality is available](https://analogsense.org/firmware/).
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
  import sys
3
+ import threading
3
4
  import warnings
4
5
 
5
6
  from .providers import ALL_PROVIDERS, AsProvider
@@ -51,10 +52,26 @@ class AnalogSense:
51
52
  try:
52
53
  vendor_id = f.get("vendor_id", 0)
53
54
  product_id = f.get("product_id", 0)
54
- for info in _hid.enumerate(vendor_id, product_id):
55
+ result = []
56
+ exc = []
57
+ def _enumerate(v=vendor_id, p=product_id):
58
+ try:
59
+ result.extend(_hid.enumerate(v, p))
60
+ except Exception as e:
61
+ exc.append(e)
62
+ t = threading.Thread(target=_enumerate, daemon=True)
63
+ t.start()
64
+ t.join(timeout=3.0)
65
+ if exc:
66
+ raise exc[0]
67
+ if t.is_alive():
68
+ warnings.warn(f"HID enumeration timed out for {vendor_id:#06x}:{product_id:#06x}, device may be in a bad state, try reconnecting")
69
+ continue
70
+ for info in result:
55
71
  dedup_key = (info["vendor_id"], info["product_id"], info.get("usage_page", 0), info.get("usage", 0))
56
72
  if dedup_key in seen: continue
57
73
  if "usage_page" in f and info.get("usage_page") != f["usage_page"]: continue
74
+ if "usage" in f and info.get("usage") != f["usage"]: continue
58
75
  seen.add(dedup_key)
59
76
  candidates.append((info, provider_cls))
60
77
  except Exception as e:
@@ -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):
@@ -4,9 +4,14 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "AnalogSensePy"
7
- version = "1.0.5"
7
+ version = "1.0.7"
8
+ readme = "README.md"
9
+ license = { text = "MIT" }
8
10
  requires-python = ">=3.10"
9
11
  dependencies = [
10
12
  "hid; sys_platform == 'linux'",
11
13
  "hidapi; sys_platform != 'linux'",
12
- ]
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/girlglock/AnalogSense-Python-SDK"
@@ -1,8 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: AnalogSensePy
3
- Version: 1.0.5
4
- Requires-Python: >=3.10
5
- License-File: LICENSE
6
- Requires-Dist: hid; sys_platform == "linux"
7
- Requires-Dist: hidapi; sys_platform != "linux"
8
- Dynamic: license-file
@@ -1,8 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: AnalogSensePy
3
- Version: 1.0.5
4
- Requires-Python: >=3.10
5
- License-File: LICENSE
6
- Requires-Dist: hid; sys_platform == "linux"
7
- Requires-Dist: hidapi; sys_platform != "linux"
8
- Dynamic: license-file
File without changes
File without changes