Reolink POE Doorbell - Webhook Integration

Begonnen von Tungsten, 15 September 2025, 13:03:41

Vorheriges Thema - Nächstes Thema

Tungsten

Hallo Zusammen,

über die Suche habe ich keinen passenden Beitrag zu den neuen Webhooks bei Reolink gefunden.

Gibt es schon eine Möglichkeit in FHEM Webhooks einzubinden und auch Events zu reagieren?

Hat das schon jemand gemacht?

Hier eine gute Anleitung zu den Reolink Webhooks im Allgemeinen:
https://discourse.nodered.org/t/reolink-doorbell-finally-supports-webhooks/93834/62

Es wäre toll, wenn man auf das Klingel Webhook Event oder Personen Erkennung als Event in FHEM ähnlich wie bei MQTT reagieren könnte.

Was muss ich in Reolink als URL und Port genau eintragen, damit FHEM auf ein http post reagiert und ein device event erzeugt?

Muss man mit dem JsonMod Modul arbeiten, damit ein Endpunkt bereitgestellt wird, an den Reolink den http post Request mit JSON Payload schicken kann?

FHEM läuft bei mir auf einem Raspberry Pi.

Danke Euch


rudolfkoenig

ZitatGibt es schon eine Möglichkeit in FHEM Webhooks einzubinden und auch Events zu reagieren?
Ja, man muss sich nur mit dem CSRF Schutz (siehe https://wiki.fhem.de/wiki/CsrfToken-HowTo) ausseinandersetzen.
Events kann man mit dem trigger Befehl generieren: https://fhem.de/commandref_modular.html#trigger

Tungsten

Danke Dir.

Ich habe jetzt mal 'attr WEB.* csrfToken none' gesetzt um testen zu können.

Auf dem PC habe ich mit Mockoon einen Mockserver aufgesetzt um Webhook von der Reolink paralell testen zu können.

Dort funktioniert es schon mal:
http://192.168.178.xx:3001/webhook

Ein drücken der Klingel liefert:
POST 200 mit Body:

{
  "alarm": {
    "alarmTime": "2025-09-22T09:28:19.000+0000",
    "channel": 0,
    "channelName": "IP-Cam-Haustuer",
    "device": "IP-Cam-Haustuer",
    "deviceModel": "Reolink Video Doorbell PoE-W",
    "message": "🔔A Visitor is Ringing the IP-Cam-Haustuer",
    "name": "Visitor Alert",
    "title": "Visitor message",
    "type": "VISITOR"
  }
}


In FHEM scheitere ich noch.

Ich habe ein Dummy Device angelegt: reolink.doorbell.events

Wenn ich folgende URL aufrufe, wird das device reading aktualisiert:
http://192.168.178.yy:8083/fhem?cmd=setreading%20reolink.doorbell.events%20Kamera_Event%20blubber&XHR=1

Wenn ich die gleiche URL in der Reolink als Webhook konfiguriere gibt es eine Fehlermeldung in der Kamera.

Mir ist aber noch nicht klar wie ich es schaffe, dass ich den Webhook konfigurieren muss um den Post Body als Reading zu bekommen, um dann darauf reagieren zu können.

Hat das schon jemand geschafft und könnte die richtigen Schritte teilen?

Danke Euch

rudolfkoenig

ZitatMir ist aber noch nicht klar wie ich es schaffe, dass ich den Webhook konfigurieren muss um den Post Body als Reading zu bekommen, um dann darauf reagieren zu können.
Muss ja kein POST sein (darf aber).
Das o.g. URL erzeugt bei mir ein Event mit dem Namen Kamera_Event und den Wert blubber.
Darauf kann man per notify/DOIF/etc reagieren.
Zum Anlegen von von diesen Instanzen kann man den Event monitor (Menu links unten) verwenden: Monitor oeffnen, URL aufrufen, Zeile mit dem Event erscheint, die relevante Zeile im Browser markieren und auf "Create/Modify Device" klicken.

Tungsten

Hallo Rudolf,

Du hast mich missverstanden.

Die URL erzeugt bei mir auch ein Reading, wie geschrieben, aber das gleiche als Test direkt von der Kamera klappt nicht.

Ebenfalls wäre es nur die halbe Miete, denn der Webhook würde ja ein viel komplexere Payload schicken als nur dieses blubber. Scheinbar sogar mit einem Klingelzeichen. Das Beispiel der HTTP-POST Payload steht oben.

Und ja, es muss HTTP-POST sein, HTTP-GET wird nicht unterstützt von Reolink:
"At present, the POST request method of http is implemented, and the Content-Type is application/x-www-form-urlencoded."

https://support.reolink.com/hc/en-us/articles/46238508855193-Overview-of-Webhook-in-Reolink-Cameras/

rudolfkoenig

Ob es HTTP POST oder GET ist, ist FHEMWEB egal, und wie im verlinkten Artikel zu lesen, kann man per Customize beliebiges eingeben, also vmtl. auch das o.g. "Test" URL.

Mit dem Default Format wird es aufwendiger, man benoetigt einen Zwischenschritt wie z.Bsp. ein notify:
define rbe_ntfy notify reolink.doorbell.events:check.* { map { json2reading($defs{$NAME}, $_) if($_ =~ m/^\{.*\}$/ms) } keys %FW_webArgs }
Ein Aufruf von
wget -O - 'http://localhost:8083/fhem?cmd=setreading%20reolink.doorbell.events%20check%201&XHR=1' --post-file t.json
(wobei t.json die o.g. Daten enthaelt) erzeugt:
fhem> l reolink.doorbell.events
Internals:
  FUUID      68d25a78-f33f-c296-7d0b-9508f16207e9d0fc
  NAME      reolink.doorbell.events
  NR        38
  STATE      ???
  TYPE      dummy
  eventCount 1
  READINGS:
    2025-09-23 10:29:46  alarm_alarmTime 2025-09-22T09:28:19.000 0000
    2025-09-23 10:29:46  alarm_channel  0
    2025-09-23 10:29:46  alarm_channelName IP-Cam-Haustuer
    2025-09-23 10:29:46  alarm_device    IP-Cam-Haustuer
    2025-09-23 10:29:46  alarm_deviceModel Reolink Video Doorbell PoE-W
    2025-09-23 10:29:46  alarm_message  🔔A Visitor is Ringing the IP-Cam-Haustuer
    2025-09-23 10:29:46  alarm_name      Visitor Alert
    2025-09-23 10:29:46  alarm_title    Visitor message
    2025-09-23 10:29:46  alarm_type      VISITOR
    2025-09-23 10:29:46  check          1
Attributes:

fhem>

Tungsten

Danke Dir, auch wenn FHEM POST oder GET egal sind, so bietet Reolink Webhook nur als POST an.

Man trägt dort eine Endpunkt URL mit Port ein, an die dann ein HTTP-POST mit Payload geschickt wird.

Es gibt insgesamt 5 verschieden Szenarien, wo die Payload entsprechend anders aussieht.

Wenn ich dich richtig verstehe gibt es bisher keine Möglichkeit einen solchen Endpunkt zu definieren und die POST Payload in ein Device Reading zu schreiben um darauf reagieren zu können.

Ich verstehe die Reihenfolge deiner Schritte mit dem notify nicht.

Welche URL mit Port gebe ich in der Reolink an, damit per POST ein Webhook an FHEM geht? Darauf kann man dann mit notify usw reagieren. Aber der erste Schritte und wie und wo es in FHEM landet ist mir nicht klar.

rudolfkoenig

ZitatMan trägt dort eine Endpunkt URL mit Port ein, an die dann ein HTTP-POST mit Payload geschickt wird.
URL bedeutet, dass man neben Rechnername und Port im Aufruf einen Pfad angeben kann.
Falls man keinen Pfad wie /fhem angeben kann, dann sehe ich keine Moeglichkeit, an FHEM die Daten zu uebersenden.
Falls Pfad moeglich ist, dann koennen alle weiteren Paramer (wie cmd=setreading...) als Teil der POST Daten gesendet werden.

Tungsten

Pfad ist möglich.

Im Grunde müsste eine URL die in der Reolink Webhook Config angegeben wird so aussehen:

http://192.168.178.yy:8083/fhem?cmd=setreading%20reolink.doorbell.events%20webhook.body%20
Reolink hängt dann die Payload als Body dran:

{
  "alarm": {
    "alarmTime": "2025-09-22T09:28:19.000+0000",
    "channel": 0,
    "channelName": "IP-Cam-Haustuer",
    "device": "IP-Cam-Haustuer",
    "deviceModel": "Reolink Video Doorbell PoE-W",
    "message": "🔔A Visitor is Ringing the IP-Cam-Haustuer",
    "name": "Visitor Alert",
    "title": "Visitor message",
    "type": "VISITOR"
  }
}

Somit würde FHEM dann dies per HTTP-POST bekommen:

http://192.168.178.yy:8083/fhem?cmd=setreading%20reolink.doorbell.events%20webhook.body%20{{"alarm":{"alarmTime": "2025-09-22T09:28:19.000+0000","channel": 0, "channelName": "IP-Cam-Haustuer","device": "IP-Cam-Haustuer","deviceModel": "Reolink Video Doorbell PoE-W","message": "🔔A Visitor is Ringing the IP-Cam-Haustuer","name": "Visitor Alert","title": "Visitor message","type": "VISITOR"}}
Das scheint aber nicht zu klappen.



rudolfkoenig

ZitatDas scheint aber nicht zu klappen.
Und wie ist es mnit meinem konkreten Vorschlag in Beitrag #5?

Tungsten

darauf hatte ich dir konkret in Beitrag #6 geantwortet.

Mit #8 hatte ich es noch einmal versucht zu verdeutlichen.

rudolfkoenig

#11
ZitatMit #8 hatte ich es noch einmal versucht zu verdeutlichen.
Stimmt, dabei wurde aber mein Vorschlag kaputtoptimiert.

Die Annahme, dass URL+POST gleichwertig ist mit dem weiter unten gezeigten GET ist nicht richtig, die beiden Teile werden per & zusammengefuegt, was wiederum zu einer Fehlermeldung fuehrt, was auch zurueckgeliefert wird:
Usage: setreading <name> [YYYY-MM-DD HH:MM:SS] <reading> <value>

ZitatIch verstehe die Reihenfolge deiner Schritte mit dem notify nicht.
Daten, die an FHEMWEB ohne ein FHEM-Befehl zugeschoben werden, werden verworfen.
Deswegen gibt man ein beliebiges Kommando per URL (z.Bsp. setreading reolink.doorbell.events check 1), was ein notify triggert (rbe_ntfy, s.o), was wiederum die POST Daten aus dem FHEMWEB-Internals rausfischt, und es dem dummy zuordnet, per json2reading.

Was man mit diesen Readings _danach_ macht, ist eine ganz andere Baustelle, dazu habe ich nichts geschrieben.

passibe

Ich nutze für sowas einen kleinen python-Container mit Flask, der parst dann alles so wie es sein soll und übergibt das an FHEM.

Hier mal als Minimalbeispiel (habe das ein bisschen gebutchered weil der webhook-container bei mir noch andere Sachen macht, 0 Garantie, dass das direkt so funktioniert, aber vielleicht als Inspiration).

Hier im Beispiel dann:curl -d '{"cmd": "set test on"}' http://$IP:5000/fhem_cmd
Bei dir müsstest du das Parsen der Daten, die die Kamera übermittelt in handle_fhem_index einbauen.



docker-compose.yml (credentials dann ganz normal in .env)
services:
  webhook:
    build:
      context: ./src
      pull: true
      dockerfile_inline: |
        FROM python:3-alpine
        RUN addgroup -g 1516 -S webhook && adduser -u 1516 -h /app -H -S -s /usr/sbin/nologin -G webhook webhook
        COPY --chown=webhook . /app
        WORKDIR /app
        RUN pip3 install --no-cache-dir -r requirements.txt
        RUN pip3 install --no-cache-dir gunicorn
        USER webhook
        CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "webhook:app"]
    pull_policy: build
    restart: unless-stopped
    user: 1516:1516
    container_name: webhook_webhook
    hostname: webhook_webhook
    networks:
      - homeautomation
    ports:
      - "5000:5000"
    environment:
      TZ: Europe/Berlin
      LOGLEVEL: DEBUG
      WEBHOOK_FHEM_URL: "http://fhem:8083"
      WEBHOOK_FHEM_USER: "${FHEM_USER}"
      WEBHOOK_FHEM_PASS: "${FHEM_PASS}"

