Themes API Reference¶
The theme system provides professionally-curated color palettes for coordinated lighting across LIFX devices.
Theme Class¶
The Theme class represents a collection of HSBK colors forming a coordinated palette.
Theme
¶
Theme(
colors: list[HSBK] | None = None,
*,
slug: str | None = None,
name: str | None = None,
unicode_name: str | None = None,
category: str | None = None,
disposition: Disposition | None = None,
replaced_by: str | None = None,
static_mode: StaticMode | None = None,
dynamic_mode: DynamicMode | None = None,
tags: Iterable[str] = (),
)
A collection of colors representing a theme or color palette.
Themes can be applied to LIFX devices to coordinate colors across multiple lights. Supports both single-zone and multi-zone devices.
| ATTRIBUTE | DESCRIPTION |
|---|---|
colors |
List of HSBK colors in the theme |
slug |
Library key for a theme from
|
name |
Display name for a theme from
|
unicode_name |
Correctly spelt display name for a theme from
|
category |
Category for a theme from
|
disposition |
Recorded fate of a theme from
|
replaced_by |
Successor key of a deprecated or renamed theme from
|
static_mode |
Still-image mode the LIFX app paints this theme with,
such as
TYPE:
|
dynamic_mode |
Effect the app's Dynamic toggle starts, when the
theme names one; see
TYPE:
|
tags |
The app's search tags for this theme, such as |
Note
shuffled() returns an identity-less copy: slug, name, category,
disposition, replaced_by, static_mode, dynamic_mode and tags do not
propagate. This is a known deferred limitation of the identity
round-trip guarantee.
(random() returns a single HSBK, not a Theme, so it carries
no identity to begin with.)
Note
== compares identity, so a Theme stays hashable and usable as
a dict key or set member. To compare palettes, call
palette_equals().
Example
# Create a theme with specific colors
theme = Theme(
[
HSBK(hue=0, saturation=1.0, brightness=1.0, kelvin=3500), # Red
HSBK(hue=120, saturation=1.0, brightness=1.0, kelvin=3500), # Green
HSBK(hue=240, saturation=1.0, brightness=1.0, kelvin=3500), # Blue
]
)
# Access colors
for color in theme:
print(f"Color: {color.hue}°")
# Get a specific color
first_color = theme[0]
# Add more colors
theme.add_color(HSBK(hue=180, saturation=1.0, brightness=1.0, kelvin=3500))
| PARAMETER | DESCRIPTION |
|---|---|
colors
|
List of HSBK colors (defaults to white if None or empty) |
slug
|
Library key for the theme (attached by
TYPE:
|
name
|
Display name for the theme (attached by
TYPE:
|
unicode_name
|
Correctly spelt display name (attached by
TYPE:
|
category
|
Category for the theme (attached by
TYPE:
|
disposition
|
Recorded fate of the theme (attached by
TYPE:
|
replaced_by
|
Successor key of a deprecated or renamed theme
(attached by
TYPE:
|
static_mode
|
Still-image mode (attached by
TYPE:
|
dynamic_mode
|
Dynamic effect mode, when the theme names one
(attached by
TYPE:
|
tags
|
Search tags (attached by |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If a mode is not a canonical identifier. |
TypeError
|
If |
Example
| METHOD | DESCRIPTION |
|---|---|
add_color |
Add a color to the theme. |
random |
Get a random color from the theme. |
shuffled |
Get a new theme with colors in random order. |
get_next_bounds_checked |
Get the next color after index or the last color if at end. |
ensure_color |
Ensure the theme has at least one color. |
__len__ |
Get the number of colors in the theme. |
__iter__ |
Iterate over colors in the theme. |
__getitem__ |
Get a color by index. |
__contains__ |
Check if a color is in the theme. |
palette_equals |
Check whether two themes carry the same palette. |
__repr__ |
Return a string representation of the theme. |
Source code in src/lifx/theme/theme.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
Attributes¶
resolved_dynamic_mode
property
¶
The effect the LIFX app's Dynamic toggle starts for this theme.
dynamic_mode when the theme names one. Otherwise the app's rule:
MORPH for a blended theme (or one with no static mode) and MOVE
for every other static mode. Substituting MORPH on a light that
cannot run MOVE depends on the device and is left to the renderer.
| RETURNS | DESCRIPTION |
|---|---|
DynamicMode
|
The dynamic mode to start. |
Methods:¶
add_color
¶
add_color(color: HSBK) -> None
Add a color to the theme.
| PARAMETER | DESCRIPTION |
|---|---|
color
|
HSBK color to add
TYPE:
|
Source code in src/lifx/theme/theme.py
random
¶
random() -> HSBK
Get a random color from the theme.
| RETURNS | DESCRIPTION |
|---|---|
HSBK
|
A random HSBK color from the theme |
Source code in src/lifx/theme/theme.py
shuffled
¶
shuffled() -> Theme
Get a new theme with colors in random order.
| RETURNS | DESCRIPTION |
|---|---|
Theme
|
New Theme instance with shuffled colors |
Source code in src/lifx/theme/theme.py
get_next_bounds_checked
¶
Get the next color after index or the last color if at end.
| PARAMETER | DESCRIPTION |
|---|---|
index
|
Index of current color
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
HSBK
|
Next HSBK color or the last color if index is at the end |
Example
Source code in src/lifx/theme/theme.py
ensure_color
¶
Ensure the theme has at least one color.
If the theme is empty, adds a default white color.
Source code in src/lifx/theme/theme.py
__iter__
¶
__getitem__
¶
Get a color by index.
| PARAMETER | DESCRIPTION |
|---|---|
index
|
Index of the color (0-based)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
HSBK
|
HSBK color at the given index |
| RAISES | DESCRIPTION |
|---|---|
IndexError
|
If index is out of range |
Source code in src/lifx/theme/theme.py
__contains__
¶
Check if a color is in the theme.
| PARAMETER | DESCRIPTION |
|---|---|
color
|
HSBK color to check
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
True if color is in theme (by value comparison) |
Source code in src/lifx/theme/theme.py
palette_equals
¶
Check whether two themes carry the same palette.
This compares palettes, not layouts, so order is never compared: two
orderings of one palette are the same palette. For an ordered
comparison, use a.colors == b.colors. Identity (slug, name,
category, disposition and replaced_by) is excluded too: an
identity-bearing library theme and a caller-built theme with the
same colors have the same palette.
Colors compare at uint16 (protocol) granularity via HSBK equality,
and duplicate counts matter — a multiset comparison, not a set
comparison.
This is deliberately a named method rather than __eq__. A
Theme's palette is mutable via add_color(), so value equality
could not be paired with a stable __hash__; making ==
compare palettes would leave Theme unhashable and silently change
what theme in [a, b], list.index() and list.remove()
mean. == therefore stays identity comparison and the palette
comparison is spelled out at the call site.
| PARAMETER | DESCRIPTION |
|---|---|
other
|
Theme to compare palettes with.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
True if both palettes are the same multiset of colors. |
Example
Source code in src/lifx/theme/theme.py
ThemeLibrary Class¶
The ThemeLibrary provides access to 378 themes, resolvable under 381 names.
ThemeLibrary
¶
Collection of built-in colour themes for LIFX devices.
Provides access to every theme in the LIFX app (sport themes excluded), the themes the app has since dropped and the pre-6.3.0 library keys, organised by the app's own categories.
Example
# Get a specific theme
evening_theme = ThemeLibrary.get("evening")
# List all available themes
all_themes = ThemeLibrary.get_available_themes()
# Get themes by category
categories = ThemeLibrary.get_categories()
holidays = ThemeLibrary.get_by_category("Holidays")
# Find themes by tag, category and effect mode. The available tags
# come from the app and change with each resync, so check first.
tags = ThemeLibrary.get_tags()
if "Calm" in tags:
calm = ThemeLibrary.get_by_tag("Calm")
blended = ThemeLibrary.find(static_mode="blended")
# Apply to a light
await light.apply_theme(evening_theme, power_on=True)
| METHOD | DESCRIPTION |
|---|---|
get |
Get a theme by name. |
get_available_themes |
Get all available themes by name. |
get_categories |
Get every category present in the library's data. |
get_by_category |
Get all themes in a category. |
get_tags |
Get every tag present in the library's data. |
get_by_tag |
Get all themes carrying a tag. |
find |
Find themes by tags, category and static mode together. |
Methods:¶
get
classmethod
¶
Get a theme by name.
| PARAMETER | DESCRIPTION |
|---|---|
name
|
Theme name (case-insensitive)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Theme
|
Theme object |
| RAISES | DESCRIPTION |
|---|---|
KeyError
|
If theme name is not found |
Example
Source code in src/lifx/theme/library.py
get_available_themes
classmethod
¶
Get all available themes by name.
| RETURNS | DESCRIPTION |
|---|---|
list[str]
|
Sorted list of theme names |
Example
Source code in src/lifx/theme/library.py
get_categories
classmethod
¶
Get every category present in the library's data.
| RETURNS | DESCRIPTION |
|---|---|
list[str]
|
Sorted |
list[str]
|
library's theme records. |
Example
Source code in src/lifx/theme/library.py
get_by_category
classmethod
¶
Get all themes in a category.
| PARAMETER | DESCRIPTION |
|---|---|
category
|
Category name. Matching is case- and punctuation-
insensitive: both sides are normalised by the slug rule, so
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Theme]
|
Dictionary of Theme objects in the category, keyed by slug and |
dict[str, Theme]
|
sorted by slug. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/lifx/theme/library.py
get_tags
classmethod
¶
Get every tag present in the library's data.
Tags are the LIFX app's search tags, such as "Calm" or
"Date night". A theme with no app source carries none.
| RETURNS | DESCRIPTION |
|---|---|
list[str]
|
Distinct tags, sorted case-insensitively. |
Source code in src/lifx/theme/library.py
get_by_tag
classmethod
¶
Get all themes carrying a tag.
| PARAMETER | DESCRIPTION |
|---|---|
tag
|
Tag name. Matching is case- and punctuation-insensitive:
both sides are normalised by the slug rule, so
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Theme]
|
Dictionary of Theme objects keyed by slug and sorted by slug. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/lifx/theme/library.py
find
classmethod
¶
find(
*,
tags: Iterable[str] = (),
match: Literal["all", "any"] = "all",
category: str | None = None,
static_mode: str | None = None,
) -> dict[str, Theme]
Find themes by tags, category and static mode together.
Every given criterion must hold. match decides only how the
tags combine: "all" requires every tag, "any" at least one.
With no criteria, every theme is returned.
| PARAMETER | DESCRIPTION |
|---|---|
tags
|
Tag names, matched as |
match
|
TYPE:
|
category
|
Category name, matched as
TYPE:
|
static_mode
|
Exact static mode, such as
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Theme]
|
Dictionary of matching Theme objects keyed by slug and sorted by |
dict[str, Theme]
|
slug; empty when valid criteria select nothing. |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
ValueError
|
If |
Example
Source code in src/lifx/theme/library.py
Effect Modes and Tags¶
Every library theme records how the LIFX app shows it:
Theme.static_modeis the still image the app paints, such asblended(a gradient from the palette) orgrid_static(the colours are an ordered grid). A theme you build yourself has none and is treated asblended.Theme.dynamic_modeis the effect the app's Dynamic toggle starts, when the theme names one.Theme.resolved_dynamic_modegives the effect either way: MORPH forblendedthemes and MOVE for the rest, unless the theme says otherwise.Theme.tagsholds the app's search tags, such asCalm.
StaticMode and DynamicMode are the matching Literal types. They list
every mode the library's data contains, so a new release can widen them.
Colours are stored in source order, which matters most for grid_static
themes and other non-blended static modes: there the order is the layout.
Tags are spelt as the app spells them (for example Cozy). Tag matching
ignores case and punctuation, so "calm" finds Calm.
Only the LIFX app's own themes carry tags; library-only and deprecated themes
have none and use the blended static mode. An unknown tag, category or static
mode raises ValueError, so check ThemeLibrary.get_tags() first.
Find themes with ThemeLibrary.get_tags(), ThemeLibrary.get_by_tag() and
ThemeLibrary.find():
from lifx.theme import ThemeLibrary
tags = ThemeLibrary.get_tags()
if "Calm" in tags:
calm = ThemeLibrary.get_by_tag("calm")
if {"Calm", "Cozy"} <= set(tags):
calm_or_cosy = ThemeLibrary.find(tags=["Calm", "Cozy"], match="any")
Moods¶
apply_mood() paints a theme the way the LIFX app paints a mood, and
animate_mood() starts the effect the app's Dynamic toggle starts. Both take
only the theme and use the app's own timings: a 0.3 second fade, and the mood
rescaled so its brightest colour matches the light's brightness, animated
palettes included. apply_mood() powers lights on only when every targeted
light is off; animate_mood() turns on every light it targets.
stop_effect() after animate_mood() puts back what the light showed before
its mood animation started: power, and its colour, zones or tiles. Starting
another mood on an animating light keeps that state, and it is restored once.
A DeviceGroup has no stop_effect(); call it on each light.
| Light | Still image (apply_mood) |
Effect (animate_mood) |
|---|---|---|
| Bulb | One of the theme's colours; a DeviceGroup deals them one per bulb |
EffectColorloop through the theme's colours; bulbs in one DeviceGroup share one loop |
| Strip | The mood across the zones | Firmware MOVE, after painting the still image |
| Matrix light | The mood's image, by static_mode |
Firmware MORPH for a MORPH mood; EffectScroll for a MOVE mood |
| Spot and Path | The mood's image, painted like any other matrix light | Firmware MORPH for every mood |
| Mirror | Stripe moods paint as bands along the long axis, first colour at the bottom; other moods as for a matrix light | Firmware MORPH for every mood |
| Candle and Tube | Stripe moods paint as bands along the long axis, first colour at the bottom | A MOVE stripe mood scrolls the bands down the light; other moods as for a matrix light |
The Mirror's still image is painted over its 4x13 buffer as a single matrix light, not ring by ring, with stripe moods as bands along its long axis. The app runs a firmware effect on Spot and Path that this library does not send, so MORPH stands in for it.
A Tile chain is painted as one image in chain order, as the app does, so tiles arranged in an L or a stack show the image in chain order rather than following their arrangement. A Tile whose accelerometer reports a rotation (left, right or upside down) is remapped; FaceUp and FaceDown are not. Only chain-capable products (the Tile) have their reported orientation applied; other matrix products, such as the Luna, are fixed panels whose accelerometer readings are not used. A chain scrolls as one canvas, in chain order.
If a light is already running a mood effect, apply_mood() restarts that
effect with the new theme instead of painting a still image.
apply_theme() is unchanged; use it for the library's own gradients.
MoodGenerator
¶
Turn a theme into the colours the LIFX app paints for it.
The get_*_colors() methods paint a still image; get_palette()
gives the colours an animated mood steps through. Each is rescaled so its
brightest colour matches the light's brightness.
Example
| PARAMETER | DESCRIPTION |
|---|---|
theme
|
The theme to paint. Its
TYPE:
|
rng
|
Random source for the shuffles, for repeatable output.
TYPE:
|
| METHOD | DESCRIPTION |
|---|---|
get_multizone_colors |
Colours for a strip, one per zone. |
get_matrix_colors |
Colours for one matrix light, in row-major order. |
get_chain_colors |
Colours for a Tile chain, laid out in chain order. |
get_bulb_colors |
Deal the theme's distinct colours, shuffled, one per bulb in turn. |
get_palette |
The theme's colours, in order, rescaled to the light's brightness. |
morph_palette |
Reduce a palette to what firmware MORPH can carry. |
Source code in src/lifx/theme/generators/mood.py
Methods:¶
get_multizone_colors
¶
Colours for a strip, one per zone.
| PARAMETER | DESCRIPTION |
|---|---|
zone_count
|
Number of zones to fill
TYPE:
|
brightness
|
The light's current brightness, 0.0 to 1.0
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[HSBK]
|
One colour per zone |
Source code in src/lifx/theme/generators/mood.py
get_matrix_colors
¶
get_matrix_colors(
width: int, height: int, brightness: float, *, vertical: bool = False
) -> list[HSBK]
Colours for one matrix light, in row-major order.
| PARAMETER | DESCRIPTION |
|---|---|
width
|
Pixels per row, as the device reports
TYPE:
|
height
|
Rows, as the device reports
TYPE:
|
brightness
|
The light's current brightness, 0.0 to 1.0
TYPE:
|
vertical
|
Paint stripe moods as bands along the long axis, as the app does on every Candle, the Tube and the Mirror
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[HSBK]
|
|
Source code in src/lifx/theme/generators/mood.py
get_chain_colors
¶
Colours for a Tile chain, laid out in chain order.
The app ignores where the tiles sit: one canvas width * tile_count
wide is painted and sliced per tile. A blended mood paints each tile
on its own.
| PARAMETER | DESCRIPTION |
|---|---|
tile_count
|
Tiles in the chain
TYPE:
|
width
|
Pixels per row of one tile
TYPE:
|
height
|
Rows of one tile
TYPE:
|
brightness
|
The light's current brightness, 0.0 to 1.0
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[list[HSBK]]
|
Per tile, |
Source code in src/lifx/theme/generators/mood.py
get_bulb_colors
¶
Deal the theme's distinct colours, shuffled, one per bulb in turn.
| PARAMETER | DESCRIPTION |
|---|---|
brightnesses
|
Each bulb's current brightness, in bulb order |
| RETURNS | DESCRIPTION |
|---|---|
list[HSBK]
|
One colour per bulb, rescaled to that bulb's brightness |
Source code in src/lifx/theme/generators/mood.py
get_palette
¶
The theme's colours, in order, rescaled to the light's brightness.
An animated mood steps through these: a bulb's colour loop and a matrix light's firmware MORPH, as the app rescales them.
| PARAMETER | DESCRIPTION |
|---|---|
brightness
|
The light's brightness, 0.0 to 1.0. At 0.0 the colours are returned unchanged.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[HSBK]
|
One colour per theme colour, the brightest at |
Source code in src/lifx/theme/generators/mood.py
morph_palette
staticmethod
¶
Reduce a palette to what firmware MORPH can carry.
The app's run-weighted reduction: deterministic, so only the order the caller shuffles it into varies.
| PARAMETER | DESCRIPTION |
|---|---|
colors
|
Palette in source order |
| RETURNS | DESCRIPTION |
|---|---|
list[HSBK]
|
The palette unchanged when it fits, else |
list[HSBK]
|
colours weighted by the area each run covers |
Source code in src/lifx/theme/generators/mood.py
Convenience Function¶
get_theme
¶
Get a theme by name.
Convenience function equivalent to ThemeLibrary.get(name).
| PARAMETER | DESCRIPTION |
|---|---|
name
|
Theme name (case-insensitive)
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Theme
|
Theme object |
Example
Source code in src/lifx/theme/library.py
Built-in Theme Catalogue¶
For the live category/count table, executable enumeration examples, compatibility notes and fidelity boundary, see the Built-in Theme Catalogue.
Theme.disposition and Theme.replaced_by record each theme's fate; the six pre-6.4.0
category names are retired and raise ValueError. Both are documented on the
Theme Taxonomy Changes page.