package main import ( "errors" "fmt" "github.com/spf13/cobra" intrgb "netdome.biz/paul/qmk-rgb/internal/rgb" "netdome.biz/paul/qmk-rgb/internal/via" ) type infoGetter interface { GetValue(via.Channel, uint8) ([]byte, error) } type infoColor struct { Hue uint8 `json:"hue"` Saturation uint8 `json:"saturation"` } type zoneInfo struct { Zone string `json:"zone"` Channel uint8 `json:"channel"` Enabled bool `json:"enabled"` Effect string `json:"effect"` EffectID uint8 `json:"effectId"` Brightness uint8 `json:"brightness"` Speed uint8 `json:"speed"` Color infoColor `json:"color"` Error string `json:"error,omitempty"` } type infoOutput struct { Enabled bool `json:"enabled"` Mode string `json:"mode"` Brightness uint8 `json:"brightness"` Speed uint8 `json:"speed"` Zones []zoneInfo `json:"zones"` } func readInfo(proto infoGetter, channels []via.Channel, display map[uint16]string, catalog *intrgb.Catalog) (infoOutput, error) { out := infoOutput{Zones: make([]zoneInfo, 0, len(channels))} var queryErrors []error for _, ch := range channels { record, err := readZoneInfo(proto, ch, display, catalog) out.Zones = append(out.Zones, record) if err != nil { queryErrors = append(queryErrors, err) } } if len(out.Zones) > 0 { first := out.Zones[0] out.Enabled = first.Enabled out.Mode = first.Effect out.Brightness = first.Brightness out.Speed = first.Speed } return out, errors.Join(queryErrors...) } func readZoneInfo(proto infoGetter, ch via.Channel, display map[uint16]string, catalog *intrgb.Catalog) (zoneInfo, error) { record := zoneInfo{Zone: channelName(ch, display), Channel: uint8(ch)} channel := ch brightness, err := getInfoValue(proto, channel, uint8(intrgb.Brightness), 1) if err != nil { record.Error = err.Error() return record, err } record.Brightness = brightness[0] effect, err := getInfoValue(proto, channel, uint8(intrgb.EffectID), 1) if err != nil { record.Error = err.Error() return record, err } record.EffectID = effect[0] record.Effect = catalog.EffectName(ch, record.EffectID) record.Enabled = lightingEnabled(record.EffectID, record.Brightness) speed, err := getInfoValue(proto, channel, uint8(intrgb.Speed), 1) if err != nil { record.Error = err.Error() return record, err } record.Speed = speed[0] color, err := getInfoValue(proto, channel, uint8(intrgb.ColorValue), 2) if err != nil { record.Error = err.Error() return record, err } record.Color = infoColor{Hue: color[0], Saturation: color[1]} return record, nil } func getInfoValue(proto infoGetter, channel via.Channel, param uint8, size int) ([]byte, error) { value, err := proto.GetValue(channel, param) if err != nil { return nil, err } if len(value) < size { return nil, fmt.Errorf("short value response: got %d, want %d", len(value), size) } return value, nil } func NewInfoCmd() *cobra.Command { return withZoneArgs(&cobra.Command{ Use: "info [zone]", Short: "Show current RGB state", Long: "Report what the keyboard holds, one record per channel. Without a zone this\n" + "reports every channel the keyboard has.", RunE: func(cmd *cobra.Command, args []string) error { zone := "" if len(args) == 1 { zone = args[0] } proto, target, channels, err := openTarget(zone) if err != nil { return err } defer proto.Close() catalog, _, err := resolveCatalog(target) if err != nil { return err } state, queryErr := readInfo(proto, channels, target.Display, catalog) if !jsonOutput { return printZoneInfoText(cmd.OutOrStdout(), state) } if err := encodeJSON(cmd.OutOrStdout(), state); err != nil { return err } return queryErr }, }, 0, 1, "at most a zone") }