Skip to content

Repository files navigation

noirgear-tools · GMK87 HUD

Turn the little screen on the abandoned Noirgear GMK87 keyboard (also sold as the Zuoya GMK87 — same board, USB 320F:5055) into something useful: a live price ticker, a Claude Code session monitor, and a clock that never drifts — with a web manager and a system-tray app to drive it all. Windows-native: talks through the stock HID driver, no Zadig / libusb driver replacement needed.

web UI

What it does

  • Price card (slot 1): BTC & ETH from Hyperliquid with 24h change, plus a USD forex rate (ECB via Frankfurter), re-rendered every minute.
  • Claude Code card (slot 2): live status of every Claude Code session on the machine — name, busy/idle, project, age — straight from ~/.claude/sessions/.
  • Auto-switch: the screen alternates between the two cards every minute.
  • No clock drift, ever: every update rewrites the keyboard's BCD clock in the same config packet.
  • Custom images/GIFs: drop any image into the web UI; it's cover-cropped to 240×135 and uploaded (GIFs resampled to ≤30 frames).
  • Web manager at http://127.0.0.1:8091 + a tray app with screen switching and session statuses in the menu.

Install

Requires Python 3.10+ on Windows.

git clone <this repo> ; cd gmk87
pip install -r requirements.txt
.\install.ps1          # registers the server task + tray autostart, starts both

install.ps1 -Uninstall removes the scheduled task and Startup shortcut.

Configuration

config.json (created next to the scripts on first change; all keys optional):

key default meaning
rotate true auto-switch between slot 1 and slot 2
slot2 "claude" what slot 2 holds: "claude" or "custom"
interval 60 update tick in seconds (see flash wear below)
fx_to "IDR" forex quote currency on the price card

Flash wear: the display images almost certainly live in SPI flash. At the default 60 s tick with only the visible slot rewritten, that's ~720 writes/day/slot — fine for years on typical parts, but raise interval if you want to be conservative.

Architecture

file role
gmk87_time.py HID protocol core + clock sync CLI + cross-process device lock
gmk87_display.py price card renderer/uploader CLI (--preview, --show)
gmk87_claude.py Claude Code session scanner + status card renderer
ui.py / ui.html local web manager + the per-minute updater thread
tray.pyw system-tray app (pystray), drives everything via the HTTP API

All device access goes through a lockfile (.device.lock) so the tray, web UI, and updater never interleave HID commands.

Protocol notes (reverse-engineered)

The Noirgear/Zuoya GMK87 is USB 320F:5055; commands go to the vendor HID interface (MI_03) as 64-byte reports:

byte 0     report ID 0x04
bytes 1-2  checksum = sum(bytes 3..63), little-endian
byte 3     command ID
byte 4     payload length
bytes 5-7  position/offset, little-endian
bytes 8+   payload (≤56 bytes)

Each command is acked on the IN endpoint (echoes report ID + checksum). Commands: 0x01 open, 0x02 commit (wait ~100 ms first), 0x05 read config, 0x06 write config, 0x23 begin image session (exactly one per upload), 0x21 image data.

The 48-byte config (read via 0x05, written via 0x06):

offset field
28 LED mode
33 screen mode: 0 clock · 1 image slot 1 · 2 slot 2
34 slot 1 frame count
35–41 clock, BCD: sec min hour weekday(raw) day month yy
43–44 animation frame delay, ms, little-endian
46 slot 2 frame count

Frames are 240×135 RGB565 big-endian, each padded to 0x10000 bytes; slot 2 starts at frames1 × 0x10000. (Not 0x8000 — half-frame offsets show erased flash as a white striped block.)

Credits

Protocol reverse-engineering by the community: jeisinger's gist, GMK87TimeSetterPy, gmk87-node.

MIT licensed.

About

Tools for the Noirgear (Zuoya) GMK87 keyboard: reverse-engineered display driver, price ticker + Claude Code monitor on the LCD, drift-free clock, web manager, tray app

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages