Add native HA integration and dashboard
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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
12
tests/test_ha_client.py
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user