@openmouse/protocol 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 (547) hide show
  1. package/CONTRIBUTING.md +167 -0
  2. package/README.md +89 -0
  3. package/dist/asus/index.d.ts +52 -0
  4. package/dist/asus/index.d.ts.map +1 -0
  5. package/dist/asus/index.js +248 -0
  6. package/dist/asus/index.js.map +1 -0
  7. package/dist/atk/index.d.ts +200 -0
  8. package/dist/atk/index.d.ts.map +1 -0
  9. package/dist/atk/index.js +480 -0
  10. package/dist/atk/index.js.map +1 -0
  11. package/dist/bitmouse/index.d.ts +284 -0
  12. package/dist/bitmouse/index.d.ts.map +1 -0
  13. package/dist/bitmouse/index.js +396 -0
  14. package/dist/bitmouse/index.js.map +1 -0
  15. package/dist/compx/codec.d.ts +31 -0
  16. package/dist/compx/codec.d.ts.map +1 -0
  17. package/dist/compx/codec.js +50 -0
  18. package/dist/compx/codec.js.map +1 -0
  19. package/dist/corsair/index.d.ts +147 -0
  20. package/dist/corsair/index.d.ts.map +1 -0
  21. package/dist/corsair/index.js +223 -0
  22. package/dist/corsair/index.js.map +1 -0
  23. package/dist/dareu/index.d.ts +136 -0
  24. package/dist/dareu/index.d.ts.map +1 -0
  25. package/dist/dareu/index.js +344 -0
  26. package/dist/dareu/index.js.map +1 -0
  27. package/dist/drivers/asus/hid.d.ts +29 -0
  28. package/dist/drivers/asus/hid.d.ts.map +1 -0
  29. package/dist/drivers/asus/hid.js +419 -0
  30. package/dist/drivers/asus/hid.js.map +1 -0
  31. package/dist/drivers/atk/bitmouse-hid.d.ts +80 -0
  32. package/dist/drivers/atk/bitmouse-hid.d.ts.map +1 -0
  33. package/dist/drivers/atk/bitmouse-hid.js +501 -0
  34. package/dist/drivers/atk/bitmouse-hid.js.map +1 -0
  35. package/dist/drivers/atk/hid.d.ts +163 -0
  36. package/dist/drivers/atk/hid.d.ts.map +1 -0
  37. package/dist/drivers/atk/hid.js +1368 -0
  38. package/dist/drivers/atk/hid.js.map +1 -0
  39. package/dist/drivers/atk/products.d.ts +13 -0
  40. package/dist/drivers/atk/products.d.ts.map +1 -0
  41. package/dist/drivers/atk/products.js +13 -0
  42. package/dist/drivers/atk/products.js.map +1 -0
  43. package/dist/drivers/attackshark/dpi.d.ts +41 -0
  44. package/dist/drivers/attackshark/dpi.d.ts.map +1 -0
  45. package/dist/drivers/attackshark/dpi.js +215 -0
  46. package/dist/drivers/attackshark/dpi.js.map +1 -0
  47. package/dist/drivers/attackshark/hid.d.ts +124 -0
  48. package/dist/drivers/attackshark/hid.d.ts.map +1 -0
  49. package/dist/drivers/attackshark/hid.js +728 -0
  50. package/dist/drivers/attackshark/hid.js.map +1 -0
  51. package/dist/drivers/corsair/hid.d.ts +70 -0
  52. package/dist/drivers/corsair/hid.d.ts.map +1 -0
  53. package/dist/drivers/corsair/hid.js +414 -0
  54. package/dist/drivers/corsair/hid.js.map +1 -0
  55. package/dist/drivers/dareu/hid.d.ts +59 -0
  56. package/dist/drivers/dareu/hid.d.ts.map +1 -0
  57. package/dist/drivers/dareu/hid.js +478 -0
  58. package/dist/drivers/dareu/hid.js.map +1 -0
  59. package/dist/drivers/endgame/egg-op1-hid.d.ts +107 -0
  60. package/dist/drivers/endgame/egg-op1-hid.d.ts.map +1 -0
  61. package/dist/drivers/endgame/egg-op1-hid.js +605 -0
  62. package/dist/drivers/endgame/egg-op1-hid.js.map +1 -0
  63. package/dist/drivers/endgame/egg-we-control.d.ts +42 -0
  64. package/dist/drivers/endgame/egg-we-control.d.ts.map +1 -0
  65. package/dist/drivers/endgame/egg-we-control.js +87 -0
  66. package/dist/drivers/endgame/egg-we-control.js.map +1 -0
  67. package/dist/drivers/endgame/egg-we-hid.d.ts +58 -0
  68. package/dist/drivers/endgame/egg-we-hid.d.ts.map +1 -0
  69. package/dist/drivers/endgame/egg-we-hid.js +514 -0
  70. package/dist/drivers/endgame/egg-we-hid.js.map +1 -0
  71. package/dist/drivers/fantech/hid.d.ts +106 -0
  72. package/dist/drivers/fantech/hid.d.ts.map +1 -0
  73. package/dist/drivers/fantech/hid.js +295 -0
  74. package/dist/drivers/fantech/hid.js.map +1 -0
  75. package/dist/drivers/finalmouse/hid.d.ts +31 -0
  76. package/dist/drivers/finalmouse/hid.d.ts.map +1 -0
  77. package/dist/drivers/finalmouse/hid.js +181 -0
  78. package/dist/drivers/finalmouse/hid.js.map +1 -0
  79. package/dist/drivers/gearhub/hid.d.ts +191 -0
  80. package/dist/drivers/gearhub/hid.d.ts.map +1 -0
  81. package/dist/drivers/gearhub/hid.js +542 -0
  82. package/dist/drivers/gearhub/hid.js.map +1 -0
  83. package/dist/drivers/glorious/classic-hid.d.ts +62 -0
  84. package/dist/drivers/glorious/classic-hid.d.ts.map +1 -0
  85. package/dist/drivers/glorious/classic-hid.js +326 -0
  86. package/dist/drivers/glorious/classic-hid.js.map +1 -0
  87. package/dist/drivers/glorious/hid.d.ts +39 -0
  88. package/dist/drivers/glorious/hid.d.ts.map +1 -0
  89. package/dist/drivers/glorious/hid.js +203 -0
  90. package/dist/drivers/glorious/hid.js.map +1 -0
  91. package/dist/drivers/gwolves/hid.d.ts +27 -0
  92. package/dist/drivers/gwolves/hid.d.ts.map +1 -0
  93. package/dist/drivers/gwolves/hid.js +225 -0
  94. package/dist/drivers/gwolves/hid.js.map +1 -0
  95. package/dist/drivers/gwolves/products.d.ts +17 -0
  96. package/dist/drivers/gwolves/products.d.ts.map +1 -0
  97. package/dist/drivers/gwolves/products.js +58 -0
  98. package/dist/drivers/gwolves/products.js.map +1 -0
  99. package/dist/drivers/hyperx/hid.d.ts +59 -0
  100. package/dist/drivers/hyperx/hid.d.ts.map +1 -0
  101. package/dist/drivers/hyperx/hid.js +229 -0
  102. package/dist/drivers/hyperx/hid.js.map +1 -0
  103. package/dist/drivers/incott/hid.d.ts +513 -0
  104. package/dist/drivers/incott/hid.d.ts.map +1 -0
  105. package/dist/drivers/incott/hid.js +1173 -0
  106. package/dist/drivers/incott/hid.js.map +1 -0
  107. package/dist/drivers/index.d.ts +5 -0
  108. package/dist/drivers/index.d.ts.map +1 -0
  109. package/dist/drivers/index.js +5 -0
  110. package/dist/drivers/index.js.map +1 -0
  111. package/dist/drivers/keychron/m6-hid.d.ts +81 -0
  112. package/dist/drivers/keychron/m6-hid.d.ts.map +1 -0
  113. package/dist/drivers/keychron/m6-hid.js +486 -0
  114. package/dist/drivers/keychron/m6-hid.js.map +1 -0
  115. package/dist/drivers/keychron/nape-hid.d.ts +60 -0
  116. package/dist/drivers/keychron/nape-hid.d.ts.map +1 -0
  117. package/dist/drivers/keychron/nape-hid.js +432 -0
  118. package/dist/drivers/keychron/nape-hid.js.map +1 -0
  119. package/dist/drivers/ksnake/hid.d.ts +74 -0
  120. package/dist/drivers/ksnake/hid.d.ts.map +1 -0
  121. package/dist/drivers/ksnake/hid.js +368 -0
  122. package/dist/drivers/ksnake/hid.js.map +1 -0
  123. package/dist/drivers/lamzu/hid.d.ts +58 -0
  124. package/dist/drivers/lamzu/hid.d.ts.map +1 -0
  125. package/dist/drivers/lamzu/hid.js +414 -0
  126. package/dist/drivers/lamzu/hid.js.map +1 -0
  127. package/dist/drivers/lamzu-atlantis/hid.d.ts +148 -0
  128. package/dist/drivers/lamzu-atlantis/hid.d.ts.map +1 -0
  129. package/dist/drivers/lamzu-atlantis/hid.js +587 -0
  130. package/dist/drivers/lamzu-atlantis/hid.js.map +1 -0
  131. package/dist/drivers/logitech/bolt.d.ts +41 -0
  132. package/dist/drivers/logitech/bolt.d.ts.map +1 -0
  133. package/dist/drivers/logitech/bolt.js +105 -0
  134. package/dist/drivers/logitech/bolt.js.map +1 -0
  135. package/dist/drivers/logitech/hidpp.d.ts +525 -0
  136. package/dist/drivers/logitech/hidpp.d.ts.map +1 -0
  137. package/dist/drivers/logitech/hidpp.js +2774 -0
  138. package/dist/drivers/logitech/hidpp.js.map +1 -0
  139. package/dist/drivers/logitech/mode-status.d.ts +53 -0
  140. package/dist/drivers/logitech/mode-status.d.ts.map +1 -0
  141. package/dist/drivers/logitech/mode-status.js +52 -0
  142. package/dist/drivers/logitech/mode-status.js.map +1 -0
  143. package/dist/drivers/logitech/onboard-profiles.d.ts +298 -0
  144. package/dist/drivers/logitech/onboard-profiles.d.ts.map +1 -0
  145. package/dist/drivers/logitech/onboard-profiles.js +965 -0
  146. package/dist/drivers/logitech/onboard-profiles.js.map +1 -0
  147. package/dist/drivers/logitech/rgb-effects.d.ts +20 -0
  148. package/dist/drivers/logitech/rgb-effects.d.ts.map +1 -0
  149. package/dist/drivers/logitech/rgb-effects.js +93 -0
  150. package/dist/drivers/logitech/rgb-effects.js.map +1 -0
  151. package/dist/drivers/mchose/a5-gen1-hid.d.ts +36 -0
  152. package/dist/drivers/mchose/a5-gen1-hid.d.ts.map +1 -0
  153. package/dist/drivers/mchose/a5-gen1-hid.js +219 -0
  154. package/dist/drivers/mchose/a5-gen1-hid.js.map +1 -0
  155. package/dist/drivers/mchose/dock-hid.d.ts +26 -0
  156. package/dist/drivers/mchose/dock-hid.d.ts.map +1 -0
  157. package/dist/drivers/mchose/dock-hid.js +176 -0
  158. package/dist/drivers/mchose/dock-hid.js.map +1 -0
  159. package/dist/drivers/mchose/hid.d.ts +118 -0
  160. package/dist/drivers/mchose/hid.d.ts.map +1 -0
  161. package/dist/drivers/mchose/hid.js +550 -0
  162. package/dist/drivers/mchose/hid.js.map +1 -0
  163. package/dist/drivers/mchose/v3-hid.d.ts +109 -0
  164. package/dist/drivers/mchose/v3-hid.d.ts.map +1 -0
  165. package/dist/drivers/mchose/v3-hid.js +572 -0
  166. package/dist/drivers/mchose/v3-hid.js.map +1 -0
  167. package/dist/drivers/microsoft/hid.d.ts +25 -0
  168. package/dist/drivers/microsoft/hid.d.ts.map +1 -0
  169. package/dist/drivers/microsoft/hid.js +247 -0
  170. package/dist/drivers/microsoft/hid.js.map +1 -0
  171. package/dist/drivers/moddo/hid.d.ts +31 -0
  172. package/dist/drivers/moddo/hid.d.ts.map +1 -0
  173. package/dist/drivers/moddo/hid.js +154 -0
  174. package/dist/drivers/moddo/hid.js.map +1 -0
  175. package/dist/drivers/mouse-types.d.ts +388 -0
  176. package/dist/drivers/mouse-types.d.ts.map +1 -0
  177. package/dist/drivers/mouse-types.js +2 -0
  178. package/dist/drivers/mouse-types.js.map +1 -0
  179. package/dist/drivers/ninjutso/hid.d.ts +62 -0
  180. package/dist/drivers/ninjutso/hid.d.ts.map +1 -0
  181. package/dist/drivers/ninjutso/hid.js +680 -0
  182. package/dist/drivers/ninjutso/hid.js.map +1 -0
  183. package/dist/drivers/orbital/hid.d.ts +24 -0
  184. package/dist/drivers/orbital/hid.d.ts.map +1 -0
  185. package/dist/drivers/orbital/hid.js +85 -0
  186. package/dist/drivers/orbital/hid.js.map +1 -0
  187. package/dist/drivers/orbital/host-protocol.d.ts +59 -0
  188. package/dist/drivers/orbital/host-protocol.d.ts.map +1 -0
  189. package/dist/drivers/orbital/host-protocol.js +884 -0
  190. package/dist/drivers/orbital/host-protocol.js.map +1 -0
  191. package/dist/drivers/pulsar/pulsar-hid.d.ts +63 -0
  192. package/dist/drivers/pulsar/pulsar-hid.d.ts.map +1 -0
  193. package/dist/drivers/pulsar/pulsar-hid.js +381 -0
  194. package/dist/drivers/pulsar/pulsar-hid.js.map +1 -0
  195. package/dist/drivers/pulsar/pulsar-pro-hid.d.ts +46 -0
  196. package/dist/drivers/pulsar/pulsar-pro-hid.d.ts.map +1 -0
  197. package/dist/drivers/pulsar/pulsar-pro-hid.js +343 -0
  198. package/dist/drivers/pulsar/pulsar-pro-hid.js.map +1 -0
  199. package/dist/drivers/pulsar/pulsar-xs1-hid.d.ts +40 -0
  200. package/dist/drivers/pulsar/pulsar-xs1-hid.d.ts.map +1 -0
  201. package/dist/drivers/pulsar/pulsar-xs1-hid.js +251 -0
  202. package/dist/drivers/pulsar/pulsar-xs1-hid.js.map +1 -0
  203. package/dist/drivers/rawm/hid.d.ts +29 -0
  204. package/dist/drivers/rawm/hid.d.ts.map +1 -0
  205. package/dist/drivers/rawm/hid.js +210 -0
  206. package/dist/drivers/rawm/hid.js.map +1 -0
  207. package/dist/drivers/razer/cobra-hid.d.ts +63 -0
  208. package/dist/drivers/razer/cobra-hid.d.ts.map +1 -0
  209. package/dist/drivers/razer/cobra-hid.js +282 -0
  210. package/dist/drivers/razer/cobra-hid.js.map +1 -0
  211. package/dist/drivers/razer/hid-open.d.ts +13 -0
  212. package/dist/drivers/razer/hid-open.d.ts.map +1 -0
  213. package/dist/drivers/razer/hid-open.js +29 -0
  214. package/dist/drivers/razer/hid-open.js.map +1 -0
  215. package/dist/drivers/razer/hid.d.ts +231 -0
  216. package/dist/drivers/razer/hid.d.ts.map +1 -0
  217. package/dist/drivers/razer/hid.js +772 -0
  218. package/dist/drivers/razer/hid.js.map +1 -0
  219. package/dist/drivers/razer/viper-hid.d.ts +33 -0
  220. package/dist/drivers/razer/viper-hid.d.ts.map +1 -0
  221. package/dist/drivers/razer/viper-hid.js +177 -0
  222. package/dist/drivers/razer/viper-hid.js.map +1 -0
  223. package/dist/drivers/razer/viper-mini-hid.d.ts +58 -0
  224. package/dist/drivers/razer/viper-mini-hid.d.ts.map +1 -0
  225. package/dist/drivers/razer/viper-mini-hid.js +273 -0
  226. package/dist/drivers/razer/viper-mini-hid.js.map +1 -0
  227. package/dist/drivers/razer/viper-v4-pro-hid.d.ts +26 -0
  228. package/dist/drivers/razer/viper-v4-pro-hid.d.ts.map +1 -0
  229. package/dist/drivers/razer/viper-v4-pro-hid.js +154 -0
  230. package/dist/drivers/razer/viper-v4-pro-hid.js.map +1 -0
  231. package/dist/drivers/registry.d.ts +70 -0
  232. package/dist/drivers/registry.d.ts.map +1 -0
  233. package/dist/drivers/registry.js +146 -0
  234. package/dist/drivers/registry.js.map +1 -0
  235. package/dist/drivers/steelseries/aerox3-hid.d.ts +53 -0
  236. package/dist/drivers/steelseries/aerox3-hid.d.ts.map +1 -0
  237. package/dist/drivers/steelseries/aerox3-hid.js +172 -0
  238. package/dist/drivers/steelseries/aerox3-hid.js.map +1 -0
  239. package/dist/drivers/steelseries/aerox5-hid.d.ts +54 -0
  240. package/dist/drivers/steelseries/aerox5-hid.d.ts.map +1 -0
  241. package/dist/drivers/steelseries/aerox5-hid.js +173 -0
  242. package/dist/drivers/steelseries/aerox5-hid.js.map +1 -0
  243. package/dist/drivers/steelseries/aerox5-wireless-hid.d.ts +69 -0
  244. package/dist/drivers/steelseries/aerox5-wireless-hid.d.ts.map +1 -0
  245. package/dist/drivers/steelseries/aerox5-wireless-hid.js +234 -0
  246. package/dist/drivers/steelseries/aerox5-wireless-hid.js.map +1 -0
  247. package/dist/drivers/steelseries/aerox9-wireless-hid.d.ts +70 -0
  248. package/dist/drivers/steelseries/aerox9-wireless-hid.d.ts.map +1 -0
  249. package/dist/drivers/steelseries/aerox9-wireless-hid.js +227 -0
  250. package/dist/drivers/steelseries/aerox9-wireless-hid.js.map +1 -0
  251. package/dist/drivers/steelseries/hid.d.ts +48 -0
  252. package/dist/drivers/steelseries/hid.d.ts.map +1 -0
  253. package/dist/drivers/steelseries/hid.js +157 -0
  254. package/dist/drivers/steelseries/hid.js.map +1 -0
  255. package/dist/drivers/steelseries/prime-mini-wireless-hid.d.ts +62 -0
  256. package/dist/drivers/steelseries/prime-mini-wireless-hid.d.ts.map +1 -0
  257. package/dist/drivers/steelseries/prime-mini-wireless-hid.js +215 -0
  258. package/dist/drivers/steelseries/prime-mini-wireless-hid.js.map +1 -0
  259. package/dist/drivers/steelseries/prime-plus-hid.d.ts +51 -0
  260. package/dist/drivers/steelseries/prime-plus-hid.d.ts.map +1 -0
  261. package/dist/drivers/steelseries/prime-plus-hid.js +150 -0
  262. package/dist/drivers/steelseries/prime-plus-hid.js.map +1 -0
  263. package/dist/drivers/steelseries/rival3-wireless-hid.d.ts +61 -0
  264. package/dist/drivers/steelseries/rival3-wireless-hid.d.ts.map +1 -0
  265. package/dist/drivers/steelseries/rival3-wireless-hid.js +185 -0
  266. package/dist/drivers/steelseries/rival3-wireless-hid.js.map +1 -0
  267. package/dist/drivers/steelseries/rival310-hid.d.ts +59 -0
  268. package/dist/drivers/steelseries/rival310-hid.d.ts.map +1 -0
  269. package/dist/drivers/steelseries/rival310-hid.js +182 -0
  270. package/dist/drivers/steelseries/rival310-hid.js.map +1 -0
  271. package/dist/drivers/steelseries/rival650-hid.d.ts +68 -0
  272. package/dist/drivers/steelseries/rival650-hid.d.ts.map +1 -0
  273. package/dist/drivers/steelseries/rival650-hid.js +209 -0
  274. package/dist/drivers/steelseries/rival650-hid.js.map +1 -0
  275. package/dist/drivers/steelseries/sensei-ten-hid.d.ts +62 -0
  276. package/dist/drivers/steelseries/sensei-ten-hid.d.ts.map +1 -0
  277. package/dist/drivers/steelseries/sensei-ten-hid.js +186 -0
  278. package/dist/drivers/steelseries/sensei-ten-hid.js.map +1 -0
  279. package/dist/drivers/teevolution/hid.d.ts +82 -0
  280. package/dist/drivers/teevolution/hid.d.ts.map +1 -0
  281. package/dist/drivers/teevolution/hid.js +607 -0
  282. package/dist/drivers/teevolution/hid.js.map +1 -0
  283. package/dist/drivers/vendors.d.ts +254 -0
  284. package/dist/drivers/vendors.d.ts.map +1 -0
  285. package/dist/drivers/vendors.js +587 -0
  286. package/dist/drivers/vendors.js.map +1 -0
  287. package/dist/drivers/vgn/hid.d.ts +33 -0
  288. package/dist/drivers/vgn/hid.d.ts.map +1 -0
  289. package/dist/drivers/vgn/hid.js +213 -0
  290. package/dist/drivers/vgn/hid.js.map +1 -0
  291. package/dist/drivers/wallhack/keyboard-hid.d.ts +28 -0
  292. package/dist/drivers/wallhack/keyboard-hid.d.ts.map +1 -0
  293. package/dist/drivers/wallhack/keyboard-hid.js +80 -0
  294. package/dist/drivers/wallhack/keyboard-hid.js.map +1 -0
  295. package/dist/drivers/wallhack/mouse-hid.d.ts +45 -0
  296. package/dist/drivers/wallhack/mouse-hid.d.ts.map +1 -0
  297. package/dist/drivers/wallhack/mouse-hid.js +288 -0
  298. package/dist/drivers/wallhack/mouse-hid.js.map +1 -0
  299. package/dist/drivers/webhid.d.ts +59 -0
  300. package/dist/drivers/webhid.d.ts.map +1 -0
  301. package/dist/drivers/webhid.js +2 -0
  302. package/dist/drivers/webhid.js.map +1 -0
  303. package/dist/drivers/wlmouse/hid.d.ts +81 -0
  304. package/dist/drivers/wlmouse/hid.d.ts.map +1 -0
  305. package/dist/drivers/wlmouse/hid.js +576 -0
  306. package/dist/drivers/wlmouse/hid.js.map +1 -0
  307. package/dist/drivers/wooting/hid.d.ts +86 -0
  308. package/dist/drivers/wooting/hid.d.ts.map +1 -0
  309. package/dist/drivers/wooting/hid.js +320 -0
  310. package/dist/drivers/wooting/hid.js.map +1 -0
  311. package/dist/drivers/zaunkoenig/hid.d.ts +31 -0
  312. package/dist/drivers/zaunkoenig/hid.d.ts.map +1 -0
  313. package/dist/drivers/zaunkoenig/hid.js +162 -0
  314. package/dist/drivers/zaunkoenig/hid.js.map +1 -0
  315. package/dist/endgame-gear/op1.d.ts +165 -0
  316. package/dist/endgame-gear/op1.d.ts.map +1 -0
  317. package/dist/endgame-gear/op1.js +384 -0
  318. package/dist/endgame-gear/op1.js.map +1 -0
  319. package/dist/endgame-gear/wireless.d.ts +93 -0
  320. package/dist/endgame-gear/wireless.d.ts.map +1 -0
  321. package/dist/endgame-gear/wireless.js +242 -0
  322. package/dist/endgame-gear/wireless.js.map +1 -0
  323. package/dist/fantech/index.d.ts +9 -0
  324. package/dist/fantech/index.d.ts.map +1 -0
  325. package/dist/fantech/index.js +11 -0
  326. package/dist/fantech/index.js.map +1 -0
  327. package/dist/finalmouse/index.d.ts +41 -0
  328. package/dist/finalmouse/index.d.ts.map +1 -0
  329. package/dist/finalmouse/index.js +106 -0
  330. package/dist/finalmouse/index.js.map +1 -0
  331. package/dist/gearhub/index.d.ts +210 -0
  332. package/dist/gearhub/index.d.ts.map +1 -0
  333. package/dist/gearhub/index.js +282 -0
  334. package/dist/gearhub/index.js.map +1 -0
  335. package/dist/glorious/index.d.ts +89 -0
  336. package/dist/glorious/index.d.ts.map +1 -0
  337. package/dist/glorious/index.js +236 -0
  338. package/dist/glorious/index.js.map +1 -0
  339. package/dist/glorious-classic/index.d.ts +103 -0
  340. package/dist/glorious-classic/index.d.ts.map +1 -0
  341. package/dist/glorious-classic/index.js +304 -0
  342. package/dist/glorious-classic/index.js.map +1 -0
  343. package/dist/gwolves/index.d.ts +82 -0
  344. package/dist/gwolves/index.d.ts.map +1 -0
  345. package/dist/gwolves/index.js +197 -0
  346. package/dist/gwolves/index.js.map +1 -0
  347. package/dist/hyperx/index.d.ts +181 -0
  348. package/dist/hyperx/index.d.ts.map +1 -0
  349. package/dist/hyperx/index.js +303 -0
  350. package/dist/hyperx/index.js.map +1 -0
  351. package/dist/incott/index.d.ts +1067 -0
  352. package/dist/incott/index.d.ts.map +1 -0
  353. package/dist/incott/index.js +1482 -0
  354. package/dist/incott/index.js.map +1 -0
  355. package/dist/index.d.ts +30 -0
  356. package/dist/index.d.ts.map +1 -0
  357. package/dist/index.js +30 -0
  358. package/dist/index.js.map +1 -0
  359. package/dist/keychron/index.d.ts +194 -0
  360. package/dist/keychron/index.d.ts.map +1 -0
  361. package/dist/keychron/index.js +341 -0
  362. package/dist/keychron/index.js.map +1 -0
  363. package/dist/ksnake/index.d.ts +157 -0
  364. package/dist/ksnake/index.d.ts.map +1 -0
  365. package/dist/ksnake/index.js +314 -0
  366. package/dist/ksnake/index.js.map +1 -0
  367. package/dist/lamzu/atlantis.d.ts +237 -0
  368. package/dist/lamzu/atlantis.d.ts.map +1 -0
  369. package/dist/lamzu/atlantis.js +263 -0
  370. package/dist/lamzu/atlantis.js.map +1 -0
  371. package/dist/lamzu/index.d.ts +60 -0
  372. package/dist/lamzu/index.d.ts.map +1 -0
  373. package/dist/lamzu/index.js +86 -0
  374. package/dist/lamzu/index.js.map +1 -0
  375. package/dist/logitech/controls.d.ts +138 -0
  376. package/dist/logitech/controls.d.ts.map +1 -0
  377. package/dist/logitech/controls.js +184 -0
  378. package/dist/logitech/controls.js.map +1 -0
  379. package/dist/logitech/friendly-name.d.ts +50 -0
  380. package/dist/logitech/friendly-name.d.ts.map +1 -0
  381. package/dist/logitech/friendly-name.js +74 -0
  382. package/dist/logitech/friendly-name.js.map +1 -0
  383. package/dist/logitech/haptics.d.ts +97 -0
  384. package/dist/logitech/haptics.d.ts.map +1 -0
  385. package/dist/logitech/haptics.js +116 -0
  386. package/dist/logitech/haptics.js.map +1 -0
  387. package/dist/logitech/hosts.d.ts +55 -0
  388. package/dist/logitech/hosts.d.ts.map +1 -0
  389. package/dist/logitech/hosts.js +80 -0
  390. package/dist/logitech/hosts.js.map +1 -0
  391. package/dist/logitech/index.d.ts +127 -0
  392. package/dist/logitech/index.d.ts.map +1 -0
  393. package/dist/logitech/index.js +233 -0
  394. package/dist/logitech/index.js.map +1 -0
  395. package/dist/logitech/wheel.d.ts +117 -0
  396. package/dist/logitech/wheel.d.ts.map +1 -0
  397. package/dist/logitech/wheel.js +133 -0
  398. package/dist/logitech/wheel.js.map +1 -0
  399. package/dist/mchose/a5-gen1.d.ts +38 -0
  400. package/dist/mchose/a5-gen1.d.ts.map +1 -0
  401. package/dist/mchose/a5-gen1.js +60 -0
  402. package/dist/mchose/a5-gen1.js.map +1 -0
  403. package/dist/mchose/buttons.d.ts +66 -0
  404. package/dist/mchose/buttons.d.ts.map +1 -0
  405. package/dist/mchose/buttons.js +184 -0
  406. package/dist/mchose/buttons.js.map +1 -0
  407. package/dist/mchose/dock.d.ts +77 -0
  408. package/dist/mchose/dock.d.ts.map +1 -0
  409. package/dist/mchose/dock.js +133 -0
  410. package/dist/mchose/dock.js.map +1 -0
  411. package/dist/mchose/index.d.ts +299 -0
  412. package/dist/mchose/index.d.ts.map +1 -0
  413. package/dist/mchose/index.js +429 -0
  414. package/dist/mchose/index.js.map +1 -0
  415. package/dist/mchose/v3-buttons.d.ts +64 -0
  416. package/dist/mchose/v3-buttons.d.ts.map +1 -0
  417. package/dist/mchose/v3-buttons.js +258 -0
  418. package/dist/mchose/v3-buttons.js.map +1 -0
  419. package/dist/mchose/v3.d.ts +380 -0
  420. package/dist/mchose/v3.d.ts.map +1 -0
  421. package/dist/mchose/v3.js +608 -0
  422. package/dist/mchose/v3.js.map +1 -0
  423. package/dist/microsoft/index.d.ts +19 -0
  424. package/dist/microsoft/index.d.ts.map +1 -0
  425. package/dist/microsoft/index.js +22 -0
  426. package/dist/microsoft/index.js.map +1 -0
  427. package/dist/moddo/index.d.ts +72 -0
  428. package/dist/moddo/index.d.ts.map +1 -0
  429. package/dist/moddo/index.js +147 -0
  430. package/dist/moddo/index.js.map +1 -0
  431. package/dist/ninjutso/index.d.ts +74 -0
  432. package/dist/ninjutso/index.d.ts.map +1 -0
  433. package/dist/ninjutso/index.js +133 -0
  434. package/dist/ninjutso/index.js.map +1 -0
  435. package/dist/orbital/index.d.ts +14 -0
  436. package/dist/orbital/index.d.ts.map +1 -0
  437. package/dist/orbital/index.js +19 -0
  438. package/dist/orbital/index.js.map +1 -0
  439. package/dist/pulsar/index.d.ts +81 -0
  440. package/dist/pulsar/index.d.ts.map +1 -0
  441. package/dist/pulsar/index.js +185 -0
  442. package/dist/pulsar/index.js.map +1 -0
  443. package/dist/rawm/index.d.ts +55 -0
  444. package/dist/rawm/index.d.ts.map +1 -0
  445. package/dist/rawm/index.js +229 -0
  446. package/dist/rawm/index.js.map +1 -0
  447. package/dist/razer/codec.d.ts +504 -0
  448. package/dist/razer/codec.d.ts.map +1 -0
  449. package/dist/razer/codec.js +648 -0
  450. package/dist/razer/codec.js.map +1 -0
  451. package/dist/razer/devices.d.ts +156 -0
  452. package/dist/razer/devices.d.ts.map +1 -0
  453. package/dist/razer/devices.js +540 -0
  454. package/dist/razer/devices.js.map +1 -0
  455. package/dist/razer/index.d.ts +2 -0
  456. package/dist/razer/index.d.ts.map +1 -0
  457. package/dist/razer/index.js +2 -0
  458. package/dist/razer/index.js.map +1 -0
  459. package/dist/razer/v4.d.ts +19 -0
  460. package/dist/razer/v4.d.ts.map +1 -0
  461. package/dist/razer/v4.js +51 -0
  462. package/dist/razer/v4.js.map +1 -0
  463. package/dist/steelseries/aerox3.d.ts +236 -0
  464. package/dist/steelseries/aerox3.d.ts.map +1 -0
  465. package/dist/steelseries/aerox3.js +311 -0
  466. package/dist/steelseries/aerox3.js.map +1 -0
  467. package/dist/steelseries/aerox5-wireless.d.ts +300 -0
  468. package/dist/steelseries/aerox5-wireless.d.ts.map +1 -0
  469. package/dist/steelseries/aerox5-wireless.js +389 -0
  470. package/dist/steelseries/aerox5-wireless.js.map +1 -0
  471. package/dist/steelseries/aerox5.d.ts +228 -0
  472. package/dist/steelseries/aerox5.d.ts.map +1 -0
  473. package/dist/steelseries/aerox5.js +294 -0
  474. package/dist/steelseries/aerox5.js.map +1 -0
  475. package/dist/steelseries/aerox9-wireless.d.ts +213 -0
  476. package/dist/steelseries/aerox9-wireless.d.ts.map +1 -0
  477. package/dist/steelseries/aerox9-wireless.js +305 -0
  478. package/dist/steelseries/aerox9-wireless.js.map +1 -0
  479. package/dist/steelseries/devices.d.ts +82 -0
  480. package/dist/steelseries/devices.d.ts.map +1 -0
  481. package/dist/steelseries/devices.js +310 -0
  482. package/dist/steelseries/devices.js.map +1 -0
  483. package/dist/steelseries/index.d.ts +13 -0
  484. package/dist/steelseries/index.d.ts.map +1 -0
  485. package/dist/steelseries/index.js +13 -0
  486. package/dist/steelseries/index.js.map +1 -0
  487. package/dist/steelseries/prime-mini-wireless.d.ts +253 -0
  488. package/dist/steelseries/prime-mini-wireless.d.ts.map +1 -0
  489. package/dist/steelseries/prime-mini-wireless.js +351 -0
  490. package/dist/steelseries/prime-mini-wireless.js.map +1 -0
  491. package/dist/steelseries/prime-plus.d.ts +179 -0
  492. package/dist/steelseries/prime-plus.d.ts.map +1 -0
  493. package/dist/steelseries/prime-plus.js +275 -0
  494. package/dist/steelseries/prime-plus.js.map +1 -0
  495. package/dist/steelseries/rival3-wireless.d.ts +222 -0
  496. package/dist/steelseries/rival3-wireless.d.ts.map +1 -0
  497. package/dist/steelseries/rival3-wireless.js +308 -0
  498. package/dist/steelseries/rival3-wireless.js.map +1 -0
  499. package/dist/steelseries/rival3.d.ts +99 -0
  500. package/dist/steelseries/rival3.d.ts.map +1 -0
  501. package/dist/steelseries/rival3.js +147 -0
  502. package/dist/steelseries/rival3.js.map +1 -0
  503. package/dist/steelseries/rival310.d.ts +230 -0
  504. package/dist/steelseries/rival310.d.ts.map +1 -0
  505. package/dist/steelseries/rival310.js +322 -0
  506. package/dist/steelseries/rival310.js.map +1 -0
  507. package/dist/steelseries/rival650.d.ts +215 -0
  508. package/dist/steelseries/rival650.d.ts.map +1 -0
  509. package/dist/steelseries/rival650.js +284 -0
  510. package/dist/steelseries/rival650.js.map +1 -0
  511. package/dist/steelseries/sensei-ten.d.ts +283 -0
  512. package/dist/steelseries/sensei-ten.d.ts.map +1 -0
  513. package/dist/steelseries/sensei-ten.js +389 -0
  514. package/dist/steelseries/sensei-ten.js.map +1 -0
  515. package/dist/teevolution/index.d.ts +211 -0
  516. package/dist/teevolution/index.d.ts.map +1 -0
  517. package/dist/teevolution/index.js +458 -0
  518. package/dist/teevolution/index.js.map +1 -0
  519. package/dist/valkyrie/index.d.ts +19 -0
  520. package/dist/valkyrie/index.d.ts.map +1 -0
  521. package/dist/valkyrie/index.js +43 -0
  522. package/dist/valkyrie/index.js.map +1 -0
  523. package/dist/valkyrie/settings.d.ts +28 -0
  524. package/dist/valkyrie/settings.d.ts.map +1 -0
  525. package/dist/valkyrie/settings.js +84 -0
  526. package/dist/valkyrie/settings.js.map +1 -0
  527. package/dist/vgn/index.d.ts +59 -0
  528. package/dist/vgn/index.d.ts.map +1 -0
  529. package/dist/vgn/index.js +165 -0
  530. package/dist/vgn/index.js.map +1 -0
  531. package/dist/wallhack/index.d.ts +165 -0
  532. package/dist/wallhack/index.d.ts.map +1 -0
  533. package/dist/wallhack/index.js +226 -0
  534. package/dist/wallhack/index.js.map +1 -0
  535. package/dist/wlmouse/index.d.ts +4 -0
  536. package/dist/wlmouse/index.d.ts.map +1 -0
  537. package/dist/wlmouse/index.js +7 -0
  538. package/dist/wlmouse/index.js.map +1 -0
  539. package/dist/wooting/index.d.ts +225 -0
  540. package/dist/wooting/index.d.ts.map +1 -0
  541. package/dist/wooting/index.js +296 -0
  542. package/dist/wooting/index.js.map +1 -0
  543. package/dist/zaunkoenig/index.d.ts +49 -0
  544. package/dist/zaunkoenig/index.d.ts.map +1 -0
  545. package/dist/zaunkoenig/index.js +127 -0
  546. package/dist/zaunkoenig/index.js.map +1 -0
  547. package/package.json +193 -0
