258 lines
9.1 KiB
Markdown
258 lines
9.1 KiB
Markdown
# Météo + Astuces — Design Document
|
|
|
|
**Date :** 2026-02-22
|
|
**Statut :** Validé
|
|
|
|
---
|
|
|
|
## Contexte
|
|
|
|
Le projet "Jardin" dispose déjà d'un service météo basique (Open-Meteo, cache JSON 3h, champs
|
|
limités). Il dispose également de deux scripts d'essai exploratoires :
|
|
|
|
- `prevision meteo/open_meteo_garden_forecast.py` — fetch Open-Meteo riche (sol, ETP, horaire)
|
|
- `station_meteo/local_station_weather.py` — scraper WeeWX via RSS + HTML + NOAA monthly
|
|
|
|
La demande est d'intégrer ces deux sources dans l'application, avec affichage synthétique côté
|
|
frontend et une nouvelle section "Astuces".
|
|
|
|
---
|
|
|
|
## Décisions d'architecture
|
|
|
|
| Question | Choix retenu |
|
|
|---|---|
|
|
| Scheduler | APScheduler intégré dans FastAPI |
|
|
| Stockage | SQLite avec tables dédiées |
|
|
| Plage temporelle | 7j passé + J0 + 7j futur |
|
|
| Modèle astuces | Bibliothèque avec catégories + tags |
|
|
|
|
---
|
|
|
|
## Modèle de données
|
|
|
|
### Table `meteo_station`
|
|
|
|
Données collectées depuis la station WeeWX locale (`http://10.0.0.8:8081/`).
|
|
|
|
```sql
|
|
CREATE TABLE meteo_station (
|
|
date_heure TEXT PRIMARY KEY, -- ISO "2026-02-22T14:00" (heure arrondie)
|
|
type TEXT NOT NULL, -- "current" | "veille"
|
|
temp_ext REAL, -- °C
|
|
temp_int REAL, -- °C (serre, si disponible)
|
|
humidite REAL, -- %
|
|
pression REAL, -- hPa
|
|
pluie_mm REAL, -- précipitations accumulées
|
|
vent_kmh REAL,
|
|
vent_dir TEXT, -- N/NE/E/SE/S/SO/O/NO
|
|
uv REAL,
|
|
solaire REAL, -- W/m²
|
|
source_url TEXT -- URL station au moment du fetch
|
|
);
|
|
```
|
|
|
|
### Table `meteo_open_meteo`
|
|
|
|
Prévisions journalières depuis l'API Open-Meteo (gratuite, sans clé).
|
|
|
|
```sql
|
|
CREATE TABLE meteo_open_meteo (
|
|
date TEXT PRIMARY KEY, -- "2026-02-22"
|
|
t_min REAL,
|
|
t_max REAL,
|
|
pluie_mm REAL,
|
|
vent_kmh REAL,
|
|
wmo INTEGER, -- code WMO
|
|
label TEXT, -- libellé WMO en français
|
|
humidite_moy REAL,
|
|
sol_0cm REAL, -- température sol surface
|
|
etp_mm REAL, -- évapotranspiration
|
|
fetched_at TEXT -- timestamp ISO du dernier fetch
|
|
);
|
|
```
|
|
|
|
### Table `astuces`
|
|
|
|
Bibliothèque d'astuces jardinage.
|
|
|
|
```sql
|
|
CREATE TABLE astuces (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
titre TEXT NOT NULL,
|
|
contenu TEXT NOT NULL,
|
|
categorie TEXT NOT NULL, -- "plante"|"jardin"|"tache"|"general"|"ravageur"|"maladie"
|
|
tags TEXT, -- JSON array, ex: ["tomate","semis","printemps"]
|
|
mois TEXT, -- JSON array, ex: [3,4,5] ou null = toute l'année
|
|
created_at TEXT NOT NULL -- ISO datetime
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Scheduler APScheduler
|
|
|
|
Intégré dans `backend/app/main.py` via `apscheduler` (lifespan).
|
|
|
|
```python
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
|
|
scheduler = AsyncIOScheduler()
|
|
|
|
# Job 1 — données station courantes (1x/h)
|
|
scheduler.add_job(collect_station_current, "interval", hours=1, id="station_current")
|
|
|
|
# Job 2 — résumé veille NOAA (1x/j à 06h00)
|
|
scheduler.add_job(collect_station_veille, "cron", hour=6, id="station_veille")
|
|
|
|
# Job 3 — prévisions Open-Meteo (1x/h)
|
|
scheduler.add_job(collect_open_meteo, "interval", hours=1, id="open_meteo")
|
|
```
|
|
|
|
Les jobs s'exécutent également au démarrage (`next_run_time=datetime.now()`).
|
|
|
|
---
|
|
|
|
## Endpoints API
|
|
|
|
### Météo
|
|
|
|
| Méthode | Route | Description |
|
|
|---|---|---|
|
|
| `GET` | `/api/meteo/station/current` | Dernière mesure station (SELECT dernière ligne) |
|
|
| `GET` | `/api/meteo/station/history?days=7` | Historique station N jours |
|
|
| `GET` | `/api/meteo/previsions?days=7` | Prévisions Open-Meteo (SELECT depuis J+1) |
|
|
| `GET` | `/api/meteo/tableau` | Tableau synthétique 7j passé + J0 + 7j futur |
|
|
| `POST` | `/api/meteo/refresh` | Déclenche les 3 jobs manuellement (admin) |
|
|
|
|
### Format réponse `/api/meteo/tableau`
|
|
|
|
```json
|
|
{
|
|
"rows": [
|
|
{
|
|
"date": "2026-02-15",
|
|
"type": "passe",
|
|
"station": { "t_min": 2.1, "t_max": 8.4, "pluie_mm": 0.0, "vent_kmh": 12.0, "humidite": 78.0 },
|
|
"open_meteo": null
|
|
},
|
|
{
|
|
"date": "2026-02-22",
|
|
"type": "aujourd_hui",
|
|
"station": { "t_ext": 6.2, "humidite": 71.0, "pluie_mm": 0.2, "vent_kmh": 8.0 },
|
|
"open_meteo": { "t_min": 4.0, "t_max": 9.0, "pluie_mm": 1.2, "wmo": 61, "label": "Pluie légère" }
|
|
},
|
|
{
|
|
"date": "2026-02-23",
|
|
"type": "futur",
|
|
"station": null,
|
|
"open_meteo": { "t_min": 3.5, "t_max": 11.2, "pluie_mm": 0.0, "wmo": 1, "label": "Plutôt clair" }
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Astuces
|
|
|
|
| Méthode | Route | Description |
|
|
|---|---|---|
|
|
| `GET` | `/api/astuces?categorie=plante&mois=3&tag=tomate` | Liste filtrée |
|
|
| `POST` | `/api/astuces` | Créer une astuce |
|
|
| `PUT` | `/api/astuces/{id}` | Modifier |
|
|
| `DELETE` | `/api/astuces/{id}` | Supprimer |
|
|
|
|
---
|
|
|
|
## Frontend
|
|
|
|
### CalendrierView.vue — onglet Météo (refonte)
|
|
|
|
L'onglet météo actuel (grille simple 7 jours) est remplacé par :
|
|
|
|
1. **Widget "Maintenant"** (en haut) — depuis `/api/meteo/station/current`
|
|
- Température extérieure, humidité, vent, pression
|
|
- Icône SVG WMO si disponible
|
|
|
|
2. **Tableau synthétique** — depuis `/api/meteo/tableau`
|
|
|
|
```
|
|
┌──────────┬────────────────────────┬──────────────────────┐
|
|
│ Date │ Station WeeWX │ Open-Meteo │
|
|
├──────────┼────────────────────────┼──────────────────────┤
|
|
│ 15 fév ← │ 2° / 8° 💧0 💨12 │ — │
|
|
│ ... │ ... │ ... │
|
|
│ Auj. 🔵 │ 6° act. 💧0.2 💨8 │ 4/9° 🌦 1.2mm │
|
|
│ Dem. → │ — │ 3/11° ☀️ 0mm │
|
|
│ ... │ — │ prévisions 7j │
|
|
└──────────┴────────────────────────┴──────────────────────┘
|
|
```
|
|
|
|
- Lignes passé : opacité réduite
|
|
- Ligne aujourd'hui : bordure verte `border-green`
|
|
- Colonne station absente pour futur → `—`
|
|
- Icône SVG WMO dans la colonne Open-Meteo (fichiers existants dans `/icons/weather/`)
|
|
|
|
### AstucessView.vue — nouvelle vue
|
|
|
|
Route : `/astuces`
|
|
Icône drawer : 💡 Astuces
|
|
|
|
**Structure :**
|
|
- Tabs par catégorie (Toutes / Plante / Jardin / Tâche / Ravageur / Maladie / Général)
|
|
- Filtre mois courant activé par défaut (badge "Ce mois" désactivable)
|
|
- Chips de tags cliquables pour filtrer
|
|
- Liste de cartes : titre + contenu (truncated) + tags + mois
|
|
- Bouton "+ Ajouter"
|
|
|
|
**Modal création/édition :**
|
|
- Titre (input)
|
|
- Contenu (textarea, multiline)
|
|
- Catégorie (select)
|
|
- Tags (input avec séparateur virgule → chips visuels)
|
|
- Mois pertinents (12 cases à cocher, ou "Toute l'année")
|
|
|
|
---
|
|
|
|
## Fichiers à créer / modifier
|
|
|
|
### Backend
|
|
|
|
| Fichier | Action |
|
|
|---|---|
|
|
| `backend/app/models/meteo.py` | Créer : modèles SQLModel `MeteoStation`, `MeteoOpenMeteo` |
|
|
| `backend/app/models/astuce.py` | Modifier : compléter le modèle existant |
|
|
| `backend/app/services/station.py` | Créer : service scraping WeeWX (extrait de `station_meteo/local_station_weather.py`) |
|
|
| `backend/app/services/meteo.py` | Modifier : enrichir le fetch Open-Meteo (champs sol, ETP, humidité) |
|
|
| `backend/app/services/scheduler.py` | Créer : APScheduler + 3 jobs |
|
|
| `backend/app/routers/meteo.py` | Modifier : ajouter les 4 nouveaux endpoints |
|
|
| `backend/app/routers/astuces.py` | Modifier : compléter le CRUD existant |
|
|
| `backend/app/main.py` | Modifier : intégrer le scheduler dans le lifespan |
|
|
| `backend/requirements.txt` | Ajouter : `apscheduler>=3.10` |
|
|
|
|
### Frontend
|
|
|
|
| Fichier | Action |
|
|
|---|---|
|
|
| `frontend/src/views/CalendrierView.vue` | Modifier : refonte onglet Météo |
|
|
| `frontend/src/views/AstucessView.vue` | Créer : nouvelle vue astuces |
|
|
| `frontend/src/api/astuces.ts` | Créer : client API astuces |
|
|
| `frontend/src/stores/astuces.ts` | Créer : store Pinia astuces |
|
|
| `frontend/src/router/index.ts` | Modifier : ajouter route `/astuces` |
|
|
| `frontend/src/components/AppDrawer.vue` | Modifier : ajouter entrée 💡 Astuces |
|
|
|
|
---
|
|
|
|
## Gestion des erreurs
|
|
|
|
- **Station hors ligne** : le job `station_current` catch l'exception, log l'erreur, ne plante pas le scheduler. L'endpoint `/api/meteo/station/current` retourne `null` si aucune donnée récente (< 2h).
|
|
- **Open-Meteo indisponible** : le job `open_meteo` catch l'exception, conserve les données existantes en BDD. Le tableau affiche les prévisions du dernier fetch réussi.
|
|
- **Tableau sans données station** : la colonne station affiche `—` pour les jours sans mesure. Pas d'erreur côté frontend.
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
- `backend/tests/test_meteo.py` : test endpoints tableau (mock des tables), test format réponse
|
|
- `backend/tests/test_astuces.py` : test CRUD complet, test filtres catégorie + mois + tag
|
|
- Tests unitaires pour `parse_station_current()` et `summarize_open_meteo()`
|