Rename profiles to automations across backend and frontend
Rename the "profiles" entity to "automations" throughout the entire codebase for clarity. Updates Python models, storage, API routes/schemas, engine, frontend JS modules, HTML templates, CSS classes, i18n keys (en/ru/zh), dashboard, tutorials, and command palette. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ from .routes.audio import router as audio_router
|
||||
from .routes.audio_sources import router as audio_sources_router
|
||||
from .routes.audio_templates import router as audio_templates_router
|
||||
from .routes.value_sources import router as value_sources_router
|
||||
from .routes.profiles import router as profiles_router
|
||||
from .routes.automations import router as automations_router
|
||||
from .routes.scene_presets import router as scene_presets_router
|
||||
|
||||
router = APIRouter()
|
||||
@@ -30,7 +30,7 @@ router.include_router(audio_sources_router)
|
||||
router.include_router(audio_templates_router)
|
||||
router.include_router(value_sources_router)
|
||||
router.include_router(picture_targets_router)
|
||||
router.include_router(profiles_router)
|
||||
router.include_router(automations_router)
|
||||
router.include_router(scene_presets_router)
|
||||
|
||||
__all__ = ["router"]
|
||||
|
||||
@@ -11,9 +11,9 @@ from wled_controller.storage.color_strip_store import ColorStripStore
|
||||
from wled_controller.storage.audio_source_store import AudioSourceStore
|
||||
from wled_controller.storage.audio_template_store import AudioTemplateStore
|
||||
from wled_controller.storage.value_source_store import ValueSourceStore
|
||||
from wled_controller.storage.profile_store import ProfileStore
|
||||
from wled_controller.storage.automation_store import AutomationStore
|
||||
from wled_controller.storage.scene_preset_store import ScenePresetStore
|
||||
from wled_controller.core.profiles.profile_engine import ProfileEngine
|
||||
from wled_controller.core.automations.automation_engine import AutomationEngine
|
||||
from wled_controller.core.backup.auto_backup import AutoBackupEngine
|
||||
|
||||
# Global instances (initialized in main.py)
|
||||
@@ -29,9 +29,9 @@ _audio_source_store: AudioSourceStore | None = None
|
||||
_audio_template_store: AudioTemplateStore | None = None
|
||||
_value_source_store: ValueSourceStore | None = None
|
||||
_processor_manager: ProcessorManager | None = None
|
||||
_profile_store: ProfileStore | None = None
|
||||
_automation_store: AutomationStore | None = None
|
||||
_scene_preset_store: ScenePresetStore | None = None
|
||||
_profile_engine: ProfileEngine | None = None
|
||||
_automation_engine: AutomationEngine | None = None
|
||||
|
||||
|
||||
def get_device_store() -> DeviceStore:
|
||||
@@ -111,11 +111,11 @@ def get_processor_manager() -> ProcessorManager:
|
||||
return _processor_manager
|
||||
|
||||
|
||||
def get_profile_store() -> ProfileStore:
|
||||
"""Get profile store dependency."""
|
||||
if _profile_store is None:
|
||||
raise RuntimeError("Profile store not initialized")
|
||||
return _profile_store
|
||||
def get_automation_store() -> AutomationStore:
|
||||
"""Get automation store dependency."""
|
||||
if _automation_store is None:
|
||||
raise RuntimeError("Automation store not initialized")
|
||||
return _automation_store
|
||||
|
||||
|
||||
def get_scene_preset_store() -> ScenePresetStore:
|
||||
@@ -125,11 +125,11 @@ def get_scene_preset_store() -> ScenePresetStore:
|
||||
return _scene_preset_store
|
||||
|
||||
|
||||
def get_profile_engine() -> ProfileEngine:
|
||||
"""Get profile engine dependency."""
|
||||
if _profile_engine is None:
|
||||
raise RuntimeError("Profile engine not initialized")
|
||||
return _profile_engine
|
||||
def get_automation_engine() -> AutomationEngine:
|
||||
"""Get automation engine dependency."""
|
||||
if _automation_engine is None:
|
||||
raise RuntimeError("Automation engine not initialized")
|
||||
return _automation_engine
|
||||
|
||||
|
||||
def get_auto_backup_engine() -> AutoBackupEngine:
|
||||
@@ -151,16 +151,16 @@ def init_dependencies(
|
||||
audio_source_store: AudioSourceStore | None = None,
|
||||
audio_template_store: AudioTemplateStore | None = None,
|
||||
value_source_store: ValueSourceStore | None = None,
|
||||
profile_store: ProfileStore | None = None,
|
||||
automation_store: AutomationStore | None = None,
|
||||
scene_preset_store: ScenePresetStore | None = None,
|
||||
profile_engine: ProfileEngine | None = None,
|
||||
automation_engine: AutomationEngine | None = None,
|
||||
auto_backup_engine: AutoBackupEngine | None = None,
|
||||
):
|
||||
"""Initialize global dependencies."""
|
||||
global _device_store, _template_store, _processor_manager
|
||||
global _pp_template_store, _pattern_template_store, _picture_source_store, _picture_target_store
|
||||
global _color_strip_store, _audio_source_store, _audio_template_store
|
||||
global _value_source_store, _profile_store, _scene_preset_store, _profile_engine, _auto_backup_engine
|
||||
global _value_source_store, _automation_store, _scene_preset_store, _automation_engine, _auto_backup_engine
|
||||
_device_store = device_store
|
||||
_template_store = template_store
|
||||
_processor_manager = processor_manager
|
||||
@@ -172,7 +172,7 @@ def init_dependencies(
|
||||
_audio_source_store = audio_source_store
|
||||
_audio_template_store = audio_template_store
|
||||
_value_source_store = value_source_store
|
||||
_profile_store = profile_store
|
||||
_automation_store = automation_store
|
||||
_scene_preset_store = scene_preset_store
|
||||
_profile_engine = profile_engine
|
||||
_automation_engine = automation_engine
|
||||
_auto_backup_engine = auto_backup_engine
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
"""Profile management API routes."""
|
||||
"""Automation management API routes."""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from wled_controller.api.auth import AuthRequired
|
||||
from wled_controller.api.dependencies import (
|
||||
get_profile_engine,
|
||||
get_profile_store,
|
||||
get_automation_engine,
|
||||
get_automation_store,
|
||||
get_scene_preset_store,
|
||||
)
|
||||
from wled_controller.api.schemas.profiles import (
|
||||
from wled_controller.api.schemas.automations import (
|
||||
AutomationCreate,
|
||||
AutomationListResponse,
|
||||
AutomationResponse,
|
||||
AutomationUpdate,
|
||||
ConditionSchema,
|
||||
ProfileCreate,
|
||||
ProfileListResponse,
|
||||
ProfileResponse,
|
||||
ProfileUpdate,
|
||||
)
|
||||
from wled_controller.core.profiles.profile_engine import ProfileEngine
|
||||
from wled_controller.storage.profile import (
|
||||
from wled_controller.core.automations.automation_engine import AutomationEngine
|
||||
from wled_controller.storage.automation import (
|
||||
AlwaysCondition,
|
||||
ApplicationCondition,
|
||||
Condition,
|
||||
@@ -25,7 +25,7 @@ from wled_controller.storage.profile import (
|
||||
SystemIdleCondition,
|
||||
TimeOfDayCondition,
|
||||
)
|
||||
from wled_controller.storage.profile_store import ProfileStore
|
||||
from wled_controller.storage.automation_store import AutomationStore
|
||||
from wled_controller.storage.scene_preset_store import ScenePresetStore
|
||||
from wled_controller.utils import get_logger
|
||||
|
||||
@@ -71,22 +71,22 @@ def _condition_to_schema(c: Condition) -> ConditionSchema:
|
||||
return ConditionSchema(**d)
|
||||
|
||||
|
||||
def _profile_to_response(profile, engine: ProfileEngine) -> ProfileResponse:
|
||||
state = engine.get_profile_state(profile.id)
|
||||
return ProfileResponse(
|
||||
id=profile.id,
|
||||
name=profile.name,
|
||||
enabled=profile.enabled,
|
||||
condition_logic=profile.condition_logic,
|
||||
conditions=[_condition_to_schema(c) for c in profile.conditions],
|
||||
scene_preset_id=profile.scene_preset_id,
|
||||
deactivation_mode=profile.deactivation_mode,
|
||||
deactivation_scene_preset_id=profile.deactivation_scene_preset_id,
|
||||
def _automation_to_response(automation, engine: AutomationEngine) -> AutomationResponse:
|
||||
state = engine.get_automation_state(automation.id)
|
||||
return AutomationResponse(
|
||||
id=automation.id,
|
||||
name=automation.name,
|
||||
enabled=automation.enabled,
|
||||
condition_logic=automation.condition_logic,
|
||||
conditions=[_condition_to_schema(c) for c in automation.conditions],
|
||||
scene_preset_id=automation.scene_preset_id,
|
||||
deactivation_mode=automation.deactivation_mode,
|
||||
deactivation_scene_preset_id=automation.deactivation_scene_preset_id,
|
||||
is_active=state["is_active"],
|
||||
last_activated_at=state.get("last_activated_at"),
|
||||
last_deactivated_at=state.get("last_deactivated_at"),
|
||||
created_at=profile.created_at,
|
||||
updated_at=profile.updated_at,
|
||||
created_at=automation.created_at,
|
||||
updated_at=automation.updated_at,
|
||||
)
|
||||
|
||||
|
||||
@@ -115,19 +115,19 @@ def _validate_scene_refs(
|
||||
# ===== CRUD Endpoints =====
|
||||
|
||||
@router.post(
|
||||
"/api/v1/profiles",
|
||||
response_model=ProfileResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations",
|
||||
response_model=AutomationResponse,
|
||||
tags=["Automations"],
|
||||
status_code=201,
|
||||
)
|
||||
async def create_profile(
|
||||
data: ProfileCreate,
|
||||
async def create_automation(
|
||||
data: AutomationCreate,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
scene_store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
):
|
||||
"""Create a new profile."""
|
||||
"""Create a new automation."""
|
||||
_validate_condition_logic(data.condition_logic)
|
||||
_validate_scene_refs(data.scene_preset_id, data.deactivation_scene_preset_id, scene_store)
|
||||
|
||||
@@ -136,7 +136,7 @@ async def create_profile(
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
profile = store.create_profile(
|
||||
automation = store.create_automation(
|
||||
name=data.name,
|
||||
enabled=data.enabled,
|
||||
condition_logic=data.condition_logic,
|
||||
@@ -146,64 +146,64 @@ async def create_profile(
|
||||
deactivation_scene_preset_id=data.deactivation_scene_preset_id,
|
||||
)
|
||||
|
||||
if profile.enabled:
|
||||
if automation.enabled:
|
||||
await engine.trigger_evaluate()
|
||||
|
||||
return _profile_to_response(profile, engine)
|
||||
return _automation_to_response(automation, engine)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/profiles",
|
||||
response_model=ProfileListResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations",
|
||||
response_model=AutomationListResponse,
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def list_profiles(
|
||||
async def list_automations(
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
):
|
||||
"""List all profiles."""
|
||||
profiles = store.get_all_profiles()
|
||||
return ProfileListResponse(
|
||||
profiles=[_profile_to_response(p, engine) for p in profiles],
|
||||
count=len(profiles),
|
||||
"""List all automations."""
|
||||
automations = store.get_all_automations()
|
||||
return AutomationListResponse(
|
||||
automations=[_automation_to_response(a, engine) for a in automations],
|
||||
count=len(automations),
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/api/v1/profiles/{profile_id}",
|
||||
response_model=ProfileResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations/{automation_id}",
|
||||
response_model=AutomationResponse,
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def get_profile(
|
||||
profile_id: str,
|
||||
async def get_automation(
|
||||
automation_id: str,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
):
|
||||
"""Get a single profile."""
|
||||
"""Get a single automation."""
|
||||
try:
|
||||
profile = store.get_profile(profile_id)
|
||||
automation = store.get_automation(automation_id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
return _profile_to_response(profile, engine)
|
||||
return _automation_to_response(automation, engine)
|
||||
|
||||
|
||||
@router.put(
|
||||
"/api/v1/profiles/{profile_id}",
|
||||
response_model=ProfileResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations/{automation_id}",
|
||||
response_model=AutomationResponse,
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def update_profile(
|
||||
profile_id: str,
|
||||
data: ProfileUpdate,
|
||||
async def update_automation(
|
||||
automation_id: str,
|
||||
data: AutomationUpdate,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
scene_store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
):
|
||||
"""Update a profile."""
|
||||
"""Update an automation."""
|
||||
if data.condition_logic is not None:
|
||||
_validate_condition_logic(data.condition_logic)
|
||||
|
||||
@@ -220,11 +220,11 @@ async def update_profile(
|
||||
try:
|
||||
# If disabling, deactivate first
|
||||
if data.enabled is False:
|
||||
await engine.deactivate_if_active(profile_id)
|
||||
await engine.deactivate_if_active(automation_id)
|
||||
|
||||
# Build update kwargs — use sentinel for Optional[str] fields
|
||||
update_kwargs = dict(
|
||||
profile_id=profile_id,
|
||||
automation_id=automation_id,
|
||||
name=data.name,
|
||||
enabled=data.enabled,
|
||||
condition_logic=data.condition_logic,
|
||||
@@ -236,34 +236,34 @@ async def update_profile(
|
||||
if data.deactivation_scene_preset_id is not None:
|
||||
update_kwargs["deactivation_scene_preset_id"] = data.deactivation_scene_preset_id
|
||||
|
||||
profile = store.update_profile(**update_kwargs)
|
||||
automation = store.update_automation(**update_kwargs)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
# Re-evaluate immediately if profile is enabled (may have new conditions/scene)
|
||||
if profile.enabled:
|
||||
# Re-evaluate immediately if automation is enabled (may have new conditions/scene)
|
||||
if automation.enabled:
|
||||
await engine.trigger_evaluate()
|
||||
|
||||
return _profile_to_response(profile, engine)
|
||||
return _automation_to_response(automation, engine)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/api/v1/profiles/{profile_id}",
|
||||
"/api/v1/automations/{automation_id}",
|
||||
status_code=204,
|
||||
tags=["Profiles"],
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def delete_profile(
|
||||
profile_id: str,
|
||||
async def delete_automation(
|
||||
automation_id: str,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
):
|
||||
"""Delete a profile."""
|
||||
# Deactivate first (stop owned targets)
|
||||
await engine.deactivate_if_active(profile_id)
|
||||
"""Delete an automation."""
|
||||
# Deactivate first
|
||||
await engine.deactivate_if_active(automation_id)
|
||||
|
||||
try:
|
||||
store.delete_profile(profile_id)
|
||||
store.delete_automation(automation_id)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
@@ -271,45 +271,45 @@ async def delete_profile(
|
||||
# ===== Enable/Disable =====
|
||||
|
||||
@router.post(
|
||||
"/api/v1/profiles/{profile_id}/enable",
|
||||
response_model=ProfileResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations/{automation_id}/enable",
|
||||
response_model=AutomationResponse,
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def enable_profile(
|
||||
profile_id: str,
|
||||
async def enable_automation(
|
||||
automation_id: str,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
):
|
||||
"""Enable a profile."""
|
||||
"""Enable an automation."""
|
||||
try:
|
||||
profile = store.update_profile(profile_id=profile_id, enabled=True)
|
||||
automation = store.update_automation(automation_id=automation_id, enabled=True)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
# Evaluate immediately so targets start without waiting for the next poll cycle
|
||||
# Evaluate immediately so scene activates without waiting for the next poll cycle
|
||||
await engine.trigger_evaluate()
|
||||
|
||||
return _profile_to_response(profile, engine)
|
||||
return _automation_to_response(automation, engine)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/api/v1/profiles/{profile_id}/disable",
|
||||
response_model=ProfileResponse,
|
||||
tags=["Profiles"],
|
||||
"/api/v1/automations/{automation_id}/disable",
|
||||
response_model=AutomationResponse,
|
||||
tags=["Automations"],
|
||||
)
|
||||
async def disable_profile(
|
||||
profile_id: str,
|
||||
async def disable_automation(
|
||||
automation_id: str,
|
||||
_auth: AuthRequired,
|
||||
store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
):
|
||||
"""Disable a profile and stop any targets it owns."""
|
||||
await engine.deactivate_if_active(profile_id)
|
||||
"""Disable an automation and deactivate it."""
|
||||
await engine.deactivate_if_active(automation_id)
|
||||
|
||||
try:
|
||||
profile = store.update_profile(profile_id=profile_id, enabled=False)
|
||||
automation = store.update_automation(automation_id=automation_id, enabled=False)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
return _profile_to_response(profile, engine)
|
||||
return _automation_to_response(automation, engine)
|
||||
@@ -7,11 +7,11 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from wled_controller.api.auth import AuthRequired
|
||||
from wled_controller.api.dependencies import (
|
||||
get_automation_engine,
|
||||
get_automation_store,
|
||||
get_device_store,
|
||||
get_picture_target_store,
|
||||
get_processor_manager,
|
||||
get_profile_engine,
|
||||
get_profile_store,
|
||||
get_scene_preset_store,
|
||||
)
|
||||
from wled_controller.api.schemas.scene_presets import (
|
||||
@@ -28,10 +28,10 @@ from wled_controller.core.scenes.scene_activator import (
|
||||
)
|
||||
from wled_controller.storage import DeviceStore
|
||||
from wled_controller.storage.picture_target_store import PictureTargetStore
|
||||
from wled_controller.storage.profile_store import ProfileStore
|
||||
from wled_controller.storage.automation_store import AutomationStore
|
||||
from wled_controller.storage.scene_preset import ScenePreset
|
||||
from wled_controller.storage.scene_preset_store import ScenePresetStore
|
||||
from wled_controller.core.profiles.profile_engine import ProfileEngine
|
||||
from wled_controller.core.automations.automation_engine import AutomationEngine
|
||||
from wled_controller.utils import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@@ -56,10 +56,10 @@ def _preset_to_response(preset: ScenePreset) -> ScenePresetResponse:
|
||||
"device_id": d.device_id,
|
||||
"software_brightness": d.software_brightness,
|
||||
} for d in preset.devices],
|
||||
profiles=[{
|
||||
"profile_id": p.profile_id,
|
||||
"enabled": p.enabled,
|
||||
} for p in preset.profiles],
|
||||
automations=[{
|
||||
"automation_id": a.automation_id,
|
||||
"enabled": a.enabled,
|
||||
} for a in preset.automations],
|
||||
order=preset.order,
|
||||
created_at=preset.created_at,
|
||||
updated_at=preset.updated_at,
|
||||
@@ -80,12 +80,12 @@ async def create_scene_preset(
|
||||
store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
target_store: PictureTargetStore = Depends(get_picture_target_store),
|
||||
device_store: DeviceStore = Depends(get_device_store),
|
||||
profile_store: ProfileStore = Depends(get_profile_store),
|
||||
automation_store: AutomationStore = Depends(get_automation_store),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
):
|
||||
"""Capture current state as a new scene preset."""
|
||||
targets, devices, profiles = capture_current_snapshot(
|
||||
target_store, device_store, profile_store, manager,
|
||||
targets, devices, automations = capture_current_snapshot(
|
||||
target_store, device_store, automation_store, manager,
|
||||
)
|
||||
|
||||
now = datetime.utcnow()
|
||||
@@ -96,7 +96,7 @@ async def create_scene_preset(
|
||||
color=data.color,
|
||||
targets=targets,
|
||||
devices=devices,
|
||||
profiles=profiles,
|
||||
automations=automations,
|
||||
order=store.count(),
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
@@ -200,12 +200,12 @@ async def recapture_scene_preset(
|
||||
store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
target_store: PictureTargetStore = Depends(get_picture_target_store),
|
||||
device_store: DeviceStore = Depends(get_device_store),
|
||||
profile_store: ProfileStore = Depends(get_profile_store),
|
||||
automation_store: AutomationStore = Depends(get_automation_store),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
):
|
||||
"""Re-capture current state into an existing preset (updates snapshot)."""
|
||||
targets, devices, profiles = capture_current_snapshot(
|
||||
target_store, device_store, profile_store, manager,
|
||||
targets, devices, automations = capture_current_snapshot(
|
||||
target_store, device_store, automation_store, manager,
|
||||
)
|
||||
|
||||
new_snapshot = ScenePreset(
|
||||
@@ -213,7 +213,7 @@ async def recapture_scene_preset(
|
||||
name="",
|
||||
targets=targets,
|
||||
devices=devices,
|
||||
profiles=profiles,
|
||||
automations=automations,
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -237,8 +237,8 @@ async def activate_scene_preset(
|
||||
store: ScenePresetStore = Depends(get_scene_preset_store),
|
||||
target_store: PictureTargetStore = Depends(get_picture_target_store),
|
||||
device_store: DeviceStore = Depends(get_device_store),
|
||||
profile_store: ProfileStore = Depends(get_profile_store),
|
||||
engine: ProfileEngine = Depends(get_profile_engine),
|
||||
automation_store: AutomationStore = Depends(get_automation_store),
|
||||
engine: AutomationEngine = Depends(get_automation_engine),
|
||||
manager: ProcessorManager = Depends(get_processor_manager),
|
||||
):
|
||||
"""Activate a scene preset — restore the captured state."""
|
||||
@@ -248,7 +248,7 @@ async def activate_scene_preset(
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
status, errors = await apply_scene_state(
|
||||
preset, target_store, device_store, profile_store, engine, manager,
|
||||
preset, target_store, device_store, automation_store, engine, manager,
|
||||
)
|
||||
|
||||
if not errors:
|
||||
|
||||
@@ -182,9 +182,9 @@ async def get_displays(
|
||||
async def get_running_processes(_: AuthRequired):
|
||||
"""Get list of currently running process names.
|
||||
|
||||
Returns a sorted list of unique process names for use in profile conditions.
|
||||
Returns a sorted list of unique process names for use in automation conditions.
|
||||
"""
|
||||
from wled_controller.core.profiles.platform_detector import PlatformDetector
|
||||
from wled_controller.core.automations.platform_detector import PlatformDetector
|
||||
|
||||
try:
|
||||
detector = PlatformDetector()
|
||||
@@ -271,7 +271,7 @@ STORE_MAP = {
|
||||
"audio_sources": "audio_sources_file",
|
||||
"audio_templates": "audio_templates_file",
|
||||
"value_sources": "value_sources_file",
|
||||
"profiles": "profiles_file",
|
||||
"automations": "automations_file",
|
||||
"scene_presets": "scene_presets_file",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Profile-related schemas."""
|
||||
"""Automation-related schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ConditionSchema(BaseModel):
|
||||
"""A single condition within a profile."""
|
||||
"""A single condition within an automation."""
|
||||
|
||||
condition_type: str = Field(description="Condition type discriminator (e.g. 'application')")
|
||||
# Application condition fields
|
||||
@@ -27,11 +27,11 @@ class ConditionSchema(BaseModel):
|
||||
match_mode: Optional[str] = Field(None, description="'exact', 'contains', or 'regex' (for mqtt condition)")
|
||||
|
||||
|
||||
class ProfileCreate(BaseModel):
|
||||
"""Request to create a profile."""
|
||||
class AutomationCreate(BaseModel):
|
||||
"""Request to create an automation."""
|
||||
|
||||
name: str = Field(description="Profile name", min_length=1, max_length=100)
|
||||
enabled: bool = Field(default=True, description="Whether the profile is enabled")
|
||||
name: str = Field(description="Automation name", min_length=1, max_length=100)
|
||||
enabled: bool = Field(default=True, description="Whether the automation is enabled")
|
||||
condition_logic: str = Field(default="or", description="How conditions combine: 'or' or 'and'")
|
||||
conditions: List[ConditionSchema] = Field(default_factory=list, description="List of conditions")
|
||||
scene_preset_id: Optional[str] = Field(None, description="Scene preset to activate")
|
||||
@@ -39,11 +39,11 @@ class ProfileCreate(BaseModel):
|
||||
deactivation_scene_preset_id: Optional[str] = Field(None, description="Scene preset for fallback deactivation")
|
||||
|
||||
|
||||
class ProfileUpdate(BaseModel):
|
||||
"""Request to update a profile."""
|
||||
class AutomationUpdate(BaseModel):
|
||||
"""Request to update an automation."""
|
||||
|
||||
name: Optional[str] = Field(None, description="Profile name", min_length=1, max_length=100)
|
||||
enabled: Optional[bool] = Field(None, description="Whether the profile is enabled")
|
||||
name: Optional[str] = Field(None, description="Automation name", min_length=1, max_length=100)
|
||||
enabled: Optional[bool] = Field(None, description="Whether the automation is enabled")
|
||||
condition_logic: Optional[str] = Field(None, description="How conditions combine: 'or' or 'and'")
|
||||
conditions: Optional[List[ConditionSchema]] = Field(None, description="List of conditions")
|
||||
scene_preset_id: Optional[str] = Field(None, description="Scene preset to activate")
|
||||
@@ -51,26 +51,26 @@ class ProfileUpdate(BaseModel):
|
||||
deactivation_scene_preset_id: Optional[str] = Field(None, description="Scene preset for fallback deactivation")
|
||||
|
||||
|
||||
class ProfileResponse(BaseModel):
|
||||
"""Profile information response."""
|
||||
class AutomationResponse(BaseModel):
|
||||
"""Automation information response."""
|
||||
|
||||
id: str = Field(description="Profile ID")
|
||||
name: str = Field(description="Profile name")
|
||||
enabled: bool = Field(description="Whether the profile is enabled")
|
||||
id: str = Field(description="Automation ID")
|
||||
name: str = Field(description="Automation name")
|
||||
enabled: bool = Field(description="Whether the automation is enabled")
|
||||
condition_logic: str = Field(description="Condition combination logic")
|
||||
conditions: List[ConditionSchema] = Field(description="List of conditions")
|
||||
scene_preset_id: Optional[str] = Field(None, description="Scene preset to activate")
|
||||
deactivation_mode: str = Field(default="none", description="Deactivation behavior")
|
||||
deactivation_scene_preset_id: Optional[str] = Field(None, description="Fallback scene preset")
|
||||
is_active: bool = Field(default=False, description="Whether the profile is currently active")
|
||||
last_activated_at: Optional[datetime] = Field(None, description="Last time this profile was activated")
|
||||
last_deactivated_at: Optional[datetime] = Field(None, description="Last time this profile was deactivated")
|
||||
is_active: bool = Field(default=False, description="Whether the automation is currently active")
|
||||
last_activated_at: Optional[datetime] = Field(None, description="Last time this automation was activated")
|
||||
last_deactivated_at: Optional[datetime] = Field(None, description="Last time this automation was deactivated")
|
||||
created_at: datetime = Field(description="Creation timestamp")
|
||||
updated_at: datetime = Field(description="Last update timestamp")
|
||||
|
||||
|
||||
class ProfileListResponse(BaseModel):
|
||||
"""List of profiles response."""
|
||||
class AutomationListResponse(BaseModel):
|
||||
"""List of automations response."""
|
||||
|
||||
profiles: List[ProfileResponse] = Field(description="List of profiles")
|
||||
count: int = Field(description="Number of profiles")
|
||||
automations: List[AutomationResponse] = Field(description="List of automations")
|
||||
count: int = Field(description="Number of automations")
|
||||
@@ -20,8 +20,8 @@ class DeviceBrightnessSnapshotSchema(BaseModel):
|
||||
software_brightness: int = 255
|
||||
|
||||
|
||||
class ProfileSnapshotSchema(BaseModel):
|
||||
profile_id: str
|
||||
class AutomationSnapshotSchema(BaseModel):
|
||||
automation_id: str
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class ScenePresetResponse(BaseModel):
|
||||
color: str
|
||||
targets: List[TargetSnapshotSchema]
|
||||
devices: List[DeviceBrightnessSnapshotSchema]
|
||||
profiles: List[ProfileSnapshotSchema]
|
||||
automations: List[AutomationSnapshotSchema]
|
||||
order: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
Reference in New Issue
Block a user