rgb.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package rgb
  2. import (
  3. "fmt"
  4. "strconv"
  5. "github.com/spf13/cobra"
  6. intdevice "github.com/wobkey/rgb/internal/device"
  7. intrgb "github.com/wobkey/rgb/internal/rgb"
  8. "github.com/wobkey/rgb/internal/via"
  9. )
  10. var (
  11. targetDevice string
  12. targetZone string
  13. )
  14. type zoneProtocol interface {
  15. SetValue(via.LEDType, uint8, uint8) error
  16. SetColor(via.LEDType, uint8, uint8) error
  17. }
  18. func forEachSelectedZone(zones []intrgb.Zone, fn func(intrgb.Zone, via.LEDType) error) error {
  19. channels := zoneChannels(zones)
  20. for i, zone := range zones {
  21. if err := fn(zone, channels[i]); err != nil {
  22. return err
  23. }
  24. }
  25. return nil
  26. }
  27. func setValueOnZones(proto zoneProtocol, zones []intrgb.Zone, param, value uint8) error {
  28. return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
  29. return proto.SetValue(channel, param, value)
  30. })
  31. }
  32. func setBrightnessOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
  33. return setValueOnZones(proto, zones, uint8(intrgb.Brightness), value)
  34. }
  35. func setSpeedOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
  36. return setValueOnZones(proto, zones, uint8(intrgb.Speed), value)
  37. }
  38. func setColorOnZones(proto zoneProtocol, zones []intrgb.Zone, hue, saturation uint8) error {
  39. return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
  40. return proto.SetColor(channel, hue, saturation)
  41. })
  42. }
  43. func disableLightingOnZones(proto zoneProtocol, zones []intrgb.Zone) error {
  44. return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
  45. if err := proto.SetValue(channel, uint8(intrgb.EffectID), 0); err != nil {
  46. return err
  47. }
  48. return proto.SetValue(channel, uint8(intrgb.Brightness), 0)
  49. })
  50. }
  51. func enableLightingOnZones(proto zoneProtocol, zones []intrgb.Zone) error {
  52. return forEachSelectedZone(zones, func(zone intrgb.Zone, channel via.LEDType) error {
  53. if err := proto.SetValue(channel, uint8(intrgb.EffectID), intrgb.DefaultEffect(zone)); err != nil {
  54. return err
  55. }
  56. return proto.SetValue(channel, uint8(intrgb.Brightness), 160)
  57. })
  58. }
  59. func setModeOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
  60. return setValueOnZones(proto, zones, uint8(intrgb.EffectID), value)
  61. }
  62. func OpenDevice() (*via.Protocol, intdevice.Keyboard, error) {
  63. if _, err := selectedZones(); err != nil {
  64. return nil, intdevice.Keyboard{}, err
  65. }
  66. keyboards, err := intdevice.LoadKeyboards()
  67. if err != nil {
  68. return nil, intdevice.Keyboard{}, fmt.Errorf("load keyboards: %w", err)
  69. }
  70. devices, err := intdevice.Discover(keyboards)
  71. if err != nil {
  72. return nil, intdevice.Keyboard{}, fmt.Errorf("discover: %w", err)
  73. }
  74. if len(devices) == 0 {
  75. return nil, intdevice.Keyboard{}, fmt.Errorf("no VIA-compatible keyboard found")
  76. }
  77. var dev intdevice.Device
  78. if targetDevice != "" {
  79. for _, d := range devices {
  80. if d.Path == targetDevice {
  81. dev = d
  82. break
  83. }
  84. }
  85. if dev.Path == "" {
  86. return nil, intdevice.Keyboard{}, fmt.Errorf("device %s not found", targetDevice)
  87. }
  88. } else if len(devices) == 1 {
  89. dev = devices[0]
  90. } else {
  91. return nil, intdevice.Keyboard{}, fmt.Errorf("multiple devices found, use --device to specify")
  92. }
  93. proto, err := via.New(dev)
  94. if err != nil {
  95. return nil, intdevice.Keyboard{}, fmt.Errorf("open protocol: %w", err)
  96. }
  97. return proto, dev.Keyboard, nil
  98. }
  99. func lightingEnabled(mode, brightness uint8) bool {
  100. return mode != 0 && brightness > 0
  101. }
  102. func ParseUint8(s string) (uint8, error) {
  103. v, err := strconv.ParseUint(s, 10, 8)
  104. if err != nil {
  105. return 0, fmt.Errorf("invalid value: %w", err)
  106. }
  107. return uint8(v), nil
  108. }
  109. func Init() *cobra.Command {
  110. cmd := &cobra.Command{
  111. Use: "rgb",
  112. Short: "RGB lighting commands",
  113. PersistentPreRunE: func(*cobra.Command, []string) error {
  114. _, err := selectedZones()
  115. return err
  116. },
  117. }
  118. cmd.PersistentFlags().StringVar(&targetDevice, "device", "", "HID device path to use")
  119. cmd.PersistentFlags().StringVar(&targetZone, "zone", "", "RGB lighting zone")
  120. return cmd
  121. }