from __future__ import annotations from app.core.models import ControlProfile, Decision, LearningProfile, RuntimeState, SafetyStage SAFE_DOMAINS = frozenset({"light", "switch", "fan", "cover", "humidifier"}) class DecisionEngineV2: def decide( self, *, actuator_entity_id: str, trigger_entity_id: str, runtime: RuntimeState, learning: LearningProfile, control: ControlProfile, ) -> Decision: blockers: list[str] = [] actuator = runtime.entities.get(actuator_entity_id) trigger = runtime.entities.get(trigger_entity_id) if actuator is None: blockers.append("Aktor ist im Runtime-State nicht bekannt.") if trigger is None: blockers.append("Trigger ist im Runtime-State nicht bekannt.") domain = actuator_entity_id.split(".", 1)[0] if domain not in SAFE_DOMAINS: blockers.append(f"Domain {domain} ist nicht fuer Active-Control freigegeben.") if control.manual_block: blockers.append("Manuelle Sicherheitssperre ist aktiv.") if control.stage is SafetyStage.ACTIVE and not control.active_ready: blockers.append("Active ist noch nicht freigegeben. Erst Dry-run-Reife erreichen.") best = None for pattern in learning.patterns: if pattern.trigger_entity_id != trigger_entity_id: continue if trigger is not None and pattern.trigger_state != trigger.state: continue if best is None or pattern.confidence > best.confidence: best = pattern if best is None: blockers.append("Kein passendes kausales Muster gefunden.") return Decision( actuator_entity_id=actuator_entity_id, allowed=False, reason="Keine Aktion faellig.", blockers=blockers, trigger_entity_id=trigger_entity_id, ) if best.confidence < control.min_confidence: blockers.append( f"Confidence {best.confidence:.0%} liegt unter {control.min_confidence:.0%}." ) if actuator is not None and actuator.state == best.target_state: blockers.append("Zielzustand ist bereits erreicht.") dry_run = control.stage is SafetyStage.DRY_RUN allowed = not blockers and control.stage in {SafetyStage.ACTIVE, SafetyStage.DRY_RUN} return Decision( actuator_entity_id=actuator_entity_id, target_state=best.target_state, confidence=best.confidence, allowed=allowed, executed=False, dry_run=dry_run, reason="Ausfuehrung freigegeben." if allowed and not dry_run else "Dry-run oder blockiert.", blockers=blockers, trigger_entity_id=trigger_entity_id, )