yjx-sdk-mouse 0.1.0

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 (66) hide show
  1. package/README.md +287 -0
  2. package/dist/constants.d.ts +79 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +100 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/controllers/button.d.ts +15 -0
  7. package/dist/controllers/button.d.ts.map +1 -0
  8. package/dist/controllers/button.js +40 -0
  9. package/dist/controllers/button.js.map +1 -0
  10. package/dist/controllers/device.d.ts +19 -0
  11. package/dist/controllers/device.d.ts.map +1 -0
  12. package/dist/controllers/device.js +111 -0
  13. package/dist/controllers/device.js.map +1 -0
  14. package/dist/controllers/dpi.d.ts +15 -0
  15. package/dist/controllers/dpi.d.ts.map +1 -0
  16. package/dist/controllers/dpi.js +40 -0
  17. package/dist/controllers/dpi.js.map +1 -0
  18. package/dist/controllers/macro.d.ts +18 -0
  19. package/dist/controllers/macro.d.ts.map +1 -0
  20. package/dist/controllers/macro.js +31 -0
  21. package/dist/controllers/macro.js.map +1 -0
  22. package/dist/controllers/other.d.ts +17 -0
  23. package/dist/controllers/other.d.ts.map +1 -0
  24. package/dist/controllers/other.js +29 -0
  25. package/dist/controllers/other.js.map +1 -0
  26. package/dist/controllers/performance.d.ts +10 -0
  27. package/dist/controllers/performance.d.ts.map +1 -0
  28. package/dist/controllers/performance.js +28 -0
  29. package/dist/controllers/performance.js.map +1 -0
  30. package/dist/device/index.d.ts +57 -0
  31. package/dist/device/index.d.ts.map +1 -0
  32. package/dist/device/index.js +341 -0
  33. package/dist/device/index.js.map +1 -0
  34. package/dist/index.d.ts +46 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +102 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/protocol/command-presets.d.ts +11 -0
  39. package/dist/protocol/command-presets.d.ts.map +1 -0
  40. package/dist/protocol/command-presets.js +89 -0
  41. package/dist/protocol/command-presets.js.map +1 -0
  42. package/dist/protocol/key.d.ts +20 -0
  43. package/dist/protocol/key.d.ts.map +1 -0
  44. package/dist/protocol/key.js +197 -0
  45. package/dist/protocol/key.js.map +1 -0
  46. package/dist/protocol/keyboard-presets.d.ts +6 -0
  47. package/dist/protocol/keyboard-presets.d.ts.map +1 -0
  48. package/dist/protocol/keyboard-presets.js +211 -0
  49. package/dist/protocol/keyboard-presets.js.map +1 -0
  50. package/dist/protocol/macro.d.ts +23 -0
  51. package/dist/protocol/macro.d.ts.map +1 -0
  52. package/dist/protocol/macro.js +144 -0
  53. package/dist/protocol/macro.js.map +1 -0
  54. package/dist/protocol/packet.d.ts +88 -0
  55. package/dist/protocol/packet.d.ts.map +1 -0
  56. package/dist/protocol/packet.js +294 -0
  57. package/dist/protocol/packet.js.map +1 -0
  58. package/dist/types.d.ts +127 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/webhid.d.ts +51 -0
  63. package/dist/webhid.d.ts.map +1 -0
  64. package/dist/webhid.js +2 -0
  65. package/dist/webhid.js.map +1 -0
  66. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # yjx-sdk-mouse
2
+
3
+ 永佳新鼠标 WebHID SDK,用于在浏览器中连接兼容设备,并读取或写入 DPI、回报率、休眠策略、按键映射和宏配置。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install yjx-sdk-mouse
9
+ ```
10
+
11
+ 也可以使用 pnpm:
12
+
13
+ ```bash
14
+ pnpm add yjx-sdk-mouse
15
+ ```
16
+
17
+ ## 使用条件
18
+
19
+ - 运行环境需要提供 WebHID,即存在 `navigator.hid`。
20
+ - 页面需要运行在安全上下文中,例如 HTTPS 或本地开发环境。
21
+ - 首次选择设备必须由用户操作触发,因此请在按钮点击等事件中调用 `requestDevices()`。
22
+ - SDK 是 ESM 包,请使用 `import` 引入。
23
+
24
+ ```ts
25
+ if (!('hid' in navigator)) {
26
+ throw new Error('当前浏览器不支持 WebHID');
27
+ }
28
+ ```
29
+
30
+ ## 快速开始
31
+
32
+ ```ts
33
+ import { createMouse } from 'yjx-sdk-mouse';
34
+
35
+ const mouse = createMouse();
36
+
37
+ document.querySelector('#connect')?.addEventListener('click', async () => {
38
+ // 首次连接时弹出浏览器设备选择框。
39
+ const selected = await mouse.requestDevices();
40
+ const device = selected[0];
41
+
42
+ if (!device) {
43
+ console.log('未选择设备');
44
+ return;
45
+ }
46
+
47
+ await mouse.init(device.id);
48
+ console.log('已连接:', mouse.currentDevice);
49
+
50
+ const dpi = await mouse.dpi.get();
51
+ console.log('DPI 配置:', dpi);
52
+ });
53
+ ```
54
+
55
+ 浏览器已经授权过设备时,可以先用 `getDevices()` 静默读取:
56
+
57
+ ```ts
58
+ const devices = await mouse.getDevices();
59
+ if (devices[0]) {
60
+ await mouse.init(devices[0].id);
61
+ }
62
+ ```
63
+
64
+ 使用完毕后关闭设备:
65
+
66
+ ```ts
67
+ await mouse.closeDevice();
68
+ ```
69
+
70
+ ## 创建设备实例
71
+
72
+ ### 使用默认设备参数
73
+
74
+ `createMouse()` 和 `Mouse.createDefault()` 都会使用 SDK 内置的有线、2.4G 接收器设备过滤参数。
75
+
76
+ ```ts
77
+ import Mouse, { createMouse } from 'yjx-sdk-mouse';
78
+
79
+ const mouse1 = createMouse();
80
+ const mouse2 = Mouse.createDefault();
81
+ ```
82
+
83
+ ### 自定义 VID、PID 和 HID Collection
84
+
85
+ ```ts
86
+ import { createMouse } from 'yjx-sdk-mouse';
87
+
88
+ const mouse = createMouse({
89
+ configs: [
90
+ {
91
+ vendorId: 0xa8a5,
92
+ productId: 0x2255,
93
+ usagePage: 0xff01,
94
+ usage: 0x10,
95
+ },
96
+ ],
97
+ timeout: 3000,
98
+ });
99
+ ```
100
+
101
+ ## DPI
102
+
103
+ ```ts
104
+ const profile = await mouse.dpi.get();
105
+ // { currentIndex, pollRate, levels }
106
+
107
+ // 切换到第 2 档,索引从 0 开始。
108
+ await mouse.dpi.setCurrentIndex(1);
109
+
110
+ // 修改第 1 档 DPI。
111
+ await mouse.dpi.setLevel(0, 1600);
112
+
113
+ // 一次写入完整配置。
114
+ await mouse.dpi.set({
115
+ currentIndex: 0,
116
+ pollRate: 1000,
117
+ levels: [800, 1200, 1600, 2400, 3200, 5000],
118
+ });
119
+ ```
120
+
121
+ 如果界面中维护了尚未写入的本地配置,请使用 `setCurrentIndexFrom()` 或 `setLevelFrom()`,避免先执行 `get()` 而覆盖本地修改:
122
+
123
+ ```ts
124
+ const draft = await mouse.dpi.get();
125
+ draft.levels[0] = 1200;
126
+
127
+ await mouse.dpi.setLevelFrom(draft, 1, 1600);
128
+ ```
129
+
130
+ ## 性能设置
131
+
132
+ ```ts
133
+ const settings = await mouse.performance.get();
134
+ console.log(settings);
135
+
136
+ await mouse.performance.set({
137
+ pollRate: 1000,
138
+ sleepMinutes: 10,
139
+ neverSleep: false,
140
+ scrollReverse: false,
141
+ wakeOnMove: true,
142
+ });
143
+
144
+ await mouse.performance.setPollRate(500);
145
+ ```
146
+
147
+ 支持的回报率为 `125 | 250 | 500 | 1000` Hz。
148
+
149
+ ## 按键映射
150
+
151
+ ```ts
152
+ import { KEY_PRESETS } from 'yjx-sdk-mouse';
153
+
154
+ const mapping = await mouse.button.get();
155
+ console.log(mapping);
156
+
157
+ const mutePreset = KEY_PRESETS.find((item) => item.id === 'mute');
158
+ if (mutePreset) {
159
+ await mouse.button.setSlot('back', mutePreset);
160
+ }
161
+
162
+ await mouse.button.resetSlot('back');
163
+ await mouse.button.resetAll();
164
+ ```
165
+
166
+ 可通过以下导出项构建按键配置界面:
167
+
168
+ - `KEY_PRESETS`:全部预设。
169
+ - `KEYBOARD_PRESETS`:键盘按键预设。
170
+ - `COMMAND_PRESETS`:系统和组合命令预设。
171
+ - `COMMAND_PRESET_GROUPS`:已分组的命令预设。
172
+ - `BUTTON_SLOTS`、`BUTTON_SLOT_LABEL`:鼠标按键槽位和显示名称。
173
+
174
+ ## 宏
175
+
176
+ `macroIndex` 范围是 `0..31`。键盘的 `keyCode` 使用 USB HID Keyboard Usage,例如字母 A 为 `0x04`。
177
+
178
+ ```ts
179
+ import type { MouseMacro } from 'yjx-sdk-mouse';
180
+
181
+ const macros: MouseMacro[] = [
182
+ {
183
+ actions: [
184
+ { type: 'keyboard', keyCode: 0x04, action: 'keydown', delay: 20 },
185
+ { type: 'keyboard', keyCode: 0x04, action: 'keyup', delay: 20 },
186
+ { type: 'mouse', button: 'left', action: 'mousedown', delay: 20 },
187
+ { type: 'mouse', button: 'left', action: 'mouseup', delay: 20 },
188
+ ],
189
+ },
190
+ ];
191
+
192
+ // 写入 profile 0,并把第 0 个宏分配给后退键。
193
+ await mouse.macro.setAndAssign(
194
+ 'back',
195
+ macros,
196
+ {
197
+ macroIndex: 0,
198
+ repeat: 1,
199
+ triggerMode: 0,
200
+ },
201
+ 0,
202
+ );
203
+ ```
204
+
205
+ 也可以分开写入和分配:
206
+
207
+ ```ts
208
+ await mouse.macro.set(macros, 0);
209
+ await mouse.macro.assignButton('back', { macroIndex: 0 });
210
+ ```
211
+
212
+ ## 设备信息和事件
213
+
214
+ ```ts
215
+ const info = await mouse.getDeviceInfo();
216
+ console.log(info.boardId, info.firmwareVersion);
217
+
218
+ mouse.on('statusChange', (event) => {
219
+ console.log('DPI 档位:', event.dpiIndex);
220
+ console.log('回报率:', event.pollRate);
221
+ });
222
+
223
+ mouse.on('usbChange', (event) => {
224
+ console.log('USB 状态:', event.type);
225
+ });
226
+ ```
227
+
228
+ 取消监听:
229
+
230
+ ```ts
231
+ const handleStatus = (event: { dpiIndex: number }) => {
232
+ console.log(event.dpiIndex);
233
+ };
234
+
235
+ mouse.on('statusChange', handleStatus);
236
+ mouse.off('statusChange', handleStatus);
237
+ ```
238
+
239
+ ## 恢复出厂设置
240
+
241
+ 此操作会重置性能、DPI、按键和宏配置:
242
+
243
+ ```ts
244
+ const result = await mouse.other.factoryReset();
245
+ console.log(result);
246
+ ```
247
+
248
+ ## 原始协议调用
249
+
250
+ 只有在需要扩展设备协议时才建议使用:
251
+
252
+ ```ts
253
+ const response = await mouse.sendRaw(new Uint8Array(64));
254
+ await mouse.sendRawNoResponse(new Uint8Array(64));
255
+ ```
256
+
257
+ ## 主要 API
258
+
259
+ | API | 说明 |
260
+ | --- | --- |
261
+ | `getDevices()` | 获取浏览器已经授权的兼容设备 |
262
+ | `requestDevices()` | 打开浏览器设备选择器 |
263
+ | `init(id)` | 打开设备并执行连接握手 |
264
+ | `closeDevice()` | 关闭当前设备 |
265
+ | `getDeviceInfo()` | 获取板卡和固件信息 |
266
+ | `getFirmwareVersion(refresh?)` | 获取固件版本,可选择强制刷新 |
267
+ | `dpi` | DPI 档位读取和写入 |
268
+ | `performance` | 回报率、休眠、滚轮和唤醒设置 |
269
+ | `button` | 按键映射读取和写入 |
270
+ | `macro` | 宏数据写入和按键分配 |
271
+ | `other.factoryReset()` | 恢复出厂设置 |
272
+
273
+ ## 错误处理
274
+
275
+ 所有设备操作都返回 Promise,建议统一使用 `try/catch`:
276
+
277
+ ```ts
278
+ try {
279
+ const devices = await mouse.requestDevices();
280
+ if (devices[0]) {
281
+ await mouse.init(devices[0].id);
282
+ }
283
+ } catch (error) {
284
+ console.error('鼠标连接失败:', error);
285
+ }
286
+ ```
287
+
@@ -0,0 +1,79 @@
1
+ /** Web HID Report ID(原厂驱动 sendReport 第一个参数) */
2
+ export declare const REPORT_ID = 240;
3
+ /** @deprecated 使用 REPORT_ID */
4
+ export declare const PACKET_HEADER = 240;
5
+ /** 逻辑包长度:byte0=ReportID + 63 字节 payload */
6
+ export declare const PACKET_SIZE = 64;
7
+ /** sendReport 实际发送的 payload 长度 */
8
+ export declare const PAYLOAD_SIZE = 63;
9
+ /** 2.4G 接收器 VID(AJ159 V2 SE) */
10
+ export declare const RECEIVER_VENDOR_ID = 43173;
11
+ /** 有线连接 VID(AJ159 V2 SE) */
12
+ export declare const WIRED_VENDOR_ID = 43172;
13
+ /** 默认 2.4G 接收器 VID */
14
+ export declare const DEFAULT_VENDOR_ID = 43173;
15
+ export declare const DEFAULT_PRODUCT_ID = 8789;
16
+ export declare const DEFAULT_USAGE_PAGE = 65281;
17
+ export declare const DEFAULT_USAGE = 16;
18
+ /** 协议命令字(抓包 + 文档推断) */
19
+ export declare const Command: {
20
+ /** 协议握手 */
21
+ readonly Handshake: 82;
22
+ /** 保活 / 确认 */
23
+ readonly Ping: 4;
24
+ /** 读内存块 */
25
+ readonly ReadBlock: 48;
26
+ /** 读内存(短) */
27
+ readonly ReadShort: 8;
28
+ /** 读内存(重试 / 等待响应) */
29
+ readonly ReadRetry: 14;
30
+ /** 写内存块 */
31
+ readonly WriteBlock: 15;
32
+ /** 写按键映射 */
33
+ readonly WriteKeys: 9;
34
+ /** 写宏数据块 */
35
+ readonly WriteMacro: 13;
36
+ /** 提交 / 保存宏数据 */
37
+ readonly CommitMacro: 16;
38
+ /** 设备主动上报:DPI / 回报率切换(原厂 onListeners statu) */
39
+ readonly StatusReport: 250;
40
+ };
41
+ export type CommandCode = (typeof Command)[keyof typeof Command];
42
+ /** 读区域基址 */
43
+ export declare const READ_ADDRESS = 2981;
44
+ /** 写区域基址 */
45
+ export declare const WRITE_ADDRESS = 2734;
46
+ /** 按键映射写地址 */
47
+ export declare const KEY_WRITE_ADDRESS = 8869;
48
+ /** 按键映射写偏移 */
49
+ export declare const KEY_WRITE_OFFSET = 48;
50
+ /** 内存偏移(原厂驱动抓包) */
51
+ export declare const MemoryOffset: {
52
+ readonly Config: 44;
53
+ readonly DeviceInfo: 46;
54
+ readonly Profile: 47;
55
+ };
56
+ /** 握手默认载荷 */
57
+ export declare const HANDSHAKE_PAYLOAD: readonly [1, 0, 26, 0];
58
+ /** 内存访问默认标志 */
59
+ export declare const MEMORY_FLAGS: readonly [1, 1, 1];
60
+ /** 回报率档位(设备存储值为 index + 1,见原厂 getMouseConfigInfo) */
61
+ export declare const PollRateIndex: {
62
+ readonly Hz125: 0;
63
+ readonly Hz250: 1;
64
+ readonly Hz500: 2;
65
+ readonly Hz1000: 3;
66
+ };
67
+ export declare const PollRateFromIndex: Record<number, 125 | 250 | 500 | 1000>;
68
+ export declare const PollRateToIndex: Record<125 | 250 | 500 | 1000, number>;
69
+ /** @deprecated 使用 PollRateToIndex + 1 写入设备 */
70
+ export declare const PollRateCode: {
71
+ readonly Hz125: 1;
72
+ readonly Hz250: 2;
73
+ readonly Hz500: 3;
74
+ readonly Hz1000: 4;
75
+ };
76
+ export declare const PollRateFromCode: Record<number, 125 | 250 | 500 | 1000>;
77
+ export declare const PollRateToCode: Record<125 | 250 | 500 | 1000, number>;
78
+ export declare function isWiredVendorId(vendorId: number): boolean;
79
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,eAAO,MAAM,SAAS,MAAO,CAAC;AAE9B,+BAA+B;AAC/B,eAAO,MAAM,aAAa,MAAY,CAAC;AAEvC,2CAA2C;AAC3C,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,kCAAkC;AAClC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,gCAAgC;AAChC,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,eAAe,QAAS,CAAC;AACtC,sBAAsB;AACtB,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AACpD,eAAO,MAAM,kBAAkB,OAAS,CAAC;AACzC,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,aAAa,KAAO,CAAC;AAElC,uBAAuB;AACvB,eAAO,MAAM,OAAO;IAClB,WAAW;;IAEX,cAAc;;IAEd,WAAW;;IAEX,aAAa;;IAEb,qBAAqB;;IAErB,WAAW;;IAEX,YAAY;;IAEZ,YAAY;;IAEZ,iBAAiB;;IAEjB,+CAA+C;;CAEvC,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;AAEjE,YAAY;AACZ,eAAO,MAAM,YAAY,OAAS,CAAC;AACnC,YAAY;AACZ,eAAO,MAAM,aAAa,OAAS,CAAC;AACpC,cAAc;AACd,eAAO,MAAM,iBAAiB,OAAS,CAAC;AACxC,cAAc;AACd,eAAO,MAAM,gBAAgB,KAAO,CAAC;AAErC,mBAAmB;AACnB,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAEX,aAAa;AACb,eAAO,MAAM,iBAAiB,wBAAoC,CAAC;AAEnE,eAAe;AACf,eAAO,MAAM,YAAY,oBAA8B,CAAC;AAExD,qDAAqD;AACrD,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAKpE,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,EAAE,MAAM,CAKlE,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAKnE,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,EAAE,MAAM,CAKjE,CAAC;AAEF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,100 @@
1
+ /** Web HID Report ID(原厂驱动 sendReport 第一个参数) */
2
+ export const REPORT_ID = 0xf0;
3
+ /** @deprecated 使用 REPORT_ID */
4
+ export const PACKET_HEADER = REPORT_ID;
5
+ /** 逻辑包长度:byte0=ReportID + 63 字节 payload */
6
+ export const PACKET_SIZE = 64;
7
+ /** sendReport 实际发送的 payload 长度 */
8
+ export const PAYLOAD_SIZE = 63;
9
+ /** 2.4G 接收器 VID(AJ159 V2 SE) */
10
+ export const RECEIVER_VENDOR_ID = 0xa8a5;
11
+ /** 有线连接 VID(AJ159 V2 SE) */
12
+ export const WIRED_VENDOR_ID = 0xa8a4;
13
+ /** 默认 2.4G 接收器 VID */
14
+ export const DEFAULT_VENDOR_ID = RECEIVER_VENDOR_ID;
15
+ export const DEFAULT_PRODUCT_ID = 0x2255;
16
+ export const DEFAULT_USAGE_PAGE = 0xff01;
17
+ export const DEFAULT_USAGE = 0x10;
18
+ /** 协议命令字(抓包 + 文档推断) */
19
+ export const Command = {
20
+ /** 协议握手 */
21
+ Handshake: 0x52,
22
+ /** 保活 / 确认 */
23
+ Ping: 0x04,
24
+ /** 读内存块 */
25
+ ReadBlock: 0x30,
26
+ /** 读内存(短) */
27
+ ReadShort: 0x08,
28
+ /** 读内存(重试 / 等待响应) */
29
+ ReadRetry: 0x0e,
30
+ /** 写内存块 */
31
+ WriteBlock: 0x0f,
32
+ /** 写按键映射 */
33
+ WriteKeys: 0x09,
34
+ /** 写宏数据块 */
35
+ WriteMacro: 0x0d,
36
+ /** 提交 / 保存宏数据 */
37
+ CommitMacro: 0x10,
38
+ /** 设备主动上报:DPI / 回报率切换(原厂 onListeners statu) */
39
+ StatusReport: 0xfa,
40
+ };
41
+ /** 读区域基址 */
42
+ export const READ_ADDRESS = 0x0ba5;
43
+ /** 写区域基址 */
44
+ export const WRITE_ADDRESS = 0x0aae;
45
+ /** 按键映射写地址 */
46
+ export const KEY_WRITE_ADDRESS = 0x22a5;
47
+ /** 按键映射写偏移 */
48
+ export const KEY_WRITE_OFFSET = 0x30;
49
+ /** 内存偏移(原厂驱动抓包) */
50
+ export const MemoryOffset = {
51
+ Config: 0x2c,
52
+ DeviceInfo: 0x2e,
53
+ Profile: 0x2f,
54
+ };
55
+ /** 握手默认载荷 */
56
+ export const HANDSHAKE_PAYLOAD = [0x01, 0x00, 0x1a, 0x00];
57
+ /** 内存访问默认标志 */
58
+ export const MEMORY_FLAGS = [0x01, 0x01, 0x01];
59
+ /** 回报率档位(设备存储值为 index + 1,见原厂 getMouseConfigInfo) */
60
+ export const PollRateIndex = {
61
+ Hz125: 0,
62
+ Hz250: 1,
63
+ Hz500: 2,
64
+ Hz1000: 3,
65
+ };
66
+ export const PollRateFromIndex = {
67
+ 0: 125,
68
+ 1: 250,
69
+ 2: 500,
70
+ 3: 1000,
71
+ };
72
+ export const PollRateToIndex = {
73
+ 125: PollRateIndex.Hz125,
74
+ 250: PollRateIndex.Hz250,
75
+ 500: PollRateIndex.Hz500,
76
+ 1000: PollRateIndex.Hz1000,
77
+ };
78
+ /** @deprecated 使用 PollRateToIndex + 1 写入设备 */
79
+ export const PollRateCode = {
80
+ Hz125: 0x01,
81
+ Hz250: 0x02,
82
+ Hz500: 0x03,
83
+ Hz1000: 0x04,
84
+ };
85
+ export const PollRateFromCode = {
86
+ 1: 125,
87
+ 2: 250,
88
+ 3: 500,
89
+ 4: 1000,
90
+ };
91
+ export const PollRateToCode = {
92
+ 125: PollRateCode.Hz125,
93
+ 250: PollRateCode.Hz250,
94
+ 500: PollRateCode.Hz500,
95
+ 1000: PollRateCode.Hz1000,
96
+ };
97
+ export function isWiredVendorId(vendorId) {
98
+ return vendorId === WIRED_VENDOR_ID;
99
+ }
100
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { DeviceLayer } from '../device/index.js';
2
+ import type { ButtonMapping, KeyPreset, MouseButtonSlot, MouseKeyMapping } from '../types.js';
3
+ export declare class ButtonController {
4
+ private readonly device;
5
+ constructor(device: DeviceLayer);
6
+ get(): Promise<ButtonMapping>;
7
+ set(mapping: ButtonMapping): Promise<ButtonMapping>;
8
+ setSlot(slot: MouseButtonSlot, preset: KeyPreset): Promise<ButtonMapping>;
9
+ resetAll(): Promise<ButtonMapping>;
10
+ resetSlot(slot: MouseButtonSlot): Promise<ButtonMapping>;
11
+ formatLabel(key: MouseKeyMapping, getMacroName?: (macroIndex: number) => string | undefined): string;
12
+ getPresets(): KeyPreset[];
13
+ getPresetId(key: MouseKeyMapping): string;
14
+ }
15
+ //# sourceMappingURL=button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../src/controllers/button.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9F,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,WAAW;IAE1C,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAM7B,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKnD,OAAO,CAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;IAMzE,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAKlC,SAAS,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;IAM9D,WAAW,CAAC,GAAG,EAAE,eAAe,EAAE,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,MAAM;IAIpG,UAAU,IAAI,SAAS,EAAE;IAIzB,WAAW,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM;CAG1C"}
@@ -0,0 +1,40 @@
1
+ import { createDefaultButtonMapping, findPresetId, formatKeyLabel, keysToMapping, mappingToKeys, parseKeysResponse, presetToKey, readKeysPacket, writeKeysPacket, KEY_PRESETS, } from '../protocol/key.js';
2
+ export class ButtonController {
3
+ device;
4
+ constructor(device) {
5
+ this.device = device;
6
+ }
7
+ async get() {
8
+ const response = await this.device.sendAndWait(readKeysPacket());
9
+ const keys = parseKeysResponse(response);
10
+ return keysToMapping(keys);
11
+ }
12
+ async set(mapping) {
13
+ await this.device.sendWriteAndWait(writeKeysPacket(mappingToKeys(mapping)));
14
+ return this.get();
15
+ }
16
+ async setSlot(slot, preset) {
17
+ const current = await this.get();
18
+ const key = presetToKey(preset, current[slot].index);
19
+ return this.set({ ...current, [slot]: key });
20
+ }
21
+ async resetAll() {
22
+ const defaults = createDefaultButtonMapping();
23
+ return this.set(defaults);
24
+ }
25
+ async resetSlot(slot) {
26
+ const defaults = createDefaultButtonMapping();
27
+ const current = await this.get();
28
+ return this.set({ ...current, [slot]: defaults[slot] });
29
+ }
30
+ formatLabel(key, getMacroName) {
31
+ return formatKeyLabel(key, getMacroName);
32
+ }
33
+ getPresets() {
34
+ return KEY_PRESETS;
35
+ }
36
+ getPresetId(key) {
37
+ return findPresetId(key);
38
+ }
39
+ }
40
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../src/controllers/button.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,YAAY,EACZ,cAAc,EACd,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,eAAe,EACf,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAI5B,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAEpD,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAsB;QAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAqB,EAAE,MAAiB;QACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,QAAQ,GAAG,0BAA0B,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAqB;QACnC,MAAM,QAAQ,GAAG,0BAA0B,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,WAAW,CAAC,GAAoB,EAAE,YAAyD;QACzF,OAAO,cAAc,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC3C,CAAC;IAED,UAAU;QACR,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,WAAW,CAAC,GAAoB;QAC9B,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import { type MouseConfigData } from '../protocol/packet.js';
2
+ import type { DeviceLayer } from '../device/index.js';
3
+ import type { DeviceInfoPayload, DpiProfile } from '../types.js';
4
+ export declare class DeviceController {
5
+ private readonly device;
6
+ private cachedFirmwareVersion;
7
+ constructor(device: DeviceLayer);
8
+ handshake(): Promise<Uint8Array>;
9
+ ping(): Promise<Uint8Array>;
10
+ /** 读取固件版本(2.4G 下为接收器固件版本,对齐原厂 getVersionInfo) */
11
+ getFirmwareVersion(refresh?: boolean): Promise<string>;
12
+ getDeviceInfo(): Promise<DeviceInfoPayload>;
13
+ getMouseConfig(): Promise<MouseConfigData>;
14
+ setMouseConfig(config: MouseConfigData): Promise<MouseConfigData>;
15
+ getDpiProfile(): Promise<DpiProfile>;
16
+ setDpiProfile(profile: DpiProfile): Promise<DpiProfile>;
17
+ connectSequence(): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/controllers/device.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAYjE,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,qBAAqB,CAAuB;gBAEvB,MAAM,EAAE,WAAW;IAE1C,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAIhC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IAIjC,iDAAiD;IAC3C,kBAAkB,CAAC,OAAO,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBpD,aAAa,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAqB3C,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC;IAK1C,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAajE,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC;IAUpC,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAkCvD,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAQvC"}
@@ -0,0 +1,111 @@
1
+ import { PollRateFromIndex } from '../constants.js';
2
+ import { createHandshakePacket, createPingPacket, dpiProfileToMouseConfig, parseMouseConfig, buildMouseConfigWritePayload, readDeviceInfoPacket, readProfilePacket, writeProfilePacket, parsePingVersionReport, toHex, } from '../protocol/packet.js';
3
+ const DPI_SLOT_COUNT = 6;
4
+ function normalizeLevels(levels, base) {
5
+ const normalized = [...levels];
6
+ while (normalized.length < DPI_SLOT_COUNT) {
7
+ normalized.push(base?.[normalized.length] ?? 800);
8
+ }
9
+ return normalized.slice(0, DPI_SLOT_COUNT).map((value) => Math.round(value));
10
+ }
11
+ export class DeviceController {
12
+ device;
13
+ cachedFirmwareVersion = null;
14
+ constructor(device) {
15
+ this.device = device;
16
+ }
17
+ async handshake() {
18
+ return this.device.sendAndWait(createHandshakePacket());
19
+ }
20
+ async ping() {
21
+ return this.device.sendAndWait(createPingPacket());
22
+ }
23
+ /** 读取固件版本(2.4G 下为接收器固件版本,对齐原厂 getVersionInfo) */
24
+ async getFirmwareVersion(refresh = false) {
25
+ if (!refresh && this.cachedFirmwareVersion) {
26
+ return this.cachedFirmwareVersion;
27
+ }
28
+ const versionPromise = this.device.waitForMatchingReport((data) => parsePingVersionReport(data));
29
+ await this.device.sendNoResponse(createPingPacket());
30
+ const version = await versionPromise;
31
+ if (!version) {
32
+ throw new Error('无法读取固件版本');
33
+ }
34
+ this.cachedFirmwareVersion = version;
35
+ return version;
36
+ }
37
+ async getDeviceInfo() {
38
+ const firmwareVersion = await this.getFirmwareVersion();
39
+ let boardId;
40
+ let raw = new Uint8Array(0);
41
+ try {
42
+ const response = await this.device.sendAndWait(readDeviceInfoPacket());
43
+ raw = new Uint8Array(response);
44
+ boardId = response[7] | (response[8] << 8);
45
+ }
46
+ catch {
47
+ // 板卡 ID 为可选信息,版本读取失败不应影响主流程
48
+ }
49
+ return {
50
+ raw,
51
+ boardId,
52
+ firmwareVersion,
53
+ receiverFirmwareVersion: firmwareVersion,
54
+ };
55
+ }
56
+ async getMouseConfig() {
57
+ const response = await this.device.sendAndWait(readProfilePacket());
58
+ return parseMouseConfig(response);
59
+ }
60
+ async setMouseConfig(config) {
61
+ const writePacket = writeProfilePacket(buildMouseConfigWritePayload(config));
62
+ const writeResponse = await this.device.sendWriteAndWait(writePacket);
63
+ if (writeResponse[0] !== 0x0f) {
64
+ throw new Error(`配置写入回包异常: 期望 cmd=0x0f, 收到 0x${(writeResponse[0] ?? 0).toString(16)} data=${toHex(writeResponse, 32)}`);
65
+ }
66
+ return parseMouseConfig(writeResponse);
67
+ }
68
+ async getDpiProfile() {
69
+ const config = await this.getMouseConfig();
70
+ return {
71
+ currentIndex: config.currentIndex,
72
+ pollRate: PollRateFromIndex[config.pollRateIndex] ?? 1000,
73
+ levels: normalizeLevels(config.dpiLevels.slice(0, config.dpiCount)),
74
+ };
75
+ }
76
+ async setDpiProfile(profile) {
77
+ const base = await this.getMouseConfig();
78
+ const levels = normalizeLevels(profile.levels, base.dpiLevels);
79
+ const config = dpiProfileToMouseConfig({
80
+ currentIndex: profile.currentIndex,
81
+ pollRate: profile.pollRate,
82
+ levels,
83
+ }, base);
84
+ const echoed = await this.setMouseConfig(config);
85
+ const echoedLevels = normalizeLevels(echoed.dpiLevels.slice(0, echoed.dpiCount));
86
+ const mismatches = levels
87
+ .map((value, index) => ({ index, expected: value, actual: echoedLevels[index] ?? -1 }))
88
+ .filter((item) => item.expected !== item.actual);
89
+ if (mismatches.length > 0) {
90
+ const detail = mismatches
91
+ .map((item) => `档位${item.index + 1}: 期望 ${item.expected}, 回显 ${item.actual}`)
92
+ .join('; ');
93
+ throw new Error(`DPI 写入回显校验失败: ${detail}`);
94
+ }
95
+ return {
96
+ currentIndex: echoed.currentIndex,
97
+ pollRate: PollRateFromIndex[echoed.pollRateIndex] ?? profile.pollRate,
98
+ levels: echoedLevels,
99
+ };
100
+ }
101
+ async connectSequence() {
102
+ await this.handshake();
103
+ try {
104
+ await this.getFirmwareVersion(true);
105
+ }
106
+ catch {
107
+ // 连接阶段版本读取失败不阻断后续配置读取
108
+ }
109
+ }
110
+ }
111
+ //# sourceMappingURL=device.js.map