9.1 KiB
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/).
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é).
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.
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).
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
{
"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 :
-
Widget "Maintenant" (en haut) — depuis
/api/meteo/station/current- Température extérieure, humidité, vent, pression
- Icône SVG WMO si disponible
-
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_currentcatch l'exception, log l'erreur, ne plante pas le scheduler. L'endpoint/api/meteo/station/currentretournenullsi aucune donnée récente (< 2h). - Open-Meteo indisponible : le job
open_meteocatch 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éponsebackend/tests/test_astuces.py: test CRUD complet, test filtres catégorie + mois + tag- Tests unitaires pour
parse_station_current()etsummarize_open_meteo()