homelab-brain/smart-home/scripts/add_oelknoten_heizung_row.py
Homelab Cursor 5161f429df Ölknoten: Grafana-Zeile auf Heizung-Dashboard + MCP/API-Fix
Heizungs-API und Hermes-MCP liefern Ölknoten-Daten aus MQTT/sensors-Influx
statt fälschlich offline zu melden wenn nur ioBroker mqtt.0 tot ist.
2026-07-01 15:07:39 +02:00

139 lines
4.4 KiB
Python
Executable file

#!/usr/bin/env python3
"""Fuegt Oelknoten-Zeile am Heizung-Dashboard hinzu (idempotent)."""
import json
import subprocess
BASE = "http://100.66.78.56:3000"
ROW_TITLE = "🛢️ Ölknoten (Erdtrasse)"
DS_UID = "ffptl25vzylfke" # InfluxDB-sensors
PANEL_IDS = [901, 902, 903, 904, 905]
def curl(path, method="GET", body=None):
cmd = [
"curl", "-s",
"-u", "admin:astral66", "-X", method, f"{BASE}{path}",
]
if body is not None:
cmd += ["-H", "Content-Type: application/json", "-d", json.dumps(body)]
r = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return json.loads(r.stdout) if r.stdout.startswith(("{", "[")) else r.stdout
def stat_panel(pid, title, sensor, x, y, w=4, h=4):
return {
"id": pid,
"type": "stat",
"title": title,
"datasource": {"type": "influxdb", "uid": DS_UID},
"gridPos": {"h": h, "w": w, "x": x, "y": y},
"fieldConfig": {
"defaults": {
"unit": "celsius",
"decimals": 1,
"color": {"mode": "thresholds"},
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "blue", "value": None},
{"color": "green", "value": 10},
{"color": "orange", "value": 40},
],
},
}
},
"options": {
"reduceOptions": {"values": False, "calcs": ["lastNotNull"]},
"colorMode": "background",
"graphMode": "none",
},
"targets": [{
"refId": "A",
"rawQuery": True,
"query": (
f'SELECT last("value") FROM "temperature" '
f"WHERE \"node\"='oelknoten' AND \"sensor\"='{sensor}' AND $timeFilter"
),
}],
}
d = curl("/api/dashboards/uid/heizung")
dash = d["dashboard"]
# Entferne alte Oelknoten-Zeile
remove_titles = {ROW_TITLE, "Ölknoten Raum", "Ölknoten HK VL", "Ölknoten Erd VL", "Ölknoten Erd RL", "Ölknoten Status"}
dash["panels"] = [
p for p in dash["panels"]
if p.get("title") not in remove_titles and p.get("id") not in PANEL_IDS
]
max_y = 0
for p in dash["panels"]:
gp = p.get("gridPos", {})
max_y = max(max_y, gp.get("y", 0) + gp.get("h", 0))
y = max_y
dash["panels"].append({
"id": 900,
"type": "row",
"title": ROW_TITLE,
"gridPos": {"h": 1, "w": 24, "x": 0, "y": y},
"collapsed": False,
})
y += 1
panels = [
(901, "Status", None, 0),
(902, "Raum", "raum", 4),
(903, "HK Vorlauf", "hk_vorlauf", 8),
(904, "Erd VL", "vl_erd_an", 12),
(905, "Erd RL", "rl_erd_ab", 16),
]
for pid, title, sensor, x in panels:
if sensor is None:
dash["panels"].append({
"id": pid,
"type": "stat",
"title": title,
"datasource": {"type": "influxdb", "uid": DS_UID},
"gridPos": {"h": 4, "w": 4, "x": x, "y": y},
"fieldConfig": {
"defaults": {
"mappings": [
{"type": "value", "options": {
"1": {"text": "ONLINE", "color": "green", "index": 0},
"0": {"text": "OFFLINE", "color": "red", "index": 1},
}},
],
"noValue": "OFFLINE",
"color": {"mode": "thresholds"},
"thresholds": {"mode": "absolute", "steps": [
{"color": "red", "value": None},
{"color": "green", "value": 1},
]},
}
},
"options": {
"reduceOptions": {"values": False, "calcs": ["lastNotNull"]},
"colorMode": "background",
"graphMode": "none",
},
"targets": [{
"refId": "A",
"rawQuery": True,
"query": 'SELECT last("value") FROM "node_status" WHERE "node"=\'oelknoten\'',
}],
})
else:
dash["panels"].append(stat_panel(pid, title, sensor, x, y))
dash["version"] = dash.get("version", 0) + 1
resp = curl("/api/dashboards/db", "POST", {
"dashboard": dash,
"folderId": 0,
"overwrite": True,
"message": "Oelknoten-Zeile hinzugefuegt",
})
print(json.dumps(resp, indent=2)[:500])