Procházet zdrojové kódy

refactor: flatten CLI by removing 'rgb' subcommand — all commands now at root level (e.g. 'qmk-rgb effect' instead of 'qmk-rgb rgb effect'), rename title to 'QMK RGB Tool', add Features section, update AGENTS.md and README.md consistently

Paul Klumpp před 2 týdny
rodič
revize
e7dd8f6044

+ 16 - 16
AGENTS.md

@@ -1,4 +1,4 @@
-# Agent Guidelines — QMK VIA RGB Tool
+# Agent Guidelines — QMK RGB Tool
 
 
 ## Objective
 ## Objective
 Cross-platform Go CLI library for programmatic/agent-friendly control of VIA-compatible keyboard RGB lighting (starting with Impact 80).
 Cross-platform Go CLI library for programmatic/agent-friendly control of VIA-compatible keyboard RGB lighting (starting with Impact 80).
@@ -17,23 +17,23 @@ keyboards.json          # VID/PID database + keyboard metadata
 
 
 ```
 ```
 qmk-rgb keyboard info
 qmk-rgb keyboard info
-qmk-rgb rgb effect breathing
-qmk-rgb rgb effect rainbow_moving_chevron
-qmk-rgb rgb effect rainbow_moving_chevron --zone backlight
-qmk-rgb rgb brightness <val>       # 0-255
-qmk-rgb rgb speed <val>            # 0-255
-qmk-rgb rgb color <hex>            # Six hexadecimal digits
-qmk-rgb rgb mode <index>           # Raw zone-specific effect ID
-qmk-rgb rgb enable
-qmk-rgb rgb disable
-qmk-rgb rgb info
-qmk-rgb rgb save <name>        # Save current RGB state to a profile
-qmk-rgb rgb load <name>        # Load a profile and apply it to the keyboard
-qmk-rgb rgb delete <name>      # Delete a saved profile
-qmk-rgb rgb list               # List saved profiles
+qmk-rgb effect breathing
+qmk-rgb effect rainbow_moving_chevron
+qmk-rgb effect rainbow_moving_chevron --zone backlight
+qmk-rgb brightness <val>       # 0-255
+qmk-rgb speed <val>            # 0-255
+qmk-rgb color <hex>            # Six hexadecimal digits
+qmk-rgb mode <index>           # Raw zone-specific effect ID
+qmk-rgb enable
+qmk-rgb disable
+qmk-rgb info
+qmk-rgb save <name>        # Save current RGB state to a profile
+qmk-rgb load <name>        # Load a profile and apply it to the keyboard
+qmk-rgb delete <name>      # Delete a saved profile
+qmk-rgb list               # List saved profiles
 ```
 ```
 
 
-`rgb` has a persistent `--zone` flag accepting `logo`, `backlight`, or `side`.
+`--zone` is persistent across commands and accepts `logo`, `backlight`, or `side`.
 Without `--zone`, commands target Logo, Backlight, and Side in that order.
 Without `--zone`, commands target Logo, Backlight, and Side in that order.
 With `--zone`, commands target exactly one zone. Unsupported default targets
 With `--zone`, commands target exactly one zone. Unsupported default targets
 are skipped with a stderr warning; unsupported explicit-zone effects and
 are skipped with a stderr warning; unsupported explicit-zone effects and

+ 47 - 28
README.md

@@ -1,4 +1,4 @@
-# QMK VIA RGB Tool
+# QMK RGB Tool
 
 
 Control the RGB lighting on QMK-compatible keyboards. Supports Impact 80 and Rainy 75 out of the box, and any other keyboard running QMK with the RGB Matrix subsystem and VIA support.
 Control the RGB lighting on QMK-compatible keyboards. Supports Impact 80 and Rainy 75 out of the box, and any other keyboard running QMK with the RGB Matrix subsystem and VIA support.
 
 
@@ -26,26 +26,32 @@ go install ./cmd/qmk-rgb/
 ./qmk-rgb keyboard info
 ./qmk-rgb keyboard info
 
 
 # RGB commands target Logo, Backlight, and Side by default
 # RGB commands target Logo, Backlight, and Side by default