@@ -0,0 +1,772 @@
1
+ import { VENDOR_ID } from "../vendors.js";
2
+ import { RATES_1K, RATES_8K, RAZER_PRODUCTS } from "@openmouse/protocol/razer-devices";
3
+ import { openRazerDevice } from "./hid-open.js";
4
+ import { RAZER_BUTTON_CONTROLS, RAZER_BUTTON_CONTROL_LABEL, RAZER_LANDING_MAX, RAZER_LANDING_MIN, RAZER_LIFT_OFF_MAX, RAZER_LIFT_OFF_MIN, RAZER_READ, RAZER_TOGGLE_CONTROLS, RAZER_TOGGLE_CONTROL_INFO, RAZER_REPORT_ID, RAZER_STORAGE, RAZER_STATUS, RAZER_TRACKING_DISTANCES, RAZER_TRANSACTION_ID, RazerProtocolError, decodeBatteryPercent, decodeCharging, decodeDpi, decodeExtendedPollingRate, decodeFirmwareVersion, decodeLegacyPollingRate, decodeLiftOff, decodeLowPowerThreshold, decodeRazerResponse, decodeSerial, decodeSleepTimeout, encodeRazerRequest, isRazerGetter, razerDecodeButtonMapping, razerDecodeToggleControl, razerReadButtonMappingCommand, razerReadToggleControlCommand, razerSetButtonMappingCommand, razerSetToggleControlCommand, razerReadDpiCommand, razerSetDpiCommand, razerSetExtendedPollingCommand, razerSetLegacyPollingCommand, razerMaxLanding, razerSetLiftOffCommand, razerSetTrackingDistanceCommand, razerEnableAsymmetricLiftOffCommand, razerEnableAsymmetricLiftOffCanonicalCommand, razerEnableSensorCalibrationCommand, razerSetLowPowerThresholdCommand, razerSetSleepTimeoutCommand, } from "@openmouse/protocol/razer";
5
+ // The sensor takes any whole DPI from here up to the model's ceiling, per axis.
6
+ const DPI_MIN = 100;
7
+ const RESPONSE_DELAY_MS = 100;
8
+ const RESPONSE_ATTEMPTS = 6;
9
+ // How long a polling-rate change takes to re-establish the wireless link. The
10
+ // Viper V4 Pro driver pauses 150 ms for the same reason.
11
+ const POLLING_LINK_SETTLE_MS = 150;
12
+ // The vendor software slides from 1 to 15 minutes, so those are the bounds this
13
+ // model is meant to hold. The firmware itself accepts less — 30 s round-tripped
14
+ // exactly, below even the 60 s floor OpenRazer documents — but nothing offers
15
+ // that, so it is not offered here either.
16
+ const SLEEP_MIN_SECONDS = 60;
17
+ const SLEEP_MAX_SECONDS = 900;
18
+ const SLEEP_STEP_SECONDS = 60;
19
+ // One entry per minute, matching the vendor slider exactly rather than sampling
20
+ // it, so no offered value is an interpolation.
21
+ const SLEEP_OPTIONS = Array.from({ length: SLEEP_MAX_SECONDS / SLEEP_STEP_SECONDS }, (_, index) => (index + 1) * SLEEP_STEP_SECONDS);
22
+ // Synapse slides this from 5 to 100 percent. The value is stored on the battery
23
+ // reads' 0–255 scale, so the offered percentages are what round-trip cleanly
24
+ // through it rather than every whole percent.
25
+ const LOW_POWER_MIN_PERCENT = 5;
26
+ const LOW_POWER_MAX_PERCENT = 100;
27
+ const LOW_POWER_STEP_PERCENT = 5;
28
+ const LOW_POWER_OPTIONS = Array.from({ length: (LOW_POWER_MAX_PERCENT - LOW_POWER_MIN_PERCENT) / LOW_POWER_STEP_PERCENT + 1 }, (_, index) => LOW_POWER_MIN_PERCENT + index * LOW_POWER_STEP_PERCENT);
29
+ // Synapse states low power mode is unavailable at 2000 Hz and above, and greys
30
+ // the slider out there. The setting still reads, so this bounds the control
31
+ // rather than the command.
32
+ const LOW_POWER_MAX_POLLING_HZ = 1000;
33
+ /**
34
+ * Razer exposes its control channel on the interface whose only collection is
35
+ * Generic Desktop Mouse. Every other interface either belongs to a different
36
+ * function or is a protected collection the browser will not talk to.
37
+ */
38
+ function isMouseControlInterface(device) {
39
+ const [collection, ...rest] = device.collections;
40
+ return rest.length === 0 && collection?.usagePage === 0x01 && collection?.usage === 0x02;
41
+ }
42
+ /**
43
+ * Older Razer mice carry the configuration channel on a vendor-defined
44
+ * interface instead, and which one varies by hardware revision. Accepting both
45
+ * shapes means the picker can offer either without the driver refusing it.
46
+ */
47
+ function hasVendorCollection(device) {
48
+ return device.collections.some((collection) => (collection.usagePage ?? 0) >= 0xff00);
49
+ }
50
+ export class RazerHidClient {
51
+ queue = Promise.resolve();
52
+ staticReads = new Map();
53
+ // The mode probe is a write, so it runs once per connection rather than on
54
+ // every background refresh. Both setters know which mode they leave behind,
55
+ // so nothing after the first read needs to ask the mouse again.
56
+ asymmetric = null;
57
+ asymmetricKnown = false;
58
+ // Same reasoning as asymmetric/asymmetricKnown: read once per connection
59
+ // rather than on every background refresh. Button-mapping writes use their
60
+ // own transaction id (RAZER_BUTTON_TRANSACTION_ID) and are confirmed by an
61
+ // immediate read-back in setButtonMapping — polling this command class on a
62
+ // timer would put an unrelated read between a write and its own verify-read
63
+ // for no benefit, since nothing changes these but setButtonMapping itself.
64
+ buttonMappings = null;
65
+ buttonMappingsKnown = false;
66
+ /** Filled for dock passthrough after the first polling probe. */
67
+ discoveredPollingRates = null;
68
+ /** Which polling command the paired mouse answers; null until probed. */
69
+ discoveredHighRatePolling = null;
70
+ device;
71
+ constructor(device) {
72
+ this.device = device;
73
+ }
74
+ static isSupported(device) {
75
+ const product = RAZER_PRODUCTS.get(device.productId);
76
+ if (device.vendorId !== VENDOR_ID.razer || !product)
77
+ return false;
78
+ // A nativeOnly model's control channel sits on a collection the browser
79
+ // refuses to expose, so the WebHID driver must never claim it even when a
80
+ // previously granted device enumerates (auto-reconnect, shared grants).
81
+ if (product.nativeOnly)
82
+ return false;
83
+ return isMouseControlInterface(device)
84
+ || (product.vendorControlInterface === true && hasVendorCollection(device));
85
+ }
86
+ profile() {
87
+ return RAZER_PRODUCTS.get(this.device.productId);
88
+ }
89
+ /**
90
+ * Whether polling uses the extended command (a divisor of 8000) rather than
91
+ * the legacy one (a divisor of 1000).
92
+ *
93
+ * Asked of the product rather than of the connection: the Viper 8KHz is wired
94
+ * and needs the extended command, while the pre-HyperPolling receivers are
95
+ * wireless and answer only the legacy one. For every model verified so far
96
+ * this is exactly `isWireless()`, which is what it used to read.
97
+ */
98
+ usesHighRatePolling() {
99
+ if (this.discoveredHighRatePolling !== null)
100
+ return this.discoveredHighRatePolling;
101
+ return this.profile()?.highRatePolling ?? this.isWireless();
102
+ }
103
+ /** DPI store byte; most models store, the V2 generation does not. */
104
+ dpiStorageByte() {
105
+ return this.profile()?.dpiStorageByte ?? RAZER_STORAGE;
106
+ }
107
+ async open() {
108
+ await openRazerDevice(this.device);
109
+ }
110
+ async close() {
111
+ this.staticReads.clear();
112
+ this.asymmetricKnown = false;
113
+ this.buttonMappingsKnown = false;
114
+ this.discoveredPollingRates = null;
115
+ this.discoveredHighRatePolling = null;
116
+ if (this.device.opened)
117
+ await this.device.close();
118
+ }
119
+ /**
120
+ * Whether this collection actually answers the Razer protocol, tried by
121
+ * sending the firmware-version read rather than inferred from the
122
+ * collection's declared shape. `isSupported()`'s shape check (single
123
+ * collection, Generic Desktop Mouse) is right for every model verified so
124
+ * far, but WebHID does not validate report ids against the descriptor (see
125
+ * the note atop codec.ts), so a collection that fails the shape check can
126
+ * still genuinely answer report 0 — used as a fallback in
127
+ * `razerProbeControlInterface` for exactly that case, not as the normal
128
+ * detection path.
129
+ */
130
+ async probeReachable() {
131
+ try {
132
+ await this.request(RAZER_READ.firmware);
133
+ return true;
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ }
139
+ displayName() {
140
+ const known = this.profile();
141
+ return known ? `Razer ${known.model}` : this.device.productName || "Razer";
142
+ }
143
+ isWireless() {
144
+ return this.profile()?.wireless ?? false;
145
+ }
146
+ /**
147
+ * Says so when the model has never been connected by this project.
148
+ *
149
+ * The commands are the ones already confirmed elsewhere, but which of them a
150
+ * given model answers is transcribed rather than measured, so the panel
151
+ * should not present it as the same thing as a tested mouse.
152
+ */
153
+ connectionDetail(wireless) {
154
+ const link = this.profile()?.connectionLabel
155
+ ?? (wireless ? "HyperSpeed receiver" : "Wired USB");
156
+ return this.profile()?.verified === false ? `${link} · untested model` : link;
157
+ }
158
+ maxDpi() {
159
+ return this.profile()?.maxDpi ?? 35000;
160
+ }
161
+ getSupportedPollingRates() {
162
+ if (this.discoveredPollingRates)
163
+ return [...this.discoveredPollingRates];
164
+ return [...(this.profile()?.pollingRates ?? RATES_1K)];
165
+ }
166
+ /** Seconds, matching what the panel labels and what `setSleepTimeout` takes. */
167
+ getSleepOptions() {
168
+ return [...SLEEP_OPTIONS];
169
+ }
170
+ /** Whole percentages, matching the vendor slider. */
171
+ getLowPowerOptions() {
172
+ return [...LOW_POWER_OPTIONS];
173
+ }
174
+ /** The rate above which the vendor software refuses to arm low power mode. */
175
+ getLowPowerPollingCeiling() {
176
+ return LOW_POWER_MAX_POLLING_HZ;
177
+ }
178
+ /** Every whole value, because the control validates entries against this list. */
179
+ getDpiOptions() {
180
+ const options = [];
181
+ for (let dpi = DPI_MIN; dpi <= this.maxDpi(); dpi += 1)
182
+ options.push(dpi);
183
+ return options;
184
+ }
185
+ async readStatus() {
186
+ await this.open();
187
+ const wireless = this.isWireless();
188
+ const firmware = await this.once("firmware", () => this.request(RAZER_READ.firmware));
189
+ if (!firmware)
190
+ throw new Error("The mouse did not report a firmware version.");
191
+ const serial = await this.once("serial", () => this.request(RAZER_READ.serial).catch(() => null));
192
+ // A wired mouse with no cell answers battery and power-management commands
193
+ // as unsupported, which would otherwise abort the whole status read.
194
+ const hasBattery = this.profile()?.hasBattery !== false;
195
+ // On a model nobody has connected, `hasBattery` is a prediction. An
196
+ // unsupported reply throws, and this read is not optional the way sleep and
197
+ // low power are, so it would abort the whole status read and take DPI and
198
+ // polling down with it rather than dropping one card. Verified models still
199
+ // fail loudly: there, a battery command that stopped answering is news.
200
+ const battery = hasBattery
201
+ ? await this.readBattery().catch((error) => {
202
+ if (this.profile()?.verified === true)
203
+ throw error;
204
+ return null;
205
+ })
206
+ : null;
207
+ // Both transports answer this, unlike polling. A transport that ever stops
208
+ // reports no timeout and hides the control rather than failing the whole
209
+ // read, which would take DPI and battery down with it.
210
+ const sleep = hasBattery ? await this.request(RAZER_READ.sleepTimeout).catch(() => null) : null;
211
+ const lowPower = hasBattery ? await this.request(RAZER_READ.lowPowerThreshold).catch(() => null) : null;
212
+ const dpi = decodeDpi(await this.request(razerReadDpiCommand(this.dpiStorageByte())));
213
+ const pollingRateHz = await this.readPollingRateHz();
214
+ const buttonMappings = await this.currentButtonMappings();
215
+ // Asked of the product, not of the mouse. A model without lift-off can
216
+ // still answer this read — the Basilisk X HyperSpeed returns status 0x02
217
+ // with an all-zero payload, which decodes as a perfectly ordinary "Low" —
218
+ // so a successful reply proves nothing and the control cannot be offered on
219
+ // the strength of one. Skipping it also spares 0x0b a round trip on every
220
+ // background refresh for every model that does not have it.
221
+ const liftOff = this.profile()?.liftOff === true ? await this.readLiftOff() : null;
222
+ return {
223
+ brand: "Razer",
224
+ name: this.displayName(),
225
+ ui: {
226
+ family: "razer",
227
+ settingsReady: true,
228
+ valuesVerified: true,
229
+ hideUnsupportedPollingRates: true,
230
+ // No sensor-processing command is confirmed, so that card is hidden
231
+ // rather than offered and left inert. Lift-off is confirmed and
232
+ // readable through `readLiftOff`, but `MouseStatus` cannot carry the
233
+ // asymmetric pair yet — see the note on `liftOffDistance` below.
234
+ hideProcessingCard: true,
235
+ // Nothing reports link quality on this model, and the section this card
236
+ // shares with sleep is opened below, so it would otherwise appear as a
237
+ // permanent "signal is unavailable" placeholder.
238
+ hideSignalCard: true,
239
+ // Auto sleep and button mappings are what this driver puts in that
240
+ // section, so it opens only when at least one of them answered.
241
+ showAdvancedSection: sleep !== null || buttonMappings !== null,
242
+ forceShowBattery: battery ? true : undefined,
243
+ defaultDisplayName: this.profile()?.model,
244
+ },
245
+ batteryPercent: battery?.percent ?? null,
246
+ batteryState: battery?.state ?? "Unknown",
247
+ dpi: dpi.x,
248
+ dpiY: dpi.y,
249
+ supportsSeparateDpiAxes: true,
250
+ pollingRateHz,
251
+ supportedPollingRates: this.getSupportedPollingRates(),
252
+ activeProfile: null,
253
+ connectionType: wireless ? "Wireless" : "Wired",
254
+ connectionDetail: this.connectionDetail(wireless),
255
+ sleepTimeout: sleep ? decodeSleepTimeout(sleep) : null,
256
+ lowBatteryWarning: lowPower ? decodeLowPowerThreshold(lowPower) : null,
257
+ unitId: serial ? decodeSerial(serial) : null,
258
+ liftOffDistance: liftOff?.tracking ?? null,
259
+ // An empty list hides the control, which is what should happen on a
260
+ // transport that does not answer class 0x0b at all.
261
+ supportedLiftOffDistances: liftOff ? [...RAZER_TRACKING_DISTANCES] : [],
262
+ asymmetricLiftOff: liftOff && this.hasAsymmetricLiftOff()
263
+ ? {
264
+ enabled: await this.asymmetricMode(liftOff),
265
+ liftOff: liftOff.liftOff,
266
+ landing: liftOff.landing,
267
+ liftOffRange: { min: RAZER_LIFT_OFF_MIN, max: RAZER_LIFT_OFF_MAX },
268
+ landingRange: { min: RAZER_LANDING_MIN, max: RAZER_LANDING_MAX },
269
+ }
270
+ : null,
271
+ razerButtonMappings: buttonMappings ?? undefined,
272
+ firmware: [`Mouse ${decodeFirmwareVersion(firmware)}`],
273
+ };
274
+ }
275
+ /**
276
+ * Every shipped control's current state, keyed by control name — the four
277
+ * cross-assignable `RazerButtonControl`s and the three two-state
278
+ * `RazerToggleControl`s share one dict, since the view layer reads each
279
+ * family by iterating its own fixed control list (`RAZER_BUTTON_CONTROLS` /
280
+ * `RAZER_TOGGLE_CONTROLS`), and the two lists share no control names. Null
281
+ * on a model that has not been confirmed to support this write at all. A
282
+ * control whose reply this driver cannot decode is omitted rather than
283
+ * guessed at.
284
+ */
285
+ async readButtonMappings() {
286
+ if (this.profile()?.buttonMapping !== true)
287
+ return null;
288
+ const mappings = {};
289
+ await Promise.all([
290
+ ...RAZER_BUTTON_CONTROLS.map(async (control) => {
291
+ const reply = await this.request(razerReadButtonMappingCommand(control)).catch(() => null);
292
+ const mapping = reply ? razerDecodeButtonMapping(reply) : null;
293
+ if (mapping)
294
+ mappings[control] = mapping;
295
+ }),
296
+ ...RAZER_TOGGLE_CONTROLS.map(async (control) => {
297
+ const reply = await this.request(razerReadToggleControlCommand(control)).catch(() => null);
298
+ const state = reply ? razerDecodeToggleControl(control, reply) : null;
299
+ if (state)
300
+ mappings[control] = state;
301
+ }),
302
+ ]);
303
+ // Every control failing means the transport does not answer class 0x02 at
304
+ // all — the cable (0x00c0) has never been tested against it, only the
305
+ // receiver. Collapse that to null so the card is hidden outright rather
306
+ // than rendered empty, the same way readLiftOff degrades.
307
+ return Object.keys(mappings).length > 0 ? mappings : null;
308
+ }
309
+ /** Probes once, then trusts what setButtonMapping leaves behind. */
310
+ async currentButtonMappings() {
311
+ if (!this.buttonMappingsKnown) {
312
+ this.buttonMappings = await this.readButtonMappings();
313
+ this.buttonMappingsKnown = true;
314
+ }
315
+ return this.buttonMappings;
316
+ }
317
+ /**
318
+ * Sets one control's mapping and confirms it by reading the same control
319
+ * back — not a formality here: this write silently no-ops under the driver's
320
+ * default transaction id, so a status-only check would have shipped a
321
+ * control that looked correct and did nothing. See
322
+ * `RAZER_BUTTON_TRANSACTION_ID`.
323
+ */
324
+ async setButtonMapping(control, mapping) {
325
+ await this.request(razerSetButtonMappingCommand(control, mapping));
326
+ const reply = await this.request(razerReadButtonMappingCommand(control));
327
+ const confirmed = razerDecodeButtonMapping(reply);
328
+ if (confirmed !== mapping) {
329
+ throw new Error(`The mouse kept ${confirmed ?? "an unrecognised mapping"} for ${RAZER_BUTTON_CONTROL_LABEL[control]} instead of ${mapping}.`);
330
+ }
331
+ this.buttonMappings = { ...this.buttonMappings, [control]: confirmed };
332
+ this.buttonMappingsKnown = true;
333
+ return confirmed;
334
+ }
335
+ /**
336
+ * Sets a two-state control (Scroll Up/Down, the bottom sensitivity button)
337
+ * and confirms it the same way `setButtonMapping` does — read-back after
338
+ * write, not status alone. `label` must be the control's own enabled label
339
+ * or "Disabled"; `razerSetToggleControlCommand` throws otherwise.
340
+ */
341
+ async setToggleControl(control, label) {
342
+ await this.request(razerSetToggleControlCommand(control, label));
343
+ const reply = await this.request(razerReadToggleControlCommand(control));
344
+ const confirmed = razerDecodeToggleControl(control, reply);
345
+ if (confirmed !== label) {
346
+ throw new Error(`The mouse kept ${confirmed ?? "an unrecognised state"} for ${RAZER_TOGGLE_CONTROL_INFO[control].label} instead of ${label}.`);
347
+ }
348
+ this.buttonMappings = { ...this.buttonMappings, [control]: confirmed };
349
+ this.buttonMappingsKnown = true;
350
+ return confirmed;
351
+ }
352
+ async setDpi(dpi, dpiY = dpi) {
353
+ const ceiling = this.maxDpi();
354
+ for (const value of [dpi, dpiY]) {
355
+ if (!Number.isInteger(value) || value < DPI_MIN || value > ceiling) {
356
+ throw new Error(`DPI must be a whole number between ${DPI_MIN} and ${ceiling.toLocaleString()}.`);
357
+ }
358
+ }
359
+ await this.request(razerSetDpiCommand(dpi, dpiY, this.dpiStorageByte()));
360
+ const confirmed = decodeDpi(await this.request(razerReadDpiCommand(this.dpiStorageByte())));
361
+ if (confirmed.x !== dpi || confirmed.y !== dpiY) {
362
+ throw new Error(`The mouse kept ${confirmed.x.toLocaleString()} DPI instead of ${dpi.toLocaleString()}.`);
363
+ }
364
+ return confirmed.x;
365
+ }
366
+ async setSleepTimeout(seconds) {
367
+ if (!Number.isInteger(seconds) || seconds < SLEEP_MIN_SECONDS || seconds > SLEEP_MAX_SECONDS) {
368
+ const minutes = SLEEP_MAX_SECONDS / SLEEP_STEP_SECONDS;
369
+ throw new Error(`Auto sleep must be between 1 and ${minutes} minutes.`);
370
+ }
371
+ await this.request(razerSetSleepTimeoutCommand(seconds));
372
+ const confirmed = decodeSleepTimeout(await this.request(RAZER_READ.sleepTimeout));
373
+ if (confirmed !== seconds) {
374
+ throw new Error(`The mouse kept ${confirmed} seconds instead of ${seconds}.`);
375
+ }
376
+ return confirmed;
377
+ }
378
+ async setLowPowerThreshold(percent) {
379
+ // A range rather than the offered list: the panel adds the mouse's own value
380
+ // when it sits off the five-point step, and that value must stay writable.
381
+ if (!Number.isInteger(percent) || percent < LOW_POWER_MIN_PERCENT || percent > LOW_POWER_MAX_PERCENT) {
382
+ throw new Error(`Low power mode must be between ${LOW_POWER_MIN_PERCENT}% and ${LOW_POWER_MAX_PERCENT}%.`);
383
+ }
384
+ // Asked of the mouse, not the panel. Staging a polling change repaints the
385
+ // control as disabled without withdrawing a threshold already queued behind
386
+ // it, so the disabled select cannot be what enforces this.
387
+ const pollingRateHz = await this.readPollingRateHz();
388
+ if (pollingRateHz > LOW_POWER_MAX_POLLING_HZ) {
389
+ throw new Error(`Low power mode is unavailable at ${pollingRateHz.toLocaleString()} Hz.`
390
+ + ` Set the polling rate to ${LOW_POWER_MAX_POLLING_HZ.toLocaleString()} Hz or lower first.`);
391
+ }
392
+ await this.request(razerSetLowPowerThresholdCommand(percent));
393
+ const confirmed = decodeLowPowerThreshold(await this.request(RAZER_READ.lowPowerThreshold));
394
+ if (confirmed !== percent) {
395
+ throw new Error(`The mouse kept ${confirmed}% instead of ${percent}%.`);
396
+ }
397
+ return confirmed;
398
+ }
399
+ async setPollingRate(pollingRateHz) {
400
+ // Dock passthrough: discover the paired mouse's ladder before validating.
401
+ if (this.profile()?.probePollingRates === true && !this.discoveredPollingRates) {
402
+ await this.readPollingRateHz();
403
+ }
404
+ if (!this.getSupportedPollingRates().includes(pollingRateHz)) {
405
+ throw new Error(`This mouse does not support ${pollingRateHz.toLocaleString()} Hz on this connection.`);
406
+ }
407
+ const highRate = this.usesHighRatePolling();
408
+ await this.request(highRate
409
+ ? razerSetExtendedPollingCommand(pollingRateHz)
410
+ : razerSetLegacyPollingCommand(pollingRateHz));
411
+ const commitTransactionId = highRate
412
+ ? this.profile()?.extendedPollingCommitTransactionId
413
+ : undefined;
414
+ if (commitTransactionId !== undefined) {
415
+ const commit = razerSetExtendedPollingCommand(pollingRateHz);
416
+ commit.args = [0x01, commit.args?.[1] ?? 0x00];
417
+ await this.request(commit, commitTransactionId);
418
+ }
419
+ // Changing the rate briefly reconfigures the wireless link, and exchanges
420
+ // sent into that window come back corrupt or unanswered — the Viper V4 Pro
421
+ // driver documents the same pause. Let it settle before the read-back; any
422
+ // stragglers are then caught by `exchange`'s own retry.
423
+ if (this.isWireless())
424
+ await this.delay(POLLING_LINK_SETTLE_MS);
425
+ const confirmed = await this.readPollingRateHz();
426
+ if (confirmed !== pollingRateHz) {
427
+ throw new Error(`The mouse kept ${confirmed.toLocaleString()} Hz instead of ${pollingRateHz.toLocaleString()} Hz.`);
428
+ }
429
+ return confirmed;
430
+ }
431
+ /**
432
+ * Battery level, and the charging state when the mouse reports one.
433
+ *
434
+ * The two are separate commands and a mouse may answer the first without the
435
+ * second — the Basilisk X HyperSpeed reports a good level and refuses
436
+ * `0x07`/`0x84` as unsupported. Letting that take the level down with it threw
437
+ * away the reading the panel actually wanted; an unknown charging state is
438
+ * only a missing word in the battery caption.
439
+ */
440
+ async readBattery() {
441
+ const level = await this.request(RAZER_READ.battery);
442
+ const charging = await this.request(RAZER_READ.charging).catch(() => null);
443
+ return {
444
+ percent: decodeBatteryPercent(level),
445
+ state: charging === null
446
+ ? "Unknown"
447
+ : decodeCharging(charging) ? "Charging" : "Discharging",
448
+ };
449
+ }
450
+ /**
451
+ * Reads the tracking level and the asymmetric lift-off/landing pair.
452
+ *
453
+ * Returns null when the mouse does not answer. Class `0x0b` has only ever
454
+ * been exercised on the receiver, so the cable may reject it, and a status
455
+ * read that throws takes the whole panel down rather than one control. The
456
+ * caller degrades instead.
457
+ */
458
+ async readLiftOff() {
459
+ const reply = await this.request(RAZER_READ.liftOff).catch(() => null);
460
+ return reply ? decodeLiftOff(reply) : null;
461
+ }
462
+ /**
463
+ * Reports whether the mouse is currently in asymmetric mode, or null when it
464
+ * cannot be established.
465
+ *
466
+ * Nothing readable carries the mode — two 47-command captures differing only
467
+ * by it were byte-identical, and a full sweep found nothing. What does report
468
+ * it is the pair write's own status: refused `0x03` in symmetric mode,
469
+ * accepted `0x02` in asymmetric.
470
+ *
471
+ * That makes this a probe rather than a read, so it is written to disturb
472
+ * nothing. Re-sending the values the mirror already holds is value-preserving
473
+ * either way, and mode-preserving too: a refusal cannot switch the mode, and
474
+ * an acceptance re-selects the mode the mouse was already in.
475
+ *
476
+ * Call it once per connection, not on every refresh — it is still a write.
477
+ */
478
+ async probeAsymmetric(current) {
479
+ // The firmware stores an inverted pair without complaint, and one session
480
+ // left it holding lift-off 2 with landing 26. Probing with that would throw
481
+ // in the command builder, so report "unknown" rather than guessing.
482
+ if (current.landing >= current.liftOff)
483
+ return null;
484
+ try {
485
+ await this.request(razerSetLiftOffCommand(current.liftOff, current.landing));
486
+ return true;
487
+ }
488
+ catch (error) {
489
+ if (error instanceof RazerProtocolError && error.status === RAZER_STATUS.failure)
490
+ return false;
491
+ return null;
492
+ }
493
+ }
494
+ /**
495
+ * Whether this model stores a separate lift-off/landing pair at all.
496
+ *
497
+ * Establishing the mode costs a *write*, so it is only attempted where the
498
+ * pair commands have been confirmed on hardware. A model that has not been
499
+ * connected keeps the plain three-stop tracking control instead, which costs
500
+ * reads only.
501
+ */
502
+ hasAsymmetricLiftOff() {
503
+ return this.profile()?.asymmetricLiftOff === true;
504
+ }
505
+ /** Probes once, then trusts what the setters leave behind. */
506
+ async asymmetricMode(current) {
507
+ if (!this.asymmetricKnown) {
508
+ this.asymmetric = await this.probeAsymmetric(current);
509
+ this.asymmetricKnown = true;
510
+ }
511
+ return this.asymmetric;
512
+ }
513
+ /**
514
+ * Selects the symmetric tracking distance, which also takes the mouse out of
515
+ * asymmetric mode — it honours whichever store was written last, and there is
516
+ * no mode flag to clear.
517
+ *
518
+ * Named for the shell's driver contract rather than the vendor's wording; the
519
+ * vendor calls this the tracking distance.
520
+ */
521
+ async setLiftOffDistance(distance) {
522
+ await this.request(razerSetTrackingDistanceCommand(distance));
523
+ const confirmed = decodeLiftOff(await this.request(RAZER_READ.liftOff));
524
+ if (confirmed.tracking !== distance) {
525
+ throw new Error(`The mouse kept ${confirmed.tracking ?? "an unknown"} tracking distance instead of ${distance}.`);
526
+ }
527
+ this.asymmetric = false;
528
+ this.asymmetricKnown = true;
529
+ return distance;
530
+ }
531
+ /**
532
+ * Writes the asymmetric pair, and switches the mouse into asymmetric mode to
533
+ * do it. The tracking level shares the same reply but not the same write, so
534
+ * it is left as the mouse holds it.
535
+ *
536
+ * Landing is capped just below lift-off rather than rejected, because
537
+ * lowering lift-off past an already-set landing is ordinary use of a pair of
538
+ * controls and the vendor software caps its own slider the same way. The
539
+ * returned values are what the mouse ended up holding, not what was asked
540
+ * for, so a caller can render the cap rather than guess at it.
541
+ */
542
+ async setLiftOff(liftOff, landing) {
543
+ const capped = Math.min(landing, razerMaxLanding(liftOff));
544
+ // Built before anything is sent, so a rejected value costs no device
545
+ // traffic and cannot leave the mouse switched into asymmetric mode over a
546
+ // write that never happened. `razerSetLiftOffCommand` validates lift-off
547
+ // before landing, so an out-of-range lift-off still reports itself rather
548
+ // than being masked by the landing it drags out of range here.
549
+ const command = razerSetLiftOffCommand(liftOff, capped);
550
+ // The pair write is refused in symmetric mode, so it is armed first. Units
551
+ // differ within the same "Mouse 1.14" version string: the 0x01 unlock is
552
+ // verified on the swept hardware, but a reporter's unit answered the armed
553
+ // pair write 0x03 in two separate sessions, so each refusal falls back to
554
+ // the next documented arm rather than giving up.
555
+ const arms = [
556
+ [razerEnableAsymmetricLiftOffCommand()],
557
+ [razerEnableAsymmetricLiftOffCanonicalCommand()],
558
+ [razerEnableSensorCalibrationCommand(), razerEnableAsymmetricLiftOffCommand()],
559
+ ];
560
+ await this.writePairArmed(command, arms);
561
+ // Some units answer a refused pair write by still moving the stored pair;
562
+ // others leave it untouched. Either way the read-back is a genuine check.
563
+ const confirmed = decodeLiftOff(await this.request(RAZER_READ.liftOff));
564
+ if (confirmed.liftOff !== liftOff || confirmed.landing !== capped) {
565
+ throw new Error(`The mouse kept lift-off ${confirmed.liftOff} and landing ${confirmed.landing} instead of ${liftOff} and ${capped}.`);
566
+ }
567
+ this.asymmetric = true;
568
+ this.asymmetricKnown = true;
569
+ return confirmed;
570
+ }
571
+ /**
572
+ * Sends one arm sequence (setting writes) followed by the pair write, and
573
+ * returns on the first success. A refused arm is safe to burn — the units
574
+ * that refuse it read the stored pair back unchanged, and the pair write
575
+ * that follows restores any pair a refusal may have disturbed. When every
576
+ * arm fails, the last refusal propagates so the caller sees the refused
577
+ * command rather than a guessed substitution.
578
+ */
579
+ async writePairArmed(command, arms) {
580
+ let lastRefusal;
581
+ for (const sequence of arms) {
582
+ for (const write of sequence) {
583
+ await this.request(write);
584
+ }
585
+ try {
586
+ await this.request(command);
587
+ return;
588
+ }
589
+ catch (error) {
590
+ if (error instanceof RazerProtocolError && error.status === RAZER_STATUS.failure) {
591
+ lastRefusal = error;
592
+ continue;
593
+ }
594
+ throw error;
595
+ }
596
+ }
597
+ throw lastRefusal ?? new RazerProtocolError(`The mouse refused the lift-off write after ${arms.length} attempts.`);
598
+ }
599
+ /**
600
+ * Wired answers only the legacy command and the receiver only the extended
601
+ * one, so ask for the expected one first and keep the other as a fallback.
602
+ * Asking in the wrong order costs a failed exchange on every refresh.
603
+ *
604
+ * Mouse Dock Pro omits a fixed rate list (`probePollingRates`): if extended
605
+ * polling answers, the paired mouse can use the HyperPolling ladder;
606
+ * otherwise stay on the 1 kHz ladder (e.g. Naga V2 Pro).
607
+ */
608
+ async readPollingRateHz() {
609
+ const probe = this.profile()?.probePollingRates === true;
610
+ const extended = [RAZER_READ.pollingRateExtended, decodeExtendedPollingRate, true];
611
+ const legacy = [RAZER_READ.pollingRate, decodeLegacyPollingRate, false];
612
+ // Until a dock probe settles, prefer the product's declared encoding so the
613
+ // first try matches what verified HyperSpeed paths already expect.
614
+ const preferExtended = this.discoveredHighRatePolling ?? this.profile()?.highRatePolling ?? this.isWireless();
615
+ for (const [command, decode, isExtended] of preferExtended ? [extended, legacy] : [legacy, extended]) {
616
+ const reply = await this.request(command).catch(() => null);
617
+ if (!reply)
618
+ continue;
619
+ const hz = decode(reply);
620
+ if (probe) {
621
+ this.discoveredHighRatePolling = isExtended;
622
+ this.discoveredPollingRates = isExtended ? RATES_8K : RATES_1K;
623
+ }
624
+ return hz;
625
+ }
626
+ throw new Error("The mouse did not report a polling rate.");
627
+ }
628
+ once(key, read) {
629
+ const pending = this.staticReads.get(key);
630
+ if (pending)
631
+ return pending;
632
+ const started = read();
633
+ this.staticReads.set(key, started);
634
+ started.catch(() => this.staticReads.delete(key));
635
+ return started;
636
+ }
637
+ async request(command, transactionId) {
638
+ const run = this.queue.then(() => this.exchange(command, transactionId), () => this.exchange(command, transactionId));
639
+ this.queue = run.catch(() => undefined);
640
+ return await run;
641
+ }
642
+ async exchange(command, transactionIdOverride) {
643
+ await this.open();
644
+ // A command may pin its own transaction id — the class `0x02` button write
645
+ // silently no-ops under the shared one. An explicit call-site override
646
+ // still wins over both.
647
+ const transactionId = transactionIdOverride
648
+ ?? command.transactionId
649
+ ?? this.profile()?.transactionId
650
+ ?? RAZER_TRANSACTION_ID;
651
+ const request = encodeRazerRequest(command, transactionId);
652
+ // A busy status means the mouse will answer this same request later, so the
653
+ // reads keep going without a re-send. A corrupt reply means the exchange
654
+ // itself was lost — the receiver can return garbage while it reconfigures
655
+ // the wireless link after a polling-rate change — so the send is retried
656
+ // from scratch. Retrying keeps one corrupt reply from failing the whole
657
+ // status read, which is what took the lift-off panel down after an 8,000 Hz
658
+ // switch.
659
+ for (let attempt = 0; attempt < RESPONSE_ATTEMPTS; attempt += 1) {
660
+ await this.write(command, request);
661
+ const reply = await this.awaitReply(command);
662
+ if (reply)
663
+ return reply;
664
+ }
665
+ throw new Error("The mouse stayed busy — it may be asleep or out of range.");
666
+ }
667
+ /**
668
+ * Sends one protocol request as a feature report. Chrome reports a refused
669
+ * write as a bare `NotAllowedError` whose message is only "Failed to write
670
+ * feature report." with no hint about why, so the throw is wrapped with the
671
+ * troubleshooting paths that actually explain it: the control interface is
672
+ * held by another program (Razer Synapse), the interface granted on the
673
+ * wireless receiver is the pointer collection rather than the control one,
674
+ * macOS has denied the browser Input Monitoring access, or the OS/browser
675
+ * refuses writes to mouse-class collections outright.
676
+ */
677
+ async write(command, request) {
678
+ try {
679
+ await this.device.sendFeatureReport(RAZER_REPORT_ID, request);
680
+ }
681
+ catch (error) {
682
+ const detail = error instanceof Error ? error.message : String(error);
683
+ throw new Error(`Chrome could not write the ${this.displayName()}'s feature report (command 0x${command.commandClass
684
+ .toString(16)}/${command.commandId.toString(16)}). `
685
+ + "Quit Razer Synapse and try again — it holds the control interface. If it still fails, "
686
+ + "re-add the device and pick the other Razer Viper interface on the receiver, and on macOS "
687
+ + "allow the browser under System Settings → Privacy & Security → Input Monitoring. "
688
+ + `(${detail})`);
689
+ }
690
+ }
691
+ /**
692
+ * Reads up to the response budget for one send. Returns the decoded reply, or
693
+ * null when the reply was corrupt or the budget ran out on busy replies — the
694
+ * caller re-sends on null, since a corrupt reply is a lost exchange rather
695
+ * than a status the mouse owns.
696
+ */
697
+ async awaitReply(command) {
698
+ for (let read = 0; read < RESPONSE_ATTEMPTS; read += 1) {
699
+ await this.delay(RESPONSE_DELAY_MS);
700
+ const reply = this.copyDataView(await this.device.receiveFeatureReport(RAZER_REPORT_ID));
701
+ try {
702
+ return decodeRazerResponse(reply, command);
703
+ }
704
+ catch (error) {
705
+ if (!(error instanceof RazerProtocolError))
706
+ throw error;
707
+ if (error.status === RAZER_STATUS.busy)
708
+ continue;
709
+ // A bad checksum or wrong length carries no status, so it is transport
710
+ // corruption rather than an answer the mouse decided on.
711
+ if (error.status === null)
712
+ return null;
713
+ // Repeating a getter is safe; setters confirm through a later read and
714
+ // must not be sent twice when an earlier reply is still buffered.
715
+ if (error.stale && isRazerGetter(command))
716
+ return null;
717
+ throw error;
718
+ }
719
+ }
720
+ return null;
721
+ }
722
+ copyDataView(view) {
723
+ return new Uint8Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
724
+ }
725
+ delay(milliseconds) {
726
+ return new Promise((resolve) => window.setTimeout(resolve, milliseconds));
727
+ }
728
+ }
729
+ /**
730
+ * Last-resort device selection for when none of the granted collections
731
+ * matched `RazerHidClient.isSupported()`'s shape check. Rather than guess a
732
+ * new shape, this tries the protocol itself against every Razer-vendor
733
+ * collection Chrome granted and returns the client for whichever one
734
+ * actually answers.
735
+ *
736
+ * This exists because the shape check assumes every model's control channel
737
+ * is a single Generic Desktop Mouse collection (verified true so far for
738
+ * every model this driver has been confirmed against) — a real assumption a
739
+ * given browser/OS combination could break for a specific model without any
740
+ * code here changing, since what collections get exposed to WebHID at all is
741
+ * decided below this driver, by the platform. Kept off the normal detection
742
+ * path (`registry.ts`'s `DEVICE_DRIVERS` entry still uses `isSupported()`
743
+ * alone) because sending real protocol traffic to every candidate is real
744
+ * device I/O, not a cheap shape check, and only worth it once the cheap
745
+ * check has already failed to find anything.
746
+ *
747
+ * `sendFeatureReport`/`receiveFeatureReport` reject immediately for a
748
+ * collection with no feature-report capability at all, so a wrong candidate
749
+ * costs one failed call, not a hang — see `write()`'s try/catch above.
750
+ */
751
+ export async function razerProbeControlInterface(devices) {
752
+ const candidates = devices.filter((device) => device.vendorId === VENDOR_ID.razer);
753
+ for (const device of candidates) {
754
+ const client = new RazerHidClient(device);
755
+ const tag = `[razer-probe] 0x${device.productId.toString(16).padStart(4, "0")} `
756
+ + device.collections.map((c) => `${(c.usagePage ?? 0).toString(16)}:${c.usage ?? 0}`).join(",");
757
+ try {
758
+ await client.open();
759
+ if (await client.probeReachable()) {
760
+ console.info(`${tag} -> answered`);
761
+ return client;
762
+ }
763
+ console.info(`${tag} -> opened, no reply`);
764
+ }
765
+ catch (error) {
766
+ console.info(`${tag} -> ${error instanceof Error ? error.message : String(error)}`);
767
+ }
768
+ await client.close().catch(() => undefined);
769
+ }
770
+ return null;
771
+ }
772
+ //# sourceMappingURL=hid.js.map