refactor: key colors targets → CSS source type, HA target improvements
Lint & Test / test (push) Successful in 1m26s

Key Colors refactor:
- New `key_colors` CSS source type with inline rectangles
- KeyColorsColorStripStream: extracts N colors from screen regions
- CSS editor: EntitySelect for picture source, IconSelect for color mode
- Configure Regions button on card opens pattern canvas editor
- Live WS preview at 5 FPS with rectangle overlay + color swatches
- Removed KC target type, pattern template entity, and related API routes
- Removed KC/pattern template sections from Targets tab

HA light target improvements:
- Update rate, transition, mappings, brightness VS now editable via PUT
- Card crosslinks for HA source, CSS source, brightness VS
- HA connection status icon, text metrics (Hz, uptime)
- Brightness value source selector in editor
This commit is contained in:
2026-03-28 15:28:22 +03:00
parent 89d1b13854
commit 3e6760f726
46 changed files with 2707 additions and 789 deletions
@@ -5,9 +5,6 @@ import pytest
from wled_controller.storage.output_target import OutputTarget
from wled_controller.storage.output_target_store import OutputTargetStore
from wled_controller.storage.wled_output_target import WledOutputTarget
from wled_controller.storage.key_colors_output_target import (
KeyColorsOutputTarget,
)
@pytest.fixture
@@ -37,18 +34,17 @@ class TestOutputTargetModel:
assert isinstance(target, WledOutputTarget)
assert target.device_id == "dev_1"
def test_key_colors_from_dict(self):
def test_key_colors_type_rejected(self):
"""key_colors target type removed — from_dict raises ValueError."""
data = {
"id": "pt_2",
"name": "KC Target",
"target_type": "key_colors",
"picture_source_id": "ps_1",
"settings": {},
"created_at": "2025-01-01T00:00:00+00:00",
"updated_at": "2025-01-01T00:00:00+00:00",
}
target = OutputTarget.from_dict(data)
assert isinstance(target, KeyColorsOutputTarget)
with pytest.raises(ValueError, match="Unknown target type"):
OutputTarget.from_dict(data)
def test_unknown_type_raises(self):
data = {
@@ -82,14 +78,10 @@ class TestOutputTargetStoreCRUD:
assert t.name == "LED 1"
assert store.count() == 1
def test_create_key_colors_target(self, store):
t = store.create_target(
name="KC 1",
target_type="key_colors",
picture_source_id="ps_1",
)
assert isinstance(t, KeyColorsOutputTarget)
assert t.picture_source_id == "ps_1"
def test_create_key_colors_target_rejected(self, store):
"""key_colors target type is no longer supported (migrated to CSS source)."""
with pytest.raises(ValueError, match="Invalid target type"):
store.create_target(name="KC 1", target_type="key_colors")
def test_create_invalid_type(self, store):
with pytest.raises(ValueError, match="Invalid target type"):
@@ -194,11 +186,13 @@ class TestOutputTargetQueries:
class TestOutputTargetPersistence:
def test_persist_and_reload(self, tmp_path):
from wled_controller.storage.database import Database
db_path = str(tmp_path / "ot_persist.db")
db = Database(db_path)
s1 = OutputTargetStore(db)
t = s1.create_target(
"Persist", "led",
"Persist",
"led",
device_id="dev_1",
fps=60,
tags=["tv"],
+4 -10
View File
@@ -2,7 +2,10 @@
import pytest
from wled_controller.core.processing.processor_manager import ProcessorDependencies, ProcessorManager
from wled_controller.core.processing.processor_manager import (
ProcessorDependencies,
ProcessorManager,
)
@pytest.fixture
@@ -230,8 +233,6 @@ def test_get_target_metrics(processor_manager):
def test_target_type_detection(processor_manager):
"""Test target type detection via processor instances."""
from wled_controller.storage.key_colors_output_target import KeyColorsSettings
from wled_controller.core.processing.kc_target_processor import KCTargetProcessor
from wled_controller.core.processing.wled_target_processor import WledTargetProcessor
processor_manager.add_device(
@@ -245,13 +246,6 @@ def test_target_type_detection(processor_manager):
device_id="test_device",
)
processor_manager.add_kc_target(
target_id="kc_target",
picture_source_id="src_1",
settings=KeyColorsSettings(),
)
assert isinstance(processor_manager._processors["kc_target"], KCTargetProcessor)
assert isinstance(processor_manager._processors["wled_target"], WledTargetProcessor)