jarvis-assist/docs/superpowers/specs/2026-09-12-weather-widget-d...

173 lines
6.3 KiB
Markdown

# JARVIS Weather Widget (Phase 4a) - Design
**Datum:** 2026-09-12
**Status:** Approved, bereit fuer Implementierungsplan
## Kontext
Phase 3b (Web-Frontend mit Chat + Dashboard) ist live. `n8n` laeuft bereits im
Stack seit Phase 2/3, wurde aber noch nie konfiguriert (kein Owner-Account,
keine Workflows). Diese Spec macht n8n zum ersten Mal produktiv nutzbar: ein
Workflow holt periodisch das Wetter fuer Crailsheim (Firmenstandort laut
`JARVIS_HANDOFF.md`) und das Frontend zeigt es oben rechts in der Nav-Leiste
an.
## Datenfluss
```
n8n (Schedule-Trigger, alle 30 Min)
-> HTTP Request: Open-Meteo API (kein API-Key noetig)
-> Code-Node: WMO-Wettercode -> deutscher Text
-> Postgres-Node: INSERT INTO weather_cache
JARVIS-API: GET /api/v1/weather liest die neueste Zeile aus weather_cache
Frontend: WeatherWidget faengt beim Laden + alle 5 Min neu ab
```
## Wetterquelle: Open-Meteo
Kein API-Key noetig (kostenlos, keine Registrierung). Endpoint:
```
https://api.open-meteo.com/v1/forecast?latitude=49.1372&longitude=10.0674&current=temperature_2m,weather_code&timezone=Europe%2FBerlin
```
Antwortformat (Auszug):
```json
{
"current": {
"time": "2026-09-12T16:00",
"temperature_2m": 18.4,
"weather_code": 3
}
}
```
WMO-Wettercode-Mapping (Open-Meteo-Standard, Teilmenge fuer die haeufigsten
Faelle - vollstaendige Tabelle im Code-Node):
| Code | Text |
|------|------|
| 0 | Klarer Himmel |
| 1-2 | Ueberwiegend klar |
| 3 | Bewoelkt |
| 45, 48 | Nebel |
| 51-57 | Nieselregen |
| 61-67 | Regen |
| 71-77 | Schnee |
| 80-82 | Regenschauer |
| 95-99 | Gewitter |
| (sonst) | Unbekannt |
## Datenmodell
Neue Tabelle `weather_cache`:
```sql
CREATE TABLE weather_cache (
id SERIAL PRIMARY KEY,
location VARCHAR(100) NOT NULL,
temperature_c NUMERIC NOT NULL,
condition_code INTEGER NOT NULL,
condition_text VARCHAR(100) NOT NULL,
fetched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_weather_cache_fetched_at ON weather_cache(fetched_at);
```
Jeder n8n-Lauf fuegt eine neue Zeile ein (kein Update/Upsert - einfacher,
und die Tabelle bleibt klein bei 30-Minuten-Intervall; alte Zeilen aufraeumen
ist ein spaeteres Follow-up, kein akutes Problem bei diesem Datenvolumen).
## n8n-Workflow: "Wetter Crailsheim"
n8n wurde bisher nie initialisiert (kein Owner-Account, kein API-Key fuer die
n8n-REST-API). Einmaliger Setup-Schritt (per Browser, da n8n das beim ersten
Aufruf erzwingt): Owner-Account anlegen, dann unter Settings -> API einen
API-Key generieren. Danach wird der Workflow per n8n-REST-API (`POST
/rest/workflows` bzw. `/api/v1/workflows` je nach n8n-Version, siehe Plan)
als JSON-Definition angelegt und aktiviert - kein manuelles Zusammenklicken
der Nodes.
Workflow-Nodes:
1. **Schedule Trigger**: Intervall 30 Minuten.
2. **HTTP Request**: GET auf die Open-Meteo-URL oben.
3. **Code** (JavaScript): mapped `weather_code` auf `condition_text` (Tabelle
oben), reicht `temperature_2m`, `weather_code` und `condition_text` weiter.
4. **Postgres**: `INSERT INTO weather_cache (location, temperature_c,
condition_code, condition_text) VALUES ('Crailsheim', ..., ..., ...)`.
n8n's Postgres-Credential zeigt auf dieselbe `jarvis`-Datenbank, die die
API auch nutzt (gleicher Host/User/Passwort wie `DATABASE_URL` der API,
siehe `.env`).
## Backend: `GET /api/v1/weather`
Neuer Endpoint in `main.py`, durch `require_admin_key` geschuetzt (wie alle
anderen `/api/v1/*`-Routen ausser `/health`):
```json
{
"location": "Crailsheim",
"temperature_c": 18.4,
"condition_text": "Bewoelkt",
"fetched_at": "2026-09-12T16:00:03.123456"
}
```
Wenn `weather_cache` leer ist (Workflow lief noch nicht): `503` mit
Fehlermeldung "Weather data not available yet" - kein stiller Platzhalter,
das Frontend zeigt das dann sichtbar als Fehler statt falscher Daten.
## Frontend: `WeatherWidget`
Kleine Komponente, in der Nav-Leiste rechtsbuendig (per CSS `margin-left:
auto` auf dem Widget-Container, bestehende Nav-Buttons bleiben linksbuendig).
Sichtbar in beiden Ansichten (Chat + Dashboard), da die Nav-Leiste immer
gerendert wird.
- Beim Mount: `GET /api/v1/weather` laden.
- Danach alle 5 Minuten neu laden (`setInterval`, aufgeraeumt in
`useEffect`-Cleanup) - haeufiger als noetig waere reine Verschwendung,
seltener als der 30-Minuten-Workflow waere unnoetig zurueckhaltend; 5
Minuten ist ein vernuenftiger Mittelweg fuer eine UI-Anzeige.
- Anzeige: `18.4°C · Bewoelkt`. Bei Fehler (503 o.ae.): unauffaellige
Kurzmeldung statt der Wetteranzeige (kein Popup/Alert), da es sich um ein
Nice-to-have-Widget handelt, keine kritische Funktion.
## Fehlerbehandlung
- Open-Meteo nicht erreichbar: n8n-Workflow-Lauf schlaegt fehl, naechster
Lauf in 30 Minuten versucht es erneut. Kein Retry innerhalb eines Laufs
(YAGNI - Schedule-Trigger uebernimmt das ohnehin).
- `weather_cache` leer: Backend liefert `503`, Frontend zeigt Kurzmeldung.
- n8n/Postgres-Verbindung fehlerhaft: sichtbar im n8n-Execution-Log (manuell
pruefbar in der n8n-UI), keine gesonderte Behandlung noetig fuer dieses
Nice-to-have-Feature.
## Testing
- Backend: Unit-Test fuer `GET /api/v1/weather` - leere Tabelle -> 503,
vorhandene Zeile -> korrektes JSON (mit einer eingefuegten Testzeile via
`TestClient` + einer echten Test-Postgres-Instanz ist hier zu viel Aufwand
fuer dieses kleine Feature; stattdessen wird die Query-Logik direkt
getestet, indem die DB-Helper-Funktion mit einer gemockten `db_query`
aufgerufen wird - siehe Plan fuer die konkrete Test-Strategie).
- Frontend: kein gesonderter Unit-Test (das Widget ist eine einfache
Fetch-und-Anzeige-Komponente, gleiches Muster wie `Dashboard.tsx`, das
auch keinen Unit-Test hat) - Build-Check (`npm run build`) reicht,
passend zum bisherigen Muster in diesem Projekt.
- Manueller End-to-End-Test nach Deployment: n8n-Workflow einmal manuell
ausloesen (n8n-UI "Execute Workflow" oder Warten auf den ersten
Schedule-Lauf), pruefen dass `weather_cache` eine Zeile hat, `GET
/api/v1/weather` testen, Widget im Browser sehen.
## Out of Scope (bewusst nicht Teil dieser Phase)
- Mehrere Standorte / nutzerkonfigurierbarer Standort.
- Wettervorhersage (nur aktuelles Wetter, kein Forecast-Display).
- Aufraeumen alter `weather_cache`-Zeilen (Retention-Policy) - spaeteres
Follow-up, kein Problem bei aktuellem Datenvolumen.
- Wetter-Icons/Grafiken - nur Text.