b5a6885126
Introduce abstract LEDClient base class with factory pattern so new LED controller types can plug in alongside WLED. ProcessorManager is now fully type-agnostic — all device-specific logic (health checks, state snapshot/restore, fast send) lives behind the LEDClient interface. - New led_client.py: LEDClient ABC, DeviceHealth, factory functions - WLEDClient inherits LEDClient, encapsulates WLED health checks and state management - device_type field on Device storage model (defaults to "wled") - Rename target_type "wled" → "led" with backward-compat migration - Frontend: "WLED" tab → "LED", device type badge, type selector in add-device modal, device type shown in target device dropdown - All wled_* API fields renamed to device_* for generic naming Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
66 lines
2.7 KiB
Python
66 lines
2.7 KiB
Python
"""LED picture target — streams a picture source to an LED device."""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from wled_controller.core.processor_manager import ProcessingSettings
|
|
from wled_controller.storage.picture_target import PictureTarget
|
|
|
|
|
|
@dataclass
|
|
class WledPictureTarget(PictureTarget):
|
|
"""LED picture target — streams a picture source to an LED device."""
|
|
|
|
device_id: str = ""
|
|
picture_source_id: str = ""
|
|
settings: ProcessingSettings = field(default_factory=ProcessingSettings)
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert to dictionary."""
|
|
d = super().to_dict()
|
|
d["device_id"] = self.device_id
|
|
d["picture_source_id"] = self.picture_source_id
|
|
d["settings"] = {
|
|
"display_index": self.settings.display_index,
|
|
"fps": self.settings.fps,
|
|
"brightness": self.settings.brightness,
|
|
"gamma": self.settings.gamma,
|
|
"saturation": self.settings.saturation,
|
|
"smoothing": self.settings.smoothing,
|
|
"interpolation_mode": self.settings.interpolation_mode,
|
|
"standby_interval": self.settings.standby_interval,
|
|
"state_check_interval": self.settings.state_check_interval,
|
|
}
|
|
return d
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: dict) -> "WledPictureTarget":
|
|
"""Create from dictionary."""
|
|
from wled_controller.core.processor_manager import DEFAULT_STATE_CHECK_INTERVAL
|
|
|
|
settings_data = data.get("settings", {})
|
|
settings = ProcessingSettings(
|
|
display_index=settings_data.get("display_index", 0),
|
|
fps=settings_data.get("fps", 30),
|
|
brightness=settings_data.get("brightness", 1.0),
|
|
gamma=settings_data.get("gamma", 2.2),
|
|
saturation=settings_data.get("saturation", 1.0),
|
|
smoothing=settings_data.get("smoothing", 0.3),
|
|
interpolation_mode=settings_data.get("interpolation_mode", "average"),
|
|
standby_interval=settings_data.get("standby_interval", 1.0),
|
|
state_check_interval=settings_data.get("state_check_interval", DEFAULT_STATE_CHECK_INTERVAL),
|
|
)
|
|
|
|
return cls(
|
|
id=data["id"],
|
|
name=data["name"],
|
|
target_type="led",
|
|
device_id=data.get("device_id", ""),
|
|
picture_source_id=data.get("picture_source_id", ""),
|
|
settings=settings,
|
|
description=data.get("description"),
|
|
created_at=datetime.fromisoformat(data.get("created_at", datetime.utcnow().isoformat())),
|
|
updated_at=datetime.fromisoformat(data.get("updated_at", datetime.utcnow().isoformat())),
|
|
)
|