v0.8.3: NINA Gebietsanzeige + AGS-Ortsname + Sachsen-Combobox
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
ff9ecadd75
commit
33928fca7b
14
CHANGELOG.md
14
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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
version: "0.8.2"
|
||||
version: "0.8.3"
|
||||
|
||||
bot:
|
||||
name: "MeshDD-Bot"
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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]) => `<option value="${escapeHtml(code)}">${escapeHtml(name)}</option>`)
|
||||
.join('');
|
||||
}
|
||||
|
||||
// ── AGS code list ────────────────────────────────────────────────────────────
|
||||
|
||||
function renderAgsList() {
|
||||
const tbody = document.getElementById('agsList');
|
||||
if (agsCodes.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="2" class="text-center text-body-secondary py-2"><small>Keine AGS-Codes konfiguriert.</small></td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="3" class="text-center text-body-secondary py-2"><small>Keine AGS-Codes konfiguriert.</small></td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = agsCodes.map((code, idx) =>
|
||||
`<tr>
|
||||
<td><code>${escapeHtml(code)}</code></td>
|
||||
<td class="text-body-secondary">${escapeHtml(agsName(code))}</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm py-0 px-1"
|
||||
onclick="removeAgs(${idx})" title="Entfernen">
|
||||
|
|
@ -170,7 +203,7 @@ const SEV_LABEL = {
|
|||
function renderAlerts() {
|
||||
const tbody = document.getElementById('alertsTable');
|
||||
if (alerts.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="5" class="text-center text-body-secondary py-3">Keine Meldungen empfangen.</td></tr>';
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-body-secondary py-3">Keine Meldungen empfangen.</td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = alerts.map(a => {
|
||||
|
|
@ -182,9 +215,11 @@ function renderAlerts() {
|
|||
const meshIcon = a.monitor_only
|
||||
? '<i class="bi bi-eye text-warning" title="Nur Weboberfläche"></i>'
|
||||
: '<i class="bi bi-broadcast text-success" title="Ins Mesh gesendet"></i>';
|
||||
const area = a.area ? escapeHtml(a.area) : '<span class="text-body-secondary">–</span>';
|
||||
return `<tr>
|
||||
<td><span class="badge bg-${bgCls} ${txCls}">${escapeHtml(sevLabel)}</span></td>
|
||||
<td>${escapeHtml(a.headline)}</td>
|
||||
<td><small class="text-body-secondary">${area}</small></td>
|
||||
<td><small class="text-body-secondary">${escapeHtml(a.id?.split('.')[0] ?? '–')}</small></td>
|
||||
<td class="text-center">${meshIcon}</td>
|
||||
<td><small class="text-body-secondary">${ts}</small></td>
|
||||
|
|
@ -217,5 +252,6 @@ function connectWebSocket() {
|
|||
|
||||
// ── Init ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
fillDatalist();
|
||||
loadConfig();
|
||||
connectWebSocket();
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@
|
|||
<table class="table table-sm table-hover table-striped mb-0 align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>AGS-Code</th>
|
||||
<th style="width:130px">AGS-Code</th>
|
||||
<th>Ort / Region</th>
|
||||
<th style="width:42px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -116,11 +117,13 @@
|
|||
</div>
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control" id="agsInput"
|
||||
placeholder="z.B. 091620000000 (München)" maxlength="12">
|
||||
list="agsSachsenList"
|
||||
placeholder="Code oder Ort eingeben…" maxlength="50">
|
||||
<button class="btn btn-outline-secondary" type="button" id="btnAddAgs">
|
||||
<i class="bi bi-plus-lg"></i> Hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
<datalist id="agsSachsenList"></datalist>
|
||||
<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"
|
||||
|
|
@ -187,13 +190,14 @@
|
|||
<tr>
|
||||
<th style="width:90px">Schweregrad</th>
|
||||
<th>Meldung</th>
|
||||
<th style="width:140px">Gebiet</th>
|
||||
<th style="width:110px">Typ</th>
|
||||
<th style="width:60px" class="text-center">Mesh</th>
|
||||
<th style="width:130px">Zeitstempel</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="alertsTable">
|
||||
<tr><td colspan="5" class="text-center text-body-secondary py-3">Keine Meldungen – NINA aktivieren und AGS-Codes konfigurieren.</td></tr>
|
||||
<tr><td colspan="6" class="text-center text-body-secondary py-3">Keine Meldungen – NINA aktivieren und AGS-Codes konfigurieren.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue