Add API Input color strip source type with REST and WebSocket push

New source_type "api_input" allows external clients to push raw LED
color arrays ([R,G,B] per LED) via REST POST or WebSocket. Includes
configurable fallback color and timeout for automatic revert when no
data is received. Stream auto-sizes LED count from the target device.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 17:07:47 +03:00
parent 1e4a7a067f
commit 67a15776b2
10 changed files with 512 additions and 10 deletions

View File

@@ -49,7 +49,7 @@ class ColorStripSourceCreate(BaseModel):
"""Request to create a color strip source."""
name: str = Field(description="Source name", min_length=1, max_length=100)
source_type: Literal["picture", "static", "gradient", "color_cycle", "effect", "composite", "mapped", "audio"] = Field(default="picture", description="Source type")
source_type: Literal["picture", "static", "gradient", "color_cycle", "effect", "composite", "mapped", "audio", "api_input"] = Field(default="picture", description="Source type")
# picture-type fields
picture_source_id: str = Field(default="", description="Picture source ID (for picture type)")
brightness: float = Field(default=1.0, description="Brightness multiplier (0.0-2.0)", ge=0.0, le=2.0)
@@ -86,6 +86,9 @@ class ColorStripSourceCreate(BaseModel):
description: Optional[str] = Field(None, description="Optional description", max_length=500)
frame_interpolation: bool = Field(default=False, description="Blend between consecutive captured frames for smoother output")
animation: Optional[AnimationConfig] = Field(None, description="Procedural animation config (static/gradient only)")
# api_input-type fields
fallback_color: Optional[List[int]] = Field(None, description="Fallback RGB color [R,G,B] when no data received (api_input type)")
timeout: Optional[float] = Field(None, description="Seconds before reverting to fallback (api_input type)", ge=0.0, le=300.0)
class ColorStripSourceUpdate(BaseModel):
@@ -128,6 +131,9 @@ class ColorStripSourceUpdate(BaseModel):
description: Optional[str] = Field(None, description="Optional description", max_length=500)
frame_interpolation: Optional[bool] = Field(None, description="Blend between consecutive captured frames")
animation: Optional[AnimationConfig] = Field(None, description="Procedural animation config (static/gradient only)")
# api_input-type fields
fallback_color: Optional[List[int]] = Field(None, description="Fallback RGB color [R,G,B] (api_input type)")
timeout: Optional[float] = Field(None, description="Timeout before fallback (api_input type)", ge=0.0, le=300.0)
class ColorStripSourceResponse(BaseModel):
@@ -172,6 +178,9 @@ class ColorStripSourceResponse(BaseModel):
description: Optional[str] = Field(None, description="Description")
frame_interpolation: Optional[bool] = Field(None, description="Blend between consecutive captured frames")
animation: Optional[AnimationConfig] = Field(None, description="Procedural animation config (static/gradient only)")
# api_input-type fields
fallback_color: Optional[List[int]] = Field(None, description="Fallback RGB color [R,G,B] (api_input type)")
timeout: Optional[float] = Field(None, description="Timeout before fallback (api_input type)")
overlay_active: bool = Field(False, description="Whether the screen overlay is currently active")
created_at: datetime = Field(description="Creation timestamp")
updated_at: datetime = Field(description="Last update timestamp")
@@ -184,6 +193,12 @@ class ColorStripSourceListResponse(BaseModel):
count: int = Field(description="Number of sources")
class ColorPushRequest(BaseModel):
"""Request to push raw LED colors to an api_input source."""
colors: List[List[int]] = Field(description="LED color array [[R,G,B], ...] (0-255 each)")
class CSSCalibrationTestRequest(BaseModel):
"""Request to run a calibration test for a color strip source on a specific device."""