Skill: wolf128058/nina-warnings (version 1.1.0)

--- name: NINA Warnings slug: nina-warnings description: Read-only access to current German public warnings via the official NINA / warnung.bund.de API. Use when asked about NINA alerts, Bevölkerungswarnungen, civil protection alerts, weather warnings, flood warnings, Katwarn-style warning checks, or whether there are active warnings for a German city, district, or ARS region code. changelog: v1.1.0 adds district-level ARS normalization, optional warning detail and GeoJSON enrichment, and nationwide source-feed access for mowas, dwd, katwarn, biwapp, lhp, and police. metadata: {"clawdbot":{"emoji":"W","requires":{"bins":["bash","curl","jq"]},"os":["linux","darwin"]}} --- # NINA Warnings Read current public warnings from the official German NINA API. Lese aktuelle oeffentliche Warnmeldungen ueber die offizielle deutsche NINA-API. Repository: [openclaw-skill-nina-warnings](https://github.com/wolf128058/openclaw-skill-nina-warnings) ## Scope / Umfang - Read-only only. Do not modify systems, accounts, settings, or local state. - Nutze ausschliesslich lesende Zugriffe. Keine Schreiboperationen, keine Konfigurationsaenderungen. - The published skill is intentionally limited to public warning lookup via `warnung.bund.de`. - Dieser Skill ist bewusst auf die Abfrage oeffentlicher Warnungen ueber `warnung.bund.de` begrenzt. - No secrets, auth tokens, or private endpoints are required. - Es sind keine Zugangsdaten, Tokens oder privaten Endpunkte noetig. ## Runtime - Requires `bash`, `curl`, and `jq`. - Benoetigt `bash`, `curl` und `jq`. - Public API base URL: `https://warnung.bund.de/api31` - Oeffentliche API-Basis-URL: `https://warnung.bund.de/api31` ## Default Behavior / Standardverhalten - Accept either a German place/district name or a 12-digit ARS code. - Akzeptiere entweder einen deutschen Orts-/Kreisnamen oder einen 12-stelligen ARS-Code. - Normalize ARS values to district level before dashboard requests. - Normalisiere ARS-Werte vor Dashboard-Abrufen auf Kreisebene. - Prefer the helper script instead of handcrafted `curl`. - Bevorzuge das Helper-Skript statt manuellem `curl`. - If a place name resolves to multiple matches, use the best local cache match and mention ambiguity briefly if needed. - Wenn ein Ortsname mehrere Treffer hat, nutze den besten lokalen Cache-Treffer und erwaehne Mehrdeutigkeit kurz, falls noetig. ## Quick Use / Schnellstart ```bash ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "Berlin" --json ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --details ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --geojson --json ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh --source dwd ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Berlin" ``` To find ARS codes for other cities or districts, use: Um ARS-Codes fuer weitere Staedte oder Landkreise zu finden, nutze: ```bash ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --ars-only ~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --first ``` ## Agent Workflow / Agenten-Workflow 1. Resolve the user location to an ARS code with `scripts/nina-lookup-ars.sh` unless the user already provided a 12-digit ARS. 2. Fetch current warnings with `scripts/nina-status.sh`. 3. Add `--details` when the user needs description or instructions. 4. Add `--geojson` when geometry or machine-readable area data is needed. 5. Use `--source <name>` for nationwide feeds like `mowas`, `dwd`, `katwarn`, `biwapp`, `lhp`, or `police`. 6. Summarize the result clearly: active warnings, sender, severity, effective time, expiry if present. 7. If there are no warnings, say so plainly. ## Output Guidance / Ausgabehinweise - Prefer concise summaries over raw JSON unless machine-readable output was requested. - Bevorzuge knappe Zusammenfassungen statt rohem JSON, ausser explizit angefordert. - Mention when an ARS was normalized to district level. - Erwaehne, wenn ein ARS auf Kreisebene normalisiert wurde. - Mention the resolved region when the user asked for a city or district. - Nenne die aufgeloeste Region, wenn der User nach einer Stadt oder einem Landkreis gefragt hat. - If lookup is ambiguous, say which match was used. - Wenn die Suche mehrdeutig ist, sage kurz, welcher Treffer verwendet wurde. - Do not overstate certainty beyond the API result. - Stelle keine groessere Sicherheit dar als die API hergibt. ## Bundled Files / Enthaltene Dateien - `scripts/nina-status.sh` Fetches and formats current warnings for an ARS code or location query. - `scripts/nina-lookup-ars.sh` Resolves German place and district names to ARS candidates using the local cache. - `references/ars-codes.json` Empty cache placeholder. Populate it during setup with installation-specific ARS mappings. ## Notes / Hinweise - ARS = `Amtlicher Regionalschluessel`, 12 digits. - The dashboard endpoint returns an array of current warning objects or `[]`. - `references/ars-codes.json` contains the 16 German state capitals as bundled starter entries. - Add more cities or districts to `references/ars-codes.json` if you want local lookup beyond the bundled defaults. - Ergaenze `references/ars-codes.json` um weitere Staedte oder Landkreise, wenn du mehr als die mitgelieferten Standard-Eintraege lokal aufloesen willst. - This skill is suitable for a conservative ClawHub release because it is public, read-only, and has no credential handling.

path: README.md
content: # NINA Warnings

## Deutsch

`nina-warnings` ist ein schlanker, read-only OpenClaw-Skill fuer aktuelle deutsche Bevoelkerungswarnungen ueber die offizielle NINA-API des BBK unter `warnung.bund.de`.

Repository: [openclaw-skill-nina-warnings](https://github.com/wolf128058/openclaw-skill-nina-warnings)

### Changelog

- `1.1.0`: Normalisiert ARS-Eingaben auf die vom Dashboard erwartete Kreisebene, kann Warnungsdetails und GeoJSON pro Meldung nachladen und unterstuetzt bundesweite Quellenfeeds wie `mowas`, `dwd`, `katwarn`, `biwapp`, `lhp` und `police`.
- `1.0.1`: Unterstuetzt verschachtelte Warnungsfelder aus dem aktuellen NINA-Dashboard-Format, zeigt Detail-Links zu einzelnen Meldungen an und haelt die lokale Ortsauflosung mit dem mitgelieferten Cache-Format kompatibel.

### Zweck

- Aktive Warnungen fuer einen Ort, Landkreis oder einen 12-stelligen ARS-Code abfragen
- Ortsnamen ueber einen beim Setup befuellten lokalen ARS-Cache aufloesen
- Warnungsdetails, GeoJSON und bundesweite Quellenfeeds abrufen
- Ergebnisse menschenlesbar oder als JSON ausgeben

### Voraussetzungen

- `bash`
- `curl`
- `jq`

### Nutzung

```bash
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "Berlin"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "Berlin" --json
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --details
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --geojson --json
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh --source mowas
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Berlin"
```

- `--details` laedt pro Warnung den offiziellen Detail-Endpunkt `/warnings/{identifier}.json` nach
- `--geojson` laedt pro Warnung `/warnings/{identifier}.geojson` nach
- `--source <name>` nutzt statt des regionalen Dashboards einen bundesweiten Feed wie `mowas`, `dwd`, `katwarn`, `biwapp`, `lhp` oder `police`
- ARS-Eingaben werden vor dem Dashboard-Abruf automatisch auf Kreisebene normalisiert

### Eigene ARS-Codes finden

Um den ARS-Code fuer deine Stadt oder deinen Landkreis zu finden, nutze das Lookup-Skript:

```bash
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Berlin"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --ars-only
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --first
```

- `--ars-only` gibt nur die passenden ARS-Codes aus
- `--first` nimmt nur den ersten Treffer
- Die Suche arbeitet ueber den lokalen Cache in `references/ars-codes.json`
- Wenn deine Stadt dort noch nicht enthalten ist, kannst du den Cache um weitere Eintraege erweitern

### Struktur

- `SKILL.md`: agentenorientierte Skill-Anleitung fuer OpenClaw
- `scripts/nina-status.sh`: Warnungen fuer Ort oder ARS abrufen
- `scripts/nina-lookup-ars.sh`: ARS-Codes lokal aufloesen
- `references/ars-codes.json`: mitgelieferter Beispiel-Cache mit den 16 Landeshauptstaedten; kann lokal erweitert werden

## English

`nina-warnings` is a small, read-only OpenClaw skill for current German public warning messages via the official BBK NINA API at `warnung.bund.de`.
The package is intentionally prepared for a conservative ClawHub release: no write operations, no secrets, no local persistence, and only public read access.

Repository: [openclaw-skill-nina-warnings](https://github.com/wolf128058/openclaw-skill-nina-warnings)

### Changelog

- `1.1.0`: Normalizes ARS inputs to the district-level format expected by the dashboard, can enrich warnings with detail and GeoJSON endpoints, and adds nationwide source feeds like `mowas`, `dwd`, `katwarn`, `biwapp`, `lhp`, and `police`.
- `1.0.1`: Supports nested warning fields from the current NINA dashboard format, adds per-warning detail links, and keeps local place lookup compatible with the bundled cache format.

### Purpose

- Check active warnings for a city, district, or 12-digit ARS code
- Resolve place names through a local ARS cache populated during setup
- Fetch warning details, GeoJSON, and nationwide source feeds
- Return either human-readable summaries or raw JSON

### Why it fits `Benign`

- Read-only access to a public API
- No control over devices, accounts, or infrastructure
- No local system modification
- No credential or private endpoint handling

### Requirements

- `bash`
- `curl`
- `jq`

### Usage

```bash
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "Berlin"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "Berlin" --json
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --details
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh "110000000000" --geojson --json
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-status.sh --source mowas
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Berlin"
```

- `--details` enriches each warning via `/warnings/{identifier}.json`
- `--geojson` enriches each warning via `/warnings/{identifier}.geojson`
- `--source <name>` uses a nationwide source feed such as `mowas`, `dwd`, `katwarn`, `biwapp`, `lhp`, or `police`
- ARS inputs are automatically normalized to district level before dashboard requests

### Finding ARS codes for your city

Use the lookup helper to find the ARS code for your city or district:

```bash
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Berlin"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg"
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --ars-only
~/.openclaw/workspace/skills/nina-warnings/scripts/nina-lookup-ars.sh "Hamburg" --first
```

- `--ars-only` prints only matching ARS codes
- `--first` returns only the first match
- The lookup uses the local cache in `references/ars-codes.json`
- If your city is missing there, extend the cache with additional entries

### Layout

- `SKILL.md`: agent-facing skill instructions for OpenClaw / ClawHub
- `scripts/nina-status.sh`: fetch warnings for a place or ARS
- `scripts/nina-lookup-ars.sh`: resolve ARS candidates locally
- `references/ars-codes.json`: bundled sample cache with the 16 state capitals; extend locally as needed


path: references/ars-codes.json
content: name: Berlin
ars: 110000000000
state: Berlin

name: München
ars: 091620000000
state: Bayern

name: Stuttgart
ars: 081110000000
state: Baden-Württemberg

name: Düsseldorf
ars: 051110000000
state: Nordrhein-Westfalen

name: Wiesbaden
ars: 064140000000
state: Hessen

name: Hannover
ars: 032410000000
state: Niedersachsen

name: Dresden
ars: 146120000000
state: Sachsen

name: Mainz
ars: 073150000000
state: Rheinland-Pfalz

name: Kiel
ars: 010020000000
state: Schleswig-Holstein

name: Saarbrücken
ars: 100410000000
state: Saarland

name: Potsdam
ars: 120540000000
state: Brandenburg

name: Schwerin
ars: 130030000000
state: Mecklenburg-Vorpommern

name: Erfurt
ars: 160630000000
state: Thüringen

name: Magdeburg
ars: 150030000000
state: Sachsen-Anhalt

name: Bremen
ars: 040110000000
state: Bremen

name: Hamburg
ars: 020000000000
state: Hamburg

path: scripts/nina-lookup-ars.sh
content: #!/bin/bash
# Lookup ARS code candidates for a place or district name.
# Usage: nina-lookup-ars.sh <city-or-district-name> [--ars-only] [--first]

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ARS_CACHE="$SCRIPT_DIR/../references/ars-codes.json"

QUERY=""
ARS_ONLY=false
FIRST_ONLY=false
PLAIN=false

while [[ $# -gt 0 ]]; do
    case "$1" in
        --ars-only)
            ARS_ONLY=true
            shift
            ;;
        --first)
            FIRST_ONLY=true
            shift
            ;;
        --plain)
            PLAIN=true
            shift
            ;;
        -*)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
        *)
            if [[ -z "$QUERY" ]]; then
                QUERY="$1"
            else
                QUERY="$QUERY $1"
            fi
            shift
            ;;
    esac
