Files
ledgrab/server/src/wled_controller/storage/automation_store.py
T
alexei.dolgolyov 492bdb95e3 feat: game integration system
Receive real-time events from games (CS2, Dota 2, LoL, etc.) and drive
LED effects through the existing color strip and value source pipelines.

Core:
- GameEventBus (thread-safe pub/sub) with standardized 23-type event vocabulary
- GameAdapter ABC + AdapterRegistry + MappingAdapter (YAML-driven)
- Built-in adapters: CS2 GSI, Dota 2 GSI, LoL Live Client, Generic Webhook
- Community YAML adapters: Minecraft, Valorant, Rocket League
- GameEventColorStripStream with 5 effects (flash/pulse/sweep/color_shift/breathing)
- GameEventValueSource with EMA smoothing and timeout
- 4 built-in effect presets (FPS Combat, MOBA Health, Racing, Generic Alert)
- Auto-setup for Valve GSI games (Steam path detection, cfg file writing)
- Demo capture engine exposed to non-demo mode

Frontend:
- Game tab in Streams tree navigation with integration cards
- Game integration editor modal with adapter picker, config fields, event mappings
- game_event source type in CSS and ValueSource editors
- Setup instructions overlay (markdown rendered)
- Live event monitor and connection test

API:
- Full CRUD for game integrations
- Event ingestion endpoint (adapter-level auth)
- Adapter metadata, presets, auto-setup, status/diagnostics endpoints
2026-03-31 13:17:52 +03:00

120 lines
4.2 KiB
Python

"""Automation storage using SQLite."""
import uuid
from datetime import datetime, timezone
from typing import List, Optional
from wled_controller.storage.automation import Automation, Rule
from wled_controller.storage.base_sqlite_store import BaseSqliteStore
from wled_controller.storage.database import Database
from wled_controller.utils import get_logger
logger = get_logger(__name__)
class AutomationStore(BaseSqliteStore[Automation]):
_table_name = "automations"
_entity_name = "Automation"
def __init__(self, db: Database):
super().__init__(db, Automation.from_dict)
# Backward-compatible aliases
get_all_automations = BaseSqliteStore.get_all
get_automation = BaseSqliteStore.get
delete_automation = BaseSqliteStore.delete
def create_automation(
self,
name: str,
enabled: bool = True,
rule_logic: str = "or",
rules: Optional[List[Rule]] = None,
scene_preset_id: Optional[str] = None,
deactivation_mode: str = "none",
deactivation_scene_preset_id: Optional[str] = None,
tags: Optional[List[str]] = None,
# Legacy parameter aliases
condition_logic: Optional[str] = None,
conditions: Optional[List[Rule]] = None,
) -> Automation:
# Support legacy parameter names
if condition_logic is not None and rule_logic == "or":
rule_logic = condition_logic
if conditions is not None and rules is None:
rules = conditions
for a in self._items.values():
if a.name == name:
raise ValueError(f"Automation with name '{name}' already exists")
automation_id = f"auto_{uuid.uuid4().hex[:8]}"
now = datetime.now(timezone.utc)
automation = Automation(
id=automation_id,
name=name,
enabled=enabled,
rule_logic=rule_logic,
rules=rules or [],
scene_preset_id=scene_preset_id,
deactivation_mode=deactivation_mode,
deactivation_scene_preset_id=deactivation_scene_preset_id,
created_at=now,
updated_at=now,
tags=tags or [],
)
self._items[automation_id] = automation
self._save_item(automation_id, automation)
logger.info(f"Created automation: {name} ({automation_id})")
return automation
def update_automation(
self,
automation_id: str,
name: Optional[str] = None,
enabled: Optional[bool] = None,
rule_logic: Optional[str] = None,
rules: Optional[List[Rule]] = None,
scene_preset_id: str = "__unset__",
deactivation_mode: Optional[str] = None,
deactivation_scene_preset_id: str = "__unset__",
tags: Optional[List[str]] = None,
# Legacy parameter aliases
condition_logic: Optional[str] = None,
conditions: Optional[List[Rule]] = None,
) -> Automation:
# Support legacy parameter names
if condition_logic is not None and rule_logic is None:
rule_logic = condition_logic
if conditions is not None and rules is None:
rules = conditions
automation = self.get(automation_id)
if name is not None:
self._check_name_unique(name, exclude_id=automation_id)
automation.name = name
if enabled is not None:
automation.enabled = enabled
if rule_logic is not None:
automation.rule_logic = rule_logic
if rules is not None:
automation.rules = rules
if scene_preset_id != "__unset__":
automation.scene_preset_id = None if scene_preset_id == "" else scene_preset_id
if deactivation_mode is not None:
automation.deactivation_mode = deactivation_mode
if deactivation_scene_preset_id != "__unset__":
automation.deactivation_scene_preset_id = (
None if deactivation_scene_preset_id == "" else deactivation_scene_preset_id
)
if tags is not None:
automation.tags = tags
automation.updated_at = datetime.now(timezone.utc)
self._save_item(automation_id, automation)
logger.info(f"Updated automation: {automation_id}")
return automation