-./qmk-rgb rgb effect breathing
-./qmk-rgb rgb effect rainbow_moving_chevron
-./qmk-rgb rgb effect rainbow_moving_chevron --zone backlight
-./qmk-rgb rgb brightness 160
-./qmk-rgb rgb speed 2
-./qmk-rgb rgb color 00ff00
+./qmk-rgb effect breathing
+./qmk-rgb effect rainbow_moving_chevron
+./qmk-rgb effect rainbow_moving_chevron --zone backlight
+./qmk-rgb brightness 160
+./qmk-rgb speed 2
+./qmk-rgb color 00ff00
 # Raw mode IDs are zone-specific; ID 17 is Backlight-only
 # Raw mode IDs are zone-specific; ID 17 is Backlight-only
-./qmk-rgb rgb mode 17 --zone backlight
-./qmk-rgb rgb enable
-./qmk-rgb rgb disable
-./qmk-rgb rgb info
+./qmk-rgb mode 17 --zone backlight
+./qmk-rgb enable
+./qmk-rgb disable
+./qmk-rgb info
 
 
 # Select exactly one zone
 # Select exactly one zone
-./qmk-rgb rgb brightness 160 --zone side
+./qmk-rgb brightness 160 --zone side
+
+# Save and load RGB profiles
+./qmk-rgb save paul
+./qmk-rgb load paul
+./qmk-rgb list
+./qmk-rgb delete paul
 
 
 # Specify a target device (when multiple are connected)
 # Specify a target device (when multiple are connected)
-./qmk-rgb rgb --device /dev/hidraw0 enable
+./qmk-rgb --device /dev/hidrawX enable
 ```
 ```
 
 
-`--zone` is persistent on `rgb` and accepts `logo`, `backlight`, or `side`.
+`--zone` is persistent across commands and accepts `logo`, `backlight`, or `side`.
 Without `--zone`, commands target Logo, Backlight, and Side in that order.
 Without `--zone`, commands target Logo, Backlight, and Side in that order.
 With `--zone`, commands target exactly the selected zone. An unsupported
 With `--zone`, commands target exactly the selected zone. An unsupported
 name for an explicit zone fails before the device is opened; a default
 name for an explicit zone fails before the device is opened; a default
@@ -53,6 +59,18 @@ command skips unsupported zones and prints a warning on stderr.
 
 
 All output is machine-parseable JSON when applicable.
 All output is machine-parseable JSON when applicable.
 
 
