Status-Webhook
Die Fotobox meldet ihren Zustand im Takt an eine Adresse, die der Betreiber einträgt. Jede Meldung ist ein JSON mit denselben Werten, die das Portal der Fernverwaltung zu dieser Fotobox zeigt. Was der Empfänger damit anfängt, bestimmt er selbst.
Eingeschaltet wird das im Admin-Bereich "Licht-/Zubehör-Steuerung" > "Status-Webhook". Die Fernverwaltung wird dafür nicht gebraucht: Der Webhook läuft auch an einer Fotobox ohne Portal.
Wofür das gut ist#
- Auf der eigenen Website zeigen, ob die Fotobox gerade im Einsatz ist und wie viele Fotos schon entstanden sind.
- Eine eigene Übersicht über mehrere Fotoboxen führen, in einem vorhandenen System.
- Benachrichtigen lassen, wenn das Papier knapp wird, der Kamera-Akku nachlässt oder eine Fotobox nicht mehr meldet - über eine Hausautomatisierung oder einen Chat.
- Zähler mitschreiben, etwa für die Abrechnung mit dem Kunden.
Status-Webhook oder Auslöser?#
Beide melden nach außen, aber verschieden:
| Status-Webhook | Auslöser für Fremdprogramme | |
|---|---|---|
| Wann | im festen Takt, ab Werk alle 30 Sekunden | in dem Moment, in dem ein Schritt passiert |
| Was | der gesamte Zustand der Fotobox | ein einzelnes Ereignis mit wenigen Werten |
| Wohin | eine Adresse im Internet oder im Netzwerk | eine Adresse oder ein Programm auf der Fotobox |
| Typisch | Anzeige, Überwachung, Statistik | Licht, Steckdosen, Skripte im Ablauf |
Beides lässt sich gleichzeitig nutzen. Die Auslöser beschreibt das Kapitel Auslöser für Fremdprogramme.
Einrichten#
- Einen Empfänger bereitstellen: eine Adresse, die eine POST-Anfrage mit JSON annimmt und mit einem Status von 200 bis 299 antwortet. Die Beispiele weiter unten reichen dafür.
- Im Bereich "Status-Webhook" die Adresse eintragen und speichern. Dabei entsteht der Schlüssel für die Signatur.
- Den Schlüssel mit "Kopieren" übernehmen und beim Empfänger eintragen.
- "Testmeldung senden" drücken. Darunter erscheinen die Antwort des Empfängers und der gesendete Inhalt.
- "Status-Webhook verwenden" einschalten. Die erste Meldung geht sofort raus, danach im eingestellten Abstand.
Was ankommt#
Jede Meldung ist eine POST-Anfrage mit diesen Kopfzeilen:
| Kopfzeile | Inhalt |
|---|---|
Content-Type | application/json; charset=utf-8 |
User-Agent | RAWCaptureBooth-Statuswebhook/<Version> |
X-RAWCaptureBooth-Timestamp | Zeitpunkt der Meldung in Sekunden seit 1970 (Unix-Zeit) |
X-RAWCaptureBooth-Signature | sha256= und die Signatur, siehe unten |
Der Inhalt sieht so aus (gekürzt auf je einen Drucker und Buzzer):
{
"schema_version": 1,
"sent_at": "2026-10-03T21:14:05+02:00",
"report_interval_seconds": 30,
"computer_name": "FOTOBOX-01",
"rawcapturebooth_version": "3.28.0",
"rawcapturebooth_latest_version": "3.28.0",
"cockpit_version": null,
"cockpit_latest_version": null,
"uptime": "5:12:03",
"network_status": "WLAN (Festsaal)",
"connectivity_mode": "online",
"email_enabled": true,
"print_enabled": true,
"cloud_enabled": false,
"event_name": "Hochzeit Müller",
"sessions_total": 1843,
"sessions_since_start": 112,
"session_limit": 0,
"total_print_jobs": 2210,
"print_jobs_since_start": 131,
"print_limit": 300,
"ai_portraits_total": 0,
"ai_portraits_since_start": 0,
"ai_portrait_limit": 0,
"ai_portrait_warn_threshold": 0,
"last_session_at": "2026-10-03T19:13:40Z",
"last_capture_at": "2026-10-03T19:13:40Z",
"error_count": 0,
"last_error": null,
"last_error_at": null,
"cpu_used_percent": 23.5,
"memory_used_percent": 61.2,
"disk_free_gb": 182.4,
"disk_total_gb": 476.3,
"outbox_pending": 0,
"outbox_email_pending": 0,
"outbox_cloud_pending": 0,
"camera_battery_percent": null,
"camera_power_source": "ac",
"printers": [
{
"role": "print",
"mode": "single",
"name": "DP-DS620",
"available": true,
"state": "ready",
"queue_length": 0,
"issues": [],
"media_remaining": 268,
"media_capacity": 400,
"total_prints": 15320
}
],
"buzzers": [
{
"name": "Buzzer 1",
"connection": "connected",
"battery_level": 3,
"millivolt": 4010,
"seconds_since_press": 25
}
]
}
Den echten Inhalt der eigenen Fotobox zeigt "Testmeldung senden" unter "Gesendeter Inhalt".
Die Felder#
Ein Feld ohne Wert steht als null da. Neue Felder können in späteren Versionen dazukommen; ein Empfänger übergeht, was er nicht kennt. schema_version steigt nur, wenn sich die Bedeutung eines Feldes ändert oder eines wegfällt.
| Feld | Inhalt |
|---|---|
schema_version | Aufbau der Meldung, derzeit 1 |
sent_at | Sendezeitpunkt in der Ortszeit der Fotobox, mit Zeitzone |
report_interval_seconds | Melde-Abstand in Sekunden |
computer_name | Rechnername der Fotobox - die Kennung, wenn mehrere Boxen an denselben Empfänger melden |
rawcapturebooth_version | Installierte Version |
rawcapturebooth_latest_version | Neueste veröffentlichte Version, leer, solange die Fotobox sie nicht abrufen konnte |
cockpit_version, cockpit_latest_version | Dasselbe für RAWCaptureBooth Cockpit, leer ohne Cockpit |
uptime | Laufzeit seit dem Programmstart, etwa 5:12:03 oder 1 day, 2:03:04 |
network_status | Tatsächliche Netzlage: LAN, WLAN (Netzname), Online oder Offline |
connectivity_mode | Eingestellte Betriebsart: online oder offline |
email_enabled, print_enabled, cloud_enabled | Ob Mail-Versand, Druck und Cloud-Upload eingeschaltet sind |
event_name | Name des aktiven Events |
sessions_total, sessions_since_start | Sessions insgesamt und seit dem Programmstart |
session_limit | Obergrenze der Sessions, 0 heißt kein Limit |
total_print_jobs, print_jobs_since_start, print_limit | Dasselbe für Druckaufträge |
ai_portraits_total, ai_portraits_since_start, ai_portrait_limit | Dasselbe für KI-Porträts |
ai_portrait_warn_threshold | Ab dieser Zahl von KI-Porträts warnt die Fotobox, 0 heißt aus |
last_session_at, last_capture_at | Zeitpunkt der letzten Session und der letzten Aufnahme, meist in UTC (endet auf Z) |
error_count | Fehler seit dem Programmstart |
last_error, last_error_at | Der letzte Fehler als Text, mit Zeitpunkt |
cpu_used_percent | Prozessor-Auslastung seit der letzten Meldung, leer bei der ersten Meldung nach dem Start |
memory_used_percent | Belegter Arbeitsspeicher |
disk_free_gb, disk_total_gb | Freier und gesamter Speicher des Laufwerks mit den Aufnahmen |
outbox_pending | Wartende Mails und Uploads, zum Beispiel ohne Netz |
outbox_email_pending, outbox_cloud_pending | Davon Mails und Cloud-Uploads |
camera_battery_percent | Kamera-Akku in Prozent, leer am Netzteil oder wenn die Kamera ihn nicht meldet |
camera_power_source | battery, ac (Netzteil) oder leer |
printers | Je Drucker ein Eintrag, siehe unten |
buzzers | Je Funk-Buzzer ein Eintrag, siehe unten |
Ein Eintrag in printers:
| Feld | Inhalt |
|---|---|
role | print (Foto-Drucker) oder strip (Fotostreifen-Drucker) |
mode | single (ein Drucker) oder pool (Drucker-Pool, dann zusätzlich strategy) |
name | Name des Druckers in Windows |
available | Ob der Drucker bereit ist |
state | Zustand, etwa ready, printing, paused, error, offline oder unknown |
queue_length | Aufträge in der Warteschlange |
issues | Liste der gemeldeten Probleme, leer ohne Befund |
media_remaining, media_capacity | Restmaterial und Fassungsvermögen in Blatt, leer ohne Angabe des Druckers |
total_prints | Drucke über die Lebensdauer, nur bei DNP-Druckern |
Ein Eintrag in buzzers:
| Feld | Inhalt |
|---|---|
name | Name des Funk-Buzzers |
connection | connected, sleeping, disconnected, never (nie verbunden) oder unavailable (Bluetooth nicht verfügbar) |
battery_level | Batterie in drei Stufen: 3 voll, 2 halb, 1 schwach, 0 unbekannt |
millivolt | Batteriespannung in Millivolt |
seconds_since_press | Sekunden seit dem letzten Drücken, leer, wenn noch nicht gedrückt |
Die Signatur prüfen#
Jede Meldung ist mit dem Schlüssel aus dem Admin-Bereich signiert. Der Empfänger rechnet die Signatur selbst nach und nimmt die Meldung nur an, wenn beide übereinstimmen:
- Den Inhalt so, wie er ankommt, als Bytes lesen - nicht erst als JSON einlesen und wieder ausgeben.
- Zeitstempel aus
X-RAWCaptureBooth-Timestamp, einen Punkt und den Inhalt aneinanderhängen. - Darüber HMAC-SHA256 mit dem Schlüssel bilden, als Hex-Zeichenkette, und
sha256=voranstellen. - Mit
X-RAWCaptureBooth-Signaturevergleichen, in konstanter Zeit. - Meldungen ablehnen, deren Zeitstempel mehr als fünf Minuten von der eigenen Uhrzeit abweicht. So lässt sich eine mitgeschnittene Meldung nicht später wieder einspielen.
"Neu erzeugen" im Admin-Bereich ersetzt den Schlüssel. Bis er beim Empfänger nachgetragen ist, lehnt dieser jede Meldung ab.
Beispiel: Anzeige auf der eigenen Website#
Eine Website kann eine Meldung nicht selbst annehmen - sie braucht ein kleines Skript auf dem Server. Dieses PHP-Skript prüft die Signatur und legt nur die Werte, die öffentlich sein sollen, in einer Datei fotobox-status.json ab:
<?php
// fotobox-webhook.php - als Adresse im Admin-Bereich eintragen
$secret = 'HIER-DEN-SCHLUESSEL-EINTRAGEN';
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_RAWCAPTUREBOOTH_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_RAWCAPTUREBOOTH_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
$status = json_decode($body, true);
if (!is_array($status)) {
http_response_code(400);
exit;
}
// Nur weitergeben, was auf die Website gehört
$public = [
'event_name' => $status['event_name'] ?? null,
'sessions_since_start' => $status['sessions_since_start'] ?? 0,
'report_interval_seconds' => $status['report_interval_seconds'] ?? 30,
'received_at' => time(),
];
file_put_contents(__DIR__ . '/fotobox-status.json', json_encode($public), LOCK_EX);
http_response_code(204);
Die Website liest die Datei und entscheidet selbst, ob die Fotobox erreichbar ist:
<p id="fotobox">Fotobox: wird geladen …</p>
<script>
fetch('/fotobox-status.json', { cache: 'no-store' })
.then((response) => response.json())
.then((status) => {
const age = Date.now() / 1000 - status.received_at;
const online = age <= 2 * status.report_interval_seconds;
document.getElementById('fotobox').textContent = online
? 'Fotobox im Einsatz, ' + status.sessions_since_start + ' Sessions seit dem Start'
: 'Fotobox gerade nicht erreichbar';
});
</script>
Beispiel: Empfänger in Node.js#
Ohne zusätzliche Pakete, mit dem eingebauten http-Modul:
const http = require('http');
const crypto = require('crypto');
const SECRET = 'HIER-DEN-SCHLUESSEL-EINTRAGEN';
http.createServer((req, res) => {
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const body = Buffer.concat(chunks);
const timestamp = req.headers['x-rawcapturebooth-timestamp'] || '';
const signature = req.headers['x-rawcapturebooth-signature'] || '';
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET)
.update(timestamp + '.').update(body).digest('hex');
const valid = signature.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
&& Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
if (!valid) {
res.writeHead(401).end();
return;
}
const status = JSON.parse(body.toString('utf8'));
console.log(status.computer_name, status.event_name, status.sessions_total);
res.writeHead(204).end();
});
}).listen(8080);
Erkennen, dass eine Fotobox nicht mehr meldet#
"Offline" kann eine Fotobox nicht selbst melden - ohne Verbindung kommt auch keine Meldung an. Der Empfänger merkt sich deshalb, wann die letzte Meldung eintraf. Bleibt sie länger als zwei Melde-Abstände aus (report_interval_seconds), ist die Fotobox nicht erreichbar. Zwei Abstände statt einem fangen eine einzelne verspätete Meldung ab.
Fehlersuche#
- "Testmeldung senden" meldet HTTP 401 oder 403: Der Empfänger lehnt die Signatur ab. Stimmt der Schlüssel? Wird der Rohinhalt geprüft (siehe oben)? Geht die Uhr des Servers richtig?
- HTTP 404: Die Adresse stimmt nicht - Pfad und Dateiname prüfen.
- "Weiterleitung nach …": Die Adresse leitet weiter, oft von
http://aufhttps://oder auf eine Adresse mitwww. Die Fotobox folgt Weiterleitungen nicht; die Zieladresse aus der Meldung direkt eintragen. - "Keine Antwort innerhalb von 15 Sekunden": Der Empfänger ist nicht erreichbar oder antwortet zu langsam. Er sollte die Meldung annehmen und sofort antworten, statt erst weiterzuarbeiten.
- Meldungen kommen seltener als eingestellt: Nach einem Fehlschlag wartet die Fotobox länger bis zum nächsten Versuch - erst 15 Sekunden, dann doppelt so lang, höchstens eine Viertelstunde. Beim ersten Erfolg gilt wieder der eingestellte Abstand. Wie viele Versuche in Folge gescheitert sind, steht im Bereich unter "Test und Zustand".