Add per-target protocol selection (DDP/HTTP) and reorganize target editor

- Add protocol field (ddp/http) to storage, API schemas, routes, processor
- WledTargetProcessor passes protocol to create_led_client(use_ddp=...)
- Target editor: protocol dropdown + keepalive in collapsible Specific Settings
- FPS, brightness threshold, adaptive FPS moved to main form area
- Hide Specific Settings section for serial devices (protocol is WLED-only)
- Card badge: show DDP/HTTP for WLED devices, Serial for serial devices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 20:52:03 +03:00
parent cadef971e7
commit fda040ae18
11 changed files with 87 additions and 21 deletions

View File

@@ -101,6 +101,7 @@ class PictureTargetStore:
state_check_interval: int = DEFAULT_STATE_CHECK_INTERVAL,
min_brightness_threshold: int = 0,
adaptive_fps: bool = False,
protocol: str = "ddp",
key_colors_settings: Optional[KeyColorsSettings] = None,
description: Optional[str] = None,
picture_source_id: str = "",
@@ -135,6 +136,7 @@ class PictureTargetStore:
state_check_interval=state_check_interval,
min_brightness_threshold=min_brightness_threshold,
adaptive_fps=adaptive_fps,
protocol=protocol,
description=description,
auto_start=auto_start,
created_at=now,
@@ -173,6 +175,7 @@ class PictureTargetStore:
state_check_interval: Optional[int] = None,
min_brightness_threshold: Optional[int] = None,
adaptive_fps: Optional[bool] = None,
protocol: Optional[str] = None,
key_colors_settings: Optional[KeyColorsSettings] = None,
description: Optional[str] = None,
auto_start: Optional[bool] = None,
@@ -203,6 +206,7 @@ class PictureTargetStore:
state_check_interval=state_check_interval,
min_brightness_threshold=min_brightness_threshold,
adaptive_fps=adaptive_fps,
protocol=protocol,
key_colors_settings=key_colors_settings,
description=description,
auto_start=auto_start,

View File

@@ -21,6 +21,7 @@ class WledPictureTarget(PictureTarget):
state_check_interval: int = DEFAULT_STATE_CHECK_INTERVAL
min_brightness_threshold: int = 0 # brightness below this → 0 (disabled when 0)
adaptive_fps: bool = False # auto-reduce FPS when device is unresponsive
protocol: str = "ddp" # "ddp" (UDP) or "http" (JSON API)
def register_with_manager(self, manager) -> None:
"""Register this WLED target with the processor manager."""
@@ -35,6 +36,7 @@ class WledPictureTarget(PictureTarget):
brightness_value_source_id=self.brightness_value_source_id,
min_brightness_threshold=self.min_brightness_threshold,
adaptive_fps=self.adaptive_fps,
protocol=self.protocol,
)
def sync_with_manager(self, manager, *, settings_changed: bool,
@@ -60,7 +62,7 @@ class WledPictureTarget(PictureTarget):
def update_fields(self, *, name=None, device_id=None, color_strip_source_id=None,
brightness_value_source_id=None,
fps=None, keepalive_interval=None, state_check_interval=None,
min_brightness_threshold=None, adaptive_fps=None,
min_brightness_threshold=None, adaptive_fps=None, protocol=None,
description=None, auto_start=None, **_kwargs) -> None:
"""Apply mutable field updates for WLED targets."""
super().update_fields(name=name, description=description, auto_start=auto_start)
@@ -80,6 +82,8 @@ class WledPictureTarget(PictureTarget):
self.min_brightness_threshold = min_brightness_threshold
if adaptive_fps is not None:
self.adaptive_fps = adaptive_fps
if protocol is not None:
self.protocol = protocol
@property
def has_picture_source(self) -> bool:
@@ -96,6 +100,7 @@ class WledPictureTarget(PictureTarget):
d["state_check_interval"] = self.state_check_interval
d["min_brightness_threshold"] = self.min_brightness_threshold
d["adaptive_fps"] = self.adaptive_fps
d["protocol"] = self.protocol
return d
@classmethod
@@ -123,6 +128,7 @@ class WledPictureTarget(PictureTarget):
state_check_interval=data.get("state_check_interval", DEFAULT_STATE_CHECK_INTERVAL),
min_brightness_threshold=data.get("min_brightness_threshold", 0),
adaptive_fps=data.get("adaptive_fps", False),
protocol=data.get("protocol", "ddp"),
description=data.get("description"),
auto_start=data.get("auto_start", False),
created_at=datetime.fromisoformat(data.get("created_at", datetime.utcnow().isoformat())),