Webhooks

Webhooks

Vorwort
Im Rahmen eines SmartHome-Projektes sind Webhooks eine phantastische Möglichkeit, um Informationen zwischen verschiedenen Systemen auszutauschen. Bisher haben wir Webhooks nur insofern verwendet, als eine Software beobachtet, ob unsere Handys zu Hause anwesend sind. Unter bestimmten Bedingungen wird dann ein Webhook an "Homee" gesendet, damit diverse Massnahmen getroffen werden, die von "Homee" gesteuert werden.
Was wir hier erreichen wollen, ist, dass wenn eine Aussenkamera in der Nacht eine Person entdeckt, die Aussenlampen von "Philips HUE" in der Nähe zuerst einige Male blinken, danach drei Minuten auf maximaler Helligkeit verbleiben und schliesslich wieder in die ursprüngliche Helligkeit zurück wechseln.

Installation
Auf den Raspis - die unter dem Betriebssystem "Raspian Pi OS Bookworm" betrieben werden - wurde das Paket "webhook" wie folgt installiert:
sudo apt-get update
sudo apt-get install webhook

Webhook als non-root betreiben
Beim Betrieb von "webhook" stellte ich fest, dass es einige Probleme bereitet, weil dieser Service unter "root" läuft. Zum Beispiel werden erstellte Files unter root:root abgespeichert und zudem sind Umgebungsvariablen wie HOME nicht richtig definiert.
Ein Vorschlag von ChatGPT, um diesen Service unter dem User USERNAME laufen zu lassen, hat sich bei mir nach einem Test bewährt:
Als root ein neues File /etc/systemd/system/webhook.service wie folgt erstellen:

[Unit]
Description=Webhook Service
After=network.target

[Service]
User=USERNAME
Group=USERNAME
ExecStart=/usr/bin/webhook -nopanic -hooks /etc/webhook.conf -verbose
Restart=always
Environment=HOME=/home/USERNAME

[Install]
WantedBy=multi-user.target
Anschliessend folgende Befehle eingeben:
sudo systemctl daemon-reload
sudo systemctl restart webhook
Danach checken, ob webhook unter USERNAME läuft:
sudo systemctl status webhook
Mir scheint der Befehl ps -ef | grep webhook | grep -v grep jedoch geeigneter!

Konfiguration
Als root (sudo su) wird folgendes File erstellt: /etc/webhook.conf

[
   {
     "id": "test_webhook",
     "execute-command": "/bin/echo",
     "command-working-directory": "/tmp",
     "response-message": "Executing test_webhook"
   },
   {
     "id": "ostlampen_hell",
     "execute-command": "/home/USERNAME/smarthome/webhooks/ostlampen_hell.sh",
     "command-working-directory": "/home/USERNAME/smarthome/webhooks"
   }
]
In diesem Beispiel sind zwei Webhooks definiert. Der erste Webhook namens "test_webhook" kann wie folgt getestet werden:
curl http://localhost:9000/hooks/test_webhook
Im zweiten Beispiel werden einige Aussenlampen z.B. aufgrund von festgestellten Bewegungen von Aussenkameras mittels einem Webhook, der in Homee in einem Homeegram definiert wurde. Dort kann als Aktion bei einem bestimmten Ereignis ein Webhook angegeben werden; in diesem Beispiel lautet die Adresse des Hooks
http://192.168.XXX.YYY:9000/hooks/ostlampen_hell
Die entsprechenden Pfade sind im File /etc/webhook.conf entsprechend anzupassen.
Wichtig: Nach jedem Editieren dieses Files muss "webhook" neu gestartet werden:
Veraltet: sudo service webhook restart
Neu und besser: sudo systemctl restart webhook

Wie die Nummern der Lampen (lampenID) bei HUE-Systemen herausgefunden werden können, ist auf der Seite Smart Lights beschrieben. Mittels diesen Webhooks sollen die Lampen 44 und 45 während drei Minuten (180 Sekunden) einer speziellen Prozedur unterzogen werden. Beim Aufrufen des Webhooks wird das Script ostlampen_temp_hell.sh aufgerufen, welches wie folgt aussieht:
#!/bin/bash
# ostlampen_temp_hell.sh  23May2024/uk
#
# Aussenlampen Ost temporär hell schalten
#
source $HOME/smarthome/HA/MASTER.file
if test "$MASTER" = "no" ; then
#   echo "Nicht Master. ostlampen_temp_hell.sh - exit! `date +%d.%m.%Y_%H:%M`"
   exit 3
fi
#
# Falls das Script vor weniger als 200 Sekunden aufgerufen wurde --> exit !!
seconds=200   # etwas mehr als die unten verwendeten 180 Sekunden
#
scriptname="`basename $0`"
lockfile=`echo $scriptname | cut -d"." -f 1`
if test -f $lockfile.lock ; then
#   find -name $lockfile.lock -mmin +4 > result.txt   # 4 Minuten, also > 180 s
   find -name $lockfile.lock -newermt "-$seconds seconds" > result.txt  # Neu: Sekunden!
   if test -s result.txt ; then
#      echo "$lockfile.lock ist jünger als $seconds"
      exit 5
   else
      rm -f $lockfile.lock
   fi
fi
touch $lockfile.lock
chmod a+w $lockfile.lock
#
./HUE_temp_bright.sh 44 180 &   # Lampe 44, Hell-Leuchtdauer 180 s
./HUE_temp_bright.sh 45 180 &   # Lampe 45, Hell-Leuchtdauer 180 s
#
Weil dieses Script beim Aufruf des Webhooks als 'root' ausgeführt wird, muss die HOME-Variable entsprechend dem Usernamen umdefiniert werden. Das Script HUE_temp_bright.sh führt folgende Prozedur aus:
1. Es wird nur ausgeführt, wenn die Lampe bereits eingeschaltet ist
2. Zuerst wird die momentane Helligkeit ausgelesen
3. Anschliessend wird die Lampe einige Male ausgeschaltet und mit der maximalen Helligkeit eingeschaltet
4. Danach wird die Lampe eine bestimmte Dauer lang in maximale Helligkleit versetzt
5. Schliesslich wird die Lampe wieder in die ursprüngliche Helligkeit versetzt
Neu am 27. Mai 2024 habe ich ein Lockfile eingeführt. Es kann vorkommen, dass aufgrund einer erkannten Person der Webhook ausgelöst wird und innerhalb der drei Minuten mit heller Beleuchtung eine weitere Personenbewegung festgestellt wird. Wenn der Webhook dann nochmals ausgeführt wird, wird die (noch maximale) Helligkeit als Startwert gespeichert und nach Ablauf des Scripts wieder erstellt. Um zu vermeiden, dass die ganze Nacht die Lampen auf maximaler Helligkeit leuchten, wird ein Lockfile erstellt und beim Ausführen des Webhooks zuerst festgestellt, ob dieses weniger alt als 200 Sekunden ist; falls dies der Fall ist, wird kein neuer Webhook ausgelöst.
Das Script HUE_temp_bright.sh sieht wie folgt aus:
#!/bin/bash
# HUE_temp_bright.sh  23May2024/uk
#
# - Hole den momentanen Helligkeits-Status einer HUE-Lampe
# - Lampe blinkt zwei Mal und wird dann maximal hell
# - Lampe bleibt hell während $duration Sekunden
# - Lampe geht danach in die ursprüngliche Helligkeit über
#
cd $HOME/smarthome/lights
#
source $HOME/smarthome/HA/MASTER.file
if test "$MASTER" = "no" ; then
#   echo "Nicht Master. HUE_temp_bright.sh - exit! `date +%d.%m.%Y_%H:%M`"
   exit
fi
#
IPbridge="192.168.0.60"
API_KEY="hnx1kQsJVy1tBTYLWifkecR2bh4n7bzHcsw5CEey"
#
n=$#
if test $n -eq 2 ; then
   lampenID=$(echo $1 | tr '[:upper:]' '[:lower:]')
   duration=$(echo $2 | tr '[:upper:]' '[:lower:]')
else
   echo "Keine Lampe spezifiziert!"
   echo "HUE_get_status.sh - exit! $(date +%d.%m.%Y_%H:%M)"
   echo "Usage:   ./HUE_get_status.sh LampenID Dauer_in_sec"
   exit
fi
#
curl --fail --silent --show-error --no-sessionid --request GET  \
 "http://$IPbridge/api/$API_KEY/lights/$lampenID/" --output out.txt
#
out=`cat out.txt | tr -s "," " " | tr -s ":" " " | tr -d '"' | tr -d "}"`
#
search="on "
##np=`echo $out | grep -ob $search | cut -d":" -f 1`
np=`echo $out | grep -ob $search | head -n 1 | cut -d":" -f 1`
let np=$np+1
OnOff=`echo $out | cut -c $np- | cut -d" " -f 2`
#echo $out
##echo "OnOff=$OnOff"
if test "$OnOff" = "false" ; then
   echo "Lampe $lampenID ist aus. HUE_temp_bright.sh - exit! `date +%d.%m.%Y_%H:%M`"
   exit
fi
#
search="bri "
np=`echo $out | grep -ob $search | head -n 1 | cut -d":" -f 1`
let np=$np+1
CURRbrightness=`echo $out | cut -c $np- | cut -d" " -f 2`
##echo "Brightness=$CURRbrightness"
#
NEWbrightness=254
OFF="false"
ON="true"
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$OFF}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
sleep 0.5
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$ON, \"bri\":$NEWbrightness}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
sleep 1.1
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$OFF}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
sleep 1.1
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$ON, \"bri\":$NEWbrightness}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
sleep 1.1
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$OFF}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
sleep 1.1
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"on\":$ON, \"bri\":$NEWbrightness}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
#
sleep $duration
#
curl --fail --silent --show-error --no-sessionid --request PUT  \
   --data "{\"bri\":$CURRbrightness}"  \
   "http://$IPbridge/api/$API_KEY/lights/$lampenID/state" --output out.txt
#

Testen des Webhooks
Getestet kann der Test-Webhook wie oben beschrieben durch Eingabe von
curl http://localhost:9000/hooks/test_webhook
Wenn dann auf dem Bildschirm die Meldung "Executing test_webhook" erscheint, ist alles in Ordnung und Webhooks scheinen zu funktionieren. :-)
Der zweite definierte Webhook kann mittels dem Befehl
curl http://localhost:9000/hooks/ostlampen_hell
ausgeführt werden.
Sollte die Fehlermeldung "Hook not found" erscheinen, können folgende Befehle vielleicht helfen (die Informationen erhielt ich durch eine entsprechende Anfrage bei ChatGPT):
sudo journalctl -u webhook
sudo journalctl -r -u webhook (neuste Einträge zuerst - praktisch!)
sudo systemctl status webhook
sudo systemctl stop webhook
sudo systemctl start webhook
sudo systemctl cat webhook
sudo netstat -tuln | grep 9000
Überpfrüfen der Syntax von /etc/webhook.conf:
Installieren von "jq" und dann:   jq . /etc/webhook.conf
Benutzen des Json-Validators JSONLint

Interaktion mit "Homee"
In den Automationen von "Homee" wird nun ein Homeegramm namens 'Cam Ost Person' wie folgt erstellt:

- Wenn Cam Ost eine Person erkennt...
- Und nur nachts...
- Dann Webhook ausführen
  URL:  http://192.168.XXX.YYY:9000/hooks/ostlampen_hell
Dieser Webhook wird an den einen raspi geschickt und weil wir ein redundantes System unterhalten, wird ein zweites Homeegramm erstellt, wobei dort als URL die IP-Adresse des zweiten Raspis angegeben wird:
http://192.168.XXX.ZZZ:9000/hooks/ostlampen_hell   .
Weil zu Beginn jedes Scripts in unserem System zuerst abgefragt wird, ob der entsprechende Raspi MASTER oder SLAVE ist, wird auch dieses Script nur auf dem momentanen Master ausgeführt.
Die Geschwindigkeit, mit welcher der Webhook ab Entdeckung einer Person durch die Kamera ausgelöst wird und die Lampen schaltet, beträgt - wie gemessen - weniger als eine Sekunde. Allerdings kann es einige Sekunden dauern, bis die Kamera das Vorhandensein einer Person entdeckt und somit reagiert dieses System langsamer als eines mit Bewegungsmeldern. Der grosse Vorteil liegt jedoch darin, dass (warme) Winde keine Fehlalarme auslösen, wie dies bei Bewegungsmeldern im Freien immer mal wieder der Fall ist!


Last update: 05Feb2025 - Created: 24May2024/uk