Files
wled-screen-controller-mixed/server/src/wled_controller/storage/profile.py
alexei.dolgolyov 29d9b95885 Add profile system for automatic target activation
Profiles monitor running processes and foreground windows to
automatically start/stop targets when conditions are met.
Includes profile engine, platform detector (WMI), REST API,
process browser endpoint, and calibration persistence fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:12:34 +03:00

92 lines
2.8 KiB
Python

"""Profile and Condition data models."""
from dataclasses import dataclass, field
from datetime import datetime
from typing import List, Optional
@dataclass
class Condition:
"""Base condition — polymorphic via condition_type discriminator."""
condition_type: str
def to_dict(self) -> dict:
return {"condition_type": self.condition_type}
@classmethod
def from_dict(cls, data: dict) -> "Condition":
"""Factory: dispatch to the correct subclass."""
ct = data.get("condition_type", "")
if ct == "application":
return ApplicationCondition.from_dict(data)
raise ValueError(f"Unknown condition type: {ct}")
@dataclass
class ApplicationCondition(Condition):
"""Activate when specified applications are running or topmost."""
condition_type: str = "application"
apps: List[str] = field(default_factory=list)
match_type: str = "running" # "running" | "topmost"
def to_dict(self) -> dict:
d = super().to_dict()
d["apps"] = list(self.apps)
d["match_type"] = self.match_type
return d
@classmethod
def from_dict(cls, data: dict) -> "ApplicationCondition":
return cls(
apps=data.get("apps", []),
match_type=data.get("match_type", "running"),
)
@dataclass
class Profile:
"""Automation profile that activates targets based on conditions."""
id: str
name: str
enabled: bool
condition_logic: str # "or" | "and"
conditions: List[Condition]
target_ids: List[str]
created_at: datetime
updated_at: datetime
def to_dict(self) -> dict:
return {
"id": self.id,
"name": self.name,
"enabled": self.enabled,
"condition_logic": self.condition_logic,
"conditions": [c.to_dict() for c in self.conditions],
"target_ids": list(self.target_ids),
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat(),
}
@classmethod
def from_dict(cls, data: dict) -> "Profile":
conditions = []
for c_data in data.get("conditions", []):
try:
conditions.append(Condition.from_dict(c_data))
except ValueError:
pass # skip unknown condition types on load
return cls(
id=data["id"],
name=data["name"],
enabled=data.get("enabled", True),
condition_logic=data.get("condition_logic", "or"),
conditions=conditions,
target_ids=data.get("target_ids", []),
created_at=datetime.fromisoformat(data.get("created_at", datetime.utcnow().isoformat())),
updated_at=datetime.fromisoformat(data.get("updated_at", datetime.utcnow().isoformat())),
)