package rgb import ( "fmt" "strconv" "strings" ) // HSV is a color in the units the keyboard uses: hue, saturation and value, // each 0-255. The keyboard stores hue and saturation only; Value is the // brightness register, which is why it is tracked apart from the color. type HSV struct { H, S, V uint8 // hasValue separates "the notation carried no value" from "the notation // asked for value 0". The hex forms mean the first, and leave V at zero. hasValue bool } // HasValue reports whether the notation carried a value component, that is, // whether the caller asked for a brightness as well as a color. func (c HSV) HasValue() bool { return c.hasValue } // ParseColorSpec parses one of the three documented color notations: a bare or // rgb:-prefixed six digit hex value, or hsv:,, in the firmware's own // 0-255 units. A hex value contributes hue and saturation; the value component // of a hex color is not a brightness request and is dropped. func ParseColorSpec(spec string) (HSV, error) { prefix, body, found := strings.Cut(spec, ":") if !found { if prefix == "hsv" || prefix == "rgb" { return HSV{}, unknownNotation(spec) } return hexToHSV(spec) } switch prefix { case "hsv": return parseHSVSpec(body) case "rgb": return hexToHSV(body) default: return HSV{}, unknownNotation(spec) } } // hexToHSV converts a hex color to the hue and saturation the keyboard stores. // The value component of the hex color is not carried over: only the hsv // notation asks for a brightness, and HasValue keeps that distinction visible. func hexToHSV(hex string) (HSV, error) { c, err := ParseHexColor(hex) if err != nil { return HSV{}, err } h, s, _ := c.HSV() return HSV{H: h, S: s}, nil } // parseHSVSpec parses the body of an hsv: notation. Three components are // required: a two component form would have to mean "leave the brightness // alone", which no other notation does. func parseHSVSpec(body string) (HSV, error) { parts := strings.Split(body, ",") if len(parts) != 3 { return HSV{}, fmt.Errorf("want hsv:,, with three components in 0-255, got %q", body) } var components [3]uint8 for i, part := range parts { n, err := strconv.ParseUint(part, 10, 8) if err != nil { return HSV{}, fmt.Errorf("invalid hsv component %q: want 0-255", part) } components[i] = uint8(n) } return HSV{H: components[0], S: components[1], V: components[2], hasValue: true}, nil } // unknownNotation names the accepted forms, because a rejected notation is // usually a typo of one of them. func unknownNotation(spec string) error { return fmt.Errorf("unknown color notation %q: use ff0000, rgb:ff0000 or hsv:0,255,255", spec) }