Add openrgb-hue: Hue scene gradients for OpenRGB lights

Applies gradients derived from 103 bundled Philips Hue scenes to
OpenRGB-controlled LEDs, with six mapping modes (sequence, per-device,
per-zone, matrix, mirror, shuffle) and five animation modes (static,
scroll, pingpong, pulse, wave). Typer/Rich CLI with dry-run preview,
device/zone targeting, and a scenes-update command to refresh the
bundled dataset from its source gist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 12:40:13 +02:00
co-authored by Claude Sonnet 5
commit 71921b5600
24 changed files with 3131 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
import pytest
from typer.testing import CliRunner
from openrgb_hue.cli import app
runner = CliRunner()
def test_scenes_list_shows_scene_count():
result = runner.invoke(app, ["scenes", "list"])
assert result.exit_code == 0
assert "103" in result.output
def test_scenes_list_filter():
result = runner.invoke(app, ["scenes", "list", "--filter", "tropical"])
assert result.exit_code == 0
assert "Tropical twilight" in result.output
def test_scenes_show_known_scene():
result = runner.invoke(app, ["scenes", "show", "Tropical twilight"])
assert result.exit_code == 0
assert "Tropical twilight" in result.output
assert "Derived gradient" in result.output
def test_scenes_show_unknown_scene_errors():
result = runner.invoke(app, ["scenes", "show", "Definitely Not A Scene"])
assert result.exit_code == 1
@pytest.mark.parametrize("mapping_mode", ["sequence", "per-device", "per-zone", "mirror", "shuffle"])
def test_apply_dry_run_across_mapping_modes(mapping_mode):
result = runner.invoke(app, ["apply", "Tropical twilight", "--dry-run", "--mapping", mapping_mode])
assert result.exit_code == 0, result.output
assert "synthetic LEDs" in result.output
def test_apply_dry_run_matrix_radial():
result = runner.invoke(
app,
["apply", "Tropical twilight", "--dry-run", "--mapping", "matrix", "--direction", "radial"],
)
assert result.exit_code == 0, result.output
@pytest.mark.parametrize("mode", ["static", "scroll", "pingpong", "pulse", "wave"])
def test_animate_dry_run_across_animation_modes(mode):
result = runner.invoke(
app, ["animate", "Tropical twilight", "--dry-run", "--mode", mode, "--dry-run-frames", "3"]
)
assert result.exit_code == 0, result.output
assert "t=0.00s" in result.output
def test_apply_unknown_scene_errors():
result = runner.invoke(app, ["apply", "Not A Real Scene", "--dry-run"])
assert result.exit_code == 1
def test_apply_unknown_mapping_mode_errors():
result = runner.invoke(app, ["apply", "Tropical twilight", "--dry-run", "--mapping", "nonsense"])
assert result.exit_code == 1