main: HeatingRule auf heizungsrelevante Sensoren begrenzen
This commit is contained in:
@@ -7,9 +7,28 @@ from app.rules.recommender import Rule
|
||||
|
||||
|
||||
class HeatingRule(Rule):
|
||||
"""Heizungsregel: Nur auf heizungsrelevante Entitäten reagieren.
|
||||
|
||||
Triggert bei:
|
||||
- `climate`-Entitäten direkt
|
||||
- `sensor` mit `device_class` in {temperature, humidity}
|
||||
- `binary_sensor` mit `device_class` in {occupancy, presence}
|
||||
|
||||
Alle anderen Domains/Device-Klassen bleiben ohne Effekt.
|
||||
"""
|
||||
|
||||
HEATING_SENSOR_CLASSES: frozenset[str] = frozenset({"temperature", "humidity"})
|
||||
HEATING_PRESENCE_CLASSES: frozenset[str] = frozenset({"occupancy", "presence"})
|
||||
|
||||
def matches(self, entities: Sequence[HaEntitySummary]) -> bool:
|
||||
domains = {item.domain for item in entities}
|
||||
return "climate" in domains or "sensor" in domains
|
||||
for item in entities:
|
||||
if item.domain == "climate":
|
||||
return True
|
||||
if item.domain == "sensor" and item.device_class in self.HEATING_SENSOR_CLASSES:
|
||||
return True
|
||||
if item.domain == "binary_sensor" and item.device_class in self.HEATING_PRESENCE_CLASSES:
|
||||
return True
|
||||
return False
|
||||
|
||||
def recommendation(self, entities: Sequence[HaEntitySummary]) -> str:
|
||||
return "Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."
|
||||
@@ -1,26 +1,64 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
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 _entity(entity_id: str, domain: str, device_class: str | None = None) -> HaEntitySummary:
|
||||
return HaEntitySummary(entity_id=entity_id, domain=domain, device_class=device_class)
|
||||
|
||||
|
||||
def _climate(entity_id: str) -> HaEntitySummary:
|
||||
return HaEntitySummary(entity_id=entity_id, domain="climate")
|
||||
|
||||
|
||||
def test_heating_rule_triggers() -> None:
|
||||
# --- positive cases --------------------------------------------------------
|
||||
@pytest.mark.parametrize(
|
||||
"entity",
|
||||
[
|
||||
_entity("climate.living_room", "climate"),
|
||||
_entity("sensor.temperature_living", "sensor", "temperature"),
|
||||
_entity("sensor.humidity_bathroom", "sensor", "humidity"),
|
||||
_entity("binary_sensor.living_room_occupancy", "binary_sensor", "occupancy"),
|
||||
_entity("binary_sensor.entrance_presence", "binary_sensor", "presence"),
|
||||
],
|
||||
ids=lambda e: e.entity_id,
|
||||
)
|
||||
def test_heating_rule_triggers_for_relevant_entities(entity: HaEntitySummary) -> None:
|
||||
rule = HeatingRule()
|
||||
assert rule.matches([_climate("climate.living_room")])
|
||||
assert rule.matches([_sensor("sensor.temperature_living")])
|
||||
assert rule.matches([entity]) is True
|
||||
|
||||
|
||||
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."
|
||||
# --- negative cases -------------------------------------------------------
|
||||
@pytest.mark.parametrize(
|
||||
"entity",
|
||||
[
|
||||
_entity("sensor.power_consumption", "sensor", "power"),
|
||||
_entity("sensor.door", "sensor", "door"),
|
||||
_entity("sensor.energy", "sensor", "energy"),
|
||||
_entity("binary_sensor.door_window", "binary_sensor", "door"),
|
||||
_entity("binary_sensor.motion", "binary_sensor", "motion"),
|
||||
_entity("light.living_room", "light"),
|
||||
_entity("switch.plug", "switch"),
|
||||
_entity("sensor.some_random", "sensor"),
|
||||
_entity("binary_sensor.some_binary", "binary_sensor"),
|
||||
],
|
||||
ids=lambda e: e.entity_id,
|
||||
)
|
||||
def test_heating_rule_ignores_non_heating_entities(entity: HaEntitySummary) -> None:
|
||||
rule = HeatingRule()
|
||||
assert rule.matches([entity]) is False
|
||||
|
||||
|
||||
def test_heating_rule_mixed_list_returns_true() -> None:
|
||||
rule = HeatingRule()
|
||||
entities = [
|
||||
_entity("sensor.power", "sensor", "power"),
|
||||
_entity("climate.living_room", "climate"),
|
||||
_entity("light.ceiling", "light"),
|
||||
]
|
||||
assert rule.matches(entities) is True
|
||||
|
||||
|
||||
def test_heating_rule_recommendation_is_stable() -> None:
|
||||
rule = HeatingRule()
|
||||
expected = "Prüfe Heizungsregelung: Aktiviere energiesparenden Modus bei Abwesenheit."
|
||||
assert rule.recommendation([_entity("climate.living_room", "climate")]) == expected
|
||||
Reference in New Issue
Block a user