Ein vollkommen individuelles Smarthome Widget

Im SmartHome ist die Visualisierung der aktuellen Zustände ein entscheidender Baustein: Wer weiß, ob Fenster offen sind, welche Temperatur in den Räumen herrscht oder wie viel Strom gerade verbraucht wird, kann effizienter steuern und Energie sparen. In diesem Artikel zeige ich dir, wie mit wenig Aufwand ein vollkommen individuelles Widget entsteht, um Messwerte und Zustände darzustellen.

Ablauf

Anfang und Ende liegt bei einem Web Image Widget (App) auf dem Smartphone. Es setzt den Request an den Node-RED HTTP-Endpunkt ab. Node-RED füllt ein beliebiges SVG-Template mit Werten und speichert dieses temporär ab. Die SVG wird in eine PNG umgewandelt und an das Web-Image-Widget ausgeliefert.

Auf einem Raspi 4 klappt das innerhalb von gut 200ms.

Schritt 1: HTTP-Endpunkt /render

Der Flow beginnt mit einem http in-Node, der auf GET-Anfragen an /render wartet.

{
  "type": "http in",
  "url": "/render",
  "method": "get"
}

Schritt 2: SVG mit den aktuellen Werten erzeugen

Der zentrale Baustein ist das Function Node, welches das vorbereitete SVG-Icon mit Live-Daten befüllt, bei mir hauptsächlich aus globalen Variablen. Der Kreativität und auch Dynamik sind hier keine Grenzen gesetzt. Eine Rundungsfunktion hab ich auch mit drin, damit die Zahlen nicht aus dem Layout laufen.

// Beispielhafte globale Variablen (im Flow vorher setzen!)
var temp_out = global.get("temp_out") || 'n/a';
var temp_wohnzimmer = global.get("temp_wohnzimmer") || 'n/a';
var temp_schlafzimmer = global.get("temp_schlafzimmer") || 'n/a';
var verbrauch = global.get("verbrauch_aktuell") || 'n/a';

function r(v) {
  return (typeof v === "number") ? Math.round(v * 10) / 10 : "-";
}

// SVG erzeugen
msg.payload = `
<svg version="1.1" viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
  <rect width="100%" height="100%" fill="#222"/>
  <text x="10" y="30" fill="#fff" font-family="DejaVu Sans" font-size="20">🏠 SmartHome Status</text>

  <g font-size="16">
    <text x="10" y="70" font-family="Noto Color Emoji">🌡️</text>
    <text x="40" y="70" fill="#fff" font-family="DejaVu Sans">Außen: ${r(temp_out)}°C</text>

    <text x="10" y="100" font-family="Noto Color Emoji">🛋️</text>
    <text x="40" y="100" fill="#fff" font-family="DejaVu Sans">Wohnzimmer: ${r(temp_wohnzimmer)}°C</text>

    <text x="10" y="130" font-family="Noto Color Emoji">🛏️</text>
    <text x="40" y="130" fill="#fff" font-family="DejaVu Sans">Schlafzimmer: ${r(temp_schlafzimmer)}°C</text>

    <text x="10" y="160" font-family="Noto Color Emoji">⚡</text>
    <text x="40" y="160" fill="#fff" font-family="DejaVu Sans">Verbrauch: ${r(verbrauch)} W</text>
  </g>
</svg>
`;
return msg;

Mein Widget wird noch durch dynamische Icons, die nur im Falle von Vorkomnissen auftauchen, der Anzeige von Temperaturtrends und diverser Haustechnik-Werte ergänzt. Farbänderungen, flexible Positionierungen und selbst Charts – alles machbar.

Schritt 3: SVG auf der Festplatte speichern

Das generierte SVG wird in /tmp/input.svg geschrieben. Dafür sorgt der file-Node. Die Datei wird immer überschrieben, damit sie den neuesten Status repräsentiert.

{
  "type": "file",
  "filename": "/tmp/input.svg",
  "overwriteFile": "true"
}

Schritt 4: SVG in PNG umwandeln

