diff --git a/src/app/OverviewPage.tsx b/src/app/OverviewPage.tsx index ad0cb314..4eb74fbb 100644 --- a/src/app/OverviewPage.tsx +++ b/src/app/OverviewPage.tsx @@ -44,6 +44,7 @@ import { OnboardProfileCard, PulsarProCard, SignalCard, + DongleLedCard, SleepCard, DpiLightingCard, } from "./cards/AdvancedCards"; @@ -463,6 +464,7 @@ export function Workspace({ show(has.lowPower, ["advanced"]) ? : null, show(has.finalmouse, ["advanced"]) ? : null, show(has.incott, ["advanced"]) ? : null, + device && show(has.dongleLed, ["advanced"]) ? : null, show(has.atkProfile, ["profiles"]) ? : null, device && show(has.atkReceiver, ["advanced"]) ? : null, show(has.atkF1Dongle, ["advanced"]) ? : null, diff --git a/src/app/cards/AdvancedCards.tsx b/src/app/cards/AdvancedCards.tsx index a50f9d09..52bfad59 100644 --- a/src/app/cards/AdvancedCards.tsx +++ b/src/app/cards/AdvancedCards.tsx @@ -134,6 +134,26 @@ export function SignalCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNo ); } +export function DongleLedCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNode { + const status = snapshot.status; + if (!status) return null; + const locale = snapshot.preferences.locale; + const staged = snapshot.pending.keys.includes("dongle-led"); + return ( +
+
+

WIRELESS

{t(locale, "adv.dongleLed")}

+ control.toggleDongleLed()} + /> +
+
+ ); +} + export function DebounceCard({ snapshot }: { snapshot: ControlSnapshot }): ReactNode { const status = snapshot.status; if (!status) return null; diff --git a/src/app/cards/availability.ts b/src/app/cards/availability.ts index 6489805f..237a6dd1 100644 --- a/src/app/cards/availability.ts +++ b/src/app/cards/availability.ts @@ -20,6 +20,7 @@ export interface CardAvailability { teevolutionDpiLighting: boolean; finalmouse: boolean; incott: boolean; + dongleLed: boolean; eggFilter: boolean; eggSpdt: boolean; eggPolling: boolean; @@ -60,6 +61,7 @@ const NOTHING: CardAvailability = { teevolutionDpiLighting: false, finalmouse: false, incott: false, + dongleLed: false, eggFilter: false, eggSpdt: false, eggPolling: false, @@ -161,6 +163,8 @@ export function cardAvailability(snapshot: ControlSnapshot): CardAvailability { // actually reported. incott: host && (status.incottFireKeyTimes != null || status.incottReceiverLedMode != null), + // Drivers report a boolean only for receivers that answer the LED command. + dongleLed: typeof status.dongleLedEnabled === "boolean", eggFilter: eggs, eggSpdt: eggs, // The 4K v2 reports no divider: its polling byte is a vendor enum. diff --git a/src/device/controller.ts b/src/device/controller.ts index 9562a008..3c5ec76c 100644 --- a/src/device/controller.ts +++ b/src/device/controller.ts @@ -3789,7 +3789,7 @@ export function applyLightforceSwitchMode(mode: NonNullable { - const client = pulsarClient(); + const client = activeSettingsClient(); if (!client) throw new Error(st("ctl.receiverGone")); if (!("setDongleLed" in client)) { throw new Error(st("ctl.noLedControl")); diff --git a/src/preview-fixtures.ts b/src/preview-fixtures.ts index b7f29056..33f5a39b 100644 --- a/src/preview-fixtures.ts +++ b/src/preview-fixtures.ts @@ -213,6 +213,7 @@ const ATTACKSHARK: MouseStatus = { brand: "Attack Shark" as MouseStatus["brand"], name: "Attack Shark R5 Ultra", ui: { family: "attack-shark", hideUnsupportedPollingRates: true, forceShowBattery: true }, + dongleLedEnabled: false, batteryPercent: 82, batteryState: "Discharging", dpi: 1600,