BEHAVIOR-004: trust HA automation actions
Some checks failed
quality / test (3.11) (push) Has been cancelled
quality / test (3.13) (push) Has been cancelled
quality / test (3.11) (pull_request) Has been cancelled
quality / test (3.13) (pull_request) Has been cancelled

This commit is contained in:
2026-06-14 15:58:05 +02:00
parent 1c5eab14b6
commit 7ad97320a2
8 changed files with 60 additions and 34 deletions

View File

@@ -161,8 +161,8 @@ def test_engine_trains_predicts_in_shadow_and_executes_only_after_approval(
shadow = engine.evaluate("light.office")
assert trained.behavior.status is BehaviorStatus.TRAINED
assert trained.behavior.sample_count == 3
assert trained.behavior.high_confidence_sample_count == 3
assert trained.behavior.sample_count == 6
assert trained.behavior.high_confidence_sample_count == 6
assert shadow.behavior.mode is BehaviorMode.SHADOW
assert shadow.behavior.prediction is not None
assert shadow.behavior.prediction.target_state == "on"
@@ -179,17 +179,27 @@ def test_engine_trains_predicts_in_shadow_and_executes_only_after_approval(
]
def test_engine_excludes_known_automation_actions(tmp_path: Path) -> None:
def test_engine_counts_known_automation_actions_like_manual_actions(
tmp_path: Path,
) -> None:
now = datetime.now(timezone.utc).replace(second=0, microsecond=0)
engine, _ = _engine(tmp_path, now)
trained = engine.train("light.office")
assert {pattern.target_state for pattern in trained.behavior.patterns} == {"on"}
assert {pattern.source for pattern in trained.behavior.patterns} == {"user"}
assert {pattern.target_state for pattern in trained.behavior.patterns} == {
"on",
"off",
}
assert {pattern.source for pattern in trained.behavior.patterns} == {
"user",
"automation",
}
assert trained.behavior.high_confidence_sample_count == 6
assert {pattern.weight for pattern in trained.behavior.patterns} == {1.0}
def test_engine_learns_causal_automation_for_shadow_without_user_credit(
def test_engine_learns_causal_automation_with_activation_credit(
tmp_path: Path,
) -> None:
now = datetime.now(timezone.utc).replace(second=0, microsecond=0)
@@ -270,7 +280,8 @@ def test_engine_learns_causal_automation_for_shadow_without_user_credit(
]
assert len(automation_patterns) == 3
assert trained.behavior.high_confidence_sample_count == 0
assert trained.behavior.high_confidence_sample_count == 3
assert {pattern.weight for pattern in automation_patterns} == {1.0}
assert {
(
pattern.trigger_entity_id,
@@ -280,6 +291,10 @@ def test_engine_learns_causal_automation_for_shadow_without_user_credit(
for pattern in automation_patterns
} == {("binary_sensor.storage_door", "off", "on")}
active = engine.set_active("light.storage", active=True)
assert active.behavior.mode is BehaviorMode.ACTIVE
def test_active_mode_rejects_unsafe_domains(tmp_path: Path) -> None:
settings = _settings(tmp_path)
@@ -301,7 +316,7 @@ def test_active_mode_rejects_unsafe_domains(tmp_path: Path) -> None:
engine.set_active("lock.front_door", active=True)
def test_active_mode_requires_user_attributed_actions(tmp_path: Path) -> None:
def test_active_mode_requires_trusted_manual_or_automation_actions(tmp_path: Path) -> None:
settings = _settings(tmp_path)
store = ActuatorStore(settings.actuator_store)
record = store.configure("light.office")
@@ -321,7 +336,7 @@ def test_active_mode_requires_user_attributed_actions(tmp_path: Path) -> None:
reader = FakeBehaviorReader(entities=[], history=[], logbook=[])
engine = BehaviorEngine(ha_reader=reader, store=store, settings=settings)
with pytest.raises(ValueError, match="eindeutig dir zugeordnete"):
with pytest.raises(ValueError, match="manuelle oder automatisierte"):
engine.set_active("light.office", active=True)

View File

@@ -15,12 +15,15 @@ def test_dashboard_is_served_at_root() -> None:
assert "Ohne deine spätere Freigabe wird nichts geschaltet" in response.text
assert "Du wählst keine Sensoren und erstellst keine Regeln" in response.text
assert "Freigabe noch gesperrt" in response.text
assert "Bediene das Licht dafür direkt über Home Assistant" in response.text
assert "manuelle oder automatisierte Handlung" in response.text
assert "Davon erkannte HA-Automationen" in response.text
assert "Aktuelle Situation auswerten" in response.text
assert "Die Prüfung simuliert keinen Sensorwechsel" in response.text
assert "Kein frischer passender Sensorwechsel erkannt" in response.text
assert "Vorhersage jetzt prüfen" not in response.text
assert 'record.behavior.status === "trained" && missingUserActions === 0' in response.text
assert (
'record.behavior.status === "trained" && missingTrustedActions === 0'
in response.text
)
assert "Automation-Entwurf" not in response.text
assert "Manuelle Overrides" not in response.text