fix(hausmeister): Raumtemperaturen aus InfluxDB sensors statt ioBroker-Archiv
get_temperaturen liest esp-satellite-Knoten (Telegraf/DB sensors) inkl. 24h-Min/Max und Gradient. Energie/Heizung bleiben auf DB iobroker.
This commit is contained in:
parent
b4bb0b1a82
commit
14e32664c8
1 changed files with 118 additions and 61 deletions
|
|
@ -1,27 +1,43 @@
|
|||
"""Grafana + InfluxDB Smart-Home Tool (CT 143, pve-mu-3)."""
|
||||
|
||||
import requests
|
||||
from urllib.parse import quote
|
||||
|
||||
GRAFANA_URL = "http://100.66.78.56:3000"
|
||||
GRAFANA_USER = "admin"
|
||||
GRAFANA_PASS = "astral66"
|
||||
INFLUX_URL = "http://100.66.78.56:8086"
|
||||
INFLUX_DB = "iobroker"
|
||||
INFLUX_DB_IOBROKER = "iobroker"
|
||||
INFLUX_DB_SENSORS = "sensors"
|
||||
|
||||
SENSOR_MAP = {
|
||||
# esp-satellite + Heizraum-Knoten (Telegraf → DB sensors, Measurement temperature, Tag node)
|
||||
SATELLITE_NODES = {
|
||||
"flur_oben": "Flur oben (Klima-Zone)",
|
||||
"flur_unten": "Flur unten",
|
||||
"wohnstube": "Wohnstube",
|
||||
"kueche": "Küche",
|
||||
"schlafzimmer": "Schlafzimmer",
|
||||
"gaestezimmer": "Gästezimmer",
|
||||
"trockenraum": "Trockenraum",
|
||||
"oelraum": "Ölraum",
|
||||
"heizraum": "Heizraum (ESP32)",
|
||||
"garage": "Garage (Test)",
|
||||
}
|
||||
|
||||
# Eingefrorenes ioBroker-Archiv (nur noch für Sensoren ohne esp-satellite)
|
||||
LEGACY_SENSOR_MAP = {
|
||||
"aussen": ("mqtt.0.Holzvergaser_Sensoren_6.Aussenfühler.temperature", "Außen"),
|
||||
"wohnstube": ("mqtt.0.Wohnstube_Temperatur_1.Wohnstube.Wohnstube_Temperatur", "Wohnstube"),
|
||||
"kueche": ("mqtt.0.ESP_Kücheneu_2.Sensor_Küche.Küche", "Küche"),
|
||||
"garage": ("mqtt.0.ESP_Easy_3.Garage_neu.Garage", "Garage"),
|
||||
"puffer_oben": ("mqtt.0.ESP_Easy_Display_4.oben.temperature", "Puffer oben"),
|
||||
"puffer_unten": ("mqtt.0.ESP_Easy_Display_4.Unten.temperature", "Puffer unten"),
|
||||
"garten": ("mqtt.0.ESP_Easy_schwimmbad_neu.Lufttemperatur.Lufttemperatur_Garten", "Garten/Luft"),
|
||||
"schwimmbad": ("mqtt.0.ESP_Easy_schwimmbad_neu.Schwimmbad.Wassertemperatur_", "Schwimmbad"),
|
||||
}
|
||||
|
||||
HEIZUNG_SENSOR_MAP = {
|
||||
"puffer_oben": ("mqtt.0.ESP_Easy_Display_4.oben.temperature", "Puffer oben"),
|
||||
"puffer_unten": ("mqtt.0.ESP_Easy_Display_4.Unten.temperature", "Puffer unten"),
|
||||
"oelkessel_vl": ("mqtt.0.Oelkessel.Oelkessel_VL.Vorlauf", "Ölkessel Vorlauf"),
|
||||
"brunnen_vl": ("mqtt.0.Brunnenwasser.Oelkessel_VL.Vorlauf", "Brunnenwasser Vorlauf"),
|
||||
"holzvergaser_1": ("mqtt.0.Holzvergaser_Sensoren_6.temp1.temperature1", "Holzvergaser Sensor 1"),
|
||||
"holzvergaser_2": ("mqtt.0.Holzvergaser_Sensoren_6.temp2.temperature2", "Holzvergaser Sensor 2"),
|
||||
"aussen": LEGACY_SENSOR_MAP["aussen"],
|
||||
}
|
||||
|
||||
ENERGIE_MEASUREMENTS = {
|
||||
|
|
@ -42,8 +58,6 @@ _ENERGIE_UNITS = {
|
|||
"Überschuss": "W",
|
||||
}
|
||||
|
||||
_HEIZUNG_KEYS = {"puffer", "vorlauf", "holz", "brunnen", "öl"}
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
"type": "function",
|
||||
|
|
@ -57,7 +71,10 @@ TOOLS = [
|
|||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_temperaturen",
|
||||
"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'.",
|
||||
"description": (
|
||||
"Aktuelle Raumtemperaturen Muldenstein (esp-satellite-Knoten via InfluxDB sensors). "
|
||||
"Nutze bei Fragen zu Temperatur, Raumklima, Klimaanlage, Kühlung, Gradient zwischen Räumen."
|
||||
),
|
||||
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||
},
|
||||
},
|
||||
|
|
@ -80,11 +97,12 @@ TOOLS = [
|
|||
]
|
||||
|
||||
SYSTEM_PROMPT_EXTRA = """Für Smart-Home-Daten (Temperaturen, Energie, Heizung) im Haus Muldenstein:
|
||||
- get_temperaturen: alle Raumtemperaturen + Außen
|
||||
- get_energie: PV, Batterie, Netz, Hausverbrauch
|
||||
- get_heizung: Brenner, Puffer, Vorlauf, Holzvergaser
|
||||
- get_temperaturen: Raum-Satelliten (DB sensors, CT143 InfluxDB) — NICHT ioBroker, NICHT 192.168.178.222
|
||||
- get_energie: PV, Batterie, Netz, Hausverbrauch (DB iobroker)
|
||||
- get_heizung: Brenner, Puffer, Vorlauf, Holzvergaser (DB iobroker)
|
||||
- get_grafana_status: Dashboard-Übersicht und Alerts
|
||||
Grafana-URL: https://grafana.orbitalo.net/
|
||||
Datenquelle Temperaturen: Telegraf/MQTT auf CT143 (192.168.178.36), DB `sensors`.
|
||||
Grafana: https://grafana.orbitalo.net/d/satelliten-temps/satelliten-temperaturen
|
||||
|
||||
WICHTIG: Die Tool-Ergebnisse sind bereits fertig formatiert.
|
||||
Gib sie 1:1 weiter, NICHT umformulieren oder kürzen.
|
||||
|
|
@ -105,11 +123,11 @@ def _api(path):
|
|||
return None
|
||||
|
||||
|
||||
def _influx_query(q):
|
||||
def _influx_query(q, db=INFLUX_DB_IOBROKER):
|
||||
try:
|
||||
r = requests.get(
|
||||
f"{INFLUX_URL}/query",
|
||||
params={"db": INFLUX_DB, "q": q},
|
||||
params={"db": db, "q": q},
|
||||
timeout=10,
|
||||
)
|
||||
if r.ok:
|
||||
|
|
@ -119,13 +137,13 @@ def _influx_query(q):
|
|||
return None
|
||||
|
||||
|
||||
def _query_latest(measurements: dict) -> list:
|
||||
"""Query last() for a dict of {key: (measurement, label)}."""
|
||||
def _query_latest_legacy(measurements: dict) -> list:
|
||||
"""Query last() from ioBroker archive for legacy measurements."""
|
||||
parts = []
|
||||
for key, (meas, label) in measurements.items():
|
||||
for _key, (meas, label) in measurements.items():
|
||||
parts.append(f'last(value) AS "{label}" FROM "{meas}"')
|
||||
q = "SELECT " + "; SELECT ".join(parts) + " WHERE time > now() - 2h"
|
||||
result = _influx_query(q)
|
||||
result = _influx_query(q, db=INFLUX_DB_IOBROKER)
|
||||
if not result:
|
||||
return []
|
||||
lines = []
|
||||
|
|
@ -133,10 +151,46 @@ def _query_latest(measurements: dict) -> list:
|
|||
for series in stmt.get("series", []):
|
||||
name = series["columns"][1] if len(series["columns"]) > 1 else series.get("name", "?")
|
||||
val = series["values"][0][1] if series.get("values") else None
|
||||
ts = series["values"][0][0] if series.get("values") else ""
|
||||
if val is not None:
|
||||
ts_short = ts[11:16] if len(ts) > 16 else ""
|
||||
lines.append((name, val, ts_short))
|
||||
lines.append((name, val))
|
||||
return lines
|
||||
|
||||
|
||||
def _query_satellite_temperatures() -> list:
|
||||
"""Query esp-satellite nodes from DB sensors."""
|
||||
q = (
|
||||
"SELECT last(value) AS jetzt, min(value) AS min24h, max(value) AS max24h "
|
||||
"FROM temperature WHERE time > now() - 24h GROUP BY node"
|
||||
)
|
||||
result = _influx_query(q, db=INFLUX_DB_SENSORS)
|
||||
if not result:
|
||||
return []
|
||||
|
||||
by_node = {}
|
||||
for series in result.get("results", [{}])[0].get("series", []):
|
||||
node = series.get("tags", {}).get("node")
|
||||
if not node or not series.get("values"):
|
||||
continue
|
||||
row = series["values"][0]
|
||||
by_node[node] = {
|
||||
"jetzt": row[1],
|
||||
"min24h": row[2],
|
||||
"max24h": row[3],
|
||||
}
|
||||
|
||||
lines = []
|
||||
for node, label in SATELLITE_NODES.items():
|
||||
if node not in by_node:
|
||||
continue
|
||||
d = by_node[node]
|
||||
lines.append((label, d["jetzt"], d["min24h"], d["max24h"]))
|
||||
|
||||
# Unbekannte Nodes (z.B. neu) am Ende
|
||||
for node, d in sorted(by_node.items()):
|
||||
if node in SATELLITE_NODES:
|
||||
continue
|
||||
lines.append((node, d["jetzt"], d["min24h"], d["max24h"]))
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
|
|
@ -174,37 +228,49 @@ def handle_get_grafana_status(**kw):
|
|||
|
||||
|
||||
def handle_get_temperaturen(**kw):
|
||||
data = _query_latest(SENSOR_MAP)
|
||||
if not data:
|
||||
return "Keine Temperaturdaten verfügbar (InfluxDB nicht erreichbar?)"
|
||||
satellites = _query_satellite_temperatures()
|
||||
legacy = _query_latest_legacy(LEGACY_SENSOR_MAP)
|
||||
|
||||
raeume = []
|
||||
heizung = []
|
||||
for name, val, ts in data:
|
||||
entry = f"- {name}: {val:.1f} °C"
|
||||
if any(k in name.lower() for k in _HEIZUNG_KEYS):
|
||||
heizung.append(entry)
|
||||
else:
|
||||
raeume.append(entry)
|
||||
if not satellites and not legacy:
|
||||
return (
|
||||
"Keine Temperaturdaten verfügbar.\n"
|
||||
"Quelle: InfluxDB CT143 (100.66.78.56:8086), DB `sensors`.\n"
|
||||
"Prüfe Telegraf/mosquitto auf CT143 — NICHT ioBroker-Host 192.168.178.222."
|
||||
)
|
||||
|
||||
lines = ["Temperaturen Muldenstein:"]
|
||||
if raeume:
|
||||
lines.append("\nRäume & Außen:")
|
||||
lines.extend(raeume)
|
||||
if heizung:
|
||||
lines.append("\nHeizung & Puffer:")
|
||||
lines.extend(heizung)
|
||||
lines.append("\nGrafana: https://grafana.orbitalo.net/d/solar/raumtemperaturen")
|
||||
lines = ["Temperaturen Muldenstein (esp-satellite → DB sensors):"]
|
||||
|
||||
if satellites:
|
||||
lines.append("\nRaum-Satelliten:")
|
||||
for label, jetzt, min24h, max24h in satellites:
|
||||
lines.append(f"- {label}: {jetzt:.1f} °C (24h: {min24h:.1f}–{max24h:.1f})")
|
||||
|
||||
temps = [t[1] for t in satellites]
|
||||
if len(temps) >= 2:
|
||||
spread = max(temps) - min(temps)
|
||||
coldest = min(satellites, key=lambda x: x[1])
|
||||
warmest = max(satellites, key=lambda x: x[1])
|
||||
lines.append(
|
||||
f"\nGradient: {spread:.1f} °C "
|
||||
f"({coldest[0]} {coldest[1]:.1f} °C → {warmest[0]} {warmest[1]:.1f} °C)"
|
||||
)
|
||||
|
||||
if legacy:
|
||||
lines.append("\nLegacy (DB iobroker):")
|
||||
for name, val in legacy:
|
||||
lines.append(f"- {name}: {val:.1f} °C")
|
||||
|
||||
lines.append("\nGrafana: https://grafana.orbitalo.net/d/satelliten-temps/satelliten-temperaturen")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def handle_get_energie(**kw):
|
||||
data = _query_latest(ENERGIE_MEASUREMENTS)
|
||||
data = _query_latest_legacy(ENERGIE_MEASUREMENTS)
|
||||
if not data:
|
||||
return "Keine Energiedaten verfügbar (InfluxDB nicht erreichbar?)"
|
||||
return "Keine Energiedaten verfügbar (InfluxDB iobroker nicht erreichbar?)"
|
||||
|
||||
lines = ["Energie Muldenstein:"]
|
||||
for name, val, ts in data:
|
||||
for name, val in data:
|
||||
unit = _ENERGIE_UNITS.get(name, "")
|
||||
if unit in ("%", "kWh"):
|
||||
lines.append(f"- {name}: {val:.1f} {unit}")
|
||||
|
|
@ -216,16 +282,7 @@ def handle_get_energie(**kw):
|
|||
|
||||
|
||||
def handle_get_heizung(**kw):
|
||||
heiz_sensors = {
|
||||
"puffer_oben": SENSOR_MAP["puffer_oben"],
|
||||
"puffer_unten": SENSOR_MAP["puffer_unten"],
|
||||
"oelkessel_vl": SENSOR_MAP["oelkessel_vl"],
|
||||
"brunnen_vl": SENSOR_MAP["brunnen_vl"],
|
||||
"holzvergaser_1": SENSOR_MAP["holzvergaser_1"],
|
||||
"holzvergaser_2": SENSOR_MAP["holzvergaser_2"],
|
||||
"aussen": SENSOR_MAP["aussen"],
|
||||
}
|
||||
data = _query_latest(heiz_sensors)
|
||||
data = _query_latest_legacy(HEIZUNG_SENSOR_MAP)
|
||||
|
||||
brenner_q = 'SELECT last(value) FROM "brennerstatus" WHERE time > now() - 2h'
|
||||
laufzeit_q = 'SELECT last(value) FROM "brenner_heute" WHERE time > now() - 2h'
|
||||
|
|
@ -233,25 +290,25 @@ def handle_get_heizung(**kw):
|
|||
|
||||
lines = ["Heizung Muldenstein:"]
|
||||
|
||||
brenner = _influx_query(brenner_q)
|
||||
brenner = _influx_query(brenner_q, db=INFLUX_DB_IOBROKER)
|
||||
if brenner and brenner.get("results", [{}])[0].get("series"):
|
||||
val = brenner["results"][0]["series"][0]["values"][0][1]
|
||||
status = "🔥 AN" if val else "⏸ AUS"
|
||||
lines.append(f"\nBrenner: {status}")
|
||||
|
||||
laufzeit = _influx_query(laufzeit_q)
|
||||
laufzeit = _influx_query(laufzeit_q, db=INFLUX_DB_IOBROKER)
|
||||
if laufzeit and laufzeit.get("results", [{}])[0].get("series"):
|
||||
val = laufzeit["results"][0]["series"][0]["values"][0][1]
|
||||
lines.append(f"Brenner heute: {val:.0f} min")
|
||||
|
||||
starts = _influx_query(starts_q)
|
||||
starts = _influx_query(starts_q, db=INFLUX_DB_IOBROKER)
|
||||
if starts and starts.get("results", [{}])[0].get("series"):
|
||||
val = starts["results"][0]["series"][0]["values"][0][1]
|
||||
lines.append(f"Brennerstarts (24h): {val:.0f}")
|
||||
|
||||
if data:
|
||||
lines.append("\nTemperaturen:")
|
||||
for name, val, ts in data:
|
||||
lines.append("\nTemperaturen (Archiv iobroker):")
|
||||
for name, val in data:
|
||||
lines.append(f"- {name}: {val:.1f} °C")
|
||||
|
||||
lines.append("\nGrafana: https://grafana.orbitalo.net/d/heizung/heizung-and-puffer")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue