492bdb95e3
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
120 lines
4.2 KiB
Python
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
|