info.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/spf13/cobra"
  6. intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
  7. "netdome.biz/paul/qmk-rgb/internal/via"
  8. )
  9. type infoGetter interface {
  10. GetValue(via.Channel, uint8) ([]byte, error)
  11. }
  12. type infoColor struct {
  13. Hue uint8 `json:"hue"`
  14. Saturation uint8 `json:"saturation"`
  15. }
  16. type zoneInfo struct {
  17. Zone string `json:"zone"`
  18. Channel uint8 `json:"channel"`
  19. Enabled bool `json:"enabled"`
  20. Effect string `json:"effect"`
  21. EffectID uint8 `json:"effectId"`
  22. Brightness uint8 `json:"brightness"`
  23. Speed uint8 `json:"speed"`
  24. Color infoColor `json:"color"`
  25. Error string `json:"error,omitempty"`
  26. }
  27. type infoOutput struct {
  28. Enabled bool `json:"enabled"`
  29. Mode string `json:"mode"`
  30. Brightness uint8 `json:"brightness"`
  31. Speed uint8 `json:"speed"`
  32. Zones []zoneInfo `json:"zones"`
  33. }
  34. func readInfo(proto infoGetter, channels []via.Channel, display map[uint16]string, catalog *intrgb.Catalog) (infoOutput, error) {
  35. out := infoOutput{Zones: make([]zoneInfo, 0, len(channels))}
  36. var queryErrors []error
  37. for _, ch := range channels {
  38. record, err := readZoneInfo(proto, ch, display, catalog)
  39. out.Zones = append(out.Zones, record)
  40. if err != nil {
  41. queryErrors = append(queryErrors, err)
  42. }
  43. }
  44. if len(out.Zones) > 0 {
  45. first := out.Zones[0]
  46. out.Enabled = first.Enabled
  47. out.Mode = first.Effect
  48. out.Brightness = first.Brightness
  49. out.Speed = first.Speed
  50. }
  51. return out, errors.Join(queryErrors...)
  52. }
  53. func readZoneInfo(proto infoGetter, ch via.Channel, display map[uint16]string, catalog *intrgb.Catalog) (zoneInfo, error) {
  54. record := zoneInfo{Zone: channelName(ch, display), Channel: uint8(ch)}
  55. channel := ch
  56. brightness, err := getInfoValue(proto, channel, uint8(intrgb.Brightness), 1)
  57. if err != nil {
  58. record.Error = err.Error()
  59. return record, err
  60. }
  61. record.Brightness = brightness[0]
  62. effect, err := getInfoValue(proto, channel, uint8(intrgb.EffectID), 1)
  63. if err != nil {
  64. record.Error = err.Error()
  65. return record, err
  66. }
  67. record.EffectID = effect[0]
  68. record.Effect = catalog.EffectName(ch, record.EffectID)
  69. record.Enabled = lightingEnabled(record.EffectID, record.Brightness)
  70. speed, err := getInfoValue(proto, channel, uint8(intrgb.Speed), 1)
  71. if err != nil {
  72. record.Error = err.Error()
  73. return record, err
  74. }
  75. record.Speed = speed[0]
  76. color, err := getInfoValue(proto, channel, uint8(intrgb.ColorValue), 2)
  77. if err != nil {
  78. record.Error = err.Error()
  79. return record, err
  80. }
  81. record.Color = infoColor{Hue: color[0], Saturation: color[1]}
  82. return record, nil
  83. }
  84. func getInfoValue(proto infoGetter, channel via.Channel, param uint8, size int) ([]byte, error) {
  85. value, err := proto.GetValue(channel, param)
  86. if err != nil {
  87. return nil, err
  88. }
  89. if len(value) < size {
  90. return nil, fmt.Errorf("short value response: got %d, want %d", len(value), size)
  91. }
  92. return value, nil
  93. }
  94. func NewInfoCmd() *cobra.Command {
  95. return withZoneArgs(&cobra.Command{
  96. Use: "info [zone]",
  97. Short: "Show current RGB state",
  98. Long: "Report what the keyboard holds, one record per channel. Without a zone this\n" +
  99. "reports every channel the keyboard has.",
  100. RunE: func(cmd *cobra.Command, args []string) error {
  101. zone := ""
  102. if len(args) == 1 {
  103. zone = args[0]
  104. }
  105. proto, target, channels, err := openTarget(zone)
  106. if err != nil {
  107. return err
  108. }
  109. defer proto.Close()
  110. catalog, _, err := resolveCatalog(target)
  111. if err != nil {
  112. return err
  113. }
  114. state, queryErr := readInfo(proto, channels, target.Display, catalog)
  115. if !jsonOutput {
  116. return printZoneInfoText(cmd.OutOrStdout(), state)
  117. }
  118. if err := encodeJSON(cmd.OutOrStdout(), state); err != nil {
  119. return err
  120. }
  121. return queryErr
  122. },
  123. }, 0, 1, "at most a zone")
  124. }