package device import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/wobkey/rgb/internal/hid" ) // Keyboard represents a VIA-compatible keyboard from keyboards.json. type Keyboard struct { Name string `json:"name"` VendorID uint16 `json:"vendorId"` ProductID uint16 `json:"productId"` Protocol string `json:"protocol"` VIAVersion int `json:"viaVersion"` LEDLayout string `json:"ledLayout"` } // Device represents a connected keyboard device. type Device struct { Keyboard Path string } // LoadKeyboards reads keyboard definitions from keyboards.json. func LoadKeyboards() ([]Keyboard, error) { path, err := findKeyboardsJSON() if err != nil { return nil, fmt.Errorf("find keyboards.json: %w", err) } data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read keyboards.json: %w", err) } var keyboards []Keyboard if err := json.Unmarshal(data, &keyboards); err != nil { return nil, fmt.Errorf("parse keyboards.json: %w", err) } return keyboards, nil } // findKeyboardsJSON searches for keyboards.json in common locations. var findKeyboardsJSON = func() (string, error) { exe, err := os.Executable() if err == nil { path := filepath.Join(filepath.Dir(exe), "keyboards.json") if _, err := os.Stat(path); err == nil { return path, nil } } path := "keyboards.json" if _, err := os.Stat(path); err == nil { return path, nil } cwd, err := os.Getwd() if err == nil { path := filepath.Join(cwd, "keyboards.json") if _, err := os.Stat(path); err == nil { return path, nil } } return "", fmt.Errorf("keyboards.json not found") } // Discover scans for connected VIA-compatible keyboards. func Discover(keyboards []Keyboard) ([]Device, error) { var found []Device for _, kb := range keyboards { err := hid.Enumerate(kb.VendorID, kb.ProductID, func(info *hid.DeviceInfo) error { found = append(found, Device{ Keyboard: kb, Path: info.Path, }) return nil }) if err != nil { return nil, fmt.Errorf("enumerate VID=0x%04x PID=0x%04x: %w", kb.VendorID, kb.ProductID, err) } } fmt.Fprintf(os.Stderr, "DEBUG discover: found %d keyboard(s)\n", len(found)) for _, d := range found { fmt.Fprintf(os.Stderr, "DEBUG discover: %s path=%s VID=0x%04x PID=0x%04x\n", d.Name, d.Path, d.VendorID, d.ProductID) } return found, nil }