+## Features
+
+- **Cross-platform** — Linux, macOS, Windows via [hidapi](https://github.com/libusb/hidapi)
+- **Zone-aware effects** — per-zone effect control (Logo, Backlight, Side) with zone-specific effect catalogs
+- **Profile system** — save, load, list, and delete RGB presets as JSON files in `profiles/`
+- **46 backlight effects** — complete Impact 80 effect family mapped to zone-aware effect names
+- **Compatibility aliases** — `off`, `breathe`, `rainbow`, `solid`, `static` resolve to correct effect IDs per zone
+- **Reactive & splash effects** — honor `rgb color` and `rgb speed` for key-press illumination
+- **Machine-parseable output** — JSON for `keyboard info`, `info`, `list`, and `effect --list`
+- **Agent-friendly** — designed for automation, scripting, and CLI-first workflows
+- **Multiple devices** — `--device` flag to target a specific keyboard when several are connected
+
 ## Impact 80 Zones and Effects
 ## Impact 80 Zones and Effects
 
 
 | Zone | CLI name | VIA channel | Effect IDs |
 | Zone | CLI name | VIA channel | Effect IDs |
@@ -205,20 +223,21 @@ New keyboards can be added to `keyboards.json`.
 | Command                         | Description                              |
 | Command                         | Description                              |
 |---------------------------------|------------------------------------------|
 |---------------------------------|------------------------------------------|
 | `qmk-rgb keyboard info`          | Discover connected VIA-compatible keyboards |
 | `qmk-rgb keyboard info`          | Discover connected VIA-compatible keyboards |
-| `qmk-rgb rgb enable`             | Enable selected lighting zones           |
-| `qmk-rgb rgb disable`            | Disable selected lighting zones           |
-| `qmk-rgb rgb info`               | Show per-zone RGB state (JSON)            |
-| `qmk-rgb rgb effect <name>`      | Set a zone-aware effect by name           |
-| `qmk-rgb rgb brightness <val>`   | Set brightness (0–255) on selected zones |
-| `qmk-rgb rgb speed <val>`        | Set effect speed (0–255) on selected zones |
-| `qmk-rgb rgb color <hex>`        | Set color (e.g. `ff0000`) on selected zones |
-| `qmk-rgb rgb mode <index>`       | Set a raw zone-specific effect ID        |
-| `qmk-rgb rgb --zone <zone> ...`  | Target `logo`, `backlight`, or `side`     |
-| `qmk-rgb rgb --device <path> ...`| Specify HID device path                  |
-| `qmk-rgb rgb save [name]`        | Save current RGB state to `profiles/<name>.json` |
-| `qmk-rgb rgb load [name]`        | Load and apply a profile from `profiles/` |
-| `qmk-rgb rgb list`               | List saved profiles                        |
-| `qmk-rgb rgb delete [name]`      | Delete a saved profile                     |
+| `qmk-rgb keyboard list`          | List all supported keyboards in database  |
+| `qmk-rgb enable`                 | Enable selected lighting zones            |
+| `qmk-rgb disable`                | Disable selected lighting zones           |
+| `qmk-rgb info`                   | Show per-zone RGB state (JSON)            |
+| `qmk-rgb effect <name>`          | Set a zone-aware effect by name           |
+| `qmk-rgb brightness <val>`       | Set brightness (0–255) on selected zones  |
+| `qmk-rgb speed <val>`            | Set effect speed (0–255) on selected zones |
+| `qmk-rgb color <hex>`            | Set color (e.g. `ff0000`) on selected zones |
+| `qmk-rgb mode <index>`           | Set a raw zone-specific effect ID         |
+| `qmk-rgb --zone <zone> ...`      | Target `logo`, `backlight`, or `side`     |
+| `qmk-rgb --device <path> ...`    | Specify HID device path                   |
+| `qmk-rgb save [name]`            | Save current RGB state to `profiles/<name>.json` |
+| `qmk-rgb load [name]`            | Load and apply a profile from `profiles/` |
+| `qmk-rgb list`                   | List saved profiles                        |
+| `qmk-rgb delete [name]`          | Delete a saved profile                     |
 
 
 ## Profiles
 ## Profiles
 
 

+ 1 - 1
cmd/qmk-rgb/rgb/brightness.go → cmd/qmk-rgb/brightness.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/color.go → cmd/qmk-rgb/color.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/commands_test.go → cmd/qmk-rgb/commands_test.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"errors"
 	"errors"

+ 1 - 1
cmd/qmk-rgb/rgb/disable.go → cmd/qmk-rgb/disable.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/effect.go → cmd/qmk-rgb/effect.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"

+ 1 - 1
cmd/qmk-rgb/rgb/effect_test.go → cmd/qmk-rgb/effect_test.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"bytes"
 	"bytes"

+ 1 - 1
cmd/qmk-rgb/rgb/enable.go → cmd/qmk-rgb/enable.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/info.go → cmd/qmk-rgb/info.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"

+ 1 - 1
cmd/qmk-rgb/rgb/info_test.go → cmd/qmk-rgb/info_test.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"bytes"
 	"bytes"

+ 28 - 19
cmd/qmk-rgb/main.go

@@ -6,20 +6,31 @@ import (
 	"os"
 	"os"
 
 
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
-	"netdome.biz/paul/impact-80/cmd/qmk-rgb/rgb"
 	"netdome.biz/paul/impact-80/internal/device"
 	"netdome.biz/paul/impact-80/internal/device"
 )
 )
 
 
 // version is set at build time via -ldflags.
 // version is set at build time via -ldflags.
 var version = "dev"
 var version = "dev"
 
 
