Cross-platform Go CLI library for programmatic/agent-friendly control of VIA-compatible keyboard RGB lighting (starting with Wobkey Impact 80).
cmd/ # Cobra-based CLI entrypoints
internal/device/ # HID device discovery (VID/PID matching)
internal/via/ # VIA protocol implementation + LED subsystem
internal/rgb/ # Effects, values, color definitions
keyboards.json # VID/PID database + keyboard metadata
wobkey keyboard info
wobkey rgb effect breathing
wobkey rgb effect rainbow_moving_chevron
wobkey rgb effect rainbow_moving_chevron --zone backlight
wobkey rgb brightness <val> # 0-255
wobkey rgb speed <val> # 0-255
wobkey rgb color <hex> # Six hexadecimal digits
wobkey rgb mode <index> # Raw zone-specific effect ID
wobkey rgb enable
wobkey rgb disable
wobkey rgb info
rgb has a persistent --zone flag accepting logo, backlight, or side.
Without --zone, commands target Logo, Backlight, and Side in that order.
With --zone, commands target exactly one zone. Unsupported default targets
are skipped with a stderr warning; unsupported explicit-zone effects and
unknown names fail before the device is opened.
Logo and Side use IDs 0–6: none, wave, fixed_wave, spectrum,
breathing, light, and shutdown.
Backlight uses the complete ID 0–45 family:
none, solid_color, alphas_mods, gradient_up_down, gradient_left_right,
breathing, band_sat, band_val, band_pinwheel_sat, band_pinwheel_val,
band_spiral_sat, band_spiral_val, cycle_all, cycle_left_right,
cycle_up_down, cycle_out_in, cycle_out_in_dual, rainbow_moving_chevron,
cycle_pinwheel, cycle_spiral, dual_beacon, rainbow_beacon,
rainbow_pinwheels, flower_blooming, raindrops, jellybean_raindrops,
hue_breathing, hue_pendulum, hue_wave, pixel_flow, digital_rain,
solid_reactive, solid_reactive_wide, solid_reactive_multiwide,
solid_reactive_cross, solid_reactive_multicross, solid_reactive_nexus,
solid_reactive_multinexus, splash, multisplash, solid_splash,
solid_multisplash, starlight, starlight_dual_hue, starlight_dual_sat,
riverflow
ID 39 is multisplash; ID 41 is solid_multisplash. Supported aliases are
off → none, breathe → breathing, rainbow (zone-dependent),
rainbow_wave (Logo/Side), solid (zone-dependent), and legacy static.
Do not assume a numeric effect ID is valid on every zone; use the zone-aware
name resolver or deliberately use rgb mode as a raw escape hatch.
rgb info emits a zones array containing each selected zone's channel,
enabled state, effect name and ID, brightness, speed, and color. The
top-level summary comes from the first selected zone. A failed zone includes
an error field while other zone results remain available; the command
prints JSON before returning non-zero.
00x07 — Custom set value0x08 — Custom get value0x02 logo, 0x03 backlight, 0x04 side0x01, effect 0x02, speed 0x03, color 0x04| Keyboard | VID | PID |
|---|---|---|
| Wobkey Rainy 75 | 0x6666 | 0x0001 |
| Wobkey Impact 80 | 0x36B0 | 0x309F |
keyboards.json maps VID+PID → VIA protocol config + RGB layout metadata.
github.com/sstallion/go-hid for HID accessgithub.com/spf13/cobra for CLI frameworkgopls — Go Language Server, built into Go toolchain (LSP, diagnostics, go-to-def, rename, references)gofmt / goimports — formatting. goimports adds/removes imports automatically. Always run before committing.go test — built-in test framework. Tests live in *_test.go files alongside source.go vet — static analysis. Run before committing.go build — compiles without installing. Fast, cached.go mod tidy — adds missing deps, removes unused ones. Run after every import change.gofmt or goimports. Tabs for indentation, no line-length limit but avoid uncomfortably long lines.MixedCaps / mixedCaps, no underscores. Short local variables (i, c, r). Descriptive for globals.URL, ID, HTTP → appID, urlPony, ServeHTTP.// Package rgb provides RGB effect handling.panic for normal control flow.go
if err != nil {
return err
}
// normal code
- Error strings: lowercase, no period — fmt.Errorf("device not found").
- Do not discard errors with _.
### Interfaces & Types
- Define interfaces in the consumer package, not the producer.
- Use pointer receivers when in doubt (mutation, large structs, sync fields).
- Return concrete types from constructors — let consumers mock if needed.
- Empty slices: var t []string (nil), not t := []string{} (non-nil), unless JSON encoding requires [].
### Concurrency
- Pass context.Context as first parameter. Never store in structs.
- Clear goroutine lifetimes — document when/why they exit. Avoid leaks via channels.
- Prefer synchronous functions — callers can add concurrency, not remove it.
### Testing
- Table-driven tests preferred for multiple cases.
- Failure messages: t.Errorf("Effect(%q) = %d, want %d", input, got, want).
- Use *_test.go files. Test package should match source package (internal tests).
- Example functions (func Example...) double as docs and tests.
### Modules & Packages
- Module path: github.com/<user>/<project> (when published).
- Package names: short, single-word, lowercase. No util, common, api, types.
- Zero values should be useful (e.g. bytes.Buffer, sync.Mutex).
## graphify
This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
When the user types /graphify, use the installed graphify skill or instructions before doing anything else.
Rules:
- For codebase questions, first run graphify query "<question>" when graphify-out/graph.json exists. Use graphify path "<A>" "<B>" for relationships and graphify explain "<concept>" for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify. Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run graphify update . to keep the graph current (AST-only, no API cost).