Bladeren bron

detect all QMK keyboards via Raw HID signature, not just known VID/PID

- Add DiscoverAllQMK: scan for all QMK Raw HID devices (0xFF60/0x61),
  mark each as supported or unknown
- keyboard info now returns both known and unknown QMK devices
- Remove keyboard list command (any QMK keyboard is supported now)
- Fix HID dedup: use device path instead of VID/PID so multiple
  identical keyboards are each detected
- Update docs: VIA-compatible -> QMK, fix architecture paths
Paul Klumpp 1 week geleden
bovenliggende
commit
7f80cb9ec9
5 gewijzigde bestanden met toevoegingen van 63 en 48 verwijderingen
  1. 8 6
      README.md
  2. 17 33
      cmd/qmk-rgb-tool/main.go
  3. 1 1
      cmd/qmk-rgb-tool/rgb.go
  4. 33 2
      internal/device/device.go
  5. 4 6
      internal/hid/hid.go

+ 8 - 6
README.md

@@ -218,19 +218,22 @@ After Linux setup, run the tool as your regular user (no `sudo` needed).
 
 ## Supported Keyboards
 
+Any keyboard running QMK with the RGB Matrix subsystem and VIA support is
+detected automatically via the QMK Raw HID signature (Usage Page 0xFF60,
+Usage 0x61). No manual configuration required.
+
+Two keyboards are fully tested and mapped:
+
 | Keyboard        | VID    | PID    | Status    |
 |-----------------|--------|--------|-----------|
 | Wobkey Rainy 75 | 0x6666 | 0x0001 | Supported |
 | Wobkey Impact 80| 0x36B0 | 0x309F | Supported |
 
-New keyboards can be added to `keyboards.json`.
-
 ## CLI Reference
 
 | Command                         | Description                              |
 |---------------------------------|------------------------------------------|
-| `qmk-rgb-tool keyboard info`          | Discover connected VIA-compatible keyboards |
-| `qmk-rgb-tool keyboard list`          | List all supported keyboards in database  |
+| `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)            |
@@ -257,8 +260,7 @@ files instead.
 ## Architecture
 
 ```
-cmd/qmk-rgb/       # Cobra-based CLI
-cmd/qmk-rgb/rgb/   # RGB subcommands
+cmd/qmk-rgb-tool/  # Cobra-based CLI
 internal/device/  # HID discovery + keyboards.json loader
 internal/hid/     # Cross-platform HID access (hidapi)
 internal/rgb/     # Effects, colors, state

+ 17 - 33
cmd/qmk-rgb-tool/main.go

