Improve SillyHome discovery and feedback learning

This commit is contained in:
2026-06-16 11:38:32 +02:00
parent c5f42a39a9
commit e2826e92ec
12 changed files with 513 additions and 8 deletions

View File

@@ -21,6 +21,7 @@ class DiscoveredEntity(BaseModel):
device_class: str | None = None
state_class: str | None = None
unit_of_measurement: str | None = None
category: str
role: EntityRole
learnable: bool
reason: str
@@ -82,14 +83,39 @@ _BINARY_CONTEXT_CLASSES = frozenset({
"window",
})
_ACTUATOR_DOMAINS = frozenset({
"button",
"climate",
"cover",
"fan",
"humidifier",
"input_boolean",
"input_button",
"lock",
"light",
"number",
"siren",
"switch",
"valve",
})
_CONTEXT_DOMAINS = frozenset({
"device_tracker",
"input_boolean",
"input_datetime",
"input_number",
"input_select",
"person",
"sun",
"weather",
"zone",
})
_LEARNABLE_CONTEXT_DOMAINS = frozenset({
"device_tracker",
"input_boolean",
"input_number",
"input_select",
"person",
"weather",
})
_CONTEXT_DOMAINS = frozenset({"device_tracker", "person", "sun", "weather", "zone"})
_LEARNABLE_CONTEXT_DOMAINS = frozenset({"device_tracker", "person", "weather"})
_NUMERIC_STATE_CLASSES = frozenset({"measurement", "total", "total_increasing"})
@@ -102,6 +128,7 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity:
return _result(
entity,
EntityRole.MEASUREMENT,
category=_measurement_category(entity),
learnable=True,
reason="Numerischer Messsensor für Zeitreihen und Training.",
)
@@ -110,6 +137,7 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity:
return _result(
entity,
EntityRole.BINARY_CONTEXT,
category=_binary_category(entity),
learnable=True,
reason="Binärer Kontextsensor für Zustands- und Anwesenheitsmuster.",
)
@@ -119,6 +147,7 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity:
return _result(
entity,
EntityRole.CONTEXT,
category=_context_category(entity),
learnable=learnable,
reason=(
"Kontextquelle für Training und Erklärungen."
@@ -131,6 +160,7 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity:
return _result(
entity,
EntityRole.ACTUATOR,
category=_actuator_category(entity),
learnable=False,
reason="Aktor ist ein mögliches Automationsziel, aber kein Trainingssensor.",
)
@@ -138,6 +168,7 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity:
return _result(
entity,
EntityRole.UNSUPPORTED,
category="unsupported",
learnable=False,
reason="Entity-Typ ist noch nicht für Lernen oder Automationen klassifiziert.",
)
@@ -162,6 +193,7 @@ def _result(
entity: HaEntitySummary,
role: EntityRole,
*,
category: str,
learnable: bool,
reason: str,
) -> DiscoveredEntity:
@@ -171,7 +203,62 @@ def _result(
device_class=entity.device_class,
state_class=entity.state_class,
unit_of_measurement=entity.unit_of_measurement,
category=category,
role=role,
learnable=learnable,
reason=reason,
)
def _actuator_category(entity: HaEntitySummary) -> str:
if entity.domain == "light":
return "light"
if entity.domain == "switch":
return "switch_socket"
if entity.domain == "button" or entity.domain == "input_button":
return "button"
if entity.domain == "cover":
return "cover_shutter"
if entity.domain == "climate":
return "heating"
if entity.domain == "lock":
return "lock"
if entity.domain == "fan":
return "fan"
if entity.domain in {"input_boolean", "number"}:
return "helper"
return entity.domain
def _measurement_category(entity: HaEntitySummary) -> str:
device_class = entity.device_class or ""
if device_class == "illuminance":
return "brightness"
if device_class == "temperature":
return "temperature"
if device_class in {"humidity", "moisture"}:
return "humidity"
if device_class in {"power", "energy", "current", "voltage"}:
return "energy_power"
if device_class in {"battery", "signal_strength"}:
return "diagnostic"
return "measurement"
def _binary_category(entity: HaEntitySummary) -> str:
device_class = entity.device_class or ""
if device_class in {"motion", "occupancy", "presence"}:
return "presence_motion"
if device_class in {"door", "garage_door", "opening", "window"}:
return "opening"
if device_class in {"smoke", "safety", "problem"}:
return "safety"
return "binary"
def _context_category(entity: HaEntitySummary) -> str:
if entity.domain.startswith("input_"):
return "helper"
if entity.domain in {"person", "device_tracker", "zone"}:
return "presence_location"
return entity.domain