Add native HA integration and dashboard

This commit is contained in:
2026-06-18 12:46:57 +02:00
parent 9d01af98b2
commit b77992606a
9 changed files with 431 additions and 12 deletions

View File

@@ -9,7 +9,7 @@ def test_addon_config_declares_future_addon() -> None:
config = yaml.safe_load(Path("addon/config.yaml").read_text(encoding="utf-8"))
assert config["slug"] == "sillyhome_future"
assert config["version"] == "2.0.0-alpha.3"
assert config["version"] == "2.0.0-alpha.4"
assert config["ingress"] is True
assert config["ingress_port"] == 8099
assert config["homeassistant_api"] is True

View File

@@ -15,7 +15,7 @@ def test_health_and_backup_roundtrip(tmp_path, monkeypatch) -> None: # type: ig
assert stores is not None
assert health.status_code == 200
assert health.json()["version"] == "2.0.0-alpha.3"
assert health.json()["version"] == "2.0.0-alpha.4"
assert backup.status_code == 200
assert restore.status_code == 200
assert restore.json() == {"status": "restored"}

View File

@@ -7,6 +7,7 @@ from app.core.handoff import HandoffMatrix
from app.core.models import (
BehaviorPatternV2,
ControlProfile,
Decision,
EntityState,
HandoffMode,
LearningProfile,
@@ -91,3 +92,64 @@ def test_handoff_matrix_detects_conflict_and_rollback() -> None:
rolled_back = matrix.rollback(controlled)
assert rolled_back.handoff_mode is HandoffMode.ROLLBACK
assert rolled_back.paused_automation_ids == []
def test_event_core_executes_allowed_active_decision(tmp_path: Path) -> None:
stores = FutureStores(tmp_path)
stores.save_runtime(
RuntimeState(
entities={
"light.storage": EntityState(
entity_id="light.storage",
domain="light",
state="off",
)
}
)
)
stores.save_learning(
LearningState(
profiles={
"light.storage": LearningProfile(
actuator_entity_id="light.storage",
patterns=[
BehaviorPatternV2(
actuator_entity_id="light.storage",
target_state="on",
trigger_entity_id="binary_sensor.storage_door",
trigger_state="on",
support=3,
confidence=0.95,
)
],
)
}
)
)
stores.save_control(
stores.control().model_copy(
update={
"profiles": {
"light.storage": ControlProfile(
actuator_entity_id="light.storage",
stage=SafetyStage.ACTIVE,
min_confidence=0.8,
)
}
}
)
)
calls: list[str] = []
def execute(decision: Decision) -> bool:
calls.append(decision.actuator_entity_id)
return True
audit = EventCore(stores).process_state_event(
StateEvent(entity_id="binary_sensor.storage_door", new_state="on"),
execute=execute,
)
decision = [item.decision for item in audit if item.decision is not None][0]
assert calls == ["light.storage"]
assert decision.executed is True

12
tests/test_ha_client.py Normal file
View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from app.core.ha_client import service_for_state
def test_service_for_state_maps_safe_domains() -> None:
assert service_for_state("light", "on") == ("light", "turn_on")
assert service_for_state("switch", "off") == ("switch", "turn_off")
assert service_for_state("cover", "open") == ("cover", "open_cover")
assert service_for_state("cover", "closed") == ("cover", "close_cover")
assert service_for_state("lock", "unlocked") is None