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
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
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
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.
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/
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>
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.
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.
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.
ZitatDas scheint aber nicht zu klappen.
Und wie ist es mnit meinem konkreten Vorschlag in Beitrag #5?
darauf hatte ich dir konkret in Beitrag #6 geantwortet.
Mit #8 hatte ich es noch einmal versucht zu verdeutlichen.
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.
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