+var (
+	targetDevice string
+	targetZone   string
+)
+
 func newRootCommand() *cobra.Command {
 func newRootCommand() *cobra.Command {
-	return &cobra.Command{
+	cmd := &cobra.Command{
 		Use:           "qmk-rgb",
 		Use:           "qmk-rgb",
-		Short:         "QMK VIA RGB CLI — control VIA-compatible keyboard lighting",
+		Short:         "QMK RGB CLI — control VIA-compatible keyboard lighting",
 		SilenceErrors: true,
 		SilenceErrors: true,
 		SilenceUsage:  true,
 		SilenceUsage:  true,
 	}
 	}
+	cmd.PersistentFlags().StringVar(&targetDevice, "device", "", "HID device path to use")
+	cmd.PersistentFlags().StringVar(&targetZone, "zone", "", "RGB lighting zone (logo, backlight, side)")
+	cmd.PersistentPreRunE = func(*cobra.Command, []string) error {
+		_, err := selectedZones()
+		return err
+	}
+	return cmd
 }
 }
 
 
 var rootCmd = newRootCommand()
 var rootCmd = newRootCommand()
@@ -102,22 +113,20 @@ func init() {
 	rootCmd.AddCommand(keyboardCmd)
 	rootCmd.AddCommand(keyboardCmd)
 	keyboardCmd.AddCommand(keyboardInfoCmd)
 	keyboardCmd.AddCommand(keyboardInfoCmd)
 	keyboardCmd.AddCommand(keyboardListCmd)
 	keyboardCmd.AddCommand(keyboardListCmd)
-	rgbCmd := rgb.Init()
-	rgbCmd.AddCommand(
-		rgb.NewEffectCmd(),
-		rgb.NewBrightnessCmd(),
-		rgb.NewSpeedCmd(),
-		rgb.NewColorCmd(),
-		rgb.NewModeCmd(),
-		rgb.NewEnableCmd(),
-		rgb.NewDisableCmd(),
-		rgb.NewInfoCmd(),
-		rgb.NewProfileSaveCmd(),
-		rgb.NewProfileLoadCmd(),
-		rgb.NewProfileListCmd(),
-		rgb.NewProfileDeleteCmd(),
-	)
-	rootCmd.AddCommand(rgbCmd)
+
+	rootCmd.AddCommand(NewEnableCmd())
+	rootCmd.AddCommand(NewDisableCmd())
+	rootCmd.AddCommand(NewInfoCmd())
+	rootCmd.AddCommand(NewEffectCmd())
+	rootCmd.AddCommand(NewBrightnessCmd())
+	rootCmd.AddCommand(NewSpeedCmd())
+	rootCmd.AddCommand(NewColorCmd())
+	rootCmd.AddCommand(NewModeCmd())
+	rootCmd.AddCommand(NewProfileSaveCmd())
+	rootCmd.AddCommand(NewProfileLoadCmd())
+	rootCmd.AddCommand(NewProfileListCmd())
+	rootCmd.AddCommand(NewProfileDeleteCmd())
+
 	rootCmd.Version = version
 	rootCmd.Version = version
 	rootCmd.SetVersionTemplate("	qmk-rgb {{.Version}}\n")
 	rootCmd.SetVersionTemplate("	qmk-rgb {{.Version}}\n")
 }
 }

+ 1 - 1
cmd/qmk-rgb/rgb/mode.go → cmd/qmk-rgb/mode.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/profile.go → cmd/qmk-rgb/profile.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"

+ 1 - 21
cmd/qmk-rgb/rgb/rgb.go → cmd/qmk-rgb/rgb.go

@@ -1,20 +1,14 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"
 	"strconv"
 	"strconv"
 
 
-	"github.com/spf13/cobra"
 	intdevice "netdome.biz/paul/impact-80/internal/device"
 	intdevice "netdome.biz/paul/impact-80/internal/device"
 	intrgb "netdome.biz/paul/impact-80/internal/rgb"
 	intrgb "netdome.biz/paul/impact-80/internal/rgb"
 	"netdome.biz/paul/impact-80/internal/via"
 	"netdome.biz/paul/impact-80/internal/via"
 )
 )
 
 
