Pārlūkot izejas kodu

Target all RGB commands by zone

Paul Klumpp 2 nedēļas atpakaļ
vecāks
revīzija
d31f46e381

+ 7 - 3
cmd/wobkey/rgb/brightness.go

@@ -5,8 +5,6 @@ import (
 	"os"
 
 	"github.com/spf13/cobra"
-	"github.com/wobkey/rgb/internal/rgb"
-	"github.com/wobkey/rgb/internal/via"
 )
 
 func NewBrightnessCmd() *cobra.Command {
@@ -22,6 +20,12 @@ func NewBrightnessCmd() *cobra.Command {
 				os.Exit(1)
 			}
 
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -29,7 +33,7 @@ func NewBrightnessCmd() *cobra.Command {
 			}
 			defer proto.Close()
 
-			if err := proto.SetValue(via.RGBLight, uint8(rgb.Brightness), val); err != nil {
+			if err := setBrightnessOnZones(proto, zones, val); err != nil {
 				fmt.Fprintf(os.Stderr, "Error setting brightness: %v\n", err)
 				os.Exit(1)
 			}

+ 7 - 1
cmd/wobkey/rgb/color.go

@@ -21,6 +21,12 @@ func NewColorCmd() *cobra.Command {
 				os.Exit(1)
 			}
 
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -29,7 +35,7 @@ func NewColorCmd() *cobra.Command {
 			defer proto.Close()
 
 			h, s, _ := c.HSV()
-			if err := proto.SetLightingColor(h, s); err != nil {
+			if err := setColorOnZones(proto, zones, h, s); err != nil {
 				fmt.Fprintf(os.Stderr, "Error setting color: %v\n", err)
 				os.Exit(1)
 			}

+ 187 - 0
cmd/wobkey/rgb/commands_test.go

@@ -0,0 +1,187 @@
+package rgb
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	intrgb "github.com/wobkey/rgb/internal/rgb"
+	"github.com/wobkey/rgb/internal/via"
+)
+
+type commandReport struct {
+	channel    via.LEDType
+	param      uint8
+	value      uint8
+	hue        uint8
+	saturation uint8
+}
+
+type fakeZoneProtocol struct {
+	reports []commandReport
+	failAt  int
+}
+
+func (f *fakeZoneProtocol) SetValue(channel via.LEDType, param, value uint8) error {
+	if f.failAt >= 0 && f.failAt == len(f.reports) {
+		return errors.New("write failed")
+	}
+	f.reports = append(f.reports, commandReport{
+		channel: channel,
+		param:   param,
+		value:   value,
+	})
+	return nil
+}
+
+func (f *fakeZoneProtocol) SetColor(channel via.LEDType, hue, saturation uint8) error {
+	if f.failAt >= 0 && f.failAt == len(f.reports) {
+		return errors.New("write failed")
+	}
+	f.reports = append(f.reports, commandReport{
+		channel:    channel,
+		param:      4,
+		hue:        hue,
+		saturation: saturation,
+	})
+	return nil
+}
+
+func TestSelectedChannelOperations(t *testing.T) {
+	defaultZones := []intrgb.Zone{intrgb.ZoneLogo, intrgb.ZoneBacklight, intrgb.ZoneSide}
+	sideZones := []intrgb.Zone{intrgb.ZoneSide}
+
+	operations := []struct {
+		name      string
+		zones     []intrgb.Zone
+		sideZones []intrgb.Zone
+		run       func(zoneProtocol, []intrgb.Zone) error
+		want      []commandReport
+		wantSide  []commandReport
+	}{
+		{
+			name:      "brightness",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run: func(protocol zoneProtocol, zones []intrgb.Zone) error {
+				return setBrightnessOnZones(protocol, zones, 160)
+			},
+			want: []commandReport{
+				{channel: 2, param: 1, value: 160},
+				{channel: 3, param: 1, value: 160},
+				{channel: 4, param: 1, value: 160},
+			},
+			wantSide: []commandReport{{channel: 4, param: 1, value: 160}},
+		},
+		{
+			name:      "speed",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run: func(protocol zoneProtocol, zones []intrgb.Zone) error {
+				return setSpeedOnZones(protocol, zones, 2)
+			},
+			want: []commandReport{
+				{channel: 2, param: 3, value: 2},
+				{channel: 3, param: 3, value: 2},
+				{channel: 4, param: 3, value: 2},
+			},
+			wantSide: []commandReport{{channel: 4, param: 3, value: 2}},
+		},
+		{
+			name:      "color",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run: func(protocol zoneProtocol, zones []intrgb.Zone) error {
+				return setColorOnZones(protocol, zones, 85, 255)
+			},
+			want: []commandReport{
+				{channel: 2, param: 4, hue: 85, saturation: 255},
+				{channel: 3, param: 4, hue: 85, saturation: 255},
+				{channel: 4, param: 4, hue: 85, saturation: 255},
+			},
+			wantSide: []commandReport{{channel: 4, param: 4, hue: 85, saturation: 255}},
+		},
+		{
+			name:      "disable",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run:       disableLightingOnZones,
+			want: []commandReport{
+				{channel: 2, param: 2, value: 0},
+				{channel: 2, param: 1, value: 0},
+				{channel: 3, param: 2, value: 0},
+				{channel: 3, param: 1, value: 0},
+				{channel: 4, param: 2, value: 0},
+				{channel: 4, param: 1, value: 0},
+			},
+			wantSide: []commandReport{
+				{channel: 4, param: 2, value: 0},
+				{channel: 4, param: 1, value: 0},
+			},
+		},
+		{
+			name:      "enable",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run:       enableLightingOnZones,
+			want: []commandReport{
+				{channel: 2, param: 2, value: 4},
+				{channel: 2, param: 1, value: 160},
+				{channel: 3, param: 2, value: 5},
+				{channel: 3, param: 1, value: 160},
+				{channel: 4, param: 2, value: 4},
+				{channel: 4, param: 1, value: 160},
+			},
+			wantSide: []commandReport{
+				{channel: 4, param: 2, value: 4},
+				{channel: 4, param: 1, value: 160},
+			},
+		},
+		{
+			name:      "mode",
+			zones:     defaultZones,
+			sideZones: sideZones,
+			run: func(protocol zoneProtocol, zones []intrgb.Zone) error {
+				return setModeOnZones(protocol, zones, 17)
+			},
+			want: []commandReport{
+				{channel: 2, param: 2, value: 17},
+				{channel: 3, param: 2, value: 17},
+				{channel: 4, param: 2, value: 17},
+			},
+			wantSide: []commandReport{{channel: 4, param: 2, value: 17}},
+		},
+	}
+
+	for _, tc := range operations {
+		t.Run(tc.name+"/default", func(t *testing.T) {
+			protocol := &fakeZoneProtocol{failAt: -1}
+			if err := tc.run(protocol, tc.zones); err != nil {
+				t.Fatalf("operation error = %v", err)
+			}
+			if !reflect.DeepEqual(protocol.reports, tc.want) {
+				t.Errorf("reports = %v, want %v", protocol.reports, tc.want)
+			}
+		})
+		t.Run(tc.name+"/side", func(t *testing.T) {
+			protocol := &fakeZoneProtocol{failAt: -1}
+			if err := tc.run(protocol, tc.sideZones); err != nil {
+				t.Fatalf("operation error = %v", err)
+			}
+			if !reflect.DeepEqual(protocol.reports, tc.wantSide) {
+				t.Errorf("reports = %v, want %v", protocol.reports, tc.wantSide)
+			}
+		})
+	}
+}
+
+func TestSelectedChannelStopsOnFirstError(t *testing.T) {
+	protocol := &fakeZoneProtocol{failAt: 1}
+	err := setBrightnessOnZones(protocol, intrgb.AllZones(), 160)
+	if err == nil {
+		t.Fatal("setBrightnessOnZones() expected error, got nil")
+	}
+	if len(protocol.reports) != 1 {
+		t.Errorf("reports = %v, want one report before failure", protocol.reports)
+	}
+}

+ 7 - 1
cmd/wobkey/rgb/disable.go

@@ -12,6 +12,12 @@ func NewDisableCmd() *cobra.Command {
 		Use:   "disable",
 		Short: "Disable RGB lighting",
 		Run: func(cmd *cobra.Command, args []string) {
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -19,7 +25,7 @@ func NewDisableCmd() *cobra.Command {
 			}
 			defer proto.Close()
 
-			if err := proto.DisableLighting(); err != nil {
+			if err := disableLightingOnZones(proto, zones); err != nil {
 				fmt.Fprintf(os.Stderr, "Error disabling RGB: %v\n", err)
 				os.Exit(1)
 			}

+ 7 - 1
cmd/wobkey/rgb/enable.go

@@ -12,6 +12,12 @@ func NewEnableCmd() *cobra.Command {
 		Use:   "enable",
 		Short: "Enable RGB lighting",
 		Run: func(cmd *cobra.Command, args []string) {
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -19,7 +25,7 @@ func NewEnableCmd() *cobra.Command {
 			}
 			defer proto.Close()
 
-			if err := proto.EnableLighting(); err != nil {
+			if err := enableLightingOnZones(proto, zones); err != nil {
 				fmt.Fprintf(os.Stderr, "Error enabling RGB: %v\n", err)
 				os.Exit(1)
 			}

+ 7 - 3
cmd/wobkey/rgb/mode.go

@@ -5,8 +5,6 @@ import (
 	"os"
 
 	"github.com/spf13/cobra"
-	"github.com/wobkey/rgb/internal/rgb"
-	"github.com/wobkey/rgb/internal/via"
 )
 
 func NewModeCmd() *cobra.Command {
@@ -22,6 +20,12 @@ func NewModeCmd() *cobra.Command {
 				os.Exit(1)
 			}
 
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -29,7 +33,7 @@ func NewModeCmd() *cobra.Command {
 			}
 			defer proto.Close()
 
-			if err := proto.SetValue(via.RGBLight, uint8(rgb.EffectID), val); err != nil {
+			if err := setModeOnZones(proto, zones, val); err != nil {
 				fmt.Fprintf(os.Stderr, "Error setting mode: %v\n", err)
 				os.Exit(1)
 			}

+ 58 - 0
cmd/wobkey/rgb/rgb.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/spf13/cobra"
 	intdevice "github.com/wobkey/rgb/internal/device"
+	intrgb "github.com/wobkey/rgb/internal/rgb"
 	"github.com/wobkey/rgb/internal/via"
 )
 
@@ -14,6 +15,63 @@ var (
 	targetZone   string
 )
 
+type zoneProtocol interface {
+	SetValue(via.LEDType, uint8, uint8) error
+	SetColor(via.LEDType, uint8, uint8) error
+}
+
+func forEachSelectedZone(zones []intrgb.Zone, fn func(intrgb.Zone, via.LEDType) error) error {
+	channels := zoneChannels(zones)
+	for i, zone := range zones {
+		if err := fn(zone, channels[i]); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func setValueOnZones(proto zoneProtocol, zones []intrgb.Zone, param, value uint8) error {
+	return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
+		return proto.SetValue(channel, param, value)
+	})
+}
+
+func setBrightnessOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
+	return setValueOnZones(proto, zones, uint8(intrgb.Brightness), value)
+}
+
+func setSpeedOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
+	return setValueOnZones(proto, zones, uint8(intrgb.Speed), value)
+}
+
+func setColorOnZones(proto zoneProtocol, zones []intrgb.Zone, hue, saturation uint8) error {
+	return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
+		return proto.SetColor(channel, hue, saturation)
+	})
+}
+
+func disableLightingOnZones(proto zoneProtocol, zones []intrgb.Zone) error {
+	return forEachSelectedZone(zones, func(_ intrgb.Zone, channel via.LEDType) error {
+		if err := proto.SetValue(channel, uint8(intrgb.EffectID), 0); err != nil {
+			return err
+		}
+		return proto.SetValue(channel, uint8(intrgb.Brightness), 0)
+	})
+}
+
+func enableLightingOnZones(proto zoneProtocol, zones []intrgb.Zone) error {
+	return forEachSelectedZone(zones, func(zone intrgb.Zone, channel via.LEDType) error {
+		if err := proto.SetValue(channel, uint8(intrgb.EffectID), intrgb.DefaultEffect(zone)); err != nil {
+			return err
+		}
+		return proto.SetValue(channel, uint8(intrgb.Brightness), 160)
+	})
+}
+
+func setModeOnZones(proto zoneProtocol, zones []intrgb.Zone, value uint8) error {
+	return setValueOnZones(proto, zones, uint8(intrgb.EffectID), value)
+}
+
 func OpenDevice() (*via.Protocol, intdevice.Keyboard, error) {
 	if _, err := selectedZones(); err != nil {
 		return nil, intdevice.Keyboard{}, err

+ 7 - 3
cmd/wobkey/rgb/speed.go

@@ -5,8 +5,6 @@ import (
 	"os"
 
 	"github.com/spf13/cobra"
-	"github.com/wobkey/rgb/internal/rgb"
-	"github.com/wobkey/rgb/internal/via"
 )
 
 func NewSpeedCmd() *cobra.Command {
@@ -22,6 +20,12 @@ func NewSpeedCmd() *cobra.Command {
 				os.Exit(1)
 			}
 
+			zones, err := selectedZones()
+			if err != nil {
+				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
+				os.Exit(1)
+			}
+
 			proto, _, err := OpenDevice()
 			if err != nil {
 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -29,7 +33,7 @@ func NewSpeedCmd() *cobra.Command {
 			}
 			defer proto.Close()
 
-			if err := proto.SetValue(via.RGBLight, uint8(rgb.Speed), val); err != nil {
+			if err := setSpeedOnZones(proto, zones, val); err != nil {
 				fmt.Fprintf(os.Stderr, "Error setting speed: %v\n", err)
 				os.Exit(1)
 			}