@@ -20,7 +20,7 @@ var (
 func newRootCommand() *cobra.Command {
 	cmd := &cobra.Command{
 		Use:           "qmk-rgb-tool",
-		Short:         "QMK RGB CLI — control VIA-compatible keyboard lighting",
+		Short:         "QMK RGB CLI — control QMK keyboard lighting",
 		SilenceErrors: true,
 		SilenceUsage:  true,
 	}
@@ -53,8 +53,8 @@ var keyboardCmd = &cobra.Command{
 
 var keyboardInfoCmd = &cobra.Command{
 	Use:   "info",
-	Short: "Discover connected VIA-compatible keyboards",
-	Long:  "Scan for connected VIA-compatible keyboards and print device info as JSON.",
+	Short: "Discover connected QMK keyboards",
+	Long:  "Scan for connected QMK keyboards (both supported and unknown) and print device info as JSON.",
 	Run: func(cmd *cobra.Command, args []string) {
 		keyboards, err := device.LoadKeyboards()
 		if err != nil {
@@ -62,48 +62,33 @@ var keyboardInfoCmd = &cobra.Command{
 			os.Exit(1)
 		}
 
-		devices, err := device.Discover(keyboards)
+		devices, err := device.DiscoverAllQMK(keyboards)
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
 			os.Exit(1)
 		}
 
 		if len(devices) == 0 {
-			fmt.Println(`{"devices":[]}`)
+			fmt.Println(`{"devices":[],"unknown":[],"total":0}`)
 			return
 		}
 
-		type Info struct {
-			Devices []device.Device `json:"devices"`
-			Total   int             `json:"total"`
-		}
-		out := Info{Devices: devices, Total: len(devices)}
-		data, _ := json.MarshalIndent(out, "", "  ")
-		fmt.Println(string(data))
-	},
-}
-
-var keyboardListCmd = &cobra.Command{
-	Use:   "list",
-	Short: "List supported keyboards",
-	Long:  "Print all keyboards from keyboards.json as JSON.",
-	Run: func(cmd *cobra.Command, args []string) {
-		keyboards, err := device.LoadKeyboards()
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
-			os.Exit(1)
-		}
-
-		if len(keyboards) == 0 {
-			fmt.Println(`{"keyboards":[]}`)
-			return
+		var known []device.UnknownDevice
+		var unknown []device.UnknownDevice
+		for _, d := range devices {
+			if d.Supported {
+				known = append(known, d)
+			} else {
+				unknown = append(unknown, d)
+			}
 		}
 
 		type Info struct {
-			Keyboards []device.Keyboard `json:"keyboards"`
-			Total     int               `json:"total"`
+			Devices  []device.UnknownDevice `json:"devices"`
+			Unknown  []device.UnknownDevice `json:"unknown"`
+			Total    int                    `json:"total"`
 		}
-		out := Info{Keyboards: keyboards, Total: len(keyboards)}
+		out := Info{Devices: devices, Unknown: unknown, Total: len(devices)}
 		data, _ := json.MarshalIndent(out, "", "  ")
 		fmt.Println(string(data))
 	},
@@ -112,7 +97,6 @@ var keyboardListCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(keyboardCmd)
 	keyboardCmd.AddCommand(keyboardInfoCmd)
-	keyboardCmd.AddCommand(keyboardListCmd)
 
 	rootCmd.AddCommand(NewEnableCmd())
 	rootCmd.AddCommand(NewDisableCmd())

+ 1 - 1
cmd/qmk-rgb-tool/rgb.go

@@ -96,7 +96,7 @@ func OpenDevice() (*via.Protocol, intdevice.Keyboard, error) {
 	}
 
 	if len(devices) == 0 {
-		return nil, intdevice.Keyboard{}, fmt.Errorf("no VIA-compatible keyboard found")
+		return nil, intdevice.Keyboard{}, fmt.Errorf("no QMK keyboard found")
 	}
 
 	var dev intdevice.Device

+ 33 - 2
internal/device/device.go

@@ -9,7 +9,7 @@ import (
 	"netdome.biz/paul/impact-80/internal/hid"
 )
 
-// Keyboard represents a VIA-compatible keyboard from keyboards.json.
+// Keyboard represents a keyboard definition from keyboards.json.
 type Keyboard struct {
 	Name       string `json:"name"`
 	VendorID   uint16 `json:"vendorId"`
@@ -71,7 +71,38 @@ var findKeyboardsJSON = func() (string, error) {
 	return "", fmt.Errorf("keyboards.json not found")
 }
 
-// Discover scans for connected VIA-compatible keyboards.
+// UnknownDevice represents a QMK Raw HID keyboard not yet in keyboards.json.
+type UnknownDevice struct {
+	VendorID  uint16 `json:"vendorId"`
+	ProductID uint16 `json:"productId"`
+	Path      string `json:"path"`
+	Supported bool   `json:"supported"`
+}
+
+// DiscoverAllQMK scans for all connected QMK keyboards (both known and unknown).
+func DiscoverAllQMK(keyboards []Keyboard) ([]UnknownDevice, error) {
+	qmkDevices, err := hid.DiscoverAll()
+	if err != nil {
+		return nil, fmt.Errorf("discover qmk devices: %w", err)
+	}
+
+	known := make(map[string]bool)
+	for _, kb := range keyboards {
+		known[fmt.Sprintf("%04x:%04x", kb.VendorID, kb.ProductID)] = true
+	}
+
+	var results []UnknownDevice
+	for _, q := range qmkDevices {
+		key := fmt.Sprintf("%04x:%04x", q.VendorID, q.ProductID)
+		results = append(results, UnknownDevice{
+			VendorID:  q.VendorID,
+			ProductID: q.ProductID,
+			Path:      q.Path,
+			Supported: known[key],
+		})
+	}
+	return results, nil
+}
 func Discover(keyboards []Keyboard) ([]Device, error) {
 	var found []Device
 	for _, kb := range keyboards {

+ 4 - 6
internal/hid/hid.go

@@ -51,11 +51,10 @@ func DiscoverAll() ([]DeviceInfo, error) {
 		if !isRawHID(info) {
 			return nil
 		}
-		key := fmt.Sprintf("%04x:%04x", info.VendorID, info.ProductID)
-		if seen[key] {
+		if seen[info.Path] {
 			return nil
 		}
-		seen[key] = true
+		seen[info.Path] = true
 		devices = append(devices, DeviceInfo{
 			Path:      info.Path,
 			VendorID:  info.VendorID,
@@ -80,11 +79,10 @@ func Enumerate(vendorID, productID uint16, fn func(info *DeviceInfo) error) erro
 		if !isRawHID(info) {
 			return nil
 		}
-		key := fmt.Sprintf("%04x:%04x", info.VendorID, info.ProductID)
-		if seen[key] {
+		if seen[info.Path] {
 			return nil
 		}
-		seen[key] = true
+		seen[info.Path] = true
 		return fn(&DeviceInfo{
 			Path:      info.Path,
 			VendorID:  info.VendorID,