Add per-layer brightness source to composite CSS and enhance selectors
- Add optional brightness_source_id per composite layer using ValueStreamManager - Use EntitySelect for composite layer source and brightness dropdowns - Use IconSelect for composite blend mode and notification filter mode - Add i18n keys for blend mode and filter mode descriptions (en/ru/zh) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,7 @@ class ColorStripStreamManager:
|
||||
keyed by ``{css_id}:{consumer_id}``.
|
||||
"""
|
||||
|
||||
def __init__(self, color_strip_store, live_stream_manager, audio_capture_manager=None, audio_source_store=None, audio_template_store=None, sync_clock_manager=None):
|
||||
def __init__(self, color_strip_store, live_stream_manager, audio_capture_manager=None, audio_source_store=None, audio_template_store=None, sync_clock_manager=None, value_stream_manager=None):
|
||||
"""
|
||||
Args:
|
||||
color_strip_store: ColorStripStore for resolving source configs
|
||||
@@ -76,6 +76,7 @@ class ColorStripStreamManager:
|
||||
audio_capture_manager: AudioCaptureManager for audio-reactive sources
|
||||
audio_source_store: AudioSourceStore for resolving audio source chains
|
||||
sync_clock_manager: SyncClockManager for acquiring clock runtimes
|
||||
value_stream_manager: ValueStreamManager for per-layer brightness sources
|
||||
"""
|
||||
self._color_strip_store = color_strip_store
|
||||
self._live_stream_manager = live_stream_manager
|
||||
@@ -83,6 +84,7 @@ class ColorStripStreamManager:
|
||||
self._audio_source_store = audio_source_store
|
||||
self._audio_template_store = audio_template_store
|
||||
self._sync_clock_manager = sync_clock_manager
|
||||
self._value_stream_manager = value_stream_manager
|
||||
self._streams: Dict[str, _ColorStripEntry] = {}
|
||||
|
||||
def _inject_clock(self, css_stream, source) -> Optional[str]:
|
||||
@@ -159,7 +161,7 @@ class ColorStripStreamManager:
|
||||
css_stream = AudioColorStripStream(source, self._audio_capture_manager, self._audio_source_store, self._audio_template_store)
|
||||
elif source.source_type == "composite":
|
||||
from wled_controller.core.processing.composite_stream import CompositeColorStripStream
|
||||
css_stream = CompositeColorStripStream(source, self)
|
||||
css_stream = CompositeColorStripStream(source, self, self._value_stream_manager)
|
||||
elif source.source_type == "mapped":
|
||||
from wled_controller.core.processing.mapped_stream import MappedColorStripStream
|
||||
css_stream = MappedColorStripStream(source, self)
|
||||
|
||||
@@ -29,12 +29,13 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
sub-stream's latest colors and blending bottom-to-top.
|
||||
"""
|
||||
|
||||
def __init__(self, source, css_manager):
|
||||
def __init__(self, source, css_manager, value_stream_manager=None):
|
||||
self._source_id: str = source.id
|
||||
self._layers: List[dict] = list(source.layers)
|
||||
self._led_count: int = source.led_count
|
||||
self._auto_size: bool = source.led_count == 0
|
||||
self._css_manager = css_manager
|
||||
self._value_stream_manager = value_stream_manager
|
||||
self._fps: int = 30
|
||||
self._frame_time: float = 1.0 / 30
|
||||
|
||||
@@ -45,7 +46,9 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
|
||||
# layer_index -> (source_id, consumer_id, stream)
|
||||
self._sub_streams: Dict[int, tuple] = {}
|
||||
self._sub_lock = threading.Lock() # guards _sub_streams access across threads
|
||||
# layer_index -> (vs_id, value_stream)
|
||||
self._brightness_streams: Dict[int, tuple] = {}
|
||||
self._sub_lock = threading.Lock() # guards _sub_streams and _brightness_streams
|
||||
|
||||
# Pre-allocated scratch (rebuilt when LED count changes)
|
||||
self._pool_n = 0
|
||||
@@ -115,9 +118,9 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
def update_source(self, source) -> None:
|
||||
"""Hot-update: rebuild sub-streams if layer config changed."""
|
||||
new_layers = list(source.layers)
|
||||
old_layer_ids = [(l.get("source_id"), l.get("blend_mode"), l.get("opacity"), l.get("enabled"))
|
||||
old_layer_ids = [(l.get("source_id"), l.get("blend_mode"), l.get("opacity"), l.get("enabled"), l.get("brightness_source_id"))
|
||||
for l in self._layers]
|
||||
new_layer_ids = [(l.get("source_id"), l.get("blend_mode"), l.get("opacity"), l.get("enabled"))
|
||||
new_layer_ids = [(l.get("source_id"), l.get("blend_mode"), l.get("opacity"), l.get("enabled"), l.get("brightness_source_id"))
|
||||
for l in new_layers]
|
||||
|
||||
self._layers = new_layers
|
||||
@@ -152,6 +155,16 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
logger.warning(
|
||||
f"Composite layer {i} (source {src_id}) failed to acquire: {e}"
|
||||
)
|
||||
# Acquire brightness value stream if configured
|
||||
vs_id = layer.get("brightness_source_id")
|
||||
if vs_id and self._value_stream_manager:
|
||||
try:
|
||||
vs = self._value_stream_manager.acquire(vs_id)
|
||||
self._brightness_streams[i] = (vs_id, vs)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Composite layer {i} brightness source {vs_id} failed: {e}"
|
||||
)
|
||||
|
||||
def _release_sub_streams(self) -> None:
|
||||
for _idx, (src_id, consumer_id, _stream) in list(self._sub_streams.items()):
|
||||
@@ -160,6 +173,14 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
except Exception as e:
|
||||
logger.warning(f"Composite layer release error ({src_id}): {e}")
|
||||
self._sub_streams.clear()
|
||||
# Release brightness value streams
|
||||
if self._value_stream_manager:
|
||||
for _idx, (vs_id, _vs) in list(self._brightness_streams.items()):
|
||||
try:
|
||||
self._value_stream_manager.release(vs_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"Composite brightness release error ({vs_id}): {e}")
|
||||
self._brightness_streams.clear()
|
||||
|
||||
# ── Scratch pool ────────────────────────────────────────────
|
||||
|
||||
@@ -299,6 +320,13 @@ class CompositeColorStripStream(ColorStripStream):
|
||||
if len(colors) != target_n:
|
||||
colors = self._resize_to_target(colors, target_n)
|
||||
|
||||
# Apply per-layer brightness from value source
|
||||
if i in self._brightness_streams:
|
||||
_vs_id, vs = self._brightness_streams[i]
|
||||
bri = vs.get_value()
|
||||
if bri < 1.0:
|
||||
colors = (colors.astype(np.uint16) * int(bri * 256) >> 8).astype(np.uint8)
|
||||
|
||||
opacity = layer.get("opacity", 1.0)
|
||||
blend_mode = layer.get("blend_mode", _BLEND_NORMAL)
|
||||
alpha = int(opacity * 256)
|
||||
|
||||
@@ -122,6 +122,8 @@ class ProcessorManager:
|
||||
live_stream_manager=self._live_stream_manager,
|
||||
audio_template_store=audio_template_store,
|
||||
) if value_source_store else None
|
||||
# Wire value stream manager into CSS stream manager for composite layer brightness
|
||||
self._color_strip_stream_manager._value_stream_manager = self._value_stream_manager
|
||||
self._overlay_manager = OverlayManager()
|
||||
self._event_queues: List[asyncio.Queue] = []
|
||||
self._metrics_history = MetricsHistory(self)
|
||||
|
||||
Reference in New Issue
Block a user