diff --git a/app.py b/app.py index 8e714b1..34d7dc4 100644 --- a/app.py +++ b/app.py @@ -226,6 +226,8 @@ def get_dwd_warnings(lat, lon, state_hint=None): "description": w.get("description", ""), "state": w.get("state", ""), "state_short": w.get("stateShort", ""), + "onset": w.get("onset", ""), + "expires": w.get("expires", ""), }) filtered = [] if state_hint: @@ -238,8 +240,10 @@ def get_dwd_warnings(lat, lon, state_hint=None): "type": w["type"], "headline": w["headline"], "description": w["description"], + "onset": w.get("onset", ""), + "expires": w.get("expires", ""), } - for w in result_source[:3] + for w in result_source[:5] ] _warn_cache[key] = result return result @@ -339,6 +343,49 @@ def get_mosmix_forecast(lat, lon, hours=72): app.logger.exception("MOSMIX forecast loading failed") return [], {} +def filter_unrealistic_warnings(warnings, forecast): + """ + Filter out warnings that contradict the actual forecast. + E.g., frost warning when min temp is > 0°C in next 48 hours. + """ + if not warnings or not forecast: + return warnings + + filtered = [] + frost_keywords = {"frost", "glatte", "glatt", "eis", "schnee"} + rain_keywords = {"regen", "starkregen", "dauerregen"} + + try: + temps_48h = [h.get("temp_c") for h in forecast[:48] if h.get("temp_c") is not None] + precip_48h = [h.get("precip_mm") for h in forecast[:48] if h.get("precip_mm") is not None] + + min_temp_48h = min(temps_48h) if temps_48h else None + max_precip_48h = max(precip_48h) if precip_48h else 0 + + for w in warnings: + warn_type = _normalize_text(w.get("type", "")) + headline = _normalize_text(w.get("headline", "")) + + skip = False + + if min_temp_48h is not None and min_temp_48h > 2: + if any(kw in warn_type or kw in headline for kw in frost_keywords): + app.logger.info("Filtered frost warning: min_temp %.1f°C", min_temp_48h) + skip = True + + if max_precip_48h < 0.1: + if any(kw in warn_type or kw in headline for kw in rain_keywords): + app.logger.info("Filtered rain warning: max_precip %.1f mm", max_precip_48h) + skip = True + + if not skip: + filtered.append(w) + except Exception: + app.logger.exception("Error filtering unrealistic warnings") + return warnings + + return filtered + @app.route("/") def index(): return render_template("index.html") @@ -395,6 +442,7 @@ def wetter(): forecast = forecast[current_idx:] sunrise, sunset, dawn, dusk = get_sun_times(lat, lon) warnings = get_dwd_warnings(lat, lon, state_hint=state_hint) + warnings = filter_unrealistic_warnings(warnings, forecast) daily = {} for h in forecast: dt = h["datetime"] diff --git a/static/css/style.css b/static/css/style.css index 9b5c20e..34cb030 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -502,6 +502,7 @@ main { flex: 1; } .warn-icon { font-size: 1.1rem; flex-shrink: 0; line-height: 1.3; } .warn-item strong { color: inherit; } .warn-item div { display: flex; flex-direction: column; gap: 0.2rem; } +.warn-time { color: #888; font-size: 0.7rem; margin: 0; margin-bottom: 0.25rem; font-weight: 500; } .warn-desc { color: var(--muted2); font-size: 0.78rem; margin: 0; } /* ═══════════════════════════════════════════════════════════ diff --git a/templates/weather.html b/templates/weather.html index 5b46542..d5cda6d 100644 --- a/templates/weather.html +++ b/templates/weather.html @@ -76,14 +76,8 @@ {{ '⚠️' if w.level == 1 else ('🟠' if w.level == 2 else ('🔴' if w.level >= 3 else '⚠️')) }}
{{ w.headline }} - {% if w.description %}

{{ w.description[:140] }}{% if w.description|length > 140 %}…{% endif %}

{% endif %} -
- - {% endfor %} - -{% endif %} - - + {% if w.onset %}

{% if w.onset is string %}{{ w.onset[8:10] }}.{{ w.onset[5:7] }}. {{ w.onset[11:16] }}{% else %}{{ w.onset }}{% endif %}{% if w.expires %} – {% if w.expires is string %}{{ w.expires[8:10] }}.{{ w.expires[5:7] }}. {{ w.expires[11:16] }}{% else %}{{ w.expires }}{% endif %}{% endif %}

{% endif %} + {% if w.description %}

{{ w.description[:250] }}{% if w.description|length > 250 %}…{% endif %}

{% endif %}

Stundenweise