Move FPS from color strip source to target; dynamic capture rate

FPS is a consumption property (how fast to send to a device), not a
production property. Two targets sharing the same source may need
different FPS. This moves the fps field from PictureColorStripSource
to WledPictureTarget across the full stack.

The capture stream now auto-adjusts its rate to max(all connected
target FPS values) via ColorStripStreamManager tracking per-consumer
FPS. UI updates: FPS slider in target editor, FPS badge on target
cards, LED count repositioned in CSS editor, consistent speed icons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 03:46:08 +03:00
parent 1204676c30
commit 1f6c913343
14 changed files with 126 additions and 57 deletions

View File

@@ -59,7 +59,6 @@ def _css_to_response(source, overlay_active: bool = False) -> ColorStripSourceRe
name=source.name,
source_type=source.source_type,
picture_source_id=getattr(source, "picture_source_id", None),
fps=getattr(source, "fps", None),
brightness=getattr(source, "brightness", None),
saturation=getattr(source, "saturation", None),
gamma=getattr(source, "gamma", None),
@@ -127,7 +126,6 @@ async def create_color_strip_source(
name=data.name,
source_type=data.source_type,
picture_source_id=data.picture_source_id,
fps=data.fps,
brightness=data.brightness,
saturation=data.saturation,
gamma=data.gamma,
@@ -187,7 +185,6 @@ async def update_color_strip_source(
source_id=source_id,
name=data.name,
picture_source_id=data.picture_source_id,
fps=data.fps,
brightness=data.brightness,
saturation=data.saturation,
gamma=data.gamma,

View File

@@ -94,6 +94,7 @@ def _target_to_response(target) -> PictureTargetResponse:
target_type=target.target_type,
device_id=target.device_id,
color_strip_source_id=target.color_strip_source_id,
fps=target.fps,
standby_interval=target.standby_interval,
state_check_interval=target.state_check_interval,
description=target.description,
@@ -148,6 +149,7 @@ async def create_target(
target_type=data.target_type,
device_id=data.device_id,
color_strip_source_id=data.color_strip_source_id,
fps=data.fps,
standby_interval=data.standby_interval,
state_check_interval=data.state_check_interval,
picture_source_id=data.picture_source_id,
@@ -243,6 +245,7 @@ async def update_target(
name=data.name,
device_id=data.device_id,
color_strip_source_id=data.color_strip_source_id,
fps=data.fps,
standby_interval=data.standby_interval,
state_check_interval=data.state_check_interval,
picture_source_id=data.picture_source_id,
@@ -254,7 +257,8 @@ async def update_target(
try:
target.sync_with_manager(
manager,
settings_changed=(data.standby_interval is not None or
settings_changed=(data.fps is not None or
data.standby_interval is not None or
data.state_check_interval is not None or
data.key_colors_settings is not None),
source_changed=data.color_strip_source_id is not None,