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,