networks:
  homeautomation:
    external: true


./src/webhook.py
#!/usr/bin/python3
from flask import Flask, abort, request
import logging
import os
import json
import requests
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
logging.basicConfig(level=LOGLEVEL, format='[%(asctime)s] %(levelname)s: %(message)s')
app = Flask(__name__)
app.config.from_prefixed_env(prefix='WEBHOOK')

def handle_fhem_cmd(cmd):
    app.logger.debug(f"Handling FHEM command: {cmd}")

    # Set url and basic auth vars
    fhem_url = app.config[f"FHEM_URL"]
    fhem_user = app.config[f"FHEM_USER"]
    fhem_pass = app.config[f"FHEM_PASS"]

    # Get csrf
    try:
        app.logger.debug(f"Getting CSRF token from {fhem_url}")
        csrf = requests.get(f"{fhem_url}/fhem?XHR=1", auth=(fhem_user, fhem_pass))
        csrf.raise_for_status()
        csrf = csrf.headers['X-FHEM-csrfToken']
    except Exception as e:
        app.logger.error(f"Could not get CSRF token: {e}")
        raise RuntimeError()
    else:
        app.logger.debug(f"Got CSRF token")
        # Send fhem command
        try:
            r = requests.get(f"{fhem_url}/fhem?fwcsrf={csrf}&cmd={cmd}&XHR=1", auth=(fhem_user, fhem_pass))
            r.raise_for_status()
        except Exception as e:
            app.logger.error(f"Could not send FHEM cmd: {e}")
            raise RuntimeError(e)
        else:
            app.logger.info(f"Sent FHEM cmd: {cmd}")

def handle_fhem_index(r):
    if r.method == 'POST':
        # Get cmd from request
        cmd = r.get_json(r)['cmd']
        # Pass to cmd handler
        try:
            handle_fhem_cmd(cmd)
        except Exception as e:
            app.logger.error(f"FHEM command handler failed for cmd: {cmd}")
            abort(500)
        else:
            return '{"success":"true"}', 200

@app.route('/fhem_cmd', methods=['POST'])
def fhem_cmd_index():
    return handle_fhem_index(request)

if __name__ == "__main__":
        app.run()

./src/requirements.txt
flask
requests