|
|
@@ -89,13 +89,12 @@ re-run the command whenever commands or flags change.
|
|
|
```
|
|
|
|
|
|
`--zone` accepts a VIA lighting channel: `backlight`, `rgblight`, `rgb_matrix`,
|
|
|
-`audio` or `led_matrix`, and may be given on any subcommand. The name follows
|
|
|
-from the channel number, so it works on every QMK keyboard. A board listed in
|
|
|
-`keyboards.json` may give a channel a display name, and that name is accepted
|
|
|
-too — the Impact 80 calls channels 2, 3 and 4 `logo`, `backlight` and `side`.
|
|
|
-Where a display name would be the subsystem name of a different channel the
|
|
|
-keyboard has, the board is refused rather than a command being sent to the wrong
|
|
|
-channel.
|
|
|
+`audio` or `led_matrix`, and may be given on any subcommand. That name follows
|
|
|
+from the channel number, so it works on every QMK keyboard. A board's definition
|
|
|
+file may name a channel differently, and that name is accepted too, ignoring
|
|
|
+case — the Impact 80 calls channels 2, 3 and 4 `logo`, `Backlight` and `side`.
|
|
|
+Where a name would be the subsystem name of a different channel the keyboard has,
|
|
|
+the board is refused rather than a command being sent to the wrong channel.
|
|
|
|
|
|
`--zone` is not remembered between runs: repeat it on every invocation. Without
|
|
|
`--zone`, commands target every channel the keyboard reports, in channel order.
|
|
|
@@ -193,14 +192,14 @@ a hex triple.
|
|
|
## Effect Names Are Per Board
|
|
|
|
|
|
The keyboard holds effect numbers, not names, so `effect <name>` needs a catalog
|
|
|
-and the tool has one for the Impact 80. Its 46 backlight names are the QMK
|
|
|
+and the tool has one for the Impact 80. Its first 46 backlight names are the QMK
|
|
|
`rgb_matrix_effects.inc` of the VIA era, transcribed from that board's vendor VIA
|
|
|
definition, and the 7 `logo` and 7 `side` names are that same definition, whose
|
|
|
-dropdowns read `fixed wave` and `breathe`; the compatibility aliases are the
|
|
|
-tool's own.
|
|
|
+dropdowns read `fixed wave` and `breathe`. The 47th backlight name, `freeze` at
|
|
|
+ID 46, is the tool's own, and so are the compatibility aliases.
|
|
|
|
|
|
`effect` writes the ID and reads the register back, because a board can refuse
|
|
|
-one. All 46 backlight IDs are taken, and so are the `logo` and `side` IDs from 1
|
|
|
+one. All 47 backlight IDs are taken, and so are the `logo` and `side` IDs from 1
|
|
|
to 6. ID 0 is not: on those two channels the firmware reads it as "lighting
|
|
|
off" and leaves the mode register where it was, so `effect none --zone logo`
|
|
|
leaves the previous effect running and says so.
|
|
|
@@ -211,9 +210,33 @@ Effect logo "wave" (1) (requested "none" (0))
|
|
|
```
|
|
|
|
|
|
The read-back proves an ID is one the keyboard takes, not that the name labels
|
|
|
-it correctly; the names come from the vendor definition. The board takes 47 IDs
|
|
|
-on the backlight channel, 0 to 46, of which this catalog names 46 — ID 46 exists,
|
|
|
-is reported as `unknown`, and is set with `mode 46`.
|
|
|
+it correctly. The board takes 47 IDs on the backlight channel, 0 to 46, and
|
|
|
+anything above 46 is clamped down to it. 46 of those names come from the vendor
|
|
|
+definition, which ends at 45; the 47th is the tool's own.
|
|
|
+
|
|
|
+### `freeze` Is the Tool's Name, Not Wobkey's
|
|
|
+
|
|
|
+Set after a moving effect, ID 46 leaves the LEDs on the pattern they are
|
|
|
+currently showing and stops the animation. It is not another effect with a
|
|
|
+pattern of its own — `cycle_left_right` frozen still looks like a standing
|
|
|
+rainbow, because that is what it froze:
|
|
|
+
|
|
|
+```
|
|
|
+$ qmk-rgb-tool effect gradient_left_right --zone backlight
|
|
|
+Effect set to "gradient_left_right"
|
|
|
+$ qmk-rgb-tool effect freeze --zone backlight
|
|
|
+Effect set to "freeze"
|
|
|
+$ qmk-rgb-tool info | jq '.zones[] | select(.zone == "backlight")'
|
|
|
+ "effect": "freeze", "effectId": 46
|
|
|
+```
|
|
|
+
|
|
|
+Wobkey publishes no name for it. The vendor's
|
|
|
+[VIA definition JSON](https://drive.wobkey.com/f/d/6BtO/Impact_80.JSON) ends its
|
|
|
+backlight dropdown at `riverflow` (ID 45) and contains no word for pause, stop,
|
|
|
+freeze or hold, which means VIA itself cannot set this ID from its dropdown —
|
|
|
+a raw `mode 46` is the only way to reach it. `freeze` is the tool's word for the
|
|
|
+measured behaviour, and it is the only name in the catalog that Wobkey did not
|
|
|
+write down.
|
|
|
|
|
|
A keyboard without a catalog is still driven: `brightness`, `speed`, `color`,
|
|
|
`mode <index>` and `info` all work, because none of them needs a name. Four
|
|
|
@@ -235,6 +258,7 @@ Warning: this keyboard has no effect catalog, so the profile records effect "unk
|
|
|
|
|
|
`enable` needs it because it has to choose an effect to turn a channel on.
|
|
|
`load` applies nothing without it, because the profile stores effect names.
|
|
|
+
|
|
|
`save` still writes the profile, but every effect is recorded as `unknown`, so
|
|
|
that file cannot restore an effect later.
|
|
|
|
|
|
@@ -248,6 +272,66 @@ $ qmk-rgb-tool effect nonsense --zone rgb_matrix
|
|
|
Error: unknown effect: nonsense
|
|
|
```
|
|
|
|
|
|
+### Where Effect Names Can Come From
|
|
|
+
|
|
|
+VIA's own keyboard collection, [`the-via/keyboards`](https://github.com/the-via/keyboards),
|
|
|
+is the only bulk source, and it is more useful than it looks. It holds 2029
|
|
|
+definitions under `v3/`, one per unique `vendorId`/`productId` pair, and not one
|
|
|
+of them has a `lighting` or an `effects` key — a keyboard's effect names are not
|
|
|
+where you would look for them.
|
|
|
+
|
|
|
+They are in `menus`. A definition either names one of VIA's built-in lighting
|
|
|
+menus, or spells out its own UI as dropdowns, and a dropdown for
|
|
|
+`id_qmk_rgb_matrix_effect` is a board's effect list, in ID order. Measured over
|
|
|
+the collection:
|
|
|
+
|
|
|
+| | Boards |
|
|
|
+|---|---|
|
|
|
+| name VIA's built-in `qmk_rgb_matrix` menu | 179 |
|
|
|
+| ship their own `id_qmk_rgb_matrix_effect` dropdown | 163 |
|
|
|
+| of those, an exact prefix of the built-in list | 10 |
|
|
|
+
|
|
|
+The built-in list lives in
|
|
|
+[`the-via/reader/src/common-menus/qmk_rgb_matrix.ts`](https://github.com/the-via/reader/blob/master/src/common-menus/qmk_rgb_matrix.ts)
|
|
|
+and has 45 names, `All Off` through `Solid Multi Splash`, at IDs 0 to 44. A board
|
|
|
+that uses it needs no work at all: the names and the IDs are the built-in ones.
|
|
|
+
|
|
|
+The other 153 deviate, and not only in how many effects they compile in. MonsGeek's
|
|
|
+M1 uses QMK's enum identifiers and its own 19: `SOLID_COLOR`, `BREATHING`,
|
|
|
+`CYCLE_ALL`, `TYPING_HEATMAP`, `MATRIX_MULTISPLASH`. Redragon's K667 ships 14 in
|
|
|
+yet another order, with `Pixel Fractal` already at ID 13. Only 11 of the 163
|
|
|
+write names as enum identifiers; 152 use display labels, so the same effect arrives
|
|
|
+as `All Off`, `NONE` or `00.NONE` depending on the vendor.
|
|
|
+
|
|
|
+That is why a catalog cannot be defaulted per subsystem, and it is the same
|
|
|
+reason the Impact 80 needs its own. Its backlight channel reorders IDs 15 to 17 —
|
|
|
+`cycle_out_in`, `cycle_out_in_dual`, then `rainbow_moving_chevron`, where the
|
|
|
+built-in list has `rainbow_moving_chevron` first — and its list runs to 45 because
|
|
|
+it adds `flower_blooming`, `starlight`, `starlight_dual_hue`, `starlight_dual_sat`
|
|
|
+and `riverflow`, none of which the built-in list has. A board can also be at an
|
|
|
+ID the built-in list does not name at all, and the source for that name is the
|
|
|
+board, not VIA:
|
|
|
+
|
|
|
+| What | Where |
|
|
|
+|------|-------|
|
|
|
+| VIA definition JSON, the origin of all three Impact 80 effect lists | [`Impact_80.JSON`](https://drive.wobkey.com/f/d/6BtO/Impact_80.JSON) |
|
|
|
+| VIA firmware image | [`impact_80.bin`](https://drive.wobkey.com/f/d/9yH8/impact_80.bin) |
|
|
|
+| Update instructions, vendor's warning against updating a working board | [Driver & Firmware page](https://wiki.wobkey.com/en/Products/PMOKEY-Impact-80/Driver-Firmware) |
|
|
|
+
|
|
|
+The Impact 80 is not in VIA's collection — there is no Wobkey entry under `v3/`
|
|
|
+— so its names are reachable only through that vendor page.
|
|
|
+
|
|
|
+[OpenRGB](https://codeberg.org/OpenRGB/OpenRGB) also drives QMK keyboards over
|
|
|
+the very same raw HID interface, but as `Controllers/QMKController/` including a
|
|
|
+Vial and a Keychron variant: C++ device code, no JSON, no effect tables. It is a
|
|
|
+good cross-check for which VID/PID belongs to which model, and no help for effect
|
|
|
+names. The project has left GitHub; the mirror is
|
|
|
+[`CalcProgrammer1/OpenRGB`](https://github.com/CalcProgrammer1/OpenRGB).
|
|
|
+
|
|
|
+So a catalog is transcribed per board, and the collection above says where to
|
|
|
+look for each board's list, but which IDs a board takes is still confirmed
|
|
|
+against its register. That part cannot be automated from any database.
|
|
|
+
|
|
|
## Brightness and Speed Are Not Applied Verbatim
|
|
|
|
|
|
The keyboard's firmware transforms these values per channel, so the accepted
|
|
|
@@ -295,7 +379,7 @@ failure, but the summary line always states the value that was actually applied.
|
|
|
- **Cross-platform** — Linux, macOS, Windows via [hidapi](https://github.com/libusb/hidapi)
|
|
|
- **Channel-aware effects** — per-channel effect control with per-board effect catalogs, so a board without one is still driven
|
|
|
- **Profile system** — save, load, list, and delete RGB presets as JSON files in `profiles/`
|
|
|
-- **46 backlight effect names** — the Impact 80's VIA-era QMK family, mapped to zone-aware names; the board takes 47 IDs, the highest has no name
|
|
|
+- **47 backlight effect names** — the Impact 80's VIA-era QMK family plus `freeze` for ID 46, which the vendor does not name
|
|
|
- **Compatibility aliases** — `off`, `breathe`, `rainbow`, `rainbow_wave`, `solid`, `static` resolve to correct effect IDs per channel
|
|
|
- **Reactive & splash effects** — honor `color` and `speed` for key-press illumination
|
|
|
- **Machine-parseable output** — JSON for `keyboard info`, `info`, `list`, and `effect --list`
|
|
|
@@ -310,9 +394,9 @@ failure, but the summary line always states the value that was actually applied.
|
|
|
| Backlight | `backlight` | 3 | 0–45 |
|
|
|
| Side | `side` | 4 | 0–6 |
|
|
|
|
|
|
-Those three display names live in `keyboards.json`; the QMK subsystem names for
|
|
|
-the same channels are `rgblight`, `rgb_matrix` and `audio`, and both spellings
|
|
|
-work on this board.
|
|
|
+Those three display names come from the board's definition file, in the sub-menu
|
|
|
+it puts each channel under. The QMK subsystem names for the same channels are
|
|
|
+`rgblight`, `rgb_matrix` and `audio`, and both spellings work on this board.
|
|
|
|
|
|
Logo and Side share this complete effect family:
|
|
|
|
|
|
@@ -353,6 +437,7 @@ Backlight uses the complete Impact 80 catalog:
|
|
|
| 20 | `dual_beacon` | 43 | `starlight_dual_hue` |
|
|
|
| 21 | `rainbow_beacon` | 44 | `starlight_dual_sat` |
|
|
|
| 22 | `rainbow_pinwheels` | 45 | `riverflow` |
|
|
|
+| | | 46 | `freeze` (tool's name) |
|
|
|
|
|
|
## Effect Behavior
|
|
|
|
|
|
@@ -381,6 +466,23 @@ for Logo/Side and `rainbow_moving_chevron` for Backlight, `rainbow_wave` →
|
|
|
for Backlight. The legacy `static` alias remains accepted as a compatibility
|
|
|
alias for `solid`.
|
|
|
|
|
|
+The aliases belong to the tool, not to a board, so they work for every catalog —
|
|
|
+including one read from a definition file. A name resolves to itself, to the name
|
|
|
+it is an alias of, or to an alias of it, and spaces and underscores are the same
|
|
|
+character as far as lookup is concerned. So on this board all four of these select
|
|
|
+the same effect:
|
|
|
+
|
|
|
+```
|
|
|
+qmk-rgb-tool effect breathing --zone logo # the compiled-in catalog's spelling
|
|
|
+qmk-rgb-tool effect breathe --zone logo # the vendor definition's spelling
|
|
|
+qmk-rgb-tool effect fixed_wave --zone logo # the tool's spelling
|
|
|
+qmk-rgb-tool effect "fixed wave" --zone logo # the vendor definition's spelling
|
|
|
+```
|
|
|
+
|
|
|
+`info` and `effect --list` report the spelling of whichever source is in use,
|
|
|
+which is why the list may show `fixed wave` where an earlier version of this
|
|
|
+document wrote `fixed_wave`. The ID beside the name is the same in both.
|
|
|
+
|
|
|
The effect catalog follows the QMK RGB Matrix firmware of the VIA era, not
|
|
|
current QMK master: the two lists differ in length, in naming and in numbering
|
|
|
(`alpha_mods` against `alphas_mods`, `colorband_sat` against `band_sat`, and
|
|
|
@@ -463,59 +565,35 @@ Any keyboard running QMK is detected automatically via the QMK Raw HID
|
|
|
signature (Usage Page 0xFF60, Usage 0x61). No manual configuration required.
|
|
|
|
|
|
That signature says nothing about lighting, so a keyboard with no VIA lighting
|
|
|
-channel at all is listed here and then fails every command that opens it with
|
|
|
+channel at all is listed and then fails every command that opens it with
|
|
|
`this keyboard exposes no VIA lighting channels`.
|
|
|
|
|
|
-`keyboards.json` is looked for next to the executable first, then in the current
|
|
|
-working directory. `go install` puts the binary in `$GOPATH/bin` while the file
|
|
|
-stays in the repository, so run the tool from the repository, put a copy of the
|
|
|
-file next to the binary, or name the zones by their QMK subsystem. Without the
|
|
|
-file the tool still works: model names, the `logo`/`backlight`/`side` display
|
|
|
-names and the effect catalog are what it supplies.
|
|
|
-
|
|
|
-`keyboards.json` is optional. When present it supplies a name for the model and,
|
|
|
-per entry, a display name per channel: `keyboard info` reports `"known": true`
|
|
|
-for models listed there and `"known": false` for every other QMK keyboard. Both
|
|
|
-are fully controllable — `known` describes the name lookup, not compatibility.
|
|
|
-Deleting or corrupting the file costs names only: the channel vocabulary
|
|
|
-survives, because the QMK subsystem name follows from the channel number.
|
|
|
-
|
|
|
-Two models are listed in `keyboards.json`:
|
|
|
-
|
|
|
-| Keyboard | VID | PID |
|
|
|
-|-----------------|--------|--------|
|
|
|
-| Wobkey Rainy 75 | 0x6666 | 0x0001 |
|
|
|
-| Wobkey Impact 80 | 0x36B0 | 0x309F |
|
|
|
-
|
|
|
-### Impact 80 Sources
|
|
|
-
|
|
|
-The vendor's
|
|
|
-[Driver & Firmware page](https://wiki.wobkey.com/en/Products/PMOKEY-Impact-80/Driver-Firmware)
|
|
|
-is where the effect catalog in `internal/rgb/impact80.go` comes from. It is the
|
|
|
-authority on the wire format, and it settles two things this tool cannot:
|
|
|
-
|
|
|
-- The board must be in **wired mode** for VIA to see it. On 2.4G or Bluetooth
|
|
|
- the keyboard is not detected at all, so a tool that finds no keyboard is
|
|
|
- consistent with the hardware being on wireless. Changes made in wired mode are
|
|
|
- saved to onboard memory and still apply in wireless mode.
|
|
|
-- The vendor ships **two firmware variants**. The VIA variant is what this tool
|
|
|
- drives. The proprietary variant offers the advanced lighting (music rhythm,
|
|
|
- SyncLight) but **disables VIA**, so a board flashed with it exposes no QMK Raw
|
|
|
- HID interface and is invisible to the tool, not unsupported by it. The audio
|
|
|
- channel this board reports as `side` is where that lighting lives.
|
|
|
-
|
|
|
-The page links the sources used here directly:
|
|
|
+A definition also names its channels, and those names take precedence over the
|
|
|
+ones in `keyboards.json`, because they are the names VIA shows — so the tool and
|
|
|
+VIA call a channel the same thing, and a name is matched ignoring case. On a board whose
|
|
|
+definition writes `Backlight`, all of `--zone backlight`, `--zone Backlight` and
|
|
|
+`--zone rgb_matrix` reach the same channel: the label is the name, the subsystem
|
|
|
+stays accepted because it follows from the channel number. `qmk-rgb-tool
|
|
|
+definition list` prints the label beside the subsystem it stands for:
|
|
|
+
|
|
|
+```
|
|
|
+Impact 80 (0x36B0/0x309F) definitions/impact80.json
|
|
|
+ logo rgblight 7 effects
|
|
|
+ Backlight rgb_matrix 46 effects
|
|
|
+ side audio 7 effects
|
|
|
+```
|
|
|
+
|
|
|
+`keyboards.json` still supplies the model name and can name a channel as well.
|
|
|
+It is looked for next to the executable first, then in the current working
|
|
|
+directory; without the file the tool still works, and a board is shown as
|
|
|
+`unknown model` when nothing else names it.
|
|
|
+
|
|
|
+```console
|
|
|
+$ qmk-rgb-tool keyboard info
|
|
|
+1 Wobkey Impact 80 0x36B0/0x309F (known model)
|
|
|
+```
|
|
|
|
|
|
-| What | Where |
|
|
|
-|------|-------|
|
|
|
-| VIA definition JSON, the origin of all three effect lists | [`Impact_80.JSON`](https://drive.wobkey.com/f/d/6BtO/Impact_80.JSON) |
|
|
|
-| VIA firmware image | [`impact_80.bin`](https://drive.wobkey.com/f/d/9yH8/impact_80.bin) |
|
|
|
-| Update instructions, vendor's warning against updating a working board | [Driver & Firmware page](https://wiki.wobkey.com/en/Products/PMOKEY-Impact-80/Driver-Firmware) |
|
|
|
|
|
|
-Stock VIA exposes no way to ask a keyboard which effect IDs it implements, so
|
|
|
-the names come from that JSON and not from the board. The counts the board
|
|
|
-actually takes were measured here: 47 on the backlight channel, 6 on `logo` and
|
|
|
-`side`.
|
|
|
|
|
|
## CLI Reference
|
|
|
|
|
|
@@ -524,17 +602,19 @@ actually takes were measured here: 47 on the backlight channel, 6 on `logo` and
|
|
|
| `qmk-rgb-tool keyboard info` | Discover connected QMK keyboards |
|
|
|
| `qmk-rgb-tool enable` | Enable selected lighting zones |
|
|
|
| `qmk-rgb-tool disable` | Disable selected lighting zones |
|
|
|
-| `qmk-rgb-tool info` | Show per-zone RGB state (JSON) |
|
|
|
+| `qmk-rgb-tool info` | Show per-zone RGB state |
|
|
|
| `qmk-rgb-tool effect <name>` | Set a zone-aware effect by name, verified by read-back |
|
|
|
-| `qmk-rgb-tool effect` | With no argument, list every effect per channel (JSON) |
|
|
|
+| `qmk-rgb-tool effect` | With no argument, list every effect per channel |
|
|
|
| `qmk-rgb-tool effect --list` | The same list, as a flag |
|
|
|
+| `qmk-rgb-tool definition fetch` | Download the VIA definition for the connected keyboard |
|
|
|
+| `qmk-rgb-tool definition list` | List the definition files in the data directory |
|
|
|
| `qmk-rgb-tool brightness <val>` | Set brightness (0–255) on selected zones, verified by read-back |
|
|
|
| `qmk-rgb-tool speed <val>` | Set effect speed (0–255) on selected zones, verified by read-back; on `logo` and `side` only 0, 1 and 4 are reachable |
|
|
|
| `qmk-rgb-tool color <hex>` | Set color (e.g. `ff0000`) on selected zones |
|
|
|
| `qmk-rgb-tool color rgb:<hex>` | The same hex color, written out |
|
|
|
| `qmk-rgb-tool color hsv:<h>,<s>,<v>` | Set hue and saturation (0–255) and write `v` to the brightness of the same zones |
|
|
|
| `qmk-rgb-tool mode <index>` | Set a raw zone-specific effect ID, verified by read-back; a board that does not implement the index clamps it to the highest it does |
|
|
|
-| `qmk-rgb-tool --zone <channel> ...` | Target one channel: `backlight`, `rgblight`, `rgb_matrix`, `audio` or `led_matrix`, or a name from `keyboards.json` |
|
|
|
+| `qmk-rgb-tool --zone <channel> ...` | Target one channel: `backlight`, `rgblight`, `rgb_matrix`, `audio` or `led_matrix`, or the name the board's definition gives it |
|
|
|
| `qmk-rgb-tool --device <n> ...` | Target keyboard by number (see `keyboard info`) |
|
|
|
| `qmk-rgb-tool -v`, `--version` | Print the version |
|
|
|
| `qmk-rgb-tool save [name]` | Save current RGB state to `profiles/<name>.json` (name lowercased, non-`[a-z0-9-_]` mapped to `-`, a leading `-` prefixed with `unnamed-`); without a name it writes `default` |
|
|
|
@@ -547,11 +627,34 @@ actually takes were measured here: 47 on the backlight channel, 6 on `logo` and
|
|
|
token. `effect`, `load`, `save` and `delete` accept an optional name.
|
|
|
`brightness`, `speed`, `color` and `mode` require exactly one argument.
|
|
|
|
|
|
+`keyboard info`, `info`, `list`, `definition list` and `effect --list` print
|
|
|
+text, because a person reads them. Pass `--json` for the machine shape, which is
|
|
|
+the same data with the same field names as before:
|
|
|
+
|
|
|
+```bash
|
|
|
+qmk-rgb-tool info
|
|
|
+qmk-rgb-tool --json info
|
|
|
+qmk-rgb-tool info --json # the flag is persistent, both spellings work
|
|
|
+```
|
|
|
+
|
|
|
+`--definition <path>` names the definition file to read instead of the one in
|
|
|
+the data directory, and applies to every command that resolves effect names.
|
|
|
+
|
|
|
## Profiles
|
|
|
|
|
|
Profiles store RGB state (effect, brightness, speed, color) per channel as JSON
|
|
|
files in the `profiles/` directory, which is resolved against the working
|
|
|
-directory the tool runs in. The keys are the names the file was written with, so
|
|
|
+directory the tool runs in. A profile also records the keyboard it was saved
|
|
|
+from, because the effect names in it are that board's:
|
|
|
+
|
|
|
+```json
|
|
|
+"board": { "vendorId": "0x36B0", "productId": "0x309F" }
|
|
|
+```
|
|
|
+
|
|
|
+Loading a profile onto another keyboard still works — names that do not exist
|
|
|
+there are reported per key and skipped — but it says which profile belongs to
|
|
|
+which keyboard. A profile written before this field existed has no board and is
|
|
|
+loaded without complaint. The keys are the names the file was written with, so
|
|
|
a profile written before a board was renamed reports the key it cannot place and
|
|
|
skips it. They are the only way to persist RGB
|
|
|
settings across reboots. The keyboard's VIA protocol does not expose a
|
|
|
@@ -562,7 +665,7 @@ files instead.
|
|
|
|
|
|
```
|
|
|
cmd/qmk-rgb-tool/ # Cobra-based CLI
|
|
|
-internal/device/ # HID discovery + keyboards.json loader
|
|
|
+internal/device/ # HID discovery
|
|
|
internal/hid/ # Cross-platform HID access (hidapi)
|
|
|
internal/rgb/ # Effects, colors, state
|
|
|
internal/via/ # VIA protocol implementation
|