Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NitroPulse

Your keyboard tells you when the work is done.

Flash your Acer Nitro's RGB keyboard the moment a long-running task finishes — then watch it fade smoothly back to exactly the colours it had before.

Built for Claude Code, but the flash engine works from anything that can run a command.

License: MIT Platform Python Hardware


The problem

You kick off a long task, alt-tab into a game, and then... you forget. Ten minutes later you check and it's been finished for nine of them.

Desktop notifications don't help — they're hidden behind a fullscreen game. Sounds get lost under game audio.

But your keyboard is right there, in your peripheral vision, always.

        task finishes
              │
              ▼
   ██████  ██████  ██████  ██████     ← 5 red blinks
    ▓▓▓▓    ▓▓▓▓    ▓▓▓▓    ▓▓▓▓      ← eased gradient back
     ░░░░    ░░░░    ░░░░    ░░░░
   ██████  ██████  ██████  ██████     ← exactly the colours you started with

Highlights

  • Restores your colours perfectly — reads the live per-zone colour off the hardware before flashing, then eases back into it. Your lighting ends up exactly as it was.
  • Nothing runs in the background — no daemon, no service, no tray icon, no memory. The flash is spawned on demand and exits in ~4 seconds.
  • One-time setup, then invisible — a single UAC prompt during install and never again. No console window ever appears.
  • Zero configuration to start — one double-click detects your Python, your paths, and wires everything up.
  • Fully tunable — colours, blink count, timing and fade length live in one small JSON file.

Requirements

Hardware Acer Nitro or Predator laptop with a 4-zone RGB keyboard
Software NitroSense installed and its service running
OS Windows 10 or 11
Python 3.8+, with Add Python to PATH ticked

Why Acer-only? The lighting is driven through Acer's AcerGamingFunction WMI interface. On other hardware the hooks still run, but the flash fails cleanly and logs why.

Set your keyboard to a static colour. While a NitroSense effect (Dynamic, Wave, Breathing…) is running, the flash cannot be seen — see Known limitation.

Quick start

git clone https://github.com/AmmarHSufyan/NitroPulse.git
cd NitroPulse

Then double-click setup.bat and accept the single UAC prompt.

That's the whole install. It detects your Python and paths, installs the one dependency, registers the tasks, wires up the hooks, and fires a test flash so you can see it work.

Finally, restart Claude Code (or open /hooks once) so it picks up the new hooks.

Undo everything at any time with uninstall.bat.

What triggers a flash

Event Fires when Pattern
done The task finishes red blinks → gradient fade back
permission Approval is needed to run something red blinks → fade back
idle You've been waiting on a prompt white → yellow → white → fade back

done is the one that matters while gaming — it fires the instant work stops, not after an idle timeout.

How it works

The Acer WMI interface needs Administrator, but editor hooks run as an ordinary non-admin process. Doing the work directly in the hook would throw a UAC prompt in your face every single time — unusable.

The trick: a Scheduled Task registered with "highest privileges" can be started by a non-admin process without any UAC prompt. So the hook simply asks Windows to start the task.

  task finishes
       │
       ▼
  Stop hook  (non-admin, async — never blocks)
       │
       ▼
  nitro_trigger.py  ──►  schtasks /Run  ──►  ┌──────────────────────────────┐
                                             │  ELEVATED · no prompt · no   │
                                             │  window                      │
                                             │                              │
                                             │  read current zone colours   │
                                             │  blink red ×5                │
                                             │  ease back over 1.2s         │
                                             │  exit                        │
                                             └──────────────────────────────┘

You grant permission once, when the task is registered. Triggering it later just cashes in that stored consent — the same model any installed Windows application uses.

Timing: the hook returns in ~0.3s and is asynchronous, so nothing ever waits on it. The flash begins ~1s later and completes in ~5s.

Why the blinks snap but the return fades

The LED controller drops rapid successive writes, so a fast fade is nearly invisible — the intermediate colours never land. Snapping to full colour and holding it is unmistakable, even in peripheral vision.

The return is the opposite case. At ~1.2 seconds it's slow enough that every step renders, so it reads as a genuine gradient. It's eased with smoothstep, so it decelerates into the final colour instead of stopping dead — that soft landing is what makes it feel finished rather than abrupt.

The fade is also driven by the clock rather than a fixed step count. A WMI write is slow and its cost varies, so a fixed count would make the fade drift longer on a busy machine. Instead each iteration asks how far through it is now — a slow machine simply renders fewer intermediate colours and the fade still takes the time it was asked for.

Configuration

Everything lives in src/nitro_config.json. Changes apply to the next flash — nothing needs restarting.

{
  "zones": [0, 1, 2, 3],
  "base_color": [30, 30, 30],
  "events": {
    "done": {
      "color": [255, 0, 0],
      "blinks": 5,
      "on_seconds": 0.22,
      "off_seconds": 0.14,
      "fade_back_seconds": 1.2
    }
  }
}
Key Meaning
zones Which zones light up, 03 (default: all four)
base_color Fallback [R,G,B], used only if reading a zone's live colour fails
color The [R,G,B] to flash
blinks How many times to blink
on_seconds / off_seconds How long the flash colour / your own colours are held
fade_back_seconds Length of the eased gradient home. Set 0 to snap back instead
Sequence events — flash through several colours

Instead of color + blinks, an event can step through a list:

"idle": {
  "sequence": [[255, 255, 255], [255, 255, 0], [255, 255, 255]],
  "hold_seconds": 0.3,
  "gap_seconds": 0.0,
  "repeat": 1,
  "fade_back_seconds": 0.9
}
Key Meaning
sequence [R,G,B] colours to step through in order
hold_seconds How long each colour is held
gap_seconds If > 0, your own colours flash back between steps (this also skips the closing fade, since the run already ends on your colours)
repeat How many times to play the whole sequence

Known limitation: NitroSense effects

If your keyboard is running a NitroSense lighting effect — Dynamic, Wave, Breathing, Neon — the flash will not be visible. Use a static colour.

An effect runs on the keyboard's onboard controller, which takes ownership of the LEDs and ignores the per-zone RGB register entirely. Writes still succeed, and reads still echo them back — the register and the actual LEDs are simply decoupled while an effect is active.

NitroPulse detects this and tells you plainly in src/nitro.log:

NOTE: NitroSense effect 9 is active. The onboard controller owns the LEDs,
      so this flash will NOT be visible.
      Switch the keyboard to a static colour in NitroSense to see alerts.
Why this isn't worked around (and please don't "fix" it)

Acer exposes Get/SetGamingKBBacklight: a 15-byte struct whose first byte is the effect ID (0 = static, 9 = Dynamic). The setter takes those 15 bytes prefixed with a leading 0, 16 total — the getter returns only the last 15, which is easy to get wrong.

Writing effect ID 0 does successfully seize the LEDs. This was tested: the keyboard becomes fully controllable.

But writing the original struct back restores the mode register without restarting the animation. The keyboard stays frozen on its last colour until NitroSense is toggled by hand. The mode register and the effect engine are separate, and whatever re-arms the engine hasn't been found.

Seizing control is a one-way door. A notification that permanently killed your lighting the first time it fired would be far worse than one you can't see. So NitroPulse only ever reads the effect ID and warns.

Troubleshooting

Everything is logged to src/nitro.log. The flash runs with no console attached, so that file is the only place errors surface.

Symptom Cause
Log says effect N is active A NitroSense effect owns the LEDs — switch to a static colour
Log says Flash failed Not an Acer, or NitroSense isn't running
Log says task … did not start setup.bat hasn't been run on this machine
Log never appears at all The editor hasn't reloaded its hooks — restart it
Worked before, stopped now You moved or renamed the folder. Paths are absolute — just re-run setup.bat

Fire the flashes by hand at any time with scripts\test_flash.bat — no admin needed.

Also in this repo

NitroPulse grew out of a set of Acer Nitro RGB tools, and they're all still here. Use them directly, or as a reference for your own project — the WMI details below are the hard-won part.

Tool What it does
src/keyboard_ui.py GUI colour picker for each of the 4 zones
src/wallpaper_sync.py Matches your keyboard to your desktop wallpaper, with a watch mode
src/screen_sync.py Real-time ambient lighting — samples your screen with DXcam and drives the keyboard at ~10fps
python src/keyboard_ui.py                          # GUI
python src/wallpaper_sync.py --watch --interval 10 # follow the wallpaper
python src/screen_sync.py                          # ambient screen sync

All of these need Administrator. The scripts\ folder has self-elevating launchers.

The WMI details — everything you need to control an Acer Nitro keyboard yourself

Interface

Namespace root\WMI
Class AcerGamingFunction
Method SetGamingRgbKb(gmInput: UInt64)

Colour packing — zone and colour go into one 32-bit value:

value = (B << 24) | (G << 16) | (R << 8) | zone_id
Bits Field
24–31 Blue
16–23 Green
8–15 Red
0–7 Zone ID

Zone IDs are powers of two, not sequential:

Zone Position ID
A Far left 1
B Centre-left 2
C Centre-right 4
D Far right 8

IDs 0, 3, 5, 6 map to no physical zone.

Reading colours back: GetGamingRgbKb(zone_id) returns a UInt64 packed identically, so R = (v >> 8) & 0xFF, G = (v >> 16) & 0xFF, B = (v >> 24) & 0xFF.

Other methodsSetGamingLEDColor, SetGamingKBBacklight, SetGamingLED, SetGamingLEDBehavior all exist. They do not reliably set per-zone colours on this hardware; SetGamingRgbKb is the one that works.

Admin is mandatory. Every one of these calls fails without elevation.

Is rapid colour switching bad for the LEDs?

No.

  • LEDs switch in nanoseconds and are designed for PWM cycling at thousands of Hz
  • Rated lifespan is 50,000–100,000 hours — 17 to 34 years at 8 hours a day
  • Colour changes don't degrade LEDs. Only heat and overcurrent do, and both are hardware-limited by the onboard controller
  • Your laptop will be replaced long before the LEDs dim

Practical update ceiling is the WMI → Embedded Controller → I²C chain, not the LEDs. NitroSense's own animations run at 15–30fps internally; 10–20 updates/sec per zone is comfortably safe.

What to actually avoid: flooding WMI in a tight loop with no sleep. Not for the LEDs' sake — it burns CPU through WmiPrvSE.exe and can slow the Embedded Controller's other duties like fan control.

Using this?

I'd genuinely love to know. If NitroPulse is running on your machine, or you've built something on top of these WMI details, open an issue and tell me — what you're using it for, what hardware, what you changed.

It's not a requirement, just a request. Seeing where your work ends up is the best part of putting it out there.

Bug reports and pull requests are equally welcome.

License

MIT © Ammar Hassan Sufyan

Use it, change it, ship it commercially — just keep the copyright notice.

About

Flash your Acer Nitro's RGB keyboard when a long task finishes, then fade smoothly back to your original colours. A Claude Code hook with no background process and no repeated UAC prompts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages