173 lines
6.3 KiB
Markdown
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¤t=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.
|