Optische Benachrichtigungen der Fritzbox bei eingehenden Anrufen mit Bash und WLED

  Ben   Lesezeit: 5 Minuten  🗪 2 Kommentare Auf Mastodon ansehen

Überwachung des Telefoniestatus der Fritzbox per Bash-Script, welches bei eingehenden Anrufen eine per WLED angesteuerte LED-Leiste blinken lässt.

optische benachrichtigungen der fritzbox bei eingehenden anrufen mit bash und wled

14.05.2026, 21:20 Uhr, Hinweis der Redaktion: Beim Übertragen des Artikels in unser CMS kam es leider zu einem Fehler und der Artikel war unvollständig. Er wurde nun ergänzt.

Ein schwerhöriger Bekannter hatte mir das Problem geschildert, dass er das Klingeln seines DECT-Telefons selbst auf voller Lautstärke immer öfter nicht mehr mitbekäme. Insbesondere beim Fernsehen (mit Funkkopfhörern auf hoher Lautstärke) sei er somit faktisch nicht mehr zu erreichen. Dabei stellten wir uns die Frage, ob man die eingehenden Anrufe nicht auch irgendwie anderweitig (z. B. optisch) signalisieren könne, sodass er sie (selbst beim Fernsehen) wieder wahrnehmen würde.

Der Bekannte nutzt – wie viele andere Haushalte in Deutschland – eine Fritz!Box als Router und DECT‑Basis. Ich wusste aufgrund meiner früheren Nutzung von jAnrufmonitor (https://www.janrufmonitor.de), dass es eine Möglichkeit geben muss, den aktuellen Telefoniestatus der Fritz!Box abzufragen und zu überwachen, ohne sich dafür jedes Mal per TR-069 authentifizieren zu müssen. Also befragte ich die Suchmaschine meiner Wahl und wurde nach einiger Zeit fündig. Die Dokumentation der Python-Library "fritzconnection" beschrieb die Schnittstelle, unter welcher die Fritz!Box diese Informationen bereitstellt (https://fritzconnection.readthedocs.io/en/1.5.0/sources/call_monitoring.html – inkl. Beispielausgabe und einem Code-Schnipsel, welcher die Auswertung in Python beispielhaft vormachte.

In Ermangelung von Python-Kenntnissen schrieb ich ein Parsing-Script in Bash, welches nun auf einem ARM-Board läuft und 24/7 auf eingehende Anrufe lauscht. Wird ein solcher registriert, sendet es einen HTTP-Aufruf, welcher einen ESP8266-01 mit WLED dazu veranlasst, eine an diesen angeschlossene LED-Leiste blinken zu lassen. Im Folgenden gehe ich nur auf das Skript ein, da es zum Flashen und Einrichten von WLED bereits etliche Dokumentationen im Netz gibt.

Das Datenformat, welches die Fritz!Box unter Port 1012 liefert (nachdem dies per "Anruf" der Nummer #965 einmalig aktiviert wurde!), sieht wie folgt aus:

28.11.20 15:17:43;RING;2;;;SIP0;
28.11.20 15:17:47;CONNECT;2;4;;
28.11.20 15:17:50;DISCONNECT;2;4;

Es enthält folgende Informationen im durch Semikolon getrennten CSV-Format:
1. Datum und Uhrzeit des Events
2. Event (CALL = ausgehender Anruf, RING = eingehender Anruf, CONNECT = angenommen/Verbindung steht,
3. DISCONNECT = eine Seite hat aufgelegt – egal, ob zuvor verbunden oder nicht) anscheinend eine Durchnummerierung, der wievielte parallele Anruf es ist(?)

Während die ersten drei Werte unabhängig vom Event immer dieselben Informationen enthalten, variieren die in den Werten ≥ 4 enthaltenen Informationen je nach Event
Event RING (eingehender Anruf):
4. Nummer des Anrufers
5. Zielrufnummer
Event CALL (ausgehender Anruf):
4. ID des lokalen Telefons, von welchem der Anruf ausgeht
5. ausgehende Rufnummer, von welcher aus der Anruf abgeht
6. Zielrufnummer, welche angerufen wird 
Event CONNECT (angenommen und Verbindung kommt zustande):
4. ID des lokalen Telefons, an welchem das Gespräch geführt wird und angenommen wurde
5. Zielrufnummer 
Event DISCONNECT (Anruf wird beendet):
4. Dauer der Verbindung in Sekunden

Ich parse über das Steuern des LED-Alarms hinaus auch den Großteil der o.g. Informationen aus den von der Fritz!Box gelieferten Zeilen, da ich beim Schreiben des Scripts mit dem Gedanken gespielt hatte, es auch mit meiner Fritz!Box einzusetzen, um mich über eingehende Anrufe (z. B. per Desktop-Notification auf meiner Linux-Workstation) samt dieser Informationen benachrichtigen zu lassen. Auch könnte man diese Informationen z. B. auf einem Smarthome-Statusdisplay anzeigen lassen. Stand jetzt gibt das Script diese Informationen als menschenlesbare Logs aus, wenn es manuell ausgeführt wird, was zu Debugging-Zwecken hilfreich sein kann.

Das Skript ist relativ simpel aufgebaut.

Zu Beginn werden die relevanten Variablen gesetzt, auf welche später zugegriffen wird:

# Definition der Fritzbox-IP basierend auf dem Default-Gateway (funktioniert nur, so lange keine VPN-Verbindung o.Ä. eine andere Default-Route gesetzt hat - alternativ statisch setzen)
FRITZBOX="$(ip route | grep default | grep -Eo 'via (\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}' | cut -d' ' -f2 | awk '!seen[$0]++')"
# Port, unter welchem die Fritzbox die callmonitor-Informationen bereitstellt
CALLMONITOR_PORT="1012"
# Definition der IP-Adresse, unter welchem das "Blitzdings" erreichbar ist BLITZDINGS_IP="192.168.178.50"

Anschließend werden die beiden Funktionen definiert, welche das "Blitzdings" (den WLED-ESP) in einem bestimmten Programm (Blinken) ein- bzw. wieder ausschalten:

start-blitzdings() {
    curl --insecure --silent "http://${BLITZDINGS_IP}/win&T=1&PL=1" > /dev/null 2>&1 && echo "[${TIME}] Blitzdings aktiviert." || echo "[${TIME}] Blitzdings aktivieren fehlgeschlagen." 1>&2
}

stop-blitzdings() {
    curl --insecure --silent "http://${BLITZDINGS_IP}/win&T=0&PL=1" > /dev/null 2>&1 && echo "[${TIME}] Blitzdings deaktiviert." || echo "[${TIME}] Blitzdings deaktivieren fehlgeschlagen." 1>&2
}

Nun folgt der permanent aktive Loop, welcher per netcat die von der Fritz!Box bereitgestellten Informationen annimmt und Zeile für Zeile in eine while-read-Schleife piped:

# Permanenter Loop für die Überwachung
while true ; do
    echo "fritzbox-callflash.sh gestartet (Fritzbox: \"${FRITZBOX}:${CALLMONITOR_PORT}\")"
    # netcat verbindet sich auf den "$CALLMONITOR_PORT" der "$FRITZBOX" und piped dessen Ausgabe Zeile für Zeile in den folgenden While-Loop.
    nc "$FRITZBOX" "$CALLMONITOR_PORT" | while read -r LINE ; do

Zum Parsen der einzelnen Felder der CSV nutze ich nach Anpassung des IFS (Internal Field Separator, https://www.baeldung.com/linux/ifs-shell-variable) "read":

# setze den Internal Field Separator auf ";", so dass die eingehenden CSV-Zeilen direkt von Bash korrekt getrennt werden können (kein zus. Funktionsaufruf)
IFS=';'
# übernimm die in $LINE als CSV hinterlegten Werte in das indizierte Array "${CALLINFO[]}"
read -r -a CALLINFO <<< "$LINE"

Nach Abspeichern von Zeitpunkt ($TIME) und Event ($ACTION) in separaten Variablen

# übernimm den 0. (1.) Wert aus "${CALLINFO[]}" als Zeitpunkt des Anrufs (formatiere das Datum dabei in ein sortierbares Format um und hänge das Jahrtausend vorne an).

TIME="$(date +%C)$(awk -F'[. ]' '{print $3"-"$2"-"$1" " $4}' <<< "${CALLINFO[0]}")"
# übernimm den 1. (2.) Wert aus "${CALLINFO[]}" als Aktion
# (CALL = ausgehender Anruf, RING = eingehender Anruf, CONNECT = angenommen/Verbindung steht, DISCONNECT = eine Seite hat aufgelegt - egal, ob zuvor verbunden oder nicht)
ACTION="${CALLINFO[1]}"
# Gleichzeitige, Xte Verbindung, welche über die Fritzbox aufgebaut wird(?)
# CONNECTION_ID="${CALLINFO[2]}"

werden alle weiteren Parsing- und Steuerungsaktivitäten vermittels eines Case-Statements abhängig vom aktuell betrachteten Event ($ACTION) durchgeführt:

# Wenn die "$ACTION"...
     case "$ACTION" in
      # "RING" (eingehender Anruf) ist...
      "RING" )
[…]

Neben dem weiteren Parsen der von der Fritzbox gelieferten Details wird im Fall eines eingehenden Anrufs (Event: RING) die Funktion `start-blitzdings` und im Fall eines angenommenen (Event: CONNECT) oder von einer der beiden Seiten beendeten (Event: DISCONNECT) Anrufs die Funktion `stop-blitzdings` ausgeführt. Somit blinkt der LED-Streifen nur so lange, wie es klingelt, der Anruf jedoch noch nicht angenommen oder abgewiesen wurde.

  # "RING" (eingehender Anruf) ist...
  "RING" )
  # rufe die Funktion "start-blitzdings" auf
  start-blitzdings
[…]
  # "CONNECT" (Verbindungsaufbau) ist...
  "CONNECT" )
  # rufe die Funktion "stop-blitzdings" auf
  stop-blitzdings
[…]
  # Wenn die "$ACTION" "DISCONNECT" (Beendigung der Verbindung) ist...
  "DISCONNECT" )
  # rufe die FUnktion "stop-blitzdings" auf
  stop-blitzdings

Um dieses Script auf dem ARM-System automatisch beim Booten starten und im Fehlerfall neu starten zu lassen, erstellte ich folgende SystemD-Unit (sicher nicht perfekt, aber funktional):
[Unit]
Description=Hält das Bash-Script /opt/fritzbox-callflash.sh am Laufen, welches bei eingehenden Anrufen eine unter WLED betriebenen ESP-LED-Leiste zum Blinken bringt
After=network.target

[Service]
Type=simple

ExecStart=/bin/bash /opt/fritzbox-callflash.sh
KillSignal=15
Restart=always

[Install]

WantedBy=multi-user.target
Evtl. dient euch dieses Script als Grundlage für eigene Smarthome-Statusdisplays oder Automatisierungen basierend auf dem aktuellen Telefoniestatus eurer Fritz!Box. Meinem Bekannten ermöglicht es, eingehende Anrufe nun trotz zunehmend schlechterem Gehör wieder wahrzunehmen.
⁣⁣Im Folgenden einmal das volle Script zum Herauskopieren und Adaptieren:

#!/bin/bash
# Dependencies: netcat-openbsd iproute2 curl coreutils

# Callmonitoring muss an der Fritzbox manuell aktiviert werden und kommuniziert nicht über TR-069:
# aktivieren: #96*5* anrufen
# deaktivieren: #96*4* anrufen

# Definition der Fritzbox-IP basierend auf dem Default-Gateway (funktioniert nur, so lange keine VPN-Verbindung o.Ä. eine andere Default-Route gesetzt hat - alternativ statisch setzen)
FRITZBOX="$(ip route | grep default | grep -Eo 'via (\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}' | cut -d' ' -f2 | awk '!seen[$0]++')"
# Port, unter welchem die Fritzbox die callmonitor-Informationen bereitstellt
CALLMONITOR_PORT="1012"
# Definition der IP-Adresse, unter welchem das "Blitzdings" erreichbar ist
BLITZDINGS_IP="192.168.178.50"

