From 33928fca7b72068bba741f8491ce31cd16d5b7f2 Mon Sep 17 00:00:00 2001 From: ppfeiffer Date: Thu, 19 Feb 2026 16:09:55 +0100 Subject: [PATCH] v0.8.3: NINA Gebietsanzeige + AGS-Ortsname + Sachsen-Combobox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Warnmeldungen zeigen jetzt das Herkunftsgebiet (AGS-Regionsname) in der Weboberfläche (neue Spalte "Gebiet") und im Mesh-Nachrichtentext (z.B. "[NINA] Schwerwiegend: Sturmböen (Dresden, Stadt)") - AGS-Code-Tabelle zeigt lesbaren Ortsnamen als zweite Spalte - Datalist mit allen sächsischen AGS-Codes bei der Eingabe Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 14 ++++++++++++++ config.yaml | 2 +- meshbot/nina.py | 39 ++++++++++++++++++++++++++++++--------- static/js/nina.js | 40 ++++++++++++++++++++++++++++++++++++++-- static/nina.html | 10 +++++++--- 5 files changed, 90 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a69ae3d..a4c78d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.8.3] - 2026-02-19 + +### Added +- **NINA Gebietsanzeige**: Warnmeldungen zeigen jetzt das Herkunftsgebiet (AGS-Regionsname) + sowohl in der Weboberfläche (neue Spalte "Gebiet" in der Alerts-Tabelle) als auch im + Mesh-Nachrichtentext (z.B. `[NINA] Schwerwiegend: Sturmböen (Dresden, Stadt)`). +- **AGS-Ortsname in der Konfigurationstabelle**: Die AGS-Code-Tabelle zeigt jetzt den + lesbaren Ortsnamen je Code als zweite Spalte an. +- **Sächsische AGS-Combobox**: Bei der Eingabe neuer AGS-Codes schlägt eine Datalist + alle sächsischen Landkreise und kreisfreien Städte vor (Name + Code). + +### Fixed +- **colspan**: Leere Zeile in der Alerts-Tabelle auf 6 Spalten aktualisiert. + ## [0.8.2] - 2026-02-19 ### Fixed diff --git a/config.yaml b/config.yaml index c328f2c..984203b 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,4 @@ -version: "0.8.2" +version: "0.8.3" bot: name: "MeshDD-Bot" diff --git a/meshbot/nina.py b/meshbot/nina.py index 9939a4b..75b5524 100644 --- a/meshbot/nina.py +++ b/meshbot/nina.py @@ -54,6 +54,23 @@ ID_NORMALIZATIONS = [ ("mow.", "mowas."), ] +# Lesbarer Name je sächsischem AGS-Code (12-stellig) +AGS_LABELS: dict[str, str] = { + "145110000000": "Chemnitz, Stadt", + "145210000000": "Erzgebirgskreis", + "145220000000": "Mittelsachsen", + "145230000000": "Vogtlandkreis", + "145240000000": "Zwickau", + "146120000000": "Dresden, Stadt", + "146250000000": "Bautzen", + "146260000000": "Görlitz", + "146270000000": "Meißen", + "146280000000": "Sächsische Schweiz-Osterzgebirge", + "147130000000": "Leipzig, Stadt", + "147290000000": "Landkreis Leipzig", + "147300000000": "Nordsachsen", +} + DEFAULT_CONFIG = { "enabled": False, "send_to_mesh": True, @@ -283,12 +300,12 @@ class NinaBot: for item in items: try: - await self._process_dashboard_item(item, min_level, channel, sources) + await self._process_dashboard_item(item, min_level, channel, sources, ags) except Exception: logger.exception("NINA dashboard: error processing %s", item.get("id")) async def _process_dashboard_item( - self, item: dict, min_level: int, channel: int, sources: dict + self, item: dict, min_level: int, channel: int, sources: dict, ags: str = "" ): identifier = item.get("id", "") if not identifier: @@ -316,10 +333,11 @@ class NinaBot: headline = data.get("headline", "Warnung") description = data.get("description", "") + area = AGS_LABELS.get(ags.ljust(12, "0"), ags) - text = self._format_alert(msg_type, severity, headline, description) - logger.info("NINA dashboard alert: %s (id=%s)", headline, identifier) - await self._send(identifier, severity, msg_type, headline, sent, text, channel) + text = self._format_alert(msg_type, severity, headline, description, area) + logger.info("NINA dashboard alert: %s (id=%s, area=%s)", headline, identifier, area) + await self._send(identifier, severity, msg_type, headline, sent, text, channel, area) # ── mapData endpoint ───────────────────────────────────────────────────── @@ -373,16 +391,17 @@ class NinaBot: text = self._format_alert(msg_type, severity, headline, "") logger.info("NINA mapData alert: %s (id=%s)", headline, identifier) - await self._send(identifier, severity, msg_type, headline, sent, text, channel) + await self._send(identifier, severity, msg_type, headline, sent, text, channel, "") # ── Shared helpers ─────────────────────────────────────────────────────── @staticmethod - def _format_alert(msg_type: str, severity: str, headline: str, description: str) -> str: + def _format_alert(msg_type: str, severity: str, headline: str, description: str, area: str = "") -> str: + area_suffix = f" ({area})" if area else "" if msg_type == "Cancel": - return f"[NINA] Aufgehoben: {headline}" + return f"[NINA] Aufgehoben: {headline}{area_suffix}" sev_text = SEVERITY_LABELS.get(severity, severity) - text = f"[NINA] {sev_text}: {headline}" + text = f"[NINA] {sev_text}: {headline}{area_suffix}" if description: short = description.strip()[:120] if len(description.strip()) > 120: @@ -399,6 +418,7 @@ class NinaBot: sent: str, text: str, channel: int, + area: str = "", ): dedup_key = self._normalise_id(identifier) @@ -427,5 +447,6 @@ class NinaBot: "msgType": msg_type, "headline": headline, "sent": sent, + "area": area, "monitor_only": not self.config.get("send_to_mesh", True), }) diff --git a/static/js/nina.js b/static/js/nina.js index 856646f..ae54c89 100644 --- a/static/js/nina.js +++ b/static/js/nina.js @@ -5,17 +5,50 @@ const alerts = []; initPage({ onAuth: (user) => { currentUser = user; } }); +// ── Sachsen AGS-Lookup ──────────────────────────────────────────────────────── + +const AGS_NAMES = { + '145110000000': 'Chemnitz, Stadt', + '145210000000': 'Erzgebirgskreis', + '145220000000': 'Mittelsachsen', + '145230000000': 'Vogtlandkreis', + '145240000000': 'Zwickau', + '146120000000': 'Dresden, Stadt', + '146250000000': 'Bautzen', + '146260000000': 'Görlitz', + '146270000000': 'Meißen', + '146280000000': 'Sächsische Schweiz-Osterzgebirge', + '147130000000': 'Leipzig, Stadt', + '147290000000': 'Landkreis Leipzig', + '147300000000': 'Nordsachsen', +}; + +function agsName(code) { + // Normalisiere auf 12 Stellen für den Lookup + const padded = code.padEnd(12, '0'); + return AGS_NAMES[padded] || AGS_NAMES[code] || '–'; +} + +function fillDatalist() { + const dl = document.getElementById('agsSachsenList'); + if (!dl) return; + dl.innerHTML = Object.entries(AGS_NAMES) + .map(([code, name]) => ``) + .join(''); +} + // ── AGS code list ──────────────────────────────────────────────────────────── function renderAgsList() { const tbody = document.getElementById('agsList'); if (agsCodes.length === 0) { - tbody.innerHTML = 'Keine AGS-Codes konfiguriert.'; + tbody.innerHTML = 'Keine AGS-Codes konfiguriert.'; return; } tbody.innerHTML = agsCodes.map((code, idx) => ` ${escapeHtml(code)} + ${escapeHtml(agsName(code))}