diff --git a/.env b/.env index da685c1..b3245ae 100644 --- a/.env +++ b/.env @@ -1,9 +1,15 @@ #connexion mysql DB_HOST=162.19.78.131 -DB_USER=excel_auth -DB_PASSWORD=%n#%3Lay1MPa$%kR^5@ +DB_USER=sondes +DB_PASSWORD=TX.)-U1!zq5Axdk4 DB_NAME=Sondes -AUTH_USERS=[{"user":"Michel","pass":"921#%!LC3^71509PiyK"}] + +# MQTT +MQTT_HOST=162.19.78.131 +MQTT_PORT=1883 +MQTT_USER=sondes +MQTT_PASS=3J@bjYP0 + # paramètres mail SMTP_HOST=smtp.mail.ovh.net diff --git a/app/Logs/monitor.csv b/app/Logs/monitor.csv index b3d0810..449309f 100644 --- a/app/Logs/monitor.csv +++ b/app/Logs/monitor.csv @@ -1,43 +1,49 @@ Date;Lieu;Sonde;Température;Seuil;État -2025-08-23 13:08:36;Saclay;Congelateur;-16.75;-15.0;Normal -2025-08-23 13:03:33;Saclay;Congelateur;-17.50;-15.0;Normal -2025-08-23 12:58:31;Saclay;Congelateur;-16.25;-15.0;Normal -2025-08-23 12:53:28;Saclay;Congelateur;-17.00;-15.0;Normal -2025-08-23 12:48:26;Saclay;Congelateur;-17.75;-15.0;Normal -2025-08-23 12:43:23;Saclay;Congelateur;-16.25;-15.0;Normal -2025-08-23 13:08:36;Saclay;BOF;2.00;8.0;Normal -2025-08-23 13:03:34;Saclay;BOF;1.25;8.0;Normal -2025-08-23 12:58:31;Saclay;BOF;1.00;8.0;Normal -2025-08-23 12:53:29;Saclay;BOF;2.00;8.0;Normal -2025-08-23 12:48:26;Saclay;BOF;0.50;8.0;Normal -2025-08-23 12:43:23;Saclay;BOF;2.50;8.0;Normal -2025-08-23 13:08:37;Saclay;Legumes;5.25;10.0;Normal -2025-08-23 13:03:35;Saclay;Legumes;4.75;10.0;Normal -2025-08-23 12:58:32;Saclay;Legumes;3.75;10.0;Normal -2025-08-23 12:53:30;Saclay;Legumes;5.50;10.0;Normal -2025-08-23 12:48:27;Saclay;Legumes;3.00;10.0;Normal -2025-08-23 12:43:24;Saclay;Legumes;6.25;10.0;Normal -2025-08-23 13:08:38;Saclay;MeP;4.75;8.0;Normal -2025-08-23 13:03:35;Saclay;MeP;3.50;8.0;Normal -2025-08-23 12:58:33;Saclay;MeP;3.75;8.0;Normal -2025-08-23 12:53:30;Saclay;MeP;4.50;8.0;Normal -2025-08-23 12:48:28;Saclay;MeP;3.00;8.0;Normal -2025-08-23 12:43:25;Saclay;MeP;5.50;8.0;Normal -2025-08-23 13:04:25;Meudon;Viandes;4.31;6.0;Normal -2025-08-23 12:59:24;Meudon;Viandes;4.13;6.0;Normal -2025-08-23 12:54:24;Meudon;Viandes;3.94;6.0;Normal -2025-08-23 12:49:23;Meudon;Viandes;3.75;6.0;Normal -2025-08-23 12:44:22;Meudon;Viandes;3.94;6.0;Normal -2025-08-23 12:39:21;Meudon;Viandes;4.13;6.0;Normal -2025-08-23 13:04:25;Meudon;Poissons;4.56;6.0;Normal -2025-08-23 12:59:24;Meudon;Poissons;3.94;6.0;Normal -2025-08-23 12:54:24;Meudon;Poissons;3.94;6.0;Normal -2025-08-23 12:49:23;Meudon;Poissons;3.88;6.0;Normal -2025-08-23 12:44:22;Meudon;Poissons;3.75;6.0;Normal -2025-08-23 12:39:21;Meudon;Poissons;3.75;6.0;Normal -2025-08-23 13:04:25;Meudon;BOF;2.00;8.0;Normal -2025-08-23 12:59:24;Meudon;BOF;2.00;8.0;Normal -2025-08-23 12:54:24;Meudon;BOF;2.25;8.0;Normal -2025-08-23 12:49:23;Meudon;BOF;2.50;8.0;Normal -2025-08-23 12:44:22;Meudon;BOF;2.50;8.0;Normal -2025-08-23 12:39:21;Meudon;BOF;2.75;8.0;Normal +2025-09-02 09:29:07;Saclay;Congelateur;-18.50;-15.0;Normal +2025-09-02 09:24:04;Saclay;Congelateur;-19.00;-15.0;Normal +2025-09-02 09:19:01;Saclay;Congelateur;-18.50;-15.0;Normal +2025-09-02 09:13:59;Saclay;Congelateur;-17.75;-15.0;Normal +2025-09-02 09:08:56;Saclay;Congelateur;-18.25;-15.0;Normal +2025-09-02 09:03:54;Saclay;Congelateur;-18.75;-15.0;Normal +2025-09-02 09:29:07;Saclay;BOF;2.50;8.0;Normal +2025-09-02 09:24:05;Saclay;BOF;0.75;8.0;Normal +2025-09-02 09:19:02;Saclay;BOF;2.00;8.0;Normal +2025-09-02 09:13:59;Saclay;BOF;2.00;8.0;Normal +2025-09-02 09:08:57;Saclay;BOF;0.75;8.0;Normal +2025-09-02 09:03:54;Saclay;BOF;1.75;8.0;Normal +2025-09-02 09:29:08;Saclay;Viandes;2.75;6.0;Normal +2025-09-02 09:24:05;Saclay;Viandes;2.00;6.0;Normal +2025-09-02 09:19:02;Saclay;Viandes;4.75;6.0;Normal +2025-09-02 09:14:00;Saclay;Viandes;4.25;6.0;Normal +2025-09-02 09:08:57;Saclay;Viandes;3.75;6.0;Normal +2025-09-02 09:03:55;Saclay;Viandes;2.50;6.0;Normal +2025-09-02 09:29:08;Saclay;Legumes;5.00;10.0;Normal +2025-09-02 09:24:06;Saclay;Legumes;4.50;10.0;Normal +2025-09-02 09:19:03;Saclay;Legumes;5.00;10.0;Normal +2025-09-02 09:14:00;Saclay;Legumes;5.50;10.0;Normal +2025-09-02 09:08:58;Saclay;Legumes;4.25;10.0;Normal +2025-09-02 09:03:55;Saclay;Legumes;5.75;10.0;Normal +2025-09-02 09:29:09;Saclay;MeP;6.50;8.0;Normal +2025-09-02 09:24:06;Saclay;MeP;3.00;8.0;Normal +2025-09-02 09:19:03;Saclay;MeP;5.75;8.0;Normal +2025-09-02 09:14:01;Saclay;MeP;7.25;8.0;Normal +2025-09-02 09:08:58;Saclay;MeP;4.00;8.0;Normal +2025-09-02 09:03:56;Saclay;MeP;4.25;8.0;Normal +2025-09-02 09:30:38;Meudon;Viandes;4.69;6.0;Normal +2025-09-02 09:25:37;Meudon;Viandes;5.38;6.0;Normal +2025-09-02 09:20:36;Meudon;Viandes;5.25;6.0;Normal +2025-09-02 09:15:36;Meudon;Viandes;4.88;6.0;Normal +2025-09-02 09:10:35;Meudon;Viandes;4.69;6.0;Normal +2025-09-02 09:05:34;Meudon;Viandes;4.44;6.0;Normal +2025-09-02 09:30:38;Meudon;Poissons;4.06;6.0;Normal +2025-09-02 09:25:37;Meudon;Poissons;4.13;6.0;Normal +2025-09-02 09:20:36;Meudon;Poissons;4.13;6.0;Normal +2025-09-02 09:15:36;Meudon;Poissons;3.94;6.0;Normal +2025-09-02 09:10:35;Meudon;Poissons;3.94;6.0;Normal +2025-09-02 09:05:34;Meudon;Poissons;4.25;6.0;Normal +2025-09-02 09:30:38;Meudon;BOF;3.25;8.0;Normal +2025-09-02 09:25:37;Meudon;BOF;2.50;8.0;Normal +2025-09-02 09:20:37;Meudon;BOF;2.50;8.0;Normal +2025-09-02 09:15:36;Meudon;BOF;2.50;8.0;Normal +2025-09-02 09:10:35;Meudon;BOF;2.25;8.0;Normal +2025-09-02 09:05:34;Meudon;BOF;2.50;8.0;Normal diff --git a/app/Monitor.py b/app/Monitor.py index 16ff65b..1b2fd3e 100644 --- a/app/Monitor.py +++ b/app/Monitor.py @@ -6,7 +6,13 @@ from utils_db import connect_to_mysql from dotenv import load_dotenv from utils_sms import envoyer_sms +# === AJOUT GYRO (MQTT) === +import paho.mqtt.client as mqtt +from contextlib import closing +# ----------------------------------------------------------- +# Logs +# ----------------------------------------------------------- if os.name != 'nt': log_dir = Path('/home/debian/Gestion_sondes/Logs') else: @@ -14,15 +20,47 @@ else: log_dir.mkdir(parents=True, exist_ok=True) +# ----------------------------------------------------------- +# Env +# ----------------------------------------------------------- load_dotenv() ENVOI_SMS = os.getenv("ENVOI_SMS") == "1" +# === AJOUT GYRO (MQTT) === +MQTT_HOST = os.getenv("MQTT_HOST", "127.0.0.1") +MQTT_PORT = int(os.getenv("MQTT_PORT", "1883")) +MQTT_USER = os.getenv("MQTT_USER", "") +MQTT_PASS = os.getenv("MQTT_PASS", "") +MQTT_QOS = 1 +GYRO_PUBLISH_GLOBAL = os.getenv("GYRO_PUBLISH_GLOBAL", "0") == "1" print("▶️ Lancement Monitor.py") # --- Suivi des alertes actives pour rappels --- alertes_actives = {} +# === AJOUT GYRO (MQTT) === +def publish_gyro_states(states_by_site: dict): + """Publie Alarmes//Gyro = ON|OFF (retained). Optionnel : Alarmes/Global/Gyro.""" + client = mqtt.Client(client_id="MonitorGyroPublisher", clean_session=True) + if MQTT_USER or MQTT_PASS: + client.username_pw_set(MQTT_USER, MQTT_PASS) + client.connect(MQTT_HOST, MQTT_PORT, keepalive=30) + client.loop_start() + try: + for site, state in states_by_site.items(): + topic = f"Alarmes/{site}/Gyro" + client.publish(topic, state, qos=MQTT_QOS, retain=True) + print(f"📣 MQTT publish {topic} = {state} (retain)", flush=True) + + if GYRO_PUBLISH_GLOBAL: + global_state = "ON" if any(v == "ON" for v in states_by_site.values()) else "OFF" + client.publish("Alarmes/Global/Gyro", global_state, qos=MQTT_QOS, retain=True) + print(f"📣 MQTT publish Alarmes/Global/Gyro = {global_state} (retain)", flush=True) + finally: + client.loop_stop() + client.disconnect() + # --- Fonction de surveillance --- def surveiller(): global alertes_actives @@ -31,6 +69,7 @@ def surveiller(): conn = connect_to_mysql() cursor = conn.cursor(dictionary=True) + # Liste des lieux cursor.execute("SELECT DISTINCT Lieu FROM `Chambres_froides`") lieux = [row['Lieu'] for row in cursor.fetchall()] @@ -38,6 +77,7 @@ def surveiller(): table_temp = lieu table_alertes = f"Alertes_{lieu}" + # Sondes actives et non en entretien cursor.execute(""" SELECT Sonde, Temp_Max FROM Sondes.Chambres_froides @@ -51,6 +91,7 @@ def surveiller(): nom_sonde = sonde['Sonde'] seuil = sonde['Temp_Max'] + # Derniers relevés (30 min = 6 pas de 5 min) cursor.execute(f""" SELECT Date, Temperature FROM {table_temp} WHERE Sonde = %s @@ -58,6 +99,7 @@ def surveiller(): """, (nom_sonde,)) releves = cursor.fetchall() + # Logging détaillé for r in releves: log_entries.append({ "Date": r['Date'], @@ -68,6 +110,7 @@ def surveiller(): "État": "Dépassement" if r['Temperature'] > seuil else "Normal" }) + # Détection dépassement > 30 min if len(releves) == 6: toutes_hors_seuil = all(r['Temperature'] > seuil for r in releves) plus_ancien = releves[-1]['Date'] @@ -96,6 +139,7 @@ def surveiller(): alertes_actives[nom_sonde] = maintenant else: + # Rappel SMS toutes les 1h si toujours en défaut dernier_envoi = alertes_actives.get(nom_sonde) if dernier_envoi and (maintenant - dernier_envoi >= timedelta(hours=1)): message = ( @@ -106,7 +150,7 @@ def surveiller(): envoyer_sms(lieu, message) alertes_actives[nom_sonde] = maintenant - # Vérifier retour à la normale (Acquittement) + # Vérifier retour à la normale (Acquittement) cursor.execute(f""" SELECT Temperature FROM {table_temp} WHERE Sonde = %s @@ -123,14 +167,30 @@ def surveiller(): if nom_sonde in alertes_actives: del alertes_actives[nom_sonde] + # --- À ce stade : tables d'alertes mises à jour. + # On calcule l'état GYRO par site et on publie MQTT. + # === AJOUT GYRO (calcul + publication) === + states = {} + for lieu in lieux: + table_alertes = f"Alertes_{lieu}" + # Etat actif si Status <> 'Acquitté' + cursor.execute(f"SELECT EXISTS(SELECT 1 FROM {table_alertes} WHERE Status <> 'Acquitté' LIMIT 1) AS actif") + actif = cursor.fetchone() + states[lieu] = "ON" if actif and list(actif.values())[0] == 1 else "OFF" + + # Commit avant publication MQTT (les états reflètent la DB) conn.commit() + + # Publie ON/OFF par site (retain) + publish_gyro_states(states) + cursor.close() conn.close() + # Ecriture log CSV if log_entries: import pandas as pd df_logs = pd.DataFrame(log_entries) - try: df_logs.to_csv(log_dir / "monitor.csv", sep=";", index=False) print(f"✅ Log écrit dans {log_dir}/monitor.csv", flush=True)