Inhaltsverzeichnis

Tibber Pulse

Tibber Pulse (IR-Lesekopf mit WLAN-Bridge)

Der Tibber Pulse ist ein optischer Auslesekopf (IR-Lesekopf) mit integrierter WLAN-Bridge. Er wird magnetisch auf die Info-Schnittstelle eines digitalen Stromzählers gesetzt, liest dessen Daten optisch aus und stellt sie über den lokalen Webserver der Bridge im Netzwerk bereit.

Ein aktiver Liefervertrag mit Tibber ist übrigens zur Nutzung des Tibber Pulse nicht notwendig!

Foto: Michael Pätzold (Michael32710), Lizenz: Creative Commons BY-SA 4.0. Quelle: Wikimedia/Wikipedia

Hardware

Der Tibber Pulse besteht aus zwei Teilen: dem eigentlichen IR-Lesekopf (Pulse) und der WLAN-Bridge (Pulse Bridge). Der Lesekopf verbindet sich per Bluetooth (?) mit der Bridge. Die Bridge verbindet sich mit dem heimischen WLAN.

Der Lesekopf liest die Zählerdaten über die optische Info-Schnittstelle. Die Kommunikation erfolgt üblicherweise mit 9600bd, 8N1 und die Daten werden im SML-Format übertragen.

Welche Zähler vom Tibber Pulse unterstützt werden, listet Tibber in der offiziellen Kompatibilitätsliste: Tibber Pulse Whitelist.

Lokalen Webserver aktivieren

Damit die Daten lokal (statt nur in der Tibber-Cloud) abgefragt werden können, muss der lokale Webserver der Bridge einmalig aktiviert werden.

Danach liefert die Bridge bei jedem Aufruf genau ein SML-Telegramm unter:

http://<benutzer>:<passwort>@<ip-der-bridge>/data.json?node_id=<node>

Auslesen mit vzLogger

Der Tibber Pulse stellt seine Daten nur über HTTP bereit – vzLogger erwartet aber ein serielles Gerät. Das folgende einfache Bash-Skript überbrückt das: Es fragt die Bridge zyklisch mit curl ab und legt die rohen SML-Telegramme mit socat auf ein virtuelles serielles Gerät (z. B. /dev/ttyTibber). vzLogger liest dieses Gerät dann wie einen ganz normalen IR-Schreib-Lesekopf.

Das Skript hat keine Abhängigkeiten außer curl und socat und läuft auf jedem Linux-System (Raspberry Pi, Server, …). Die Zugangsdaten werden oben im Skript eingetragen.

tibberpulse2serial.sh
#!/bin/bash
#
# tibberpulse2serial.sh
# =====================
#
# Macht einen Tibber Pulse (IR-Lesekopf + WLAN-Bridge) auf einem beliebigen
# Linux-System als virtuelles serielles Geraet verfuegbar. Danach kann ihn
# z. B. vzLogger genauso auslesen wie einen direkt angeschlossenen IR-Lesekopf.
#
# So funktioniert es:
#   Die Bridge liefert unter
#       http://<benutzer>:<passwort>@<ip>/data.json?node_id=<node>
#   bei jedem Aufruf genau ein transportkodiertes SML-Telegramm
#   (beginnt mit 1b 1b 1b 1b). Dieses Skript ruft die URL zyklisch per curl ab
#   und schreibt die rohen Telegramme in ein virtuelles serielles Geraet, das
#   mit socat als PTY angelegt wird (z. B. /dev/ttyTibber).
#
# Voraussetzungen:
#   - Der lokale Webserver der Tibber-Pulse-Bridge muss aktiviert sein
#     (einmalige Einrichtung, siehe Tibber-Anleitung). Benutzer ist "admin",
#     das Passwort steht im QR-Code auf der Rueckseite der Bridge.
#   - Installierte Programme: curl, socat  (z. B. "sudo apt-get install curl socat").
#     od gehoert zu den coreutils und ist ueberall vorhanden.
#   - Root-Rechte, weil das Geraet unter /dev angelegt wird ("sudo ./tibberpulse2serial.sh").
#
# Benutzung:
#   1. Unten im Abschnitt "Konfiguration" die Werte anpassen.
#   2. Skript ausfuehrbar machen:  chmod +x tibberpulse2serial.sh
#   3. Starten:                    sudo ./tibberpulse2serial.sh
#   4. Beenden mit Strg-C.
#
# Schnelltest ohne vzLogger: das Skript in einem Terminal starten und in einem
# zweiten Terminal die rohen Telegramme mitlesen:
#   cat /dev/ttyTibber | od -A x -t x1z
# Bei jedem Abfrageintervall erscheint ein SML-Telegramm, das mit den Bytes
# 1b 1b 1b 1b beginnt. Kommt nichts, stimmen IP/Node/Benutzer/Passwort nicht
# oder der lokale Webserver der Bridge ist nicht aktiviert.
#
# vzLogger-Beispiel (Ausschnitt der vzlogger.conf) fuer das angelegte Geraet:
#   "protocol": "sml", "device": "/dev/ttyTibber", "baudrate": 9600, "parity": "8n1"
#
# Dieses Skript ist bewusst einfach gehalten und hat keinerlei Bezug zu LoxBerry:
# keine Konfigurationsdatei, kein Logfile.
 
