fix(notruf): ehrlicher Status, wenn niemand verständigt werden kann

Bisher meldete ein Notruf ohne nutzbaren Kanal trotzdem Erfolg
(status="alerted", grüner Haken), obwohl tatsächlich niemand
benachrichtigt wurde — gefährlich für Senioren.

- emergency.py: drei klare Endzustände alerted / no_recipients /
  delivery_failed; notify_readiness() prüft, ob ein Kanal wirklich
  senden würde (SMTP+Empfaenger bzw. Webhook). Mehrsprachige Hinweise
  fuer die Fehlfaelle (nennen Selbsthilfe 112). Antwort traegt jetzt
  notified + notify_ready; Warn-Log, wenn nichts zugestellt wurde.
- /api/emergency/status liefert notify_ready fuer die Vorab-Warnung.
- Frontend: gelbe Vorab-Warnung im SOS-Dialog, wenn kein Empfaenger
  eingerichtet ist; bei no_recipients/delivery_failed kein gruenes
  Erfolgssignal mehr, sondern roter Fehlerzustand (app.js v54).
- Tests an die neue Semantik angepasst.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-27 16:50:03 +02:00
commit 37b4c175d2
6 changed files with 126 additions and 18 deletions

View file

@ -2380,6 +2380,7 @@ async function runEmergency() {
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
if (data && data.notify_ready) sosNotifyReady = !!data.notify_ready.any;
if (data && data.status === "cooldown") {
// Server hat den (Doppel-)Alarm unterdrückt -> nur beruhigen, kein neuer Alarm.
enterCooldown(data.remaining_seconds || 0, data.minutes_ago || 0);
@ -2389,12 +2390,19 @@ async function runEmergency() {
const notice = (data && data.notice) ||
"ACHTUNG: Es ist noch keine Benachrichtigung eingebaut.";
addMessage("emergency", "🆘 " + notice);
const cd = (data && data.cooldown_seconds) || 0;
if (cd > 0) {
enterCooldown(cd, 0); // grün/pulsierend für die Sperre
if (data && data.status === "alerted") {
const cd = data.cooldown_seconds || 0;
if (cd > 0) {
enterCooldown(cd, 0); // grün/pulsierend für die Sperre
} else {
setSosState("done"); // Sperre aus -> nur kurzes Erfolgs-Feedback
setTimeout(() => { if (!sosCooldownActive) setSosState("idle"); }, 6000);
}
} else {
setSosState("done"); // Sperre aus -> nur kurzes Erfolgs-Feedback
setTimeout(() => { if (!sosCooldownActive) setSosState("idle"); }, 6000);
// no_recipients / delivery_failed: es ging NICHTS raus -> KEIN grünes Erfolgssignal,
// sondern roter Fehlerzustand; der Hinweis nennt die Selbsthilfe (112).
setSosState("error");
setTimeout(() => { if (!sosCooldownActive) setSosState("idle"); }, 8000);
}
} catch (e) {
addMessage("emergency", "🆘 Fehler beim Auslösen des Notrufs.");
@ -2403,9 +2411,17 @@ async function runEmergency() {
}
}
// Ist (serverseitig) überhaupt ein Benachrichtigungskanal nutzbar? null = noch
// unbekannt; erst bei sicherem `false` zeigen wir die Vorab-Warnung im Dialog.
let sosNotifyReady = null;
// Großer eigener Bestätigungsdialog statt native confirm() — seniorentauglich.
const sosConfirm = $("#sos-confirm");
function openSosConfirm() { if (sosConfirm) sosConfirm.classList.remove("hidden"); }
function openSosConfirm() {
const warn = $("#sos-confirm-warn");
if (warn) warn.classList.toggle("hidden", sosNotifyReady !== false);
if (sosConfirm) sosConfirm.classList.remove("hidden");
}
function closeSosConfirm() { if (sosConfirm) sosConfirm.classList.add("hidden"); }
// Beruhigender grüner Hinweis, wenn während der Sperre erneut gedrückt wird.
@ -2428,6 +2444,7 @@ async function initSosCooldown() {
const res = await fetch("/api/emergency/status", { headers: { Accept: "application/json" } });
if (!res.ok) return;
const s = await res.json();
if (s && s.notify_ready) sosNotifyReady = !!s.notify_ready.any;
if (s && s.active) enterCooldown(s.remaining_seconds || 0, s.minutes_ago || 0);
} catch (e) { /* ignore */ }
}