Files
Gestion_sondes/app/Mqtt_meudon.py
2025-12-11 17:01:29 +01:00

204 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""
Mqtt_meudon.py
Récupère les mesures MQTT du site Meudon et les insère dans la table Sondes.Meudon.
"""
import os
import logging
from logging.handlers import RotatingFileHandler
import socket
import mysql.connector
from mysql.connector import Error
import paho.mqtt.client as mqtt
from paho.mqtt.client import CallbackAPIVersion
from dotenv import load_dotenv
# =========================
# Chargement du .env
# =========================
load_dotenv()
# --- MySQL (commun) ---
DB_HOST = os.getenv("DB_HOST")
DB_USER = os.getenv("DB_USER")
DB_PASS = os.getenv("DB_PASS")
DB_NAME = os.getenv("DB_NAME")
# --- MQTT Meudon ---
MQTT_HOST = os.getenv("MQTT_HOST_MEUDON")
MQTT_USER = os.getenv("MQTT_USER_MEUDON")
MQTT_PASS = os.getenv("MQTT_PASS_MEUDON")
MQTT_PORT = int(os.getenv("MQTT_PORT_MEUDON", "1883"))
# Client ID (configurable, sinon suffixé avec le hostname)
MQTT_CLIENT_ID = os.getenv(
"MQTT_CLIENT_ID_MEUDON",
f"Mqtt_meudon_{socket.gethostname()}"
)
GYRO_TOPIC_MEUDON = os.getenv("GYRO_MQTT_TOPIC_MEUDON", "Meudon/gyrophare")
# Nom de la table de destination
TABLE_NAME = "Meudon"
# =========================
# Logging
# =========================
def setup_logging():
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
"%(asctime)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# Console
console = logging.StreamHandler()
console.setFormatter(formatter)
logger.addHandler(console)
# Logs fichier (même logique que Saclay)
log_dir = os.getenv("LOG_DIR", "./Logs")
try:
os.makedirs(log_dir, exist_ok=True)
file_handler = RotatingFileHandler(
os.path.join(log_dir, "Mqtt_meudon.log"),
maxBytes=1_000_000,
backupCount=5,
encoding="utf-8",
)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
except Exception as e:
logging.warning("Impossible de créer le fichier de log : %s", e)
# =========================
# Accès MySQL
# =========================
def insert_temperature(sonde: str, temperature: float) -> None:
"""
Insère une mesure dans la table Sondes.Meudon.
La colonne Date utilise CURRENT_TIMESTAMP par défaut dans MySQL.
"""
try:
conn = mysql.connector.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASS,
database=DB_NAME,
)
cursor = conn.cursor()
sql = f"INSERT INTO {TABLE_NAME} (Sonde, Temperature) VALUES (%s, %s)"
cursor.execute(sql, (sonde, temperature))
conn.commit()
logging.info("Insertion OK (Meudon) -> %s = %.2f", sonde, temperature)
except Error as e:
logging.exception("Erreur MySQL (Meudon) pour la sonde %s : %s", sonde, e)
finally:
try:
if cursor:
cursor.close()
if conn and conn.is_connected():
conn.close()
except Exception:
pass
# =========================
# Callbacks MQTT (API v2)
# =========================
def on_connect(client, userdata, flags, reason_code, properties=None):
if reason_code == 0:
logging.info("Connecté au broker MQTT Meudon (%s)", MQTT_HOST)
# Abonnement à TOUT ce qui commence par "Meudon/"
result, mid = client.subscribe("Meudon/#")
logging.info("Abonné au topic : Meudon/# (result=%s, mid=%s)", result, mid)
else:
logging.error("Échec de connexion MQTT (Meudon), code retour = %s", reason_code)
def on_message(client, userdata, msg: mqtt.MQTTMessage):
topic = msg.topic
payload_raw = msg.payload.decode("utf-8", errors="ignore").strip()
logging.debug("Msg reçu (Meudon) : topic=%s payload=%s", topic, payload_raw)
# On ignore le gyrophare
if topic == GYRO_TOPIC_MEUDON:
logging.debug("Topic gyrophare Meudon ignoré : %s", topic)
return
# Nom de la sonde = dernier segment du topic
sonde = topic.split("/")[-1] if "/" in topic else topic
# Conversion du payload en float
try:
value = float(payload_raw.replace(",", "."))
except ValueError:
logging.warning(
"Payload non numérique (Meudon), mesure ignorée (topic=%s, payload=%s)",
topic,
payload_raw,
)
return
insert_temperature(sonde, value)
# =========================
# Programme principal
# =========================
def main():
setup_logging()
logging.info("Démarrage du script Mqtt_meudon")
# Vérif minimale des variables d'env MySQL
for var in ["DB_HOST", "DB_USER", "DB_PASS", "DB_NAME"]:
if os.getenv(var) in (None, ""):
logging.error("Variable d'environnement %s manquante !", var)
# Vérif minimale des variables d'env MQTT
if not MQTT_HOST:
logging.error("MQTT_HOST_MEUDON manquant !")
if not MQTT_USER:
logging.warning("MQTT_USER_MEUDON non défini (connexion sans login ?)")
if not MQTT_PORT or MQTT_PORT <= 0:
logging.error("MQTT_PORT_MEUDON invalide : %s", MQTT_PORT)
client = mqtt.Client(
client_id=MQTT_CLIENT_ID,
callback_api_version=CallbackAPIVersion.VERSION2
)
client.username_pw_set(MQTT_USER, MQTT_PASS)
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(MQTT_HOST, MQTT_PORT, keepalive=60)
except Exception as e:
logging.exception("Impossible de se connecter au broker MQTT Meudon : %s", e)
return
logging.info("Boucle MQTT Meudon en cours (Ctrl+C pour arrêter)...")
try:
client.loop_forever()
except KeyboardInterrupt:
logging.info("Arrêt demandé par l'utilisateur (Meudon).")
finally:
client.disconnect()
logging.info("Client MQTT Meudon déconnecté.")
if __name__ == "__main__":
main()