set -u
 
# ============================ Konfiguration ============================
HOST="192.168.1.50"          # IP-Adresse (oder Hostname) der Tibber-Pulse-Bridge
NODE="1"                     # Node-Nummer (bei nur einem Zaehler meist 1)
USERNAME="admin"            # Benutzername der Bridge (in der Regel "admin")
PASSWORD="GEHEIM"            # Passwort der Bridge (QR-Code auf der Rueckseite)
 
DEVICE="/dev/ttyTibber"      # virtuelles serielles Geraet, das angelegt wird
INTERVAL=3                   # Sekunden zwischen zwei Abfragen
TIMEOUT=4                    # curl-Timeout je Abfrage in Sekunden
# ======================================================================
 
# ---- Vorpruefungen ---------------------------------------------------
if [ "$(id -u)" -ne 0 ]; then
	echo "Bitte als root starten (sudo): das Geraet wird unter /dev angelegt." >&2
	exit 1
fi
command -v curl  >/dev/null 2>&1 || { echo "curl wird benoetigt: sudo apt-get install curl"   >&2; exit 1; }
command -v socat >/dev/null 2>&1 || { echo "socat wird benoetigt: sudo apt-get install socat" >&2; exit 1; }
 
# ---- curl-Konfigurationsdatei (haelt das Passwort aus der Prozessliste) ----
# Statt das Passwort als Kommandozeilen-Argument zu uebergeben (waere in
# "ps" sichtbar), schreiben wir URL und Zugangsdaten in eine temporaere,
# nur fuer uns lesbare Datei und uebergeben sie curl mit -K.
CURLCFG="$(mktemp)"
TMPBIN="$(mktemp)"
chmod 600 "$CURLCFG"
 
# Sonderzeichen (\ und ") im Passwort fuer die curl-Konfig maskieren.
esc_pw=$(printf '%s' "$PASSWORD" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
{
	printf 'url = "http://%s/data.json?node_id=%s"\n' "$HOST" "$NODE"
	printf 'user = "%s:%s"\n' "$USERNAME" "$esc_pw"
} > "$CURLCFG"
 
cleanup() {
	rm -f "$CURLCFG" "$TMPBIN" "$DEVICE" 2>/dev/null
	pkill -P $$ 2>/dev/null
}
trap cleanup EXIT INT TERM
 
mkdir -p "$(dirname "$DEVICE")"
 
echo "Tibber-Pulse-Bridge : http://$HOST/data.json?node_id=$NODE"
echo "Serielles Geraet    : $DEVICE"
echo "Abfrageintervall    : ${INTERVAL}s   (Strg-C zum Beenden)"
echo
 
# ---- Eine Abfrage: SML-Telegramm holen und weiterreichen -------------
# Nur echte SML-Telegramme (Startkennung 1b1b1b1b) werden weitergegeben;
# HTML-Fehlerseiten o. Ae. werden verworfen. Meldungen gehen nach stderr,
# damit sie nicht in den Datenstrom (stdout -> socat) geraten.
fetch_one() {
	if curl -sS --max-time "$TIMEOUT" -K "$CURLCFG" -o "$TMPBIN" 2>/dev/null \
		&& [ -s "$TMPBIN" ] \
		&& [ "$(od -An -tx1 -N4 "$TMPBIN" | tr -d ' \n')" = "1b1b1b1b" ]; then
		cat "$TMPBIN" || return 1        # return 1 = Leser (socat) ist weg
	else
		echo "$(date '+%H:%M:%S')  Bridge nicht erreichbar oder keine SML-Daten - neuer Versuch..." >&2
	fi
	return 0
}
 
# socat legt das PTY an und wartet auf einen Leser (wait-slave); trennt sich
# der Leser (z. B. vzLogger-Neustart), endet socat und die aeussere Schleife
# legt das Geraet fuer den naechsten Leser neu an.
while true; do
	while fetch_one; do
		sleep "$INTERVAL"
	done | socat -u - "PTY,link=$DEVICE,raw,echo=0,mode=0666,wait-slave"
	sleep 1
done

Das Skript im Hintergrund laufen lassen (z. B. als systemd-Dienst oder mit nohup … &), damit das Gerät dauerhaft zur Verfügung steht.

Beispielkonfiguration

Die vzlogger.conf unterscheidet sich nicht von der eines normalen SML-Zählers – als device wird das vom Skript angelegte virtuelle Gerät eingetragen:

vzlogger.conf
    {
      "retry": 0,
      "verbosity": 5,
      "local": {
        "enabled": false,
        "port": 8081
      },
      "meters": [
        {
          "enabled": true,
          "protocol": "sml",
          "device": "/dev/ttyTibber",
          "baudrate": 9600,
          "parity": "8n1",
          "interval": -1,
          "use_local_time": true,
          "channels": [
            {
              "uuid": "die UUID deines im Frontend erstellten Kanals",
              "identifier": "1-0:1.8.0",
              "api": "volkszaehler",
              "middleware": "http://localhost/middleware.php",
              "aggmode": "none",
              "duplicates": 0
            }
          ]
        }
      ]
    }