Add sync clock entity for synchronized animation timing

Introduces Synchronization Clocks — shared, controllable time bases
that CSS sources can optionally reference for synchronized animation.

Backend:
- New SyncClock dataclass, JSON store, Pydantic schemas, REST API
- Runtime clock with thread-safe pause/resume/reset and speed control
- Ref-counted runtime pool with eager creation for API control
- clock_id field on all ColorStripSource types
- Stream integration: clock time/speed replaces source-local values
- Paused clock skips rendering (saves CPU + stops frame pushes)
- Included in backup/restore via STORE_MAP

Frontend:
- Sync Clocks tab in Streams section with cards and controls
- Clock dropdown in CSS editor (hidden speed slider when clock set)
- Clock crosslink badge on CSS source cards (replaces speed badge)
- Targets tab uses DataCache for picture/audio sources and sync clocks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 21:46:55 +03:00
parent 52ee4bdeb6
commit aa1e4a6afc
32 changed files with 1255 additions and 58 deletions

View File

@@ -29,6 +29,8 @@ import wled_controller.core.audio # noqa: F401 — trigger engine auto-registra
from wled_controller.storage.value_source_store import ValueSourceStore
from wled_controller.storage.automation_store import AutomationStore
from wled_controller.storage.scene_preset_store import ScenePresetStore
from wled_controller.storage.sync_clock_store import SyncClockStore
from wled_controller.core.processing.sync_clock_manager import SyncClockManager
from wled_controller.core.automations.automation_engine import AutomationEngine
from wled_controller.core.mqtt.mqtt_service import MQTTService
from wled_controller.core.devices.mqtt_client import set_mqtt_service
@@ -56,6 +58,8 @@ audio_template_store = AudioTemplateStore(config.storage.audio_templates_file)
value_source_store = ValueSourceStore(config.storage.value_sources_file)
automation_store = AutomationStore(config.storage.automations_file)
scene_preset_store = ScenePresetStore(config.storage.scene_presets_file)
sync_clock_store = SyncClockStore(config.storage.sync_clocks_file)
sync_clock_manager = SyncClockManager(sync_clock_store)
processor_manager = ProcessorManager(
picture_source_store=picture_source_store,
@@ -67,6 +71,7 @@ processor_manager = ProcessorManager(
audio_source_store=audio_source_store,
value_source_store=value_source_store,
audio_template_store=audio_template_store,
sync_clock_manager=sync_clock_manager,
)
@@ -135,6 +140,8 @@ async def lifespan(app: FastAPI):
scene_preset_store=scene_preset_store,
automation_engine=automation_engine,
auto_backup_engine=auto_backup_engine,
sync_clock_store=sync_clock_store,
sync_clock_manager=sync_clock_manager,
)
# Register devices in processor manager for health monitoring