diff --git a/src/device/traits.ts b/src/device/traits.ts index 00159e46..f79a719f 100644 --- a/src/device/traits.ts +++ b/src/device/traits.ts @@ -83,6 +83,9 @@ const BY_FAMILY: Readonly>> = { // it publishes its own sleep and debounce option lists. "mchose-v3": { advancedSection: true, sleep: true, debounce: true }, dareu: { advancedSection: true, sleep: true }, + // Amkette's XS firmware stores auto sleep as whole minutes and publishes its + // own list through getSleepOptions. + "amkette-xs": { advancedSection: true, sleep: true }, // The Model O2 Pro 4K/8K writes a debounce block and a motion sync flag; it // has no sleep command wired in, so no sleep flag. "glorious-core2": { advancedSection: true, debounce: true }, diff --git a/src/shared/device-images.test.ts b/src/shared/device-images.test.ts index 074ce838..ae8d547c 100644 --- a/src/shared/device-images.test.ts +++ b/src/shared/device-images.test.ts @@ -40,6 +40,16 @@ test("PRO X3 SUPERSTRIKE gets its own render, not the X2's", () => { assert.equal(deviceImage(null, "Attack Shark X3"), CDN + "attackshark-x3.png"); }); +test("Amkette XS Flow 2 Speed resolves by model name, never from the shared ids", () => { + assert.equal(deviceImage(dev(0x04f3, 0x026e), "XS Flow 2 Speed"), CDN + "amkette-xs-flow-2-speed.png"); + assert.equal(deviceImage(dev(0x04f3, 0x026d), "XS Flow 2 Speed"), CDN + "amkette-xs-flow-2-speed.png"); + assert.equal(deviceImage(null, "Amkette XS Flow 2 Speed"), CDN + "amkette-xs-flow-2-speed.png"); + // Without a model name the mouse could be any XS model, or share ids with a + // Ryunix Kyu Pro MX1, so it keeps the placeholder. + assert.equal(deviceImage(dev(0x04f3, 0x026e), "XS series"), CDN + "unknown-device.png"); + assert.equal(deviceImage(dev(0x04f3, 0x026e), "Kyu Pro MX1"), CDN + "unknown-device.png"); +}); + test("Razer Orochi V2 resolves by name", () => { assert.equal(deviceImage(dev(0x1532, 0x0094), "Razer Orochi V2"), CDN + "razer-orochi-v2.png"); }); diff --git a/src/shared/device-images.ts b/src/shared/device-images.ts index 413c18dd..bfdffe69 100644 --- a/src/shared/device-images.ts +++ b/src/shared/device-images.ts @@ -65,6 +65,11 @@ function computeDeviceImageFilename(displayName: string): string { } if (/\br5\s*ultra\b/i.test(displayName)) return "attackshark-r5-ultra.png"; if (/\bfloat\s*88\b/i.test(displayName)) return "ipi-float-88.png"; + // Amkette XS Flow 2 Speed: the driver reports this model name only once the + // mouse names itself ("XS Speed" over USB, "XS speed N" over Bluetooth). Its + // nameless fallback, "XS series", could be any XS model, so it keeps the + // placeholder rather than guess. + if (/\bxs\s*flow\s*2\s*speed\b/i.test(displayName)) return "amkette-xs-flow-2-speed.png"; if (/\batk\s*zero\b/i.test(displayName)) return "atk-zero.png"; // R2 shares PID 0x402D with the Lingbao M5 Pro, so it can only be told apart // by the name the gearhub driver reads back from the device id.