main.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "github.com/spf13/cobra"
  7. "netdome.biz/paul/qmk-rgb/internal/device"
  8. )
  9. // version is set at build time via -ldflags.
  10. var version = "dev"
  11. // Group IDs for the root help. They name a group in the help output and say
  12. // nothing else: no behaviour hangs off them.
  13. const (
  14. groupLighting = "lighting"
  15. groupProfile = "profile"
  16. groupKeyboard = "keyboard"
  17. )
  18. var (
  19. targetDevice string
  20. targetZone string
  21. )
  22. func newRootCommand() *cobra.Command {
  23. cmd := &cobra.Command{
  24. Use: "qmk-rgb-tool",
  25. Short: "QMK RGB CLI — control QMK keyboard lighting",
  26. SilenceErrors: true,
  27. SilenceUsage: true,
  28. }
  29. // The commands are grouped because fifteen of them in one flat list is where
  30. // a user starts reading the help to find a command and gives up. A group says
  31. // what a command is for, and a command in none of them lands under
  32. // "Additional Commands" where the grouping says nothing.
  33. cmd.AddGroup(
  34. &cobra.Group{ID: groupLighting, Title: "Lighting Commands:"},
  35. &cobra.Group{ID: groupProfile, Title: "Profile Commands:"},
  36. &cobra.Group{ID: groupKeyboard, Title: "Keyboard Commands:"},
  37. )
  38. cmd.PersistentFlags().StringVar(&targetDevice, "device", "", "Keyboard number as printed by 'keyboard info', starting at 1")
  39. cmd.PersistentFlags().StringVar(&targetZone, "zone", "", "RGB lighting channel: backlight, rgblight, rgb_matrix, audio or led_matrix, or the name this keyboard's definition gives it")
  40. cmd.PersistentFlags().StringVar(&definitionFlag, "definition", "", "VIA definition file to read effect names from, instead of the one in the data directory")
  41. cmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "Print JSON instead of text")
  42. return cmd
  43. }
  44. var rootCmd = newRootCommand()
  45. func Execute() {
  46. if err := rootCmd.Execute(); err != nil {
  47. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  48. os.Exit(1)
  49. }
  50. }
  51. func main() {
  52. Execute()
  53. }
  54. // keyboardCmd is the namespace for everything that is about one board rather
  55. // than about its lighting: which boards are connected, and the definition file
  56. // whose effect names belong to a board. The definition prose lives here because
  57. // there is no `definition` command of its own any more.
  58. var keyboardCmd = &cobra.Command{
  59. Use: "keyboard",
  60. Short: "Keyboard management commands",
  61. GroupID: groupKeyboard,
  62. Long: "A keyboard's effect names come from a VIA definition file, the same one VIA\n" +
  63. "itself uses. `fetch` downloads the file for the connected keyboard into the\n" +
  64. "data directory; to use a file you already have, put it in that directory or\n" +
  65. "pass it with --definition.",
  66. }
  67. // inGroup places a command in one of the root help's groups.
  68. func inGroup(cmd *cobra.Command, group string) *cobra.Command {
  69. cmd.GroupID = group
  70. return cmd
  71. }
  72. // discoverAll is a seam for tests.
  73. var discoverAll = device.DiscoverAll
  74. // runKeyboardInfo writes the connected QMK keyboards, as text unless --json
  75. // asks otherwise. The 1-based index that --device accepts is in the "index"
  76. // field, so the machine shape emits no banner alongside it. Whether a keyboard
  77. // has effect names is reported per keyboard, because it differs: one board may
  78. // have a definition and the next may not.
  79. func runKeyboardInfo(out io.Writer) error {
  80. devices, err := discoverAll()
  81. if err != nil {
  82. return err
  83. }
  84. if devices == nil {
  85. devices = []device.Device{}
  86. }
  87. lines := describeDevices(devices)
  88. if !jsonOutput {
  89. return printKeyboardInfoText(out, lines)
  90. }
  91. type info struct {
  92. Devices []keyboardLine `json:"devices"`
  93. Total int `json:"total"`
  94. }
  95. return encodeJSON(out, info{Devices: lines, Total: len(lines)})
  96. }
  97. var keyboardInfoCmd = &cobra.Command{
  98. Use: "info",
  99. Short: "Discover connected QMK keyboards",
  100. Long: "Scan for connected QMK keyboards and print device info as JSON.\n" +
  101. "Each device carries the 1-based index that --device accepts.",
  102. Args: cobra.NoArgs,
  103. RunE: func(cmd *cobra.Command, args []string) error {
  104. return runKeyboardInfo(cmd.OutOrStdout())
  105. },
  106. }
  107. func init() {
  108. registerCommands(rootCmd)
  109. registerFlagCompletions(rootCmd)
  110. rootCmd.Version = version
  111. rootCmd.SetVersionTemplate(" qmk-rgb-tool {{.Version}}\n")
  112. }
  113. // registerCommands adds every command to the root, each in the group the help
  114. // output lists it under. It is a function of its own so that a test can build the
  115. // same tree the binary runs and check the grouping is real, rather than repeating
  116. // a list that could fall behind the one that ships.
  117. func registerCommands(root *cobra.Command) {
  118. keyboardCmd.AddCommand(
  119. keyboardInfoCmd,
  120. NewKeyboardFetchCmd(),
  121. NewKeyboardDefinitionsCmd(),
  122. )
  123. root.AddCommand(
  124. inGroup(NewEnableCmd(), groupLighting),
  125. inGroup(NewDisableCmd(), groupLighting),
  126. inGroup(NewInfoCmd(), groupLighting),
  127. inGroup(NewEffectCmd(), groupLighting),
  128. inGroup(NewBrightnessCmd(), groupLighting),
  129. inGroup(NewSpeedCmd(), groupLighting),
  130. inGroup(NewColorCmd(), groupLighting),
  131. inGroup(NewProfileSaveCmd(), groupProfile),
  132. inGroup(NewProfileLoadCmd(), groupProfile),
  133. inGroup(NewProfileListCmd(), groupProfile),
  134. inGroup(NewProfileDeleteCmd(), groupProfile),
  135. inGroup(keyboardCmd, groupKeyboard),
  136. )
  137. }