-var (
-	targetDevice string
-	targetZone   string
-)
-
 type zoneProtocol interface {
 type zoneProtocol interface {
 	SetValue(via.LEDType, uint8, uint8) error
 	SetValue(via.LEDType, uint8, uint8) error
 	SetColor(via.LEDType, uint8, uint8) error
 	SetColor(via.LEDType, uint8, uint8) error
@@ -141,17 +135,3 @@ func ParseUint8(s string) (uint8, error) {
 	}
 	}
 	return uint8(v), nil
 	return uint8(v), nil
 }
 }
-
-func Init() *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "rgb",
-		Short: "RGB lighting commands",
-		PersistentPreRunE: func(*cobra.Command, []string) error {
-			_, err := selectedZones()
-			return err
-		},
-	}
-	cmd.PersistentFlags().StringVar(&targetDevice, "device", "", "HID device path to use")
-	cmd.PersistentFlags().StringVar(&targetZone, "zone", "", "RGB lighting zone")
-	return cmd
-}

+ 6 - 6
cmd/qmk-rgb/rgb/rgb_test.go → cmd/qmk-rgb/rgb_test.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"reflect"
 	"reflect"
@@ -32,14 +32,14 @@ func TestLightingEnabledRequiresActiveEffectAndBrightness(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestDeviceFlagIsInheritedByRGBCommands(t *testing.T) {
+func TestDeviceFlagIsInheritedByCommands(t *testing.T) {
 	originalTargetDevice := targetDevice
 	originalTargetDevice := targetDevice
 	t.Cleanup(func() { targetDevice = originalTargetDevice })
 	t.Cleanup(func() { targetDevice = originalTargetDevice })
 
 
 	const wantPath = "hid-device-0"
 	const wantPath = "hid-device-0"
 	ran := false
 	ran := false
 
 
-	cmd := Init()
+	cmd := newRootCommand()
 	cmd.AddCommand(&cobra.Command{
 	cmd.AddCommand(&cobra.Command{
 		Use: "probe",
 		Use: "probe",
 		Run: func(*cobra.Command, []string) { ran = true },
 		Run: func(*cobra.Command, []string) { ran = true },
@@ -121,7 +121,7 @@ func TestZoneChannels(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestZoneFlagIsInheritedByRGBCommands(t *testing.T) {
+func TestZoneFlagIsInheritedByCommands(t *testing.T) {
 	originalTargetDevice := targetDevice
 	originalTargetDevice := targetDevice
 	originalTargetZone := targetZone
 	originalTargetZone := targetZone
 	t.Cleanup(func() {
 	t.Cleanup(func() {
@@ -131,7 +131,7 @@ func TestZoneFlagIsInheritedByRGBCommands(t *testing.T) {
 
 
 	const wantPath = "hid-device-0"
 	const wantPath = "hid-device-0"
 	ran := false
 	ran := false
-	cmd := Init()
+	cmd := newRootCommand()
 	cmd.AddCommand(&cobra.Command{
 	cmd.AddCommand(&cobra.Command{
 		Use: "probe",
 		Use: "probe",
 		Run: func(*cobra.Command, []string) { ran = true },
 		Run: func(*cobra.Command, []string) { ran = true },
@@ -174,7 +174,7 @@ func TestZoneValidationRunsBeforeCommandOpener(t *testing.T) {
 		targetZone = originalTargetZone
 		targetZone = originalTargetZone
 	})
 	})
 
 
-	cmd := Init()
+	cmd := newRootCommand()
 	openerCalled := false
 	openerCalled := false
 	cmd.AddCommand(&cobra.Command{
 	cmd.AddCommand(&cobra.Command{
 		Use: "probe",
 		Use: "probe",

+ 1 - 1
cmd/qmk-rgb/rgb/speed.go → cmd/qmk-rgb/speed.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	"fmt"
 	"fmt"

+ 1 - 1
cmd/qmk-rgb/rgb/zones.go → cmd/qmk-rgb/zones.go

@@ -1,4 +1,4 @@
-package rgb
+package main
 
 
 import (
 import (
 	intrgb "netdome.biz/paul/impact-80/internal/rgb"
 	intrgb "netdome.biz/paul/impact-80/internal/rgb"