# `Color.LED`

Colour conversion for multi-channel addressable LEDs — fixtures
with one or more extra **white** channels alongside R, G, B.

Two channel layouts are supported:

* **RGBW** — `Color.LED.RGBW`. Three coloured LEDs plus a single
  white LED per pixel. The white LED's colour temperature is fixed
  by the chip variant. Used by e.g. **WS2814** (warm, neutral and
  cool variants) and the SK6812-RGBW family.

* **RGBWW** / **RGB+CCT** — `Color.LED.RGBWW`. Three coloured LEDs
  plus a **warm white** and a **cool white** LED per pixel. Used
  by e.g. **WS2805** and several SK6812 variants. The two whites
  can be blended to reach any CCT on the line between them.

These structs are intentionally **device-referred**. They do
not implement `Color.Behaviour` (which assumes a device-
independent colour space with a single `from_xyz/1`) because
converting *to* an RGBW/RGBWW pixel requires the white LED's
colour temperature — there is no canonical answer without that
parameter. Use `Color.LED.RGBW.from_srgb/2` and
`Color.LED.RGBWW.from_srgb/2` explicitly.

## Channel semantics

All channels — `r`, `g`, `b`, `w`, `ww`, `cw` — are **linear
drive values in `[0.0, 1.0]`**, not sRGB-companded values. A
value of `0.0` means "LED off"; `1.0` means "LED at full". The
library applies sRGB companding when converting from a
`Color.SRGB` struct, and un-compands back on the way out.

The R/G/B primaries are assumed to match the sRGB working space
(close enough for all WS281x / SK681x chips; exact for chips
binned to sRGB). If you need a different primary set, stage the
conversion through `Color.RGB` (linear, any working space) and
then translate the matrix yourself.

## Extraction algorithm

For RGBW, given a target linear-sRGB `(R, G, B)` and a white LED
whose spectral output corresponds to linear-sRGB `(Rw, Gw, Bw)`
at full drive, the extraction is:

    w = min(1, R/Rw, G/Gw, B/Bw)          (clamped to ≥ 0)
    r = R − w·Rw
    g = G − w·Gw
    b = B − w·Bw

This maximises the white channel while keeping all RGB channels
non-negative — more light from the (usually more efficient)
white LED, less from the RGB LEDs.

For RGBWW the algorithm first picks a blend ratio between the
warm and cool whites matching the target's correlated colour
temperature, synthesises an effective mixed white at that
blend, then runs the RGBW extraction against that mixed white.
The resulting total white is split back into `ww` and `cw` by
the same ratio.

## Chip presets

`chip_options/1` returns the default CCT options for the common
chip variants:

| Chip | Kind | Typical white temperature(s) |
|---|---|---|
| `:ws2814_ww` | RGBW | 3000 K (warm) |
| `:ws2814_nw` | RGBW | 4500 K (neutral) |
| `:ws2814_cw` | RGBW | 6000 K (cool) |
| `:sk6812_ww` | RGBW | 3000 K |
| `:sk6812_nw` | RGBW | 4500 K |
| `:sk6812_cw` | RGBW | 6500 K |
| `:ws2805`    | RGBWW | 3000 K warm + 6500 K cool |

These are typical values from datasheets and vendor pages —
actual parts vary by batch and binning. Measure yours if you
need calibration-grade accuracy.

## Example

    # Build a pixel for a WS2805 fixture from an sRGB colour
    options = Color.LED.chip_options(:ws2805)
    {:ok, target} = Color.new("#ffa500")                 # orange
    pixel = Color.LED.RGBWW.from_srgb(target, options)
    # => %Color.LED.RGBWW{r: …, g: …, b: …, ww: …, cw: …,
    #                     warm_temperature: 3000,
    #                     cool_temperature: 6500,
    #                     alpha: nil}

    # Preview what the pixel will actually emit:
    {:ok, srgb_preview} = Color.LED.RGBWW.to_srgb(pixel)

# `chip`

```elixir
@type chip() ::
  :ws2814_ww
  | :ws2814_nw
  | :ws2814_cw
  | :sk6812_ww
  | :sk6812_nw
  | :sk6812_cw
  | :ws2805
```

# `chip_options`

```elixir
@spec chip_options(chip()) :: keyword()
```

Returns the recommended extraction options for a named LED chip
variant.

The returned keyword list can be passed directly to
`Color.LED.RGBW.from_srgb/2` or `Color.LED.RGBWW.from_srgb/2` —
the `:kind` entry tells you which one.

### Arguments

* `chip` is a chip atom. See the chip table in the module doc.

### Returns

* A keyword list with at least `:kind` (`:rgbw` or `:rgbww`)
  and the relevant temperature(s).

### Examples

    iex> Color.LED.chip_options(:ws2814_ww)
    [kind: :rgbw, white_temperature: 3000]

    iex> Color.LED.chip_options(:ws2805)
    [kind: :rgbww, warm_temperature: 3000, cool_temperature: 6500]

# `chips`

```elixir
@spec chips() :: [chip()]
```

Returns the list of supported chip presets.

### Examples

    iex> :ws2814_nw in Color.LED.chips()
    true

    iex> :ws2805 in Color.LED.chips()
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
