Rework API input CSS: segments, remove led_count, HAOS light, test preview
API Input CSS rework:
- Remove led_count field from ApiInputColorStripSource (always auto-sizes)
- Add segment-based payload: solid, per_pixel, gradient modes
- Segments applied in order (last wins on overlap), auto-grow buffer
- Backward compatible: legacy {"colors": [...]} still works
- Pydantic validation: mode-specific field requirements
Test preview:
- Enable test preview button on api_input cards
- Hide LED/FPS controls for api_input (sender controls those)
- Show input source selector for all CSS tests (preselected)
- FPS sparkline chart using shared createFpsSparkline (same as target cards)
- Server only sends frames when push_generation changes (no idle frames)
HAOS integration:
- New light.py: ApiInputLight entity per api_input source (RGB + brightness)
- turn_on pushes solid segment, turn_off pushes fallback color
- Register wled_screen_controller.set_leds service for arbitrary segments
- New services.yaml with field definitions
- Coordinator: push_colors() and push_segments() methods
- Platform.LIGHT added to platforms list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
from wled_controller.api.schemas.devices import Calibration
|
||||
|
||||
@@ -237,10 +237,52 @@ class ColorStripSourceListResponse(BaseModel):
|
||||
count: int = Field(description="Number of sources")
|
||||
|
||||
|
||||
class ColorPushRequest(BaseModel):
|
||||
"""Request to push raw LED colors to an api_input source."""
|
||||
class SegmentPayload(BaseModel):
|
||||
"""A single segment for segment-based LED color updates."""
|
||||
|
||||
colors: List[List[int]] = Field(description="LED color array [[R,G,B], ...] (0-255 each)")
|
||||
start: int = Field(ge=0, description="Starting LED index")
|
||||
length: int = Field(ge=1, description="Number of LEDs in segment")
|
||||
mode: Literal["solid", "per_pixel", "gradient"] = Field(description="Fill mode")
|
||||
color: Optional[List[int]] = Field(None, description="RGB for solid mode [R,G,B]")
|
||||
colors: Optional[List[List[int]]] = Field(None, description="Colors for per_pixel/gradient [[R,G,B],...]")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_mode_fields(self) -> "SegmentPayload":
|
||||
if self.mode == "solid":
|
||||
if self.color is None or len(self.color) != 3:
|
||||
raise ValueError("solid mode requires 'color' as a list of 3 ints [R,G,B]")
|
||||
if not all(0 <= c <= 255 for c in self.color):
|
||||
raise ValueError("solid color values must be 0-255")
|
||||
elif self.mode == "per_pixel":
|
||||
if not self.colors:
|
||||
raise ValueError("per_pixel mode requires non-empty 'colors' list")
|
||||
for c in self.colors:
|
||||
if len(c) != 3:
|
||||
raise ValueError("each color in per_pixel must be [R,G,B]")
|
||||
elif self.mode == "gradient":
|
||||
if not self.colors or len(self.colors) < 2:
|
||||
raise ValueError("gradient mode requires 'colors' with at least 2 stops")
|
||||
for c in self.colors:
|
||||
if len(c) != 3:
|
||||
raise ValueError("each color stop in gradient must be [R,G,B]")
|
||||
return self
|
||||
|
||||
|
||||
class ColorPushRequest(BaseModel):
|
||||
"""Request to push raw LED colors to an api_input source.
|
||||
|
||||
Accepts either 'colors' (legacy flat array) or 'segments' (new segment-based).
|
||||
At least one must be provided.
|
||||
"""
|
||||
|
||||
colors: Optional[List[List[int]]] = Field(None, description="LED color array [[R,G,B], ...] (0-255 each)")
|
||||
segments: Optional[List[SegmentPayload]] = Field(None, description="Segment-based color updates")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _require_colors_or_segments(self) -> "ColorPushRequest":
|
||||
if self.colors is None and self.segments is None:
|
||||
raise ValueError("Either 'colors' or 'segments' must be provided")
|
||||
return self
|
||||
|
||||
|
||||
class NotifyRequest(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user