Bootstrap SillyHome Future v2 core
This commit is contained in:
66
app/core/decision.py
Normal file
66
app/core/decision.py
Normal file
@@ -0,0 +1,66 @@
|
||||
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.")
|
||||
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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user