Webhook di stato
La cabina invia a intervalli regolari il proprio stato a un indirizzo inserito dal gestore. Ogni messaggio è un JSON con gli stessi valori che il portale di gestione remota mostra per questa cabina. Cosa farne lo decide il destinatario.
Si attiva nell'area di amministrazione « Luci e accessori » > « Webhook di stato ». La gestione remota non è necessaria: il webhook funziona anche su una cabina senza portale.
A cosa serve#
- Mostrare sul proprio sito se la cabina è in funzione e quante foto sono già state scattate.
- Tenere una propria panoramica di più cabine, in un sistema già esistente.
- Ricevere un avviso quando la carta sta finendo, la batteria della fotocamera cala o una cabina non si fa più sentire - tramite domotica o una chat.
- Registrare i contatori, ad esempio per la fatturazione al cliente.
Webhook di stato o attivatori?#
Entrambi inviano verso l'esterno, ma in modo diverso:
| Webhook di stato | Attivatori per programmi esterni | |
|---|---|---|
| Quando | a intervallo fisso, di default ogni 30 secondi | nel momento in cui avviene un passaggio |
| Cosa | lo stato completo della cabina | un singolo evento con pochi valori |
| Dove | un indirizzo su Internet o in rete | un indirizzo o un programma sulla cabina |
| Uso tipico | visualizzazione, monitoraggio, statistiche | luci, prese, script durante la sessione |
Si possono usare entrambi contemporaneamente. Gli attivatori sono descritti nel capitolo Attivatori per programmi esterni.
Configurazione#
- Predisporre un destinatario: un indirizzo che accetti una richiesta POST con JSON e risponda con uno stato tra 200 e 299. Gli esempi più sotto bastano.
- Nell'area « Webhook di stato » inserire l'indirizzo e salvare. Viene così creata la chiave per la firma.
- Prendere la chiave con « Copia » e inserirla presso il destinatario.
- Premere « Invia messaggio di prova ». Sotto compaiono la risposta del destinatario e il contenuto inviato.
- Attivare « Usa il webhook di stato ». Il primo messaggio parte subito, poi all'intervallo impostato.
Cosa arriva#
Ogni messaggio è una richiesta POST con queste intestazioni:
| Intestazione | Contenuto |
|---|---|
Content-Type | application/json; charset=utf-8 |
User-Agent | RAWCaptureBooth-Statuswebhook/<versione> |
X-RAWCaptureBooth-Timestamp | Momento del messaggio in secondi dal 1970 (tempo Unix) |
X-RAWCaptureBooth-Signature | sha256= seguito dalla firma, vedi sotto |
Il contenuto ha questo aspetto (ridotto a una stampante e un 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 (Sala feste)",
"connectivity_mode": "online",
"email_enabled": true,
"print_enabled": true,
"cloud_enabled": false,
"event_name": "Matrimonio Rossi",
"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
}
]
}
« Invia messaggio di prova » mostra il contenuto reale della propria cabina sotto « Contenuto inviato ».
I campi#
Un campo senza valore è null. Nelle versioni successive possono aggiungersi nuovi campi; un destinatario ignora ciò che non conosce. schema_version aumenta solo se cambia il significato di un campo o se un campo viene eliminato.
| Campo | Contenuto |
|---|---|
schema_version | Struttura del messaggio, attualmente 1 |
sent_at | Momento dell'invio nell'ora locale della cabina, con fuso orario |
report_interval_seconds | Intervallo di segnalazione in secondi |
computer_name | Nome del computer della cabina - l'identificativo quando più cabine inviano allo stesso destinatario |
rawcapturebooth_version | Versione installata |
rawcapturebooth_latest_version | Ultima versione pubblicata, vuoto finché la cabina non ha potuto recuperarla |
cockpit_version, cockpit_latest_version | Lo stesso per RAWCaptureBooth Cockpit, vuoto senza Cockpit |
uptime | Tempo di funzionamento dall'avvio del programma, ad esempio 5:12:03 o 1 day, 2:03:04 |
network_status | Situazione di rete effettiva: LAN, WLAN (nome della rete), Online o Offline |
connectivity_mode | Modalità di funzionamento impostata: online o offline |
email_enabled, print_enabled, cloud_enabled | Se invio e-mail, stampa e caricamento nel cloud sono attivi |
event_name | Nome dell'evento attivo |
sessions_total, sessions_since_start | Sessioni in totale e dall'avvio del programma |
session_limit | Numero massimo di sessioni, 0 significa nessun limite |
total_print_jobs, print_jobs_since_start, print_limit | Lo stesso per le stampe |
ai_portraits_total, ai_portraits_since_start, ai_portrait_limit | Lo stesso per i ritratti IA |
ai_portrait_warn_threshold | Da questo numero di ritratti IA la cabina avvisa, 0 significa disattivato |
last_session_at, last_capture_at | Momento dell'ultima sessione e dell'ultimo scatto, di solito in UTC (termina con Z) |
error_count | Errori dall'avvio del programma |
last_error, last_error_at | L'ultimo errore come testo, con il suo momento |
cpu_used_percent | Carico del processore dall'ultimo messaggio, vuoto nel primo messaggio dopo l'avvio |
memory_used_percent | Memoria occupata |
disk_free_gb, disk_total_gb | Spazio libero e totale dell'unità con gli scatti |
outbox_pending | E-mail e caricamenti in attesa, ad esempio senza rete |
outbox_email_pending, outbox_cloud_pending | Di cui e-mail e caricamenti nel cloud |
camera_battery_percent | Batteria della fotocamera in percentuale, vuoto con alimentatore o se la fotocamera non la segnala |
camera_power_source | battery, ac (alimentatore) o vuoto |
printers | Una voce per stampante, vedi sotto |
buzzers | Una voce per buzzer wireless, vedi sotto |
Una voce in printers:
| Campo | Contenuto |
|---|---|
role | print (stampante foto) o strip (stampante per strisce) |
mode | single (una stampante) o pool (pool di stampanti, con in più strategy) |
name | Nome della stampante in Windows |
available | Se la stampante è pronta |
state | Stato, ad esempio ready, printing, paused, error, offline o unknown |
queue_length | Lavori in coda |
issues | Elenco dei problemi segnalati, vuoto se non ce ne sono |
media_remaining, media_capacity | Materiale residuo e capacità in fogli, vuoto se la stampante non li segnala |
total_prints | Stampe nell'intera vita della stampante, solo stampanti DNP |
Una voce in buzzers:
| Campo | Contenuto |
|---|---|
name | Nome del buzzer wireless |
connection | connected, sleeping, disconnected, never (mai connesso) o unavailable (Bluetooth non disponibile) |
battery_level | Batteria in tre livelli: 3 piena, 2 a metà, 1 scarica, 0 sconosciuta |
millivolt | Tensione della batteria in millivolt |
seconds_since_press | Secondi dall'ultima pressione, vuoto se non ancora premuto |
Verificare la firma#
Ogni messaggio è firmato con la chiave dell'area di amministrazione. Il destinatario ricalcola la firma e accetta il messaggio solo se le due coincidono:
- Leggere il contenuto così come arriva, come byte - senza prima interpretarlo come JSON e poi riscriverlo.
- Unire il timestamp di
X-RAWCaptureBooth-Timestamp, un punto e il contenuto. - Calcolare su questo un HMAC-SHA256 con la chiave, come stringa esadecimale, e anteporre
sha256=. - Confrontare con
X-RAWCaptureBooth-Signature, in tempo costante. - Rifiutare i messaggi il cui timestamp si discosta di più di cinque minuti dal proprio orologio. Così un messaggio intercettato non può essere reinviato in seguito.
« Genera nuova » nell'area di amministrazione sostituisce la chiave. Finché non è stata inserita presso il destinatario, questo rifiuta ogni messaggio.
Esempio: visualizzazione sul proprio sito#
Un sito web non può ricevere da solo un messaggio - serve un piccolo script sul server. Questo script PHP verifica la firma e salva in un file fotobox-status.json solo i valori destinati al pubblico:
<?php
// fotobox-webhook.php - da inserire come indirizzo nell'area di amministrazione
$secret = 'INSERIRE-QUI-LA-CHIAVE';
$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;
}
// Trasmettere solo ciò che va sul sito
$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);
Il sito legge il file e decide da sé se la cabina è raggiungibile:
<p id="fotobox">Cabina: caricamento …</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
? 'Cabina in funzione, ' + status.sessions_since_start + ' sessioni dall\'avvio'
: 'Cabina al momento non raggiungibile';
});
</script>
Esempio: destinatario in Node.js#
Senza pacchetti aggiuntivi, con il modulo integrato http:
const http = require('http');
const crypto = require('crypto');
const SECRET = 'INSERIRE-QUI-LA-CHIAVE';
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);
Riconoscere che una cabina non si fa più sentire#
Una cabina non può segnalare da sola di essere « offline » - senza connessione non arriva nemmeno alcun messaggio. Il destinatario memorizza quindi quando è arrivato l'ultimo messaggio. Se non ne arrivano per più di due intervalli (report_interval_seconds), la cabina non è raggiungibile. Due intervalli invece di uno assorbono un singolo messaggio in ritardo.
Risoluzione dei problemi#
- « Invia messaggio di prova » restituisce HTTP 401 o 403: Il destinatario rifiuta la firma. La chiave è corretta? Viene verificato il contenuto grezzo (vedi sopra)? L'orologio del server è giusto?
- HTTP 404: L'indirizzo è errato - controllare percorso e nome del file.
- « Reindirizzamento a … »: L'indirizzo reindirizza, spesso da
http://ahttps://o a un indirizzo conwww. La cabina non segue i reindirizzamenti; inserire direttamente l'indirizzo di destinazione indicato nel messaggio. - « Nessuna risposta entro 15 secondi »: Il destinatario non è raggiungibile o risponde troppo lentamente. Dovrebbe accettare il messaggio e rispondere subito, invece di elaborarlo prima.
- I messaggi arrivano meno spesso di quanto impostato: Dopo un errore la cabina attende più a lungo prima del tentativo successivo - prima 15 secondi, poi ogni volta il doppio, al massimo un quarto d'ora. Al primo successo torna a valere l'intervallo impostato. Quanti tentativi consecutivi sono falliti è indicato nell'area sotto « Test e stato ».