Files
wled-screen-controller-mixed/server/src/wled_controller/api/schemas/system.py
alexei.dolgolyov 05152a0f51 Settings tabs, log overlay, external URL, Sources tree restructure, audio fixes
- Settings modal split into 3 tabs: General, Backup, MQTT
- Log viewer moved to full-screen overlay with compact toolbar
- External URL setting: API endpoints + UI for configuring server domain
  used in webhook/WS URLs instead of auto-detected local IP
- Sources tab tree restructured: Picture Source (Screen Capture/Static/
  Processed sub-groups), Color Strip, Audio, Utility
- TreeNav extended to support nested groups (3-level tree)
- Audio tab split into Sources and Templates sub-tabs
- Fix audio template test: device picker now filters by engine type
  (was showing WASAPI indices for sounddevice templates)
- Audio template test device picker disabled during active test
- Rename "Input Source" to "Source" in CSS test preview (en/ru/zh)
- Fix i18n: log filter/level items deferred to avoid stale t() calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:16:57 +03:00

172 lines
6.7 KiB
Python

"""System-related schemas (health, version, displays)."""
from datetime import datetime
from typing import List, Literal
from pydantic import BaseModel, Field
class HealthResponse(BaseModel):
"""Health check response."""
status: Literal["healthy", "unhealthy"] = Field(description="Service health status")
timestamp: datetime = Field(description="Current server time")
version: str = Field(description="Application version")
class VersionResponse(BaseModel):
"""Version information response."""
version: str = Field(description="Application version")
python_version: str = Field(description="Python version")
api_version: str = Field(description="API version")
class DisplayInfo(BaseModel):
"""Display/monitor information."""
index: int = Field(description="Display index")
name: str = Field(description="Display name")
width: int = Field(description="Display width in pixels")
height: int = Field(description="Display height in pixels")
x: int = Field(description="Display X position")
y: int = Field(description="Display Y position")
is_primary: bool = Field(default=False, description="Whether this is the primary display")
refresh_rate: int = Field(description="Display refresh rate in Hz")
class DisplayListResponse(BaseModel):
"""List of available displays."""
displays: List[DisplayInfo] = Field(description="Available displays")
count: int = Field(description="Number of displays")
class ProcessListResponse(BaseModel):
"""List of running processes."""
processes: List[str] = Field(description="Sorted list of unique process names")
count: int = Field(description="Number of unique processes")
class GpuInfo(BaseModel):
"""GPU performance information."""
name: str | None = Field(default=None, description="GPU device name")
utilization: float | None = Field(default=None, description="GPU core usage percent")
memory_used_mb: float | None = Field(default=None, description="GPU memory used in MB")
memory_total_mb: float | None = Field(default=None, description="GPU total memory in MB")
temperature_c: float | None = Field(default=None, description="GPU temperature in Celsius")
class PerformanceResponse(BaseModel):
"""System performance metrics."""
cpu_name: str | None = Field(default=None, description="CPU model name")
cpu_percent: float = Field(description="System-wide CPU usage percent")
ram_used_mb: float = Field(description="RAM used in MB")
ram_total_mb: float = Field(description="RAM total in MB")
ram_percent: float = Field(description="RAM usage percent")
gpu: GpuInfo | None = Field(default=None, description="GPU info (null if unavailable)")
timestamp: datetime = Field(description="Measurement timestamp")
class RestoreResponse(BaseModel):
"""Response after restoring configuration backup."""
status: str = Field(description="Status of restore operation")
stores_written: int = Field(description="Number of stores successfully written")
stores_total: int = Field(description="Total number of known stores")
missing_stores: List[str] = Field(default_factory=list, description="Store keys not found in backup")
restart_scheduled: bool = Field(description="Whether server restart was scheduled")
message: str = Field(description="Human-readable status message")
# ─── Auto-backup schemas ──────────────────────────────────────
class AutoBackupSettings(BaseModel):
"""Settings for automatic backup."""
enabled: bool = Field(description="Whether auto-backup is enabled")
interval_hours: float = Field(ge=0.5, le=168, description="Backup interval in hours")
max_backups: int = Field(ge=1, le=100, description="Maximum number of backup files to keep")
class AutoBackupStatusResponse(BaseModel):
"""Auto-backup settings plus runtime status."""
enabled: bool
interval_hours: float
max_backups: int
last_backup_time: str | None = None
next_backup_time: str | None = None
class BackupFileInfo(BaseModel):
"""Information about a saved backup file."""
filename: str
size_bytes: int
created_at: str
class BackupListResponse(BaseModel):
"""List of saved backup files."""
backups: List[BackupFileInfo]
count: int
# ─── MQTT schemas ──────────────────────────────────────────────
class MQTTSettingsResponse(BaseModel):
"""MQTT broker settings response (password is masked)."""
enabled: bool = Field(description="Whether MQTT is enabled")
broker_host: str = Field(description="MQTT broker hostname or IP")
broker_port: int = Field(ge=1, le=65535, description="MQTT broker port")
username: str = Field(description="MQTT username (empty = anonymous)")
password_set: bool = Field(description="Whether a password is configured")
client_id: str = Field(description="MQTT client ID")
base_topic: str = Field(description="Base topic prefix")
class MQTTSettingsRequest(BaseModel):
"""MQTT broker settings update request."""
enabled: bool = Field(description="Whether MQTT is enabled")
broker_host: str = Field(description="MQTT broker hostname or IP")
broker_port: int = Field(ge=1, le=65535, description="MQTT broker port")
username: str = Field(default="", description="MQTT username (empty = anonymous)")
password: str = Field(default="", description="MQTT password (empty = keep existing if omitted)")
client_id: str = Field(default="ledgrab", description="MQTT client ID")
base_topic: str = Field(default="ledgrab", description="Base topic prefix")
# ─── External URL schema ───────────────────────────────────────
class ExternalUrlResponse(BaseModel):
"""External URL setting response."""
external_url: str = Field(description="External base URL (e.g. https://myserver.example.com:8080). Empty = use auto-detected URL.")
class ExternalUrlRequest(BaseModel):
"""External URL setting update request."""
external_url: str = Field(default="", description="External base URL. Empty string to clear.")
# ─── Log level schemas ─────────────────────────────────────────
class LogLevelResponse(BaseModel):
"""Current log level response."""
level: str = Field(description="Current effective log level name (e.g. DEBUG, INFO, WARNING, ERROR, CRITICAL)")
class LogLevelRequest(BaseModel):
"""Request to change the log level."""
level: str = Field(description="New log level name (DEBUG, INFO, WARNING, ERROR, CRITICAL)")