From bc8bec6aa9acb37b5209d65a4d826911333486c6 Mon Sep 17 00:00:00 2001 From: Otto Date: Tue, 16 Jun 2026 23:46:33 +0200 Subject: [PATCH] Improve dashboard categories and loading --- app/ha/discovery.py | 74 +++++++++++++++++++++++++------------------ app/static/index.html | 49 +++++++++++++++++++++------- 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/app/ha/discovery.py b/app/ha/discovery.py index fe8dd64..8a976c3 100644 --- a/app/ha/discovery.py +++ b/app/ha/discovery.py @@ -105,6 +105,7 @@ _CONTEXT_DOMAINS = frozenset({ "input_datetime", "input_number", "input_select", + "input_text", "person", "sun", "weather", @@ -122,6 +123,29 @@ _NUMERIC_STATE_CLASSES = frozenset({"measurement", "total", "total_increasing"}) def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity: + if entity.domain in _ACTUATOR_DOMAINS: + return _result( + entity, + EntityRole.ACTUATOR, + category=_actuator_category(entity), + learnable=False, + reason="Aktor ist ein mögliches Automationsziel, aber kein Trainingssensor.", + ) + + if entity.domain in _CONTEXT_DOMAINS: + learnable = entity.domain in _LEARNABLE_CONTEXT_DOMAINS + return _result( + entity, + EntityRole.CONTEXT, + category=_context_category(entity), + learnable=learnable, + reason=( + "Kontextquelle für Training und Erklärungen." + if learnable + else "Kontextquelle ohne direkte Trainingsfreigabe." + ), + ) + if entity.domain == "sensor" and ( entity.state_class in _NUMERIC_STATE_CLASSES or entity.device_class in _MEASUREMENT_CLASSES @@ -144,29 +168,6 @@ def classify_entity(entity: HaEntitySummary) -> DiscoveredEntity: reason="Binärer Kontextsensor für Zustands- und Anwesenheitsmuster.", ) - if entity.domain in _CONTEXT_DOMAINS: - learnable = entity.domain in _LEARNABLE_CONTEXT_DOMAINS - return _result( - entity, - EntityRole.CONTEXT, - category=_context_category(entity), - learnable=learnable, - reason=( - "Kontextquelle für Training und Erklärungen." - if learnable - else "Kontextquelle ohne direkte Trainingsfreigabe." - ), - ) - - if entity.domain in _ACTUATOR_DOMAINS: - return _result( - entity, - EntityRole.ACTUATOR, - category=_actuator_category(entity), - learnable=False, - reason="Aktor ist ein mögliches Automationsziel, aber kein Trainingssensor.", - ) - return _result( entity, EntityRole.UNSUPPORTED, @@ -226,26 +227,33 @@ def _actuator_category(entity: HaEntitySummary) -> str: if entity.domain == "lock": return "lock" if entity.domain == "fan": - return "fan" + return "ventilation" + if entity.domain == "humidifier": + return "climate" if entity.domain in {"media_player", "remote"}: return "media_tv" - if entity.domain in {"input_boolean", "number"}: + if entity.domain == "input_boolean" or entity.domain == "number" or entity.domain == "input_button": return "helper" + if entity.domain == "sensor" and (entity.device_class or entity.unit_of_measurement): + return "measurement" return entity.domain def _measurement_category(entity: HaEntitySummary) -> str: device_class = entity.device_class or "" - if device_class == "illuminance": + unit = (entity.unit_of_measurement or "").lower() + if device_class == "illuminance" or unit == "lx": return "brightness" - if device_class == "temperature": + if device_class == "temperature" or unit in {"°c", "°f", "k"}: return "temperature" - if device_class in {"humidity", "moisture"}: + if device_class in {"humidity", "moisture"} or unit in {"%", "rh", "g/m³", "kg/m³"}: return "humidity" - if device_class in {"power", "energy", "current", "voltage"}: + if device_class in {"power", "energy", "current", "voltage"} or unit in {"w", "kw", "kwh", "a", "v", "va", "var"}: return "energy_power" - if device_class in {"battery", "signal_strength"}: + if device_class in {"battery", "signal_strength"} or unit in {"%", "dbm"}: return "diagnostic" + if unit: + return f"measurement:{unit}" return "measurement" @@ -253,7 +261,7 @@ 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"}: + if device_class in {"door", "garage_door", "opening", "window", "button"}: return "opening" if device_class in {"smoke", "safety", "problem"}: return "safety" @@ -265,4 +273,8 @@ def _context_category(entity: HaEntitySummary) -> str: return "helper" if entity.domain in {"person", "device_tracker", "zone"}: return "presence_location" + if entity.domain == "sun": + return "weather" + if entity.domain == "weather": + return "weather" return entity.domain diff --git a/app/static/index.html b/app/static/index.html index 545587c..b395dfb 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -354,20 +354,17 @@ async function loadActuatorSuggestions() { function actuatorGroupLabel(domain) { const labels = { - button: "Buttons", - climate: "Heizungen / Klima", light: "Lichter", - input_boolean: "Helper-Schalter", - input_button: "Helper-Buttons", + switch_socket: "Steckdosen / Schalter", + button: "Buttons", + cover_shutter: "Rollläden / Fenster", + heating: "Heizungen / Klima", + ventilation: "Lüftung / Ventilatoren", + climate: "Klima", + helper: "Helper", + measurement: "Sensoren / Messung", lock: "Schlösser", - media_player: "TV / Medien", - number: "Numerische Helper", - remote: "Fernbedienungen", - switch: "Schalter / Steckdosen", - cover: "Rollläden / Cover", - fan: "Lüftung / Ventilatoren", - humidifier: "Befeuchter / Entfeuchter", - valve: "Ventile", + media_tv: "TV / Medien", }; return labels[domain] || domain; } @@ -815,3 +812,31 @@ loadOverview(); + + +function categoryLabel(category) { + const labels = { + light: "Licht", + switch_socket: "Steckdosen / Schalter", + button: "Buttons", + cover_shutter: "Rollläden / Cover", + heating: "Heizungen / Klima", + fan: "Lüftung / Ventilatoren", + ventilation: "Lüftung / Ventilatoren", + media_tv: "TV / Medien", + helper: "Helper", + presence_motion: "Bewegung / Präsenz", + opening: "Fenster / Türen", + weather: "Wetter", + brightness: "Helligkeit", + temperature: "Temperatur", + humidity: "Feuchtigkeit", + energy_power: "Strom / Energie", + diagnostic: "Diagnose", + measurement: "Messung", + binary: "Binär", + presence_location: "Anwesenheit / Ort", + climate: "Klima", + }; + return labels[category] || category; +} \ No newline at end of file