fix(grafana): Umlaute, °C, saubere Formatierung fuer Temperaturen/Energie/Heizung

- SENSOR_MAP Labels mit echten Umlauten (Außen, Küche, Ölkessel)
- Gradzeichen °C statt nur C
- Listenformat mit Bindestrich statt Einrueckung
- Abschnitte Räume & Außen / Heizung & Puffer
- Energie-Tool mit passenden Einheiten (W, kWh, %)
- SYSTEM_PROMPT_EXTRA: LLM soll Tool-Output 1:1 weitergeben
This commit is contained in:
Homelab Cursor 2026-03-21 02:02:29 +01:00
parent db61aaedae
commit 4d38c5302c

View file

@ -10,29 +10,40 @@ INFLUX_URL = "http://100.66.78.56:8086"
INFLUX_DB = "iobroker" INFLUX_DB = "iobroker"
SENSOR_MAP = { SENSOR_MAP = {
"aussen": ("mqtt.0.Holzvergaser_Sensoren_6.Aussenfühler.temperature", "Aussen"), "aussen": ("mqtt.0.Holzvergaser_Sensoren_6.Aussenfühler.temperature", "Außen"),
"wohnstube": ("mqtt.0.Wohnstube_Temperatur_1.Wohnstube.Wohnstube_Temperatur", "Wohnstube"), "wohnstube": ("mqtt.0.Wohnstube_Temperatur_1.Wohnstube.Wohnstube_Temperatur", "Wohnstube"),
"kueche": ("mqtt.0.ESP_Kücheneu_2.Sensor_Küche.Küche", "Kueche"), "kueche": ("mqtt.0.ESP_Kücheneu_2.Sensor_Küche.Küche", "Küche"),
"garage": ("mqtt.0.ESP_Easy_3.Garage_neu.Garage", "Garage"), "garage": ("mqtt.0.ESP_Easy_3.Garage_neu.Garage", "Garage"),
"puffer_oben": ("mqtt.0.ESP_Easy_Display_4.oben.temperature", "Puffer oben"), "puffer_oben": ("mqtt.0.ESP_Easy_Display_4.oben.temperature", "Puffer oben"),
"puffer_unten": ("mqtt.0.ESP_Easy_Display_4.Unten.temperature", "Puffer unten"), "puffer_unten": ("mqtt.0.ESP_Easy_Display_4.Unten.temperature", "Puffer unten"),
"garten": ("mqtt.0.ESP_Easy_schwimmbad_neu.Lufttemperatur.Lufttemperatur_Garten", "Garten/Luft"), "garten": ("mqtt.0.ESP_Easy_schwimmbad_neu.Lufttemperatur.Lufttemperatur_Garten", "Garten/Luft"),
"schwimmbad": ("mqtt.0.ESP_Easy_schwimmbad_neu.Schwimmbad.Wassertemperatur_", "Schwimmbad"), "schwimmbad": ("mqtt.0.ESP_Easy_schwimmbad_neu.Schwimmbad.Wassertemperatur_", "Schwimmbad"),
"oelkessel_vl": ("mqtt.0.Oelkessel.Oelkessel_VL.Vorlauf", "Oelkessel Vorlauf"), "oelkessel_vl": ("mqtt.0.Oelkessel.Oelkessel_VL.Vorlauf", "Ölkessel Vorlauf"),
"brunnen_vl": ("mqtt.0.Brunnenwasser.Oelkessel_VL.Vorlauf", "Brunnenwasser Vorlauf"), "brunnen_vl": ("mqtt.0.Brunnenwasser.Oelkessel_VL.Vorlauf", "Brunnenwasser Vorlauf"),
"holzvergaser_1": ("mqtt.0.Holzvergaser_Sensoren_6.temp1.temperature1", "Holzvergaser Sensor 1"), "holzvergaser_1": ("mqtt.0.Holzvergaser_Sensoren_6.temp1.temperature1", "Holzvergaser Sensor 1"),
"holzvergaser_2": ("mqtt.0.Holzvergaser_Sensoren_6.temp2.temperature2", "Holzvergaser Sensor 2"), "holzvergaser_2": ("mqtt.0.Holzvergaser_Sensoren_6.temp2.temperature2", "Holzvergaser Sensor 2"),
} }
ENERGIE_MEASUREMENTS = { ENERGIE_MEASUREMENTS = {
"pv_leistung": ("mqtt.1.openWB.pv.W", "PV Leistung (W)"), "pv_leistung": ("mqtt.1.openWB.pv.W", "PV Leistung"),
"pv_tagesertrag": ("mqtt.1.openWB.pv.DailyYieldKwh", "PV Tagesertrag (kWh)"), "pv_tagesertrag": ("mqtt.1.openWB.pv.DailyYieldKwh", "PV Tagesertrag"),
"netz_bezug": ("mqtt.1.openWB.evu.W", "Netz Bezug/Einspeisung (W)"), "netz_bezug": ("mqtt.1.openWB.evu.W", "Netz Bezug/Einspeisung"),
"hausverbrauch": ("mqtt.1.openWB.global.WHouseConsumption", "Hausverbrauch (W)"), "hausverbrauch": ("mqtt.1.openWB.global.WHouseConsumption", "Hausverbrauch"),
"batterie_soc": ("mqtt.1.openWB.housebattery.%Soc", "Batterie SoC (%)"), "batterie_soc": ("mqtt.1.openWB.housebattery.%Soc", "Batterie SoC"),
"ueberschuss": ("mqtt.1.openWB.SmartHome.Status.uberschuss", "Ueberschuss (W)"), "ueberschuss": ("mqtt.1.openWB.SmartHome.Status.uberschuss", "Überschuss"),
} }
_ENERGIE_UNITS = {
"PV Leistung": "W",
"PV Tagesertrag": "kWh",
"Netz Bezug/Einspeisung": "W",
"Hausverbrauch": "W",
"Batterie SoC": "%",
"Überschuss": "W",
}
_HEIZUNG_KEYS = {"puffer", "vorlauf", "holz", "brunnen", "öl"}
TOOLS = [ TOOLS = [
{ {
"type": "function", "type": "function",
@ -46,7 +57,7 @@ TOOLS = [
"type": "function", "type": "function",
"function": { "function": {
"name": "get_temperaturen", "name": "get_temperaturen",
"description": "Aktuelle Temperaturen aus dem Haus in Muldenstein: Raeume, Aussen, Puffer, Heizung. Nutze bei Fragen zu Temperatur, Heizung, Puffer, Raumklima, 'wie warm ist es'.", "description": "Aktuelle Temperaturen aus dem Haus in Muldenstein: Räume, Außen, Puffer, Heizung. Nutze bei Fragen zu Temperatur, Heizung, Puffer, Raumklima, 'wie warm ist es'.",
"parameters": {"type": "object", "properties": {}, "required": []}, "parameters": {"type": "object", "properties": {}, "required": []},
}, },
}, },
@ -62,18 +73,21 @@ TOOLS = [
"type": "function", "type": "function",
"function": { "function": {
"name": "get_heizung", "name": "get_heizung",
"description": "Heizungsstatus: Brenner, Puffertemperaturen, Vorlauf, Holzvergaser. Nutze bei Fragen zu Heizung, Brenner, Puffer, Holzvergaser, Oelkessel.", "description": "Heizungsstatus: Brenner, Puffertemperaturen, Vorlauf, Holzvergaser. Nutze bei Fragen zu Heizung, Brenner, Puffer, Holzvergaser, Ölkessel.",
"parameters": {"type": "object", "properties": {}, "required": []}, "parameters": {"type": "object", "properties": {}, "required": []},
}, },
}, },
] ]
SYSTEM_PROMPT_EXTRA = """Fuer Smart-Home-Daten (Temperaturen, Energie, Heizung) im Haus Muldenstein: SYSTEM_PROMPT_EXTRA = """Für Smart-Home-Daten (Temperaturen, Energie, Heizung) im Haus Muldenstein:
- get_temperaturen: alle Raumtemperaturen + Aussen - get_temperaturen: alle Raumtemperaturen + Außen
- get_energie: PV, Batterie, Netz, Hausverbrauch - get_energie: PV, Batterie, Netz, Hausverbrauch
- get_heizung: Brenner, Puffer, Vorlauf, Holzvergaser - get_heizung: Brenner, Puffer, Vorlauf, Holzvergaser
- get_grafana_status: Dashboard-Uebersicht und Alerts - get_grafana_status: Dashboard-Übersicht und Alerts
Grafana-URL: https://grafana.orbitalo.net/ Grafana-URL: https://grafana.orbitalo.net/
WICHTIG: Die Tool-Ergebnisse sind bereits fertig formatiert.
Gib sie 1:1 weiter, NICHT umformulieren oder kürzen.
""" """
@ -162,41 +176,42 @@ def handle_get_grafana_status(**kw):
def handle_get_temperaturen(**kw): def handle_get_temperaturen(**kw):
data = _query_latest(SENSOR_MAP) data = _query_latest(SENSOR_MAP)
if not data: if not data:
return "Keine Temperaturdaten verfuegbar (InfluxDB nicht erreichbar?)" return "Keine Temperaturdaten verfügbar (InfluxDB nicht erreichbar?)"
raeume = [] raeume = []
heizung = [] heizung = []
for name, val, ts in data: for name, val, ts in data:
entry = f" {name}: {val:.1f} C ({ts})" entry = f"- {name}: {val:.1f} °C"
if any(k in name.lower() for k in ["puffer", "vorlauf", "holz", "brunnen", "oel"]): if any(k in name.lower() for k in _HEIZUNG_KEYS):
heizung.append(entry) heizung.append(entry)
else: else:
raeume.append(entry) raeume.append(entry)
lines = ["Temperaturen Muldenstein (live):"] lines = ["Temperaturen Muldenstein:"]
if raeume: if raeume:
lines.append("\nRaeume & Aussen:") lines.append("\nRäume & Außen:")
lines.extend(raeume) lines.extend(raeume)
if heizung: if heizung:
lines.append("\nHeizung & Puffer:") lines.append("\nHeizung & Puffer:")
lines.extend(heizung) lines.extend(heizung)
lines.append(f"\nGrafana: https://grafana.orbitalo.net/d/solar/raumtemperaturen") lines.append("\nGrafana: https://grafana.orbitalo.net/d/solar/raumtemperaturen")
return "\n".join(lines) return "\n".join(lines)
def handle_get_energie(**kw): def handle_get_energie(**kw):
data = _query_latest(ENERGIE_MEASUREMENTS) data = _query_latest(ENERGIE_MEASUREMENTS)
if not data: if not data:
return "Keine Energiedaten verfuegbar (InfluxDB nicht erreichbar?)" return "Keine Energiedaten verfügbar (InfluxDB nicht erreichbar?)"
lines = ["Energie Muldenstein (live):\n"] lines = ["Energie Muldenstein:"]
for name, val, ts in data: for name, val, ts in data:
if "%" in name or "kWh" in name: unit = _ENERGIE_UNITS.get(name, "")
lines.append(f" {name}: {val:.1f} ({ts})") if unit in ("%", "kWh"):
lines.append(f"- {name}: {val:.1f} {unit}")
else: else:
lines.append(f" {name}: {val:.0f} ({ts})") lines.append(f"- {name}: {val:.0f} {unit}")
lines.append(f"\nGrafana: https://grafana.orbitalo.net/d/solar/energie-uebersicht") lines.append("\nGrafana: https://grafana.orbitalo.net/d/solar/energie-uebersicht")
return "\n".join(lines) return "\n".join(lines)
@ -216,13 +231,13 @@ def handle_get_heizung(**kw):
laufzeit_q = 'SELECT last(value) FROM "brenner_heute" WHERE time > now() - 2h' laufzeit_q = 'SELECT last(value) FROM "brenner_heute" WHERE time > now() - 2h'
starts_q = 'SELECT last(value) FROM "brennerstarts" WHERE time > now() - 24h' starts_q = 'SELECT last(value) FROM "brennerstarts" WHERE time > now() - 24h'
lines = ["Heizung Muldenstein (live):\n"] lines = ["Heizung Muldenstein:"]
brenner = _influx_query(brenner_q) brenner = _influx_query(brenner_q)
if brenner and brenner.get("results", [{}])[0].get("series"): if brenner and brenner.get("results", [{}])[0].get("series"):
val = brenner["results"][0]["series"][0]["values"][0][1] val = brenner["results"][0]["series"][0]["values"][0][1]
status = "AN" if val else "AUS" status = "🔥 AN" if val else "AUS"
lines.append(f" Brenner: {status}") lines.append(f"\nBrenner: {status}")
laufzeit = _influx_query(laufzeit_q) laufzeit = _influx_query(laufzeit_q)
if laufzeit and laufzeit.get("results", [{}])[0].get("series"): if laufzeit and laufzeit.get("results", [{}])[0].get("series"):
@ -232,14 +247,14 @@ def handle_get_heizung(**kw):
starts = _influx_query(starts_q) starts = _influx_query(starts_q)
if starts and starts.get("results", [{}])[0].get("series"): if starts and starts.get("results", [{}])[0].get("series"):
val = starts["results"][0]["series"][0]["values"][0][1] val = starts["results"][0]["series"][0]["values"][0][1]
lines.append(f" Brennerstarts: {val:.0f}") lines.append(f"Brennerstarts (24h): {val:.0f}")
if data: if data:
lines.append("") lines.append("\nTemperaturen:")
for name, val, ts in data: for name, val, ts in data:
lines.append(f" {name}: {val:.1f} C ({ts})") lines.append(f"- {name}: {val:.1f} °C")
lines.append(f"\nGrafana: https://grafana.orbitalo.net/d/heizung/heizung-and-puffer") lines.append("\nGrafana: https://grafana.orbitalo.net/d/heizung/heizung-and-puffer")
return "\n".join(lines) return "\n".join(lines)