Hue.Color (Hue v0.2.0)

Copy Markdown View Source

Colour conversion for Hue lights.

Developers think in hex, RGB, and Kelvin. Hue speaks CIE xy and mirek, and the representable range differs per light. This module bridges the two, always against the gamut of the light being addressed rather than a generic one — see Hue.Color.Gamut.

{:ok, xy} = Hue.Color.to_xy("#ff8800", light)
370 = Hue.Color.kelvin_to_mirek(2700)

On accuracy

to_hex/1 is not "a shade off" — it discards luminance entirely and always answers with the brightest colour of the given hue. An xy pair carries no luminance, so converting back to RGB requires inventing one, and this invents the maximum (yY: 1.0). Two colours that differ only in brightness — #000000, #808080, and #ffffff are all achromatic, so they share one chromaticity, the D65 white point — convert to the exact same hex: #000000 becomes #ffffff. Use it to show which colour a light is set to, never how bright.

Bridge-data errors versus caller bugs

A colour that cannot be represented because the bridge reports data this module cannot use — no "color" key, or one that cannot be turned into a gamut triangle (see Hue.Color.Gamut.from_light/1) — comes back as {:error, %Hue.Error{}}. A colour that cannot be represented because the caller passed something that was never valid — an RGB component outside 0..255, a hex string with non-hex characters, a non-positive Kelvin value — raises, consistent with the rest of this library (see Hue.Error's moduledoc).

Summary

Functions

Converts a colour temperature in Kelvin to mirek, Hue's reciprocal unit.

Converts Kelvin to mirek and clamps it to the range light reports for itself, rather than to a hardcoded constant.

Builds the CLIP v2 body for setting a colour on light.

Converts an xy pair to a hex string, for display only.

Converts a colour into an xy pair inside light's gamut.

Types

input()

@type input() :: String.t() | {0..255, 0..255, 0..255} | {:xy, number(), number()}

Functions

kelvin_to_mirek(kelvin)

@spec kelvin_to_mirek(pos_integer()) :: pos_integer()

Converts a colour temperature in Kelvin to mirek, Hue's reciprocal unit.

Raises for a non-positive Kelvin value — dividing by zero or a negative temperature was never a valid call, so this is a caller bug rather than something to clamp away.

iex> Hue.Color.kelvin_to_mirek(2700)
370

mirek_for(kelvin, light)

@spec mirek_for(pos_integer(), map()) ::
  {:ok, pos_integer()} | {:error, Hue.Error.t()}

Converts Kelvin to mirek and clamps it to the range light reports for itself, rather than to a hardcoded constant.

Falls back to 153-500 (Philips's published absolute mirek bounds) only if light reports "color_temperature" but no "mirek_schema" — see the moduledoc note on those constants above.

payload(input, light)

@spec payload(input(), map()) :: {:ok, map()} | {:error, Hue.Error.t()}

Builds the CLIP v2 body for setting a colour on light.

to_hex(arg)

@spec to_hex(Hue.Color.Gamut.point()) :: {:ok, String.t()} | {:error, Hue.Error.t()}

Converts an xy pair to a hex string, for display only.

Not a lossless round trip: an xy pair carries no luminance, so this invents the maximum (yY: 1.0) and always answers with the brightest colour of that hue. Every achromatic input — {0.3127, 0.3290}, the D65 white point that #000000, any grey, and #ffffff all share — comes back "#ffffff". See the moduledoc's "On accuracy" section. Good for showing which colour a light is set to; never use it to judge brightness.

to_xy(input, light)

@spec to_xy(input(), map()) ::
  {:ok, Hue.Color.Gamut.point()} | {:error, Hue.Error.t()}

Converts a colour into an xy pair inside light's gamut.

Accepts a hex string ("#ff8800"), an {r, g, b} tuple of 0..255 integers, or an explicit {:xy, x, y} pair. Whatever the input resolves to is clamped into light's own reported gamut (see Hue.Color.Gamut.clamp/2) — a colour outside that triangle is not merely approximate, it is unrepresentable on this specific light.

Returns {:error, %Hue.Error{reason: :not_color_capable}} if light has no colour support at all, and {:error, %Hue.Error{reason: :invalid_gamut}} if it claims colour support but the bridge's gamut data could not be parsed — see Hue.Color.Gamut.from_light/1. Both are bridge-data problems, not caller bugs.

Raises if input itself could never have been valid — an RGB component outside 0..255, or a hex string that is not valid hex.