MeshDD-Bot/static/nina.html
ppfeiffer 0ca0ffb0d1 feat: NINA BBK Warn-App Integration (v0.8.0)
Neue NINA-Integration: Automatisches Polling der BBK-Warn-API
(warnung.bund.de/api31) und Weiterleitung von Warnmeldungen ins
Meshtastic-Netz. Separate Admin-Konfigurationsseite (/nina) analog
zum Scheduler.

- meshbot/nina.py: NinaBot – Polling, De-Duplikation, Schweregrad-
  und Quellen-Filterung, WebSocket-Broadcast (nina_alert)
- nina.yaml + conf/nina.yaml: Hot-reload-faehige Konfiguration
- static/nina.html + static/js/nina.js: Konfigurationsseite mit
  AGS-Code-Verwaltung, Quellen-Auswahl und Live-Alerts-Tabelle
- webserver.py: GET/PUT /api/nina/config + GET /nina (Admin-only)
- main.py: NinaBot initialisieren, watch/start/stop im Lifecycle
- app.js: NINA-Sidebar-Eintrag (Admin-only, shield-exclamation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 11:21:01 +01:00

188 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MeshDD-Bot NINA</title>
<link href="https://cdn.jsdelivr.net/npm/@tabler/core@1.4.0/dist/css/tabler.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body class="antialiased">
<!-- Top Navbar -->
<nav class="top-navbar d-flex align-items-center px-3">
<button class="btn btn-link text-body p-0 me-2 d-lg-none" id="sidebarToggle">
<i class="bi bi-list fs-5"></i>
</button>
<span class="fw-bold me-auto">
<i class="bi bi-broadcast-pin text-info me-1"></i>MeshDD-Bot
<small class="text-body-secondary fw-normal" id="versionLabel"></small>
</span>
<div class="d-flex align-items-center gap-2">
<span id="userMenu" class="d-none">
<small class="text-body-secondary me-1"><i class="bi bi-person-fill me-1"></i><span id="userName"></span></small>
<a href="/auth/logout" class="btn btn-sm btn-outline-secondary py-0 px-1" title="Abmelden">
<i class="bi bi-box-arrow-right" style="font-size:.75rem"></i>
</a>
</span>
<a href="/login" id="loginBtn" class="btn btn-sm btn-outline-info py-0 px-1 d-none">
<i class="bi bi-person" style="font-size:.75rem"></i> Login
</a>
<button class="btn btn-sm btn-outline-secondary py-0 px-1" id="themeToggle" title="Theme wechseln">
<i class="bi bi-sun-fill" id="themeIcon" style="font-size:.75rem"></i>
</button>
</div>
</nav>
<aside class="sidebar" id="sidebar"></aside>
<div class="sidebar-backdrop" id="sidebarBackdrop"></div>
<!-- Content -->
<main class="content-wrapper">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="mb-0"><i class="bi bi-shield-exclamation me-1 text-warning"></i>NINA Warnmeldungen</h6>
<div class="d-flex gap-2 align-items-center">
<span class="badge bg-secondary" id="statusBadge">Lade...</span>
</div>
</div>
<div class="row g-3">
<!-- Settings Card -->
<div class="col-12 col-xl-5">
<div class="card card-outline card-warning h-100">
<div class="card-header">
<h6 class="card-title mb-0"><i class="bi bi-gear me-1"></i>Einstellungen</h6>
</div>
<div class="card-body">
<form id="ninaForm">
<!-- Enable toggle -->
<div class="mb-3 d-flex align-items-center gap-3">
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" id="ninaEnabled" role="switch">
<label class="form-check-label fw-semibold" for="ninaEnabled">Aktiviert</label>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-7">
<label for="pollInterval" class="form-label">Abfrageintervall (Sek.)</label>
<input type="number" class="form-control form-control-sm" id="pollInterval"
min="60" max="3600" step="60" value="300">
<div class="form-text">Min. 60 Sekunden</div>
</div>
<div class="col-5">
<label for="ninaChannel" class="form-label">Kanal</label>
<input type="number" class="form-control form-control-sm" id="ninaChannel"
min="0" max="7" value="0">
</div>
</div>
<div class="mb-3">
<label for="minSeverity" class="form-label">Mindest-Schweregrad</label>
<select class="form-select form-select-sm" id="minSeverity">
<option value="Minor">Gering (Minor)</option>
<option value="Moderate">Mäßig (Moderate)</option>
<option value="Severe" selected>Schwerwiegend (Severe)</option>
<option value="Extreme">Extrem (Extreme)</option>
</select>
</div>
<!-- AGS Codes -->
<div class="mb-3">
<label class="form-label">AGS-Codes (Amtliche Gemeindeschlüssel)</label>
<div id="agsList" class="mb-2"></div>
<div class="input-group input-group-sm">
<input type="text" class="form-control" id="agsInput"
placeholder="z.B. 091620000000 (München)" maxlength="12">
<button class="btn btn-outline-secondary" type="button" id="btnAddAgs">
<i class="bi bi-plus-lg"></i>
</button>
</div>
<div class="form-text">
8- oder 12-stelliger AGS-Code des Landkreises/der kreisfreien Stadt.
<a href="https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/_inhalt.html"
target="_blank" rel="noopener" class="text-info">AGS-Verzeichnis</a>
</div>
</div>
<!-- Sources -->
<div class="mb-3">
<label class="form-label">Quellen</label>
<div class="row g-2">
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcKatwarn" checked>
<label class="form-check-label" for="srcKatwarn">Katwarn</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcBiwapp" checked>
<label class="form-check-label" for="srcBiwapp">BIWAPP</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcMowas" checked>
<label class="form-check-label" for="srcMowas">MoWaS</label>
</div>
</div>
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcDwd" checked>
<label class="form-check-label" for="srcDwd">DWD (Wetter)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcLhp" checked>
<label class="form-check-label" for="srcLhp">LHP (Hochwasser)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="srcPolice">
<label class="form-check-label" for="srcPolice">Polizei</label>
</div>
</div>
</div>
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-warning btn-sm" id="btnSaveNina">
<i class="bi bi-floppy me-1"></i>Speichern
</button>
<span id="saveStatus" class="d-none align-self-center small"></span>
</div>
</form>
</div>
</div>
</div>
<!-- Alerts Card -->
<div class="col-12 col-xl-7">
<div class="card card-outline card-danger h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="card-title mb-0"><i class="bi bi-bell-fill me-1 text-danger"></i>Letzte Warnmeldungen</h6>
<small class="text-body-secondary">Live via WebSocket</small>
</div>
<div class="card-body p-0 table-responsive">
<table class="table table-hover table-sm table-striped mb-0 align-middle">
<thead class="table-dark">
<tr>
<th style="width:90px">Schweregrad</th>
<th>Meldung</th>
<th style="width:110px">Typ</th>
<th style="width:130px">Zeitstempel</th>
</tr>
</thead>
<tbody id="alertsTable">
<tr><td colspan="4" class="text-center text-body-secondary py-3">Keine Meldungen NINA aktivieren und AGS-Codes konfigurieren.</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
<footer id="pageFooter" class="page-footer"></footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/static/js/app.js"></script>
<script src="/static/js/nina.js"></script>
</body>
</html>