start-blitzdings() {
   curl --insecure --silent "http://${BLITZDINGS_IP}/win&T=1&PL=1" > /dev/null 2>&1 && echo "[${TIME}] Blitzdings aktiviert." || echo "[${TIME}] Blitzdings aktivieren fehlgeschlagen." 1>&2
}

stop-blitzdings() {
   curl --insecure --silent "http://${BLITZDINGS_IP}/win&T=0&PL=1" > /dev/null 2>&1 && echo "[${TIME}] Blitzdings deaktiviert." || echo "[${TIME}] Blitzdings deaktivieren fehlgeschlagen." 1>&2
}

# Permanenter Loop für die Überwachung
   while true ; do
   echo "fritzbox-callflash.sh gestartet (Fritzbox: \"${FRITZBOX}:${CALLMONITOR_PORT}\")"
   # netcat verbindet sich auf den "$CALLMONITOR_PORT" der "$FRITZBOX" und piped dessen Ausgabe Zeile für Zeile in den folgenden While-Loop.
   nc "$FRITZBOX" "$CALLMONITOR_PORT" | while read -r LINE ; do
     # Debugging: einmal die volle Ausgabe der Fritzbox je Anruf ausgeben:
     # echo "$LINE"

     # setze den Internal Field Separator auf ";", so dass die eingehenden CSV-Zeilen direkt von Bash korrekt getrennt werden können (kein zus. Funktionsaufruf)
     IFS=';'
     # übernimm die in $LINE als CSV hinterlegten Werte in das indizierte Array "${CALLINFO[]}"
     read -r -a CALLINFO <<< "$LINE"

     # übernimm den 0. (1.) Wert aus "${CALLINFO[]}" als Zeitpunkt des Anrufs (formatiere das Datum dabei in ein sortierbares Format um und hänge das Jahrtausend vorne an).
     TIME="$(date +%C)$(awk -F'[. ]' '{print $3"-"$2"-"$1" " $4}' <<< "${CALLINFO[0]}")"
     # übernimm den 1. (2.) Wert aus "${CALLINFO[]}" als Aktion
     # (CALL = ausgehender Anruf, RING = eingehender Anruf, CONNECT = angenommen/Verbindung steht, DISCONNECT = eine Seite hat aufgelegt - egal, ob zuvor verbunden oder nicht)
     ACTION="${CALLINFO[1]}"
     # Gleichzeitige, Xte Verbindung, welche über die Fritzbox aufgebaut wird(?)
     # CONNECTION_ID="${CALLINFO[2]}"

     # beginne den später im Log des Scripts auszugebenden "$OUTPUT_STRING" mit der "$TIME" des geparsten Events
     OUTPUT_STRING="[${TIME}]"

     # Wenn die "$ACTION"...
     case "$ACTION" in
       # "RING" (eingehender Anruf) ist...
       "RING" )
       # rufe die Funktion "start-blitzdings" auf
       start-blitzdings

       # übernimm den 3. (4.) Wert aus "${CALLINFO[]}" als Nummer des Anrufers
       CALLER=${CALLINFO[3]}
       # übernimm den 4. (5.) Wert aus "${CALLINFO[]}" als Zielrufnummer
       DESTINATION=${CALLINFO[4]}
       # übernimm den 5. (6.) Wert aus "${CALLINFO[]}" als Methode/Anrufprotokoll [SIP oder externe Rufnummer(?)]
       METHOD=${CALLINFO[5]}

       # Formatiere den Output-String aus den zuvor extrahierten Informationen
       OUTPUT_STRING="${OUTPUT_STRING} RING: Eingehender Anruf von ${CALLER} an ${DESTINATION} via ${METHOD}." ;;
       # "CALL" (ausgehender Anruf) ist...
       "CALL" )
       # übernimm den 3. (4.) Wert aus "${CALLINFO[]}" als "$MSN" - ID des registrierten lokalen Telefons(?)
       MSN=${CALLINFO[3]}
       # übernimm den 4. (5.) Wert aus "${CALLINFO[]}" als Nummer des Anrufers
       SOURCE=${CALLINFO[4]}
       # übernimm den 5. (6.) Wert aus "${CALLINFO[]}" als Zielrufnummer
       DESTINATION=${CALLINFO[5]}
       # übernimm den 6. (7.) Wert aus "${CALLINFO[]}" als Methode/Anrufprotokoll [SIP oder externe Rufnummer(?)]
       METHOD=${CALLINFO[6]}

       # Formatiere den Output-String aus den zuvor extrahierten Informationen
       OUTPUT_STRING="${OUTPUT_STRING} CALL: Ausgehender Anruf an ${DESTINATION} von ${SOURCE} (Gerät: ${MSN}) via ${METHOD}." ;;
       # "CONNECT" (Verbindungsaufbau) ist...
       "CONNECT" )
       # rufe die Funktion "stop-blitzdings" auf
       stop-blitzdings

       # übernimm den 3. (4.) Wert aus "${CALLINFO[]}" als "$MSN" - ID des registrierten lokalen Telefons(?)
       MSN=${CALLINFO[3]}
       # übernimm den 4. (5.) Wert aus "${CALLINFO[]}" als Zielrufnummer
       DESTINATION=${CALLINFO[4]}

       # Formatiere den Output-String aus den zuvor extrahierten Informationen
       OUTPUT_STRING="${OUTPUT_STRING} CONNECT: Verbindung aufgebaut mit ${DESTINATION}." ;;
       # Wenn die "$ACTION" "DISCONNECT" (Beendigung der Verbindung) ist...
       "DISCONNECT" )
       # rufe die FUnktion "stop-blitzdings" auf
       stop-blitzdings

       # übernimm den 3. (4.) Wert aus "${CALLINFO[]}" als Gesprächsdauer
       DURATION_S=${CALLINFO[3]}
       # baue mit printf und sed aus den Sekunden eine menschenlesbare Zeitangabe im Format "1h:33m:15s"
       DURATION_HUMAN_READABLE="$(printf '%02dh:%02dm:%02ds\n' $((DURATION_S/3600)) $((DURATION_S%3600/60)) $((DURATION_S%60)) | sed -e 's/^[0hms:]*//g')"

       # Formatiere den Output-String aus den zuvor extrahierten Informationen
       OUTPUT_STRING="${OUTPUT_STRING} DISCONNECT: Verbindung nach ${DURATION_HUMAN_READABLE} beendet." ;;
       # irgendeinen anderen, nicht definierten Wert enthält...
       * )
       # setze die Variable "$UNDEFINED_BEHAVIOUR" auf 1
       UNDEFINED_BEHAVIOUR=1
       # Formatiere den Output-String aus den zuvor extrahierten Informationen
       OUTPUT_STRING="${OUTPUT_STRING} Undefinierte Aktion: \"${LINE}\"" ;;
      esac

     # Wenn "UNDEFINED_BEHAVIOUR" gesetzt ist, gebe den "$OUTPUT_STRING" als stderr aus, sonst als stdout
     [ "$UNDEFINED_BEHAVIOUR" == 1 ] && echo "$OUTPUT_STRING" 1>&2 || echo "$OUTPUT_STRING"
   done
done

Quellen:
https://fritzconnection.readthedocs.io/en/1.5.0/sources/call_monitoring.html
https://stackoverflow.com/questions/12199631/convert-seconds-to-hours-minutes-seconds/39452629#39452629

Tags

Fritzbox, WLED, Bash, Hörverlust, Wettbewerb-Frühling-2026

Ken Guru
Geschrieben von Ken Guru am 14. Mai 2026 um 09:17

Schöne Bastelei. Als Betroffener nutze ich seit Jahrzehnten die Lichtsignalanlage (LiSa) von Humantechnik. Diese deckt Telefon und Haustür ab und meldet sowohl optisch, wie auch akustisch. Einmal investiert und nie wieder Anrufer oder Handelsvertreter verpassen.

Tom
Geschrieben von Tom am 14. Mai 2026 um 11:50

Da mein Festnetztelefon etwas entfernt vom Rechner steht und ich bei Musik das Klingeln auch schon mal überhöre, nutze ich für Desktop-Benachrichtigungen das hier: https://github.com/ElTh0r0/fritzcallindicator Die Anzeige wer anruft, ist praktisch um schon vorher zu sehen, ob sich das Aufstehen vom Rechner lohnt 😉