Animation None option, FPS min 1, serial COM lifecycle fixes

- Replace animation Enable checkbox with None option in effect selector;
  show effect description tooltip; disable speed slider when None selected
- Allow target FPS range 1-90 (was 10-90) across UI and backend validation
- Scope serial COM connections to target lifetime (no idle caching);
  use temporary connections for power-off/test mode
- Fix serial black frame on stop: flush after write, delay after task
  cancel to prevent race with in-flight thread pool write

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 04:33:56 +03:00
parent 8a0730d91b
commit ee52e2d98f
15 changed files with 126 additions and 71 deletions

View File

@@ -13,7 +13,7 @@ class PictureSourceCreate(BaseModel):
stream_type: Literal["raw", "processed", "static_image"] = Field(description="Stream type")
display_index: Optional[int] = Field(None, description="Display index (raw streams)", ge=0)
capture_template_id: Optional[str] = Field(None, description="Capture template ID (raw streams)")
target_fps: Optional[int] = Field(None, description="Target FPS (raw streams)", ge=10, le=90)
target_fps: Optional[int] = Field(None, description="Target FPS (raw streams)", ge=1, le=90)
source_stream_id: Optional[str] = Field(None, description="Source stream ID (processed streams)")
postprocessing_template_id: Optional[str] = Field(None, description="Postprocessing template ID (processed streams)")
image_source: Optional[str] = Field(None, description="Image URL or file path (static_image streams)")
@@ -26,7 +26,7 @@ class PictureSourceUpdate(BaseModel):
name: Optional[str] = Field(None, description="Stream name", min_length=1, max_length=100)
display_index: Optional[int] = Field(None, description="Display index (raw streams)", ge=0)
capture_template_id: Optional[str] = Field(None, description="Capture template ID (raw streams)")
target_fps: Optional[int] = Field(None, description="Target FPS (raw streams)", ge=10, le=90)
target_fps: Optional[int] = Field(None, description="Target FPS (raw streams)", ge=1, le=90)
source_stream_id: Optional[str] = Field(None, description="Source stream ID (processed streams)")
postprocessing_template_id: Optional[str] = Field(None, description="Postprocessing template ID (processed streams)")
image_source: Optional[str] = Field(None, description="Image URL or file path (static_image streams)")

View File

@@ -53,7 +53,7 @@ class PictureTargetCreate(BaseModel):
# LED target fields
device_id: str = Field(default="", description="LED device ID")
color_strip_source_id: str = Field(default="", description="Color strip source ID")
fps: int = Field(default=30, ge=10, le=90, description="Target send FPS (10-90)")
fps: int = Field(default=30, ge=1, le=90, description="Target send FPS (1-90)")
standby_interval: float = Field(default=1.0, description="Keepalive send interval when screen is static (0.5-5.0s)", ge=0.5, le=5.0)
state_check_interval: int = Field(default=DEFAULT_STATE_CHECK_INTERVAL, description="Device health check interval (5-600s)", ge=5, le=600)
# KC target fields
@@ -69,7 +69,7 @@ class PictureTargetUpdate(BaseModel):
# LED target fields
device_id: Optional[str] = Field(None, description="LED device ID")
color_strip_source_id: Optional[str] = Field(None, description="Color strip source ID")
fps: Optional[int] = Field(None, ge=10, le=90, description="Target send FPS (10-90)")
fps: Optional[int] = Field(None, ge=1, le=90, description="Target send FPS (1-90)")
standby_interval: Optional[float] = Field(None, description="Keepalive interval (0.5-5.0s)", ge=0.5, le=5.0)
state_check_interval: Optional[int] = Field(None, description="Health check interval (5-600s)", ge=5, le=600)
# KC target fields