Add adaptive FPS and honest device reachability during streaming
DDP uses fire-and-forget UDP, so when a WiFi device becomes overwhelmed by sustained traffic, sends appear successful while the device is actually unreachable. This adds: - HTTP liveness probe (GET /json/info, 2s timeout) every 10s during streaming, exposed as device_streaming_reachable in target state - Adaptive FPS (opt-in): exponential backoff when device is unreachable, gradual recovery when it stabilizes — finds sustainable send rate - Honest health checks: removed the lie that forced device_online=true during streaming; now runs actual health checks regardless - Target editor toggle, FPS display shows effective rate when throttled, health dot reflects streaming reachability, red highlight when unreachable - Auto-backup scheduling support in settings modal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,7 @@ class PictureTargetCreate(BaseModel):
|
||||
keepalive_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)
|
||||
min_brightness_threshold: int = Field(default=0, ge=0, le=254, description="Min brightness threshold (0=disabled); below this → off")
|
||||
adaptive_fps: bool = Field(default=False, description="Auto-reduce FPS when device is unresponsive")
|
||||
# KC target fields
|
||||
picture_source_id: str = Field(default="", description="Picture source ID (for key_colors targets)")
|
||||
key_colors_settings: Optional[KeyColorsSettingsSchema] = Field(None, description="Key colors settings (for key_colors targets)")
|
||||
@@ -78,6 +79,7 @@ class PictureTargetUpdate(BaseModel):
|
||||
keepalive_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)
|
||||
min_brightness_threshold: Optional[int] = Field(None, ge=0, le=254, description="Min brightness threshold (0=disabled); below this → off")
|
||||
adaptive_fps: Optional[bool] = Field(None, description="Auto-reduce FPS when device is unresponsive")
|
||||
# KC target fields
|
||||
picture_source_id: Optional[str] = Field(None, description="Picture source ID (for key_colors targets)")
|
||||
key_colors_settings: Optional[KeyColorsSettingsSchema] = Field(None, description="Key colors settings (for key_colors targets)")
|
||||
@@ -99,6 +101,7 @@ class PictureTargetResponse(BaseModel):
|
||||
keepalive_interval: float = Field(default=1.0, description="Keepalive interval (s)")
|
||||
state_check_interval: int = Field(default=DEFAULT_STATE_CHECK_INTERVAL, description="Health check interval (s)")
|
||||
min_brightness_threshold: int = Field(default=0, description="Min brightness threshold (0=disabled)")
|
||||
adaptive_fps: bool = Field(default=False, description="Auto-reduce FPS when device is unresponsive")
|
||||
# KC target fields
|
||||
picture_source_id: str = Field(default="", description="Picture source ID (key_colors)")
|
||||
key_colors_settings: Optional[KeyColorsSettingsSchema] = Field(None, description="Key colors settings")
|
||||
@@ -152,6 +155,8 @@ class TargetProcessingState(BaseModel):
|
||||
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")
|
||||
device_streaming_reachable: Optional[bool] = Field(None, description="Device reachable during streaming (HTTP probe)")
|
||||
fps_effective: Optional[int] = Field(None, description="Effective FPS after adaptive reduction")
|
||||
|
||||
|
||||
class TargetMetricsResponse(BaseModel):
|
||||
|
||||
@@ -79,3 +79,38 @@ class RestoreResponse(BaseModel):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user