204 lines
5.8 KiB
Python
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 ---
|
|
MQTT_HOST = os.getenv("MQTT_HOST")
|
|
MQTT_USER = os.getenv("MQTT_USER")
|
|
MQTT_PASS = os.getenv("MQTT_PASS")
|
|
MQTT_PORT = int(os.getenv("MQTT_PORT", "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()
|