Nun wird das gespeicherte SVG in ein PNG konvertiert, und zwar mit dem Kommandozeilentool resvg. Dies übernimmt ein exec-Node, der den Befehl ausführt. Bei mir sieht der Befehl so aus:

/home/pi/.cargo/bin/resvg /tmp/input.svg /tmp/output.png

Resvg ist ein leistungsfähiger SVG-Renderer, der perfekt für solche Zwecke geeignet ist und auch komplexe SVGs mit Emojis extrem schnell und sauber rendert.

Schritt 5: PNG auslesen und HTTP-Response liefern

Der finale Teil des Flows besteht darin, das fertige PNG aus /tmp/output.png mit einem file in-Node zu lesen und die Binärdaten anschließend direkt über den http response-Node an den Client zurückzugeben. Wichtig: Der Response-Node setzt den HTTP-Header Content-Type: image/png, damit Browser oder Apps das Bild korrekt interpretieren.

Vorteile

Kompakte und individuelle Darstellung: Widget als SVG gestalten und anschließend mit Platzhaltern füllen – individueller geht es nicht. Gestaltung flexibel und kann beliebig erweitert oder angepasst werden (Farben, Emojis, Layout, etc.).

Low-Tech: Ohne komplizierte Einrichtung auf dem Smartphone – und auch in Node-RED hält sich der Einrichtungsaufwand in Grenzen.

Performance: Mit gut 200ms ist die Lösung extrem schnell.

Nachteile

Linear: Reiner Abruf und Darstellung von Infos – keine Interaktivität.


Den Wifi-Tasmota IR-Lesekopf (Hichi) per HTTP Request auslesen

Der IR-Lesekopf für den Stromzähler von Hichi ist seit Jahren eine etablierte Lösung für das Auslesen des Stromzählers. Schon lange habe ich selbst den USB-Lesekopf im Einsatz und er läuft extrem zuverlässig.

Einem Freund hatte ich kurzerhand die WLAN-Variante mit ESP-01 geschenkt, ohne jedoch auf die Limitierung zu achten, dass ein sinnvolles Auslesen nur mit SmartHome über MQTT (Tasmota als MQTT Client) möglich ist. Also musste etwas Bastelei her, um auch ohne MQTT Broker bequem an die Daten zu kommen.

Für die Einrichtung des Tasmota auf dem Hichi gibt es sehr gute Anleitungen im Netz, die will ich nicht wiederkäuen. Dazu gibt es für eigentliche alle Stromzähler im Tasmota Wiki das passende Script.

Das Auslesen ohne SmartHome

Um ohne SmartHome bequem an die Daten zu kommen, ist MQTT eine schlechte Option. Gewählt habe ich daher eine Variante über die sehr schöne Open Source-App „HTTP Request Shortcuts„.

Endpoint des HTTP Requests ist (bei mir) die IP + ?m=1:

http://xx.xx.xx.xx/?m=1

(Im Zweifel bekommt man die korrekte URL auf der Tasmota Seite des Devices, über die Developer Tools – Netzwerk.)

Man bekommt einen nicht ganz so schön nutzbaren Datensatz, den man mittels JavaScript aber leicht etwas zerlegen kann. Den Response des Request deaktiviert man am besten („Nichts anzeigen“), weil man diesen dann über das Scripting steuert.

Das Script selber muss man an die individuelle Zählerkonfiguration anpassen. Bei meinem Fall war es so sehr gut nutzbar:

const regex = /\{s\}(.*?)\{m\}(.*?)kWh/g;
let match;
const extractedValues = {};

while ((match = regex.exec(response.body)) !== null) {
  extractedValues[match[1].replace("LK13BE ","")] = match[2].replace("000","");
}

showDialog("Aktueller Verbauch: "+extractedValues["Verbrauch aktuell"].split(",0")[0] + " W\n\n1 Tag: " + extractedValues["Verbrauch 1 Tag"] + " kWh\n7 Tage: "+ extractedValues["Verbrauch 7 Tage"] +" kWh\n30 Tage:" + extractedValues["Verbrauch 30 Tage"] + " kWh\n365 Tage: "+extractedValues["Verbrauch 365 Tage"]+" kWh\n\nZählerstand: "+extractedValues["Gesamtverbrauch"]+" kWh");

Alternativ kann man es auch als Toast ausgeben lassen (showToast) oder vorlesen lassen (speak). Die Verknüpfung zum Request lässt sich dann auf den Homescreen legen und die aktuellen Zählerdaten sind nur einen Tab entfernt.

Fröhliches Ausprobieren …

Workarround: GPIO Raspberry Pi 5 / Bookworm

Der neue Raspberry PI 5 unter Raspberry Pi OS Bookworm schmeißt einiges in Sachen GPIO über den Haufen. Viele altbekannte Tools funktionieren nichtmehr. Damit war nach dem Upgrade auf Pi 5 auch meine Velux Rollosteuerung im Eimer, da diese über ein Relaisboard an den GPIOs gesteuert werden.

Obwohl der Raspberry Pi 5 immer noch über den standardmäßigen 40-Pin GPIO verfügt, unterscheidet sich seine Funktionsweise nun, da er an den neuen RP1-Southbridge-Chip angebunden ist. Dies ist von Bedeutung, da es die Art beeinflusst, wie manCode zur Interaktion mit dem GPIO schreibt. Früher setzten viele Projekte auf RPi.GPIO, ein von der Community initiiertes Projekt von Ben Croston, das mehrere Generationen des Raspberry Pi unterstützte. Beim Raspberry Pi 5 ist die Verwendung von RPi.GPIO jedoch nicht mehr möglich.

Mit libgpiod gibt es wohl auch schon einen Ersatz für RPi.GPIO, aber mich tiefgreifend damit zu beschäftigen und in NodeRED zum Laufen zu bekommen, dafür habe ich in der „ruhigen“ Adventszeit irgendwie keine Zeit. Hinweise nehme ich gern entgegen. Bis dahin mein quick-and-dirty Workarround mit pinctrl.

Befehle

-p listet dir die verfügbaren GPIOs und deren Status auf:

sudo pinctrl -p

set 18 op dh initialisiert den GPIO18 als Output und setzt ihn auf high (dh)

sudo pinctrl set 18 op dh

Achtung! op ist nicht bootfest.

set 18 dl setzt GPIO18 auf low (dl)

sudo pinctrl set 18 dl

In Node-Red

In Node-Red kriegt ihr das ganze mit dem exec Node ausgeführt. Ich habe entsprechende Trigger integriert, damit mein Relais nur für 500ms angetippt wird.

Maybe to be continued

Pi-hole über das Smart Home steuern

Möchte man Seiten flexibel auf die Black- oder Whitelist von Pi-hole setzen, so gibt es dafür eine recht karge HTTP-API (GET), mit welcher du die „Domains“ von Pi-hole befüllen oder bereinigen kannst.

Diese findest du unter folgender – für deine Situation anzupassenden – URL:

http://[IP]/admin/api.php?list=regex_white&add=(%5c.%7c%5e)youtube%5c.com%24&auth=[Auth-Code]

Erläuterung der Bestandteile

Die IP deines Pi-hole solltest du kennen – genauso ob du den Adminbereich per http oder https erreichst.

Wie in der Adminoberfläche hast du auch über die API die Möglichkeit, die URLs auf die folgenden vier Listen einzutragen:

  • white
  • black
  • regex_white
  • regex_black

Du hast die Möglichkeit eine Domain hinzuzufügen (add) oder zu entfernen (sub).

Die Domain selbst muss encodiert sein. Ein Regex-Filter für youtube.com als Wildcard lautet somit:

(%5c.%7c%5e)youtube%5c.com%24

Das %5c steht z.B. für ein Backslash „\“, %7c ist ein senkrechter Strich „|“, %5e ist ein Zirkumflex „^“ und %24 ist ein Dollar Zeichen „$“.

Zuletzt brauchst du noch den Auth-Code. Diesen findest du in der Pi-hole-Adminoberfläche unter [Settings] -> [API] -> [Show API token].