ml/rules-recommendations: regelbasierte Heizungsempfehlung und Recommender
This commit is contained in:
1
app/rules/__init__.py
Normal file
1
app/rules/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# sillyhome-next.rules
|
||||
15
app/rules/heating.py
Normal file
15
app/rules/heating.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from app.ha.models import HaEntitySummary
|
||||
from app.rules.recommender import Rule
|
||||
|
||||
|
||||
class HeatingRule(Rule):
|
||||
def matches(self, entities: Sequence[HaEntitySummary]) -> bool:
|
||||
domains = {item.domain for item in entities}
|
||||
return "climate" in domains or "sensor" in domains
|
||||
|
||||
def recommendation(self, entities: Sequence[HaEntitySummary]) -> str:
|
||||
return "Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."
|
||||
25
app/rules/recommender.py
Normal file
25
app/rules/recommender.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from app.ha.models import HaEntitySummary
|
||||
|
||||
|
||||
class Rule:
|
||||
def matches(self, entities: Sequence[HaEntitySummary]) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def recommendation(self, entities: Sequence[HaEntitySummary]) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Recommender:
|
||||
def __init__(self, rules: Sequence[Rule]) -> None:
|
||||
self._rules = rules
|
||||
|
||||
def run(self, entities: Sequence[HaEntitySummary]) -> list[str]:
|
||||
results: list[str] = []
|
||||
for rule in self._rules:
|
||||
if rule.matches(entities):
|
||||
results.append(rule.recommendation(entities))
|
||||
return results
|
||||
@@ -25,4 +25,4 @@ strict = true
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py311"
|
||||
target-version = "py311"
|
||||
26
tests/rules/test_heating.py
Normal file
26
tests/rules/test_heating.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.ha.models import HaEntitySummary
|
||||
from app.rules.heating import HeatingRule
|
||||
from app.rules.recommender import Recommender
|
||||
|
||||
|
||||
def _sensor(entity_id: str) -> HaEntitySummary:
|
||||
return HaEntitySummary(entity_id=entity_id, domain="sensor")
|
||||
|
||||
|
||||
def _climate(entity_id: str) -> HaEntitySummary:
|
||||
return HaEntitySummary(entity_id=entity_id, domain="climate")
|
||||
|
||||
|
||||
def test_heating_rule_triggers() -> None:
|
||||
rule = HeatingRule()
|
||||
assert rule.matches([_climate("climate.living_room")])
|
||||
assert rule.matches([_sensor("sensor.temperature_living")])
|
||||
|
||||
|
||||
def test_recommender_uses_rule() -> None:
|
||||
recommender = Recommender(rules=[HeatingRule()])
|
||||
assert recommender.run([_climate("climate.living_room")]) == [
|
||||
"Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."
|
||||
]
|
||||
Reference in New Issue
Block a user