Python
        Force et direction du vent
FFVL
Liste des balises : https://data.ffvl.fr/json/balises.json
Relevés : https://data.ffvl.fr/json/relevesmeteo.json
Pioupiou
Liste des balises et relevés : https://api.pioupiou.fr/v1/live/all
Prévision pluie heure par heure
Lib :
pip install meteofrance-api
Code :
from meteofrance_api import MeteoFranceClient
import time
client = MeteoFranceClient()
rain = client.get_rain(latitude=49.276437, longitude=-0.703140)
for forecast in rain.forecast:
    hour = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(forecast['dt']))
    print(hour, forecast['desc'], forecast['rain'])
Sortie :
12/07/2023 13:30:00 Temps sec 1
12/07/2023 13:35:00 Temps sec 1
12/07/2023 13:40:00 Temps sec 1
12/07/2023 13:45:00 Temps sec 1
12/07/2023 13:50:00 Temps sec 1
12/07/2023 13:55:00 Temps sec 1
12/07/2023 14:05:00 Temps sec 1
12/07/2023 14:15:00 Temps sec 1
12/07/2023 14:25:00 Temps sec 1
Détermination du jour et de la nuit
pip install pyephem
import ephem
import datetime
import math
# Parameters : latitude, longitude and timestamp utc of observer
# Return : the height of the sun
def sun_height(latitude, longitude, timestamp):
    observer = ephem.Observer()
    observer.lat = latitude * math.pi / 180.0
    observer.lon = longitude * math.pi / 180.0
    observer.date = datetime.datetime.fromtimestamp(timestamp).strftime("%Y/%m/%d %H:%M:%S")
    sun = ephem.Sun(observer)
    return sun.alt * 180.0 / math.pi
# If the top of the sun is higher than the horizon (Diameter of sun = 0.5 degrees)
if sun_height(49.276437, -0.703140, 1689161276) > -0.25:
    print('jour')
else:
    print('nuit')
Téléchargement de météogramme
import requests
from lxml import etree
from io import StringIO
def download_meteogramme(url_page, fichier_telecharge):
    username = "************"
    password = "************"
    url = "https://www.meteoblue.com/fr/user/login/index"
    session = requests.Session()
    response = session.get(url)
    if response.status_code == 200:
        parser = etree.HTMLParser()
        tree = etree.parse(StringIO(response.text), parser)
        csrf = tree.find("//input[@id='csrf']")
        csrf_name = csrf.get('name')
        csrf_value = csrf.get('value')
        data = {"username": username, "password": password, csrf_name: csrf_value}
        response = session.post(url, data=data)
        url = url_page
        response = session.get(url)
        if response.status_code == 200:
            html = etree.HTML(response.content)
            div = html.xpath("//div[@id='blooimage']")[0]
            img_url = f'https:{div.get("data-href")}'
            response = requests.get(img_url)
            with open(fichier_telecharge, "wb") as f:
                f.write(response.content) 
    else:
        print("Error:", response.status_code)
Historique de la batterie de la balise FFVL
Crédit : David L. – Licence : CC BY-SA 4.0
Script utilisé pour générer le graphique :
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#Auteur : David LOUISE
import os
import sys
import argparse
import requests
import pandas as pd
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
API_BASE = "https://data.ffvl.fr/api/"
DEFAULT_HOURS = 72
DEFAULT_API_KEY = os.getenv("FFVL_API_KEY", "00000000000000000000000000000000")
def fetch_balise_data(balise_id: int, hours: int = DEFAULT_HOURS, api_key: str = DEFAULT_API_KEY):
    """Récupère les données JSON pour une balise donnée."""
    params = {
        "base": "balises",
        "r": "histo",
        "hours": str(hours),
        "idbalise": str(balise_id),
        "mode": "json",
        "key": api_key,
    }
    try:
        resp = requests.get(API_BASE, params=params, timeout=20)
        resp.raise_for_status()
    except requests.RequestException as e:
        print(f"Erreur HTTP en récupérant les données: {e}", file=sys.stderr)
        sys.exit(1)
    try:
        data = resp.json()
    except ValueError:
        print("Réponse non JSON reçue depuis l’API.", file=sys.stderr)
        sys.exit(1)
    if not isinstance(data, list) or len(data) == 0:
        print("Aucune donnée renvoyée par l’API pour cette balise/intervalle.", file=sys.stderr)
        sys.exit(1)
    return data
def build_dataframe(records):
    """Transforme la liste JSON en DataFrame propre."""
    df = pd.DataFrame.from_records(records)
    df["date"] = pd.to_datetime(df["date"], errors="coerce")
    for col in ("u_sol", "u_bat"):
        if col in df.columns:
            df[col] = pd.to_numeric(df[col], errors="coerce")
        else:
            df[col] = pd.NA
    df = df.dropna(subset=["date"])
    df = df.sort_values("date").reset_index(drop=True)
    return df
def plot_panneau_et_batterie(df: pd.DataFrame, balise_id: int, out_path: str):
    """Trace panneau solaire et batterie sur le même axe Y, en points non reliés, axe X formaté toutes les 4 h."""
    if df["u_sol"].isna().all() and df["u_bat"].isna().all():
        print("Aucune valeur exploitable pour le panneau solaire ou la batterie.", file=sys.stderr)
        sys.exit(1)
    fig, ax = plt.subplots(figsize=(10, 5))
    # Panneau solaire (rouge)
    if not df["u_sol"].isna().all():
        ax.scatter(df["date"], df["u_sol"], color="red", label="Panneau solaire (mV)", s=10)
    # Batterie (bleu)
    if not df["u_bat"].isna().all():
        ax.scatter(df["date"], df["u_bat"], color="blue", label="Batterie (mV)", s=10)
    ax.set_xlabel("Date / heure")
    ax.set_ylabel("Tension (mV)")
    ax.legend(loc="best")
    plt.title(f"Balise {balise_id} — Panneau solaire (rouge) & Batterie (bleu)")
    # Axe X : un tick toutes les 4h, format jour/heure
    ax.xaxis.set_major_locator(mdates.HourLocator(interval=4))
    ax.xaxis.set_major_formatter(mdates.DateFormatter("%d/%m %Hh"))
    fig.autofmt_xdate(rotation=45)
    ax.grid(True, which='major', linestyle='-', linewidth=0.3)
    plt.tight_layout()
    plt.savefig(out_path, dpi=150)
    plt.close(fig)
def main():
    parser = argparse.ArgumentParser(description="Génère un graphique panneau solaire & batterie pour une balise FFVL (PNG).")
    parser.add_argument("balise_id", type=int, help="Identifiant de la balise (ex: 97)")
    parser.add_argument("--hours", type=int, default=DEFAULT_HOURS, help=f"Fenêtre historique en heures (défaut: {DEFAULT_HOURS})")
    parser.add_argument("--out", type=str, default=None, help="Fichier PNG de sortie (défaut: <balise_id>.png)")
    parser.add_argument("--api-key", type=str, default=DEFAULT_API_KEY, help="Clé API FFVL (optionnelle)")
    args = parser.parse_args()
    out_path = args.out or f"{args.balise_id}.png"
    data = fetch_balise_data(args.balise_id, hours=args.hours, api_key=args.api_key)
    df = build_dataframe(data)
    plot_panneau_et_batterie(df, args.balise_id, out_path)
    print(f"Fichier généré : {out_path}")
if __name__ == "__main__":
    main()