- Add Color Strip Processing Template (CSPT) entity: reusable filter chains for 1D LED strip postprocessing (backend, storage, API, frontend CRUD) - Add "processed" color strip source type that wraps another CSS source and applies a CSPT filter chain (dataclass, stream, schema, modal, cards) - Add Reverse filter for strip LED order reversal - Add CSPT and processed CSS nodes/edges to visual graph editor - Add CSPT test preview WS endpoint with input source selection - Add device settings CSPT template selector (add + edit modals with hints) - Use icon grids for palette quantization preset selector in filter lists - Use EntitySelect for template references and test modal source selectors - Fix filters.css_filter_template.desc missing localization - Fix icon grid cell height inequality (grid-auto-rows: 1fr) - Rename "Processed" subtab to "Processing Templates" - Localize all new strings (en/ru/zh) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
240 lines
14 KiB
Python
240 lines
14 KiB
Python
"""Device-related schemas (CRUD, calibration, device state)."""
|
|
|
|
from datetime import datetime
|
|
from typing import Dict, List, Literal, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class DeviceCreate(BaseModel):
|
|
"""Request to create/attach an LED device."""
|
|
|
|
name: str = Field(description="Device name", min_length=1, max_length=100)
|
|
url: str = Field(description="Device URL (e.g., http://192.168.1.100 or COM3)")
|
|
device_type: str = Field(default="wled", description="LED device type (e.g., wled, adalight)")
|
|
led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (required for adalight)")
|
|
baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
|
|
auto_shutdown: Optional[bool] = Field(default=None, description="Turn off device when server stops (defaults to true for adalight)")
|
|
send_latency_ms: Optional[int] = Field(None, ge=0, le=5000, description="Simulated send latency in ms (mock devices)")
|
|
rgbw: Optional[bool] = Field(None, description="RGBW mode (mock devices)")
|
|
zone_mode: Optional[str] = Field(None, description="OpenRGB zone mode: combined or separate")
|
|
tags: List[str] = Field(default_factory=list, description="User-defined tags")
|
|
# DMX (Art-Net / sACN) fields
|
|
dmx_protocol: Optional[str] = Field(None, description="DMX protocol: artnet or sacn")
|
|
dmx_start_universe: Optional[int] = Field(None, ge=0, le=32767, description="DMX start universe")
|
|
dmx_start_channel: Optional[int] = Field(None, ge=1, le=512, description="DMX start channel (1-512)")
|
|
# ESP-NOW fields
|
|
espnow_peer_mac: Optional[str] = Field(None, description="ESP-NOW peer MAC address (e.g. AA:BB:CC:DD:EE:FF)")
|
|
espnow_channel: Optional[int] = Field(None, ge=1, le=14, description="ESP-NOW WiFi channel (1-14)")
|
|
# Philips Hue fields
|
|
hue_username: Optional[str] = Field(None, description="Hue bridge username (from pairing)")
|
|
hue_client_key: Optional[str] = Field(None, description="Hue entertainment client key (hex)")
|
|
hue_entertainment_group_id: Optional[str] = Field(None, description="Hue entertainment group/zone ID")
|
|
# SPI Direct fields
|
|
spi_speed_hz: Optional[int] = Field(None, ge=100000, le=4000000, description="SPI clock speed in Hz")
|
|
spi_led_type: Optional[str] = Field(None, description="LED chipset: WS2812, WS2812B, WS2811, SK6812, SK6812_RGBW")
|
|
# Razer Chroma fields
|
|
chroma_device_type: Optional[str] = Field(None, description="Chroma peripheral type: keyboard, mouse, mousepad, headset, chromalink, keypad")
|
|
# SteelSeries GameSense fields
|
|
gamesense_device_type: Optional[str] = Field(None, description="GameSense device type: keyboard, mouse, headset, mousepad, indicator")
|
|
default_css_processing_template_id: Optional[str] = Field(None, description="Default color strip processing template ID")
|
|
|
|
|
|
class DeviceUpdate(BaseModel):
|
|
"""Request to update device information."""
|
|
|
|
name: Optional[str] = Field(None, description="Device name", min_length=1, max_length=100)
|
|
url: Optional[str] = Field(None, description="Device URL or serial port")
|
|
enabled: Optional[bool] = Field(None, description="Whether device is enabled")
|
|
led_count: Optional[int] = Field(None, ge=1, le=10000, description="Number of LEDs (for devices with manual_led_count capability)")
|
|
baud_rate: Optional[int] = Field(None, description="Serial baud rate (for adalight devices)")
|
|
auto_shutdown: Optional[bool] = Field(None, description="Turn off device when server stops")
|
|
send_latency_ms: Optional[int] = Field(None, ge=0, le=5000, description="Simulated send latency in ms (mock devices)")
|
|
rgbw: Optional[bool] = Field(None, description="RGBW mode (mock devices)")
|
|
zone_mode: Optional[str] = Field(None, description="OpenRGB zone mode: combined or separate")
|
|
tags: Optional[List[str]] = None
|
|
dmx_protocol: Optional[str] = Field(None, description="DMX protocol: artnet or sacn")
|
|
dmx_start_universe: Optional[int] = Field(None, ge=0, le=32767, description="DMX start universe")
|
|
dmx_start_channel: Optional[int] = Field(None, ge=1, le=512, description="DMX start channel (1-512)")
|
|
espnow_peer_mac: Optional[str] = Field(None, description="ESP-NOW peer MAC address")
|
|
espnow_channel: Optional[int] = Field(None, ge=1, le=14, description="ESP-NOW WiFi channel")
|
|
hue_username: Optional[str] = Field(None, description="Hue bridge username")
|
|
hue_client_key: Optional[str] = Field(None, description="Hue entertainment client key")
|
|
hue_entertainment_group_id: Optional[str] = Field(None, description="Hue entertainment group ID")
|
|
spi_speed_hz: Optional[int] = Field(None, ge=100000, le=4000000, description="SPI clock speed")
|
|
spi_led_type: Optional[str] = Field(None, description="LED chipset type")
|
|
chroma_device_type: Optional[str] = Field(None, description="Chroma peripheral type")
|
|
gamesense_device_type: Optional[str] = Field(None, description="GameSense device type")
|
|
default_css_processing_template_id: Optional[str] = Field(None, description="Default color strip processing template ID")
|
|
|
|
|
|
class CalibrationLineSchema(BaseModel):
|
|
"""One LED line in advanced calibration."""
|
|
|
|
picture_source_id: str = Field(description="Picture source (monitor) to sample from")
|
|
edge: Literal["top", "right", "bottom", "left"] = Field(description="Screen edge to sample")
|
|
led_count: int = Field(ge=1, description="Number of LEDs in this line")
|
|
span_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start fraction along edge")
|
|
span_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End fraction along edge")
|
|
reverse: bool = Field(default=False, description="Reverse LED direction")
|
|
border_width: int = Field(default=10, ge=1, le=100, description="Sampling depth in pixels")
|
|
|
|
|
|
class Calibration(BaseModel):
|
|
"""Calibration configuration for pixel-to-LED mapping."""
|
|
|
|
mode: Literal["simple", "advanced"] = Field(
|
|
default="simple",
|
|
description="Calibration mode: simple (4-edge) or advanced (multi-source lines)"
|
|
)
|
|
# Advanced mode: ordered list of lines
|
|
lines: Optional[List[CalibrationLineSchema]] = Field(
|
|
default=None,
|
|
description="Line list for advanced mode (ignored in simple mode)"
|
|
)
|
|
# Simple mode fields
|
|
layout: Literal["clockwise", "counterclockwise"] = Field(
|
|
default="clockwise",
|
|
description="LED strip layout direction"
|
|
)
|
|
start_position: Literal["top_left", "top_right", "bottom_left", "bottom_right"] = Field(
|
|
default="bottom_left",
|
|
description="Starting corner of the LED strip"
|
|
)
|
|
offset: int = Field(
|
|
default=0,
|
|
ge=0,
|
|
description="Number of LEDs from physical LED 0 to start corner (along strip direction)"
|
|
)
|
|
leds_top: int = Field(default=0, ge=0, description="Number of LEDs on the top edge")
|
|
leds_right: int = Field(default=0, ge=0, description="Number of LEDs on the right edge")
|
|
leds_bottom: int = Field(default=0, ge=0, description="Number of LEDs on the bottom edge")
|
|
leds_left: int = Field(default=0, ge=0, description="Number of LEDs on the left edge")
|
|
# Per-edge span: fraction of screen side covered by LEDs (0.0-1.0)
|
|
span_top_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of top edge coverage")
|
|
span_top_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of top edge coverage")
|
|
span_right_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of right edge coverage")
|
|
span_right_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of right edge coverage")
|
|
span_bottom_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of bottom edge coverage")
|
|
span_bottom_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of bottom edge coverage")
|
|
span_left_start: float = Field(default=0.0, ge=0.0, le=1.0, description="Start of left edge coverage")
|
|
span_left_end: float = Field(default=1.0, ge=0.0, le=1.0, description="End of left edge coverage")
|
|
# Skip LEDs at start/end of strip
|
|
skip_leds_start: int = Field(default=0, ge=0, description="LEDs to skip (black out) at the start of the strip")
|
|
skip_leds_end: int = Field(default=0, ge=0, description="LEDs to skip (black out) at the end of the strip")
|
|
border_width: int = Field(default=10, ge=1, le=100, description="Border width in pixels for edge sampling")
|
|
|
|
|
|
class CalibrationTestModeRequest(BaseModel):
|
|
"""Request to set calibration test mode with multiple edges."""
|
|
|
|
edges: Dict[str, List[int]] = Field(
|
|
default_factory=dict,
|
|
description="Map of active edge names to RGB colors. "
|
|
"E.g. {'top': [255, 0, 0], 'left': [255, 255, 0]}. "
|
|
"Empty dict = exit test mode."
|
|
)
|
|
|
|
|
|
class CalibrationTestModeResponse(BaseModel):
|
|
"""Response for calibration test mode."""
|
|
|
|
test_mode: bool = Field(description="Whether test mode is active")
|
|
active_edges: List[str] = Field(default_factory=list, description="Currently lit edges")
|
|
device_id: str = Field(description="Device ID")
|
|
|
|
|
|
class DeviceResponse(BaseModel):
|
|
"""Device information response."""
|
|
|
|
id: str = Field(description="Device ID")
|
|
name: str = Field(description="Device name")
|
|
url: str = Field(description="Device URL")
|
|
device_type: str = Field(default="wled", description="LED device type")
|
|
led_count: int = Field(description="Total number of LEDs")
|
|
enabled: bool = Field(description="Whether device is enabled")
|
|
baud_rate: Optional[int] = Field(None, description="Serial baud rate")
|
|
auto_shutdown: bool = Field(default=False, description="Restore device to idle state when targets stop")
|
|
send_latency_ms: int = Field(default=0, description="Simulated send latency in ms (mock devices)")
|
|
rgbw: bool = Field(default=False, description="RGBW mode (mock devices)")
|
|
zone_mode: str = Field(default="combined", description="OpenRGB zone mode: combined or separate")
|
|
capabilities: List[str] = Field(default_factory=list, description="Device type capabilities")
|
|
tags: List[str] = Field(default_factory=list, description="User-defined tags")
|
|
dmx_protocol: str = Field(default="artnet", description="DMX protocol: artnet or sacn")
|
|
dmx_start_universe: int = Field(default=0, description="DMX start universe")
|
|
dmx_start_channel: int = Field(default=1, description="DMX start channel (1-512)")
|
|
espnow_peer_mac: str = Field(default="", description="ESP-NOW peer MAC address")
|
|
espnow_channel: int = Field(default=1, description="ESP-NOW WiFi channel")
|
|
hue_username: str = Field(default="", description="Hue bridge username")
|
|
hue_client_key: str = Field(default="", description="Hue entertainment client key")
|
|
hue_entertainment_group_id: str = Field(default="", description="Hue entertainment group ID")
|
|
spi_speed_hz: int = Field(default=800000, description="SPI clock speed in Hz")
|
|
spi_led_type: str = Field(default="WS2812B", description="LED chipset type")
|
|
chroma_device_type: str = Field(default="chromalink", description="Chroma peripheral type")
|
|
gamesense_device_type: str = Field(default="keyboard", description="GameSense device type")
|
|
default_css_processing_template_id: str = Field(default="", description="Default color strip processing template ID")
|
|
created_at: datetime = Field(description="Creation timestamp")
|
|
updated_at: datetime = Field(description="Last update timestamp")
|
|
|
|
|
|
class DeviceListResponse(BaseModel):
|
|
"""List of devices response."""
|
|
|
|
devices: List[DeviceResponse] = Field(description="List of devices")
|
|
count: int = Field(description="Number of devices")
|
|
|
|
|
|
class DeviceStateResponse(BaseModel):
|
|
"""Device health/connection state response."""
|
|
|
|
device_id: str = Field(description="Device ID")
|
|
device_type: str = Field(default="wled", description="LED device type")
|
|
device_online: bool = Field(default=False, description="Whether device is reachable")
|
|
device_latency_ms: Optional[float] = Field(None, description="Health check latency in ms")
|
|
device_name: Optional[str] = Field(None, description="Device name reported by firmware")
|
|
device_version: Optional[str] = Field(None, description="Firmware version")
|
|
device_led_count: Optional[int] = Field(None, description="LED count reported by device")
|
|
device_rgbw: Optional[bool] = Field(None, description="Whether device uses RGBW LEDs")
|
|
device_led_type: Optional[str] = Field(None, description="LED chip type (e.g. WS2812B, SK6812 RGBW)")
|
|
device_fps: Optional[int] = Field(None, description="Device-reported FPS (WLED internal refresh rate)")
|
|
device_last_checked: Optional[datetime] = Field(None, description="Last health check time")
|
|
device_error: Optional[str] = Field(None, description="Last health check error")
|
|
test_mode: bool = Field(default=False, description="Whether calibration test mode is active")
|
|
test_mode_edges: List[str] = Field(default_factory=list, description="Currently lit edges in test mode")
|
|
|
|
|
|
class DiscoveredDeviceResponse(BaseModel):
|
|
"""A single device found via network discovery."""
|
|
|
|
name: str = Field(description="Device name (from mDNS or firmware)")
|
|
url: str = Field(description="Device URL")
|
|
device_type: str = Field(default="wled", description="Device type")
|
|
ip: str = Field(description="IP address")
|
|
mac: str = Field(default="", description="MAC address")
|
|
led_count: Optional[int] = Field(None, description="LED count (if reachable)")
|
|
version: Optional[str] = Field(None, description="Firmware version")
|
|
already_added: bool = Field(default=False, description="Whether this device is already in the system")
|
|
|
|
|
|
class DiscoverDevicesResponse(BaseModel):
|
|
"""Response from device discovery scan."""
|
|
|
|
devices: List[DiscoveredDeviceResponse] = Field(description="Discovered devices")
|
|
count: int = Field(description="Total devices found")
|
|
scan_duration_ms: float = Field(description="How long the scan took in milliseconds")
|
|
|
|
|
|
class OpenRGBZoneResponse(BaseModel):
|
|
"""A single zone on an OpenRGB device."""
|
|
|
|
name: str = Field(description="Zone name (e.g. JRAINBOW2)")
|
|
led_count: int = Field(description="Number of LEDs in this zone")
|
|
zone_type: str = Field(description="Zone type (linear, single, matrix)")
|
|
|
|
|
|
class OpenRGBZonesResponse(BaseModel):
|
|
"""Response from OpenRGB zone listing."""
|
|
|
|
device_name: str = Field(description="OpenRGB device name")
|
|
zones: List[OpenRGBZoneResponse] = Field(description="Available zones")
|