done

if [[ -z "$QUERY" ]]; then
    echo "Usage: $0 <city-or-district-name> [--ars-only] [--first] [--plain]" >&2
    echo "  Example: $0 Berlin" >&2
    echo "  Example: $0 Hamburg" >&2
    echo "  --ars-only: Print only matching ARS code(s)" >&2
    echo "  --first: Return only the first match" >&2
    echo "  --plain: Print raw 'name|ars|state' rows for scripting" >&2
    exit 1
fi

# Query local ARS cache (flat array format)
QUERY_LOWER=$(echo "$QUERY" | tr '[:upper:]' '[:lower:]')

result=$(jq -r --arg q "$QUERY_LOWER" '
    .[] | 
    select((.name | ascii_downcase) | contains($q)) |
    "\(.name) | \(.ars) | \(.state // .bundesland // "")"
' "$ARS_CACHE" 2>/dev/null)

if [[ -z "$result" ]]; then
    echo "❌ Kein ARS-Code gefunden für: $QUERY"
    echo ""
    echo "Tipp: Nutze den ARS-Code direkt (z.B. 110000000000 fuer Berlin)"
    echo ""
    echo "Häufige ARS-Codes:"
    jq -r '.[] | "  \(.name): \(.ars)"' "$ARS_CACHE" | head -10
    exit 1
fi

if [[ "$FIRST_ONLY" == "true" ]]; then
    result=$(echo "$result" | head -1)
else
    result=$(echo "$result" | head -5)
fi

if [[ "$ARS_ONLY" == "true" ]]; then
    echo "$result" | while IFS='|' read -r _ ars _; do
        echo "$ars" | xargs
    done
    exit 0
fi

if [[ "$PLAIN" == "true" ]]; then
    echo "$result" | sed 's/ | /|/g'
    exit 0
fi

echo "📍 ARS-Codes für '$QUERY':"
echo ""
echo "$result" | while IFS='|' read -r name ars bundesland; do
    name=$(echo "$name" | xargs)
    ars=$(echo "$ars" | xargs)
    bundesland=$(echo "$bundesland" | xargs)
    echo "  $name"
    echo "    ARS: $ars"
    echo "    Bundesland: $bundesland"
    echo ""
done


path: scripts/nina-status.sh
content: #!/bin/bash
# Fetch current NINA warnings for an ARS code, location, or source feed.
# Usage: nina-status.sh <ARS-code-or-location> [options]

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
API_BASE="https://warnung.bund.de/api31"
EVENT_CODES_URL="$API_BASE/appdata/gsb/eventCodes/eventCodes.json"
LOOKUP_SCRIPT="$SCRIPT_DIR/nina-lookup-ars.sh"

INPUT=""
JSON_OUTPUT=false
DETAILS=false
GEOJSON=false
SOURCE=""
ARS=""
REGION_LABEL=""
NORMALIZED_ARS=""

usage() {
    echo "Usage: $0 <ARS-code-or-location> [options]" >&2
    echo "Usage: $0 --source <mowas|dwd|katwarn|biwapp|lhp|police> [options]" >&2
    echo "" >&2
    echo "  ARS-code: Amtlicher Regionalschluessel (z.B. 110000000000 fuer Berlin)" >&2
    echo "  location: city or district name from the local cache (z.B. Berlin)" >&2
    echo "  --json: Output raw JSON for the selected feed or enriched warnings" >&2
    echo "  --details: Fetch full warning details via /warnings/{identifier}.json" >&2
    echo "  --geojson: Fetch /warnings/{identifier}.geojson for each warning" >&2
    echo "  --source: Use a nationwide source feed instead of the regional dashboard" >&2
    exit 1
}

for bin in curl jq; do
    if ! command -v "$bin" >/dev/null 2>&1; then
        echo "Error: required command not found: $bin" >&2
        exit 1
    fi
done

while [[ $# -gt 0 ]]; do
    case "$1" in
        --json)
            JSON_OUTPUT=true
            shift
            ;;
        --details)
            DETAILS=true
            shift
            ;;
        --geojson)
            GEOJSON=true
            shift
            ;;
        --source)
            if [[ $# -lt 2 ]]; then
                echo "Error: --source requires a value" >&2
                usage
            fi
            SOURCE="$2"
            shift 2
            ;;
        -h|--help)
            usage
            ;;
        -*)
            echo "Unknown option: $1" >&2
            usage
            ;;
        *)
            if [[ -z "$INPUT" ]]; then
                INPUT="$1"
            else
                echo "Error: unexpected extra argument: $1" >&2
                usage
            fi
            shift
            ;;
    esac
done

normalize_ars() {
    local raw="$1"
    if [[ ! "$raw" =~ ^[0-9]{12}$ ]]; then
        echo "$raw"
        return
    fi
    echo "${raw:0:5}0000000"
}

fetch_json() {
    local url="$1"
    curl --fail --silent --show-error --location --max-time 20 "$url"
}

event_label() {
    local code="$1"
    if [[ -z "$code" ]]; then
        echo "Unbekannter Typ"
        return
    fi
    echo "$EVENT_CODES_JSON" | jq -r --arg code "$code" '
        [
            (.[]? | objects | select(.value == $code) | .label.de),
            (.eventCodes[]? | objects | select(.eventCode == $code) | .eventCode)
        ]
        | map(select(. != null and . != ""))
        | .[0] // empty
    ' 2>/dev/null
}

warning_identifier() {
    jq -r '.id // .identifier // .payload.id // empty'
}

warning_headline() {
    jq -r '.payload.data.headline // .headline // .info[0].headline // .payload.data.i18nTitle.de // .i18nTitle.de // "Keine Überschrift"'
}

warning_provider() {
    jq -r '
        .payload.data.provider
        // .provider
        // .sender
        // first(.info[0].parameter[]? | select(.valueName == "sender_langname") | .value)
        // "Unknown"
    ' 2>/dev/null
}

warning_severity() {
    jq -r '.payload.data.severity // .severity // .info[0].severity // "Unknown"'
}

warning_urgency() {
    jq -r '.payload.data.urgency // .urgency // .info[0].urgency // "Unknown"'
}

warning_effective() {
    jq -r '.sent // .payload.data.sent // .effective // .startDate // "Unknown"'
}

warning_expires() {
    jq -r '.payload.data.expires // .expires // empty'
}

warning_event_code() {
    jq -r '.payload.data.transKeys.event // .transKeys.event // .info[0].eventCode[0].value // empty'
}

warning_description() {
    jq -r '.info[0].description // .description // empty'
}

warning_instruction() {
    jq -r '.info[0].instruction // .instruction // empty'
}

warning_area_desc() {
    jq -r '.info[0].area[0].areaDesc // .payload.data.area.data // empty'
}

warning_msg_type() {
    jq -r '.payload.data.msgType // .msgType // empty'
}

format_multiline_text() {
    sed 's/<br\/>/\
/g; s/<br>/\
/g; s/<[^>]*>//g' | awk '{print "   " $0}'
}

detail_link_for_warning() {
    local warning_json="$1"
    local warning_id headline_slug headline
    warning_id=$(echo "$warning_json" | warning_identifier)
    headline=$(echo "$warning_json" | warning_headline)
    if [[ -z "$warning_id" ]]; then
        echo ""
        return
    fi
    headline_slug=$(echo "$headline" | sed 's/  */_/g' | sed 's/[^a-zA-Z0-9_-]//g')
    echo "https://warnung.bund.de/meldungen/${warning_id}/${headline_slug}"
}

validate_source() {
    case "$1" in
        mowas|dwd|katwarn|biwapp|lhp|police)
            return 0
            ;;
        *)
            echo "Error: unsupported source '$1'" >&2
            echo "Supported values: mowas, dwd, katwarn, biwapp, lhp, police" >&2
            exit 1
            ;;
    esac
}

resolve_input() {
    if [[ -n "$SOURCE" ]]; then
        validate_source "$SOURCE"
        REGION_LABEL="Quelle $SOURCE (bundesweit)"
        return
    fi

    if [[ -z "$INPUT" ]]; then
        usage
    fi

    if [[ "$INPUT" =~ ^[0-9]{12}$ ]]; then
        ARS="$INPUT"
        REGION_LABEL="ARS $ARS"
    else
        lookup_line=$("$LOOKUP_SCRIPT" "$INPUT" --plain --first | head -1 || true)
        if [[ -z "$lookup_line" ]]; then
            echo "Error: could not resolve location to ARS: $INPUT" >&2
            exit 1
        fi
        REGION_LABEL=$(echo "$lookup_line" | cut -d'|' -f1 | xargs)
        ARS=$(echo "$lookup_line" | cut -d'|' -f2 | xargs)
    fi

    NORMALIZED_ARS=$(normalize_ars "$ARS")
}

fetch_feed() {
    if [[ -n "$SOURCE" ]]; then
        fetch_json "$API_BASE/$SOURCE/mapData.json"
    else
        fetch_json "$API_BASE/dashboard/$NORMALIZED_ARS.json"
    fi
}

enrich_warning_json() {
    local warning_json="$1"
    local warning_id detail_json geojson_json

    warning_id=$(echo "$warning_json" | warning_identifier)
    detail_json='null'
    geojson_json='null'

    if [[ "$DETAILS" == "true" && -n "$warning_id" ]]; then
        detail_json=$(fetch_json "$API_BASE/warnings/$warning_id.json" 2>/dev/null || echo 'null')
    fi

    if [[ "$GEOJSON" == "true" && -n "$warning_id" ]]; then
        geojson_json=$(fetch_json "$API_BASE/warnings/$warning_id.geojson" 2>/dev/null || echo 'null')
    fi

    jq -cn \
        --argjson summary "$warning_json" \
        --argjson detail "$detail_json" \
        --argjson geojson "$geojson_json" \
        '{summary: $summary, detail: $detail, geojson: $geojson}'
}

render_warning_text() {
    local warning_json="$1"
    local display_json detail_json
    local warning_id headline severity urgency effective expires provider event_code event_desc detail_link description instruction area_desc msg_type

    display_json="$warning_json"
    detail_json=$(echo "$warning_json" | jq -c '.detail // null')
    if [[ "$detail_json" != "null" ]]; then
        display_json="$detail_json"
    else
        display_json=$(echo "$warning_json" | jq -c '.summary // .')
    fi

    warning_id=$(echo "$display_json" | warning_identifier)
    headline=$(echo "$display_json" | warning_headline)
    severity=$(echo "$display_json" | warning_severity)
    urgency=$(echo "$display_json" | warning_urgency)
    effective=$(echo "$display_json" | warning_effective)
    expires=$(echo "$display_json" | warning_expires)
    provider=$(echo "$display_json" | warning_provider)
    event_code=$(echo "$display_json" | warning_event_code)
    event_desc=$(event_label "$event_code")
    if [[ -z "$event_desc" ]]; then
        event_desc="${event_code:-Unbekannter Typ}"
    fi
    detail_link=$(detail_link_for_warning "$display_json")
    description=$(echo "$display_json" | warning_description)
    instruction=$(echo "$display_json" | warning_instruction)
    area_desc=$(echo "$display_json" | warning_area_desc)
    msg_type=$(echo "$display_json" | warning_msg_type)

    echo "────────────────────────────────────────"
    echo "📋 $headline"
    echo "   Typ: $event_desc"
    if [[ -n "$event_code" && "$event_desc" != "$event_code" ]]; then
        echo "   Code: $event_code"
    fi
    echo "   Schwere: $severity | Dringlichkeit: $urgency"
    echo "   Anbieter: $provider"
    if [[ -n "$msg_type" ]]; then
        echo "   Meldungsart: $msg_type"
    fi
    echo "   Gesendet: $effective"
    if [[ -n "$expires" && "$expires" != "null" && "$expires" != "Unknown" ]]; then
        echo "   Gueltig bis: $expires"
    fi
    if [[ -n "$area_desc" ]]; then
        echo "   Gebiet: $area_desc"
    fi
    if [[ -n "$warning_id" ]]; then
        echo "   ID: $warning_id"
    fi
    if [[ -n "$detail_link" ]]; then
        echo "   🔗 Details: $detail_link"
    fi
    if [[ "$DETAILS" == "true" && -n "$description" ]]; then
        echo ""
        echo "   Beschreibung:"
        echo "$description" | format_multiline_text
    fi
    if [[ "$DETAILS" == "true" && -n "$instruction" ]]; then
        echo ""
        echo "   Handlungshinweise:"
        echo "$instruction" | format_multiline_text
    fi
    if [[ "$GEOJSON" == "true" ]]; then
        local feature_count
        feature_count=$(echo "$warning_json" | jq -r '
            if .geojson == null then 0
            elif .geojson.type == "FeatureCollection" then (.geojson.features | length)
            else 1
            end
        ')
        echo ""
        echo "   GeoJSON: $feature_count Feature(s) geladen"
    fi
    echo ""
}

resolve_input
EVENT_CODES_JSON=$(fetch_json "$EVENT_CODES_URL" 2>/dev/null || echo '[]')
warnings=$(fetch_feed)
count=$(echo "$warnings" | jq 'length')

if [[ "$JSON_OUTPUT" == "true" ]]; then
    if [[ "$DETAILS" == "true" || "$GEOJSON" == "true" ]]; then
        echo "$warnings" | jq -c '.[]' | while IFS= read -r warning_line; do
            enrich_warning_json "$warning_line"
        done | jq -s \
            --arg label "$REGION_LABEL" \
            --arg ars "$ARS" \
            --arg normalized_ars "$NORMALIZED_ARS" \
            --arg source "$SOURCE" \
            '{
                region: $label,
                ars: (if $ars == "" then null else $ars end),
                normalized_ars: (if $normalized_ars == "" then null else $normalized_ars end),
                source: (if $source == "" then null else $source end),
                count: length,
                warnings: .
            }'
    else
        echo "$warnings"
    fi
    exit 0
fi

if [[ "$count" -eq 0 ]]; then
    echo "✅ Keine aktuellen Warnungen fuer $REGION_LABEL"
    if [[ -n "$NORMALIZED_ARS" && "$ARS" != "$NORMALIZED_ARS" ]]; then
        echo "   Hinweis: NINA-Dashboard auf Kreisebene verwendet ARS $NORMALIZED_ARS"
    fi
    exit 0
fi

echo "⚠️  $count Warnung(en) fuer $REGION_LABEL:"
if [[ -n "$NORMALIZED_ARS" && "$ARS" != "$NORMALIZED_ARS" ]]; then
    echo "   Hinweis: Kreisebenen-ARS fuer Dashboard: $NORMALIZED_ARS"
fi
echo ""

while IFS= read -r warning_line; do
    if [[ "$DETAILS" == "true" || "$GEOJSON" == "true" ]]; then
        render_warning_text "$(enrich_warning_json "$warning_line")"
    else
        render_warning_text "$warning_line"
    fi
done < <(echo "$warnings" | jq -c '.[]')


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal:  | SkillSpector: clean (LOW)