package main import ( "fmt" "strconv" intdevice "netdome.biz/paul/impact-80/internal/device" intrgb "netdome.biz/paul/impact-80/internal/rgb" "netdome.biz/paul/impact-80/internal/via" ) type zoneProtocol interface { SetValue(via.LEDType, uint8, uint8) error SetColor(via.LEDType, uint8, uint8) error } type rgbProtocol interface { zoneProtocol GetValue(via.LEDType, uint8) ([]byte, error) Close() error } var openRGBProtocol = func() (rgbProtocol, error) { return OpenDevice() } 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) } // OpenDevice discovers the connected QMK keyboards and opens the one selected // by --device. Without --device it only proceeds when exactly one keyboard is // connected, so a command can never hit an unintended keyboard. func OpenDevice() (*via.Protocol, error) { if _, err := selectedZones(); err != nil { return nil, err } devices, err := intdevice.DiscoverAll() if err != nil { return nil, fmt.Errorf("discover: %w", err) } dev, err := selectDevice(devices, targetDevice) if err != nil { return nil, err } proto, err := via.New(dev) if err != nil { return nil, fmt.Errorf("open protocol: %w", err) } return proto, nil } func lightingEnabled(mode, brightness uint8) bool { return mode != 0 && brightness > 0 } func ParseUint8(s string) (uint8, error) { v, err := strconv.ParseUint(s, 10, 8) if err != nil { return 0, fmt.Errorf("invalid value: %w", err) } return uint8(v), nil }