From 807df69a3e9ba69414fb71deb1f75d82e9951e80 Mon Sep 17 00:00:00 2001 From: Michel Date: Fri, 30 May 2025 14:46:02 +0200 Subject: [PATCH] =?UTF-8?q?Mise=20=C3=A0=20jour=20du=2030/5/2025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Logs/monitor.csv | 96 +++++++++++------------ Monitor.py | 2 +- alerte_sms.py | 199 ++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 220 insertions(+), 77 deletions(-) diff --git a/Logs/monitor.csv b/Logs/monitor.csv index 3b3e256..c5716e6 100644 --- a/Logs/monitor.csv +++ b/Logs/monitor.csv @@ -1,49 +1,49 @@ Date;Lieu;Sonde;Température;Seuil;État -2025-05-07 15:50:55;Saclay;Congelateur;-18.25;-15.0;Normal -2025-05-07 15:45:52;Saclay;Congelateur;-18.75;-15.0;Normal -2025-05-07 15:40:50;Saclay;Congelateur;-18.50;-15.0;Normal -2025-05-07 15:35:47;Saclay;Congelateur;-18.75;-15.0;Normal -2025-05-07 15:30:45;Saclay;Congelateur;-18.50;-15.0;Normal -2025-05-07 15:25:42;Saclay;Congelateur;-18.00;-15.0;Normal -2025-05-07 15:50:56;Saclay;BOF;1.50;8.0;Normal -2025-05-07 15:45:53;Saclay;BOF;1.00;8.0;Normal -2025-05-07 15:40:50;Saclay;BOF;1.50;8.0;Normal -2025-05-07 15:35:48;Saclay;BOF;1.00;8.0;Normal -2025-05-07 15:30:45;Saclay;BOF;2.00;8.0;Normal -2025-05-07 15:25:43;Saclay;BOF;1.25;8.0;Normal -2025-05-07 15:50:56;Saclay;Viandes;3.25;6.0;Normal -2025-05-07 15:45:53;Saclay;Viandes;4.00;6.0;Normal -2025-05-07 15:40:51;Saclay;Viandes;3.00;6.0;Normal -2025-05-07 15:35:48;Saclay;Viandes;3.25;6.0;Normal -2025-05-07 15:30:46;Saclay;Viandes;3.00;6.0;Normal -2025-05-07 15:25:43;Saclay;Viandes;4.50;6.0;Normal -2025-05-07 15:50:57;Saclay;Legumes;4.25;10.0;Normal -2025-05-07 15:45:54;Saclay;Legumes;4.50;10.0;Normal -2025-05-07 15:40:51;Saclay;Legumes;5.75;10.0;Normal -2025-05-07 15:35:49;Saclay;Legumes;5.25;10.0;Normal -2025-05-07 15:30:46;Saclay;Legumes;4.00;10.0;Normal -2025-05-07 15:25:44;Saclay;Legumes;5.00;10.0;Normal -2025-05-07 15:50:57;Saclay;MeP;6.00;8.0;Normal -2025-05-07 15:45:54;Saclay;MeP;3.75;8.0;Normal -2025-05-07 15:40:52;Saclay;MeP;3.75;8.0;Normal -2025-05-07 15:35:49;Saclay;MeP;4.50;8.0;Normal -2025-05-07 15:30:47;Saclay;MeP;4.75;8.0;Normal -2025-05-07 15:25:44;Saclay;MeP;3.50;8.0;Normal -2025-05-07 15:51:21;Meudon;Viandes;2.63;6.0;Normal -2025-05-07 15:46:17;Meudon;Viandes;2.44;6.0;Normal -2025-05-07 15:41:13;Meudon;Viandes;4.00;6.0;Normal -2025-05-07 15:36:09;Meudon;Viandes;3.81;6.0;Normal -2025-05-07 15:31:05;Meudon;Viandes;3.63;6.0;Normal -2025-05-07 15:26:02;Meudon;Viandes;3.25;6.0;Normal -2025-05-07 15:51:21;Meudon;Poissons;3.19;6.0;Normal -2025-05-07 15:46:18;Meudon;Poissons;3.13;6.0;Normal -2025-05-07 15:41:14;Meudon;Poissons;2.81;6.0;Normal -2025-05-07 15:36:10;Meudon;Poissons;2.75;6.0;Normal -2025-05-07 15:31:06;Meudon;Poissons;3.06;6.0;Normal -2025-05-07 15:26:02;Meudon;Poissons;3.00;6.0;Normal -2025-05-07 15:51:22;Meudon;BOF;4.25;8.0;Normal -2025-05-07 15:46:18;Meudon;BOF;4.00;8.0;Normal -2025-05-07 15:41:14;Meudon;BOF;3.25;8.0;Normal -2025-05-07 15:36:11;Meudon;BOF;2.75;8.0;Normal -2025-05-07 15:31:07;Meudon;BOF;1.75;8.0;Normal -2025-05-07 15:26:03;Meudon;BOF;1.00;8.0;Normal +2025-05-30 14:40:14;Saclay;Congelateur;-15.00;-15.0;Normal +2025-05-30 14:35:11;Saclay;Congelateur;-15.00;-15.0;Normal +2025-05-30 14:30:08;Saclay;Congelateur;-14.75;-15.0;Dépassement +2025-05-30 14:25:06;Saclay;Congelateur;-14.75;-15.0;Dépassement +2025-05-30 14:22:11;Saclay;Congelateur;-14.25;-15.0;Dépassement +2025-05-30 14:20:03;Saclay;Congelateur;-14.25;-15.0;Dépassement +2025-05-30 14:40:14;Saclay;BOF;1.75;8.0;Normal +2025-05-30 14:35:11;Saclay;BOF;2.25;8.0;Normal +2025-05-30 14:30:09;Saclay;BOF;1.25;8.0;Normal +2025-05-30 14:25:06;Saclay;BOF;1.50;8.0;Normal +2025-05-30 14:22:11;Saclay;BOF;1.75;8.0;Normal +2025-05-30 14:20:04;Saclay;BOF;1.75;8.0;Normal +2025-05-30 14:40:15;Saclay;Viandes;4.50;6.0;Normal +2025-05-30 14:35:12;Saclay;Viandes;3.75;6.0;Normal +2025-05-30 14:30:09;Saclay;Viandes;2.25;6.0;Normal +2025-05-30 14:25:07;Saclay;Viandes;3.75;6.0;Normal +2025-05-30 14:22:11;Saclay;Viandes;4.50;6.0;Normal +2025-05-30 14:20:04;Saclay;Viandes;4.50;6.0;Normal +2025-05-30 14:40:15;Saclay;Legumes;6.00;10.0;Normal +2025-05-30 14:35:12;Saclay;Legumes;5.50;10.0;Normal +2025-05-30 14:30:10;Saclay;Legumes;4.75;10.0;Normal +2025-05-30 14:25:07;Saclay;Legumes;4.25;10.0;Normal +2025-05-30 14:22:11;Saclay;Legumes;6.00;10.0;Normal +2025-05-30 14:20:05;Saclay;Legumes;6.00;10.0;Normal +2025-05-30 14:40:16;Saclay;MeP;7.75;8.0;Normal +2025-05-30 14:35:13;Saclay;MeP;6.00;8.0;Normal +2025-05-30 14:30:10;Saclay;MeP;6.25;8.0;Normal +2025-05-30 14:25:08;Saclay;MeP;4.25;8.0;Normal +2025-05-30 14:22:11;Saclay;MeP;4.75;8.0;Normal +2025-05-30 14:20:05;Saclay;MeP;4.75;8.0;Normal +2025-05-30 14:43:30;Meudon;Viandes;2.44;6.0;Normal +2025-05-30 14:38:26;Meudon;Viandes;3.00;6.0;Normal +2025-05-30 14:33:22;Meudon;Viandes;3.25;6.0;Normal +2025-05-30 14:28:18;Meudon;Viandes;3.75;6.0;Normal +2025-05-30 14:23:15;Meudon;Viandes;4.63;6.0;Normal +2025-05-30 14:22:11;Meudon;Viandes;4.50;6.0;Normal +2025-05-30 14:43:31;Meudon;Poissons;4.88;6.0;Normal +2025-05-30 14:38:27;Meudon;Poissons;4.81;6.0;Normal +2025-05-30 14:33:23;Meudon;Poissons;4.50;6.0;Normal +2025-05-30 14:28:19;Meudon;Poissons;4.50;6.0;Normal +2025-05-30 14:23:15;Meudon;Poissons;4.63;6.0;Normal +2025-05-30 14:22:11;Meudon;Poissons;4.50;6.0;Normal +2025-05-30 14:43:31;Meudon;BOF;3.25;8.0;Normal +2025-05-30 14:38:27;Meudon;BOF;3.75;8.0;Normal +2025-05-30 14:33:24;Meudon;BOF;3.50;8.0;Normal +2025-05-30 14:28:20;Meudon;BOF;2.75;8.0;Normal +2025-05-30 14:23:16;Meudon;BOF;2.00;8.0;Normal +2025-05-30 14:22:11;Meudon;BOF;2.50;8.0;Normal diff --git a/Monitor.py b/Monitor.py index ee5420b..a08768a 100644 --- a/Monitor.py +++ b/Monitor.py @@ -25,7 +25,7 @@ config = { "password": os.getenv("DB_PASSWORD"), "database": os.getenv("DB_NAME") } - +print("▶️ Lancement Monitor.py") # --- Suivi des alertes actives pour rappels --- alertes_actives = {} diff --git a/alerte_sms.py b/alerte_sms.py index 280e40b..444fd55 100644 --- a/alerte_sms.py +++ b/alerte_sms.py @@ -1,33 +1,176 @@ -def send_sms(message: str, site: str) -> None: - phone_numbers = PHONE_NUMBERS_BY_SITE.get(site) - if not phone_numbers or phone_numbers == ['']: - print(f"[!] Aucun numéro défini pour le site {site}. SMS non envoyé.") - return +import mysql.connector +from datetime import datetime, timedelta +import time +from dotenv import load_dotenv +import os +from pathlib import Path + +if os.name != 'nt': + log_dir = Path('/home/debian/Gestion_sondes/Logs') +else: + log_dir = Path.cwd() / 'Logs' + +log_dir.mkdir(parents=True, exist_ok=True) + +load_dotenv() +ENVOI_SMS = os.getenv("ENVOI_SMS") == "1" + +# --- Config MySQL --- +config = { + "host": os.getenv("DB_HOST"), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME") +} + +# --- Suivi des alertes actives pour rappels --- +alertes_actives = {} + +# --- Fonction d'envoi de mail --- +def envoyer_sms_ovh(message, lieu): try: - client = ovh.Client( - endpoint=os.getenv("OVH_ENDPOINT"), - application_key=os.getenv("OVH_APP_KEY"), - application_secret=os.getenv("OVH_APP_SECRET"), - consumer_key=os.getenv("OVH_CONSUMER_KEY"), - ) + import requests + sms_data = { + "account": os.getenv("OVH_ACCOUNT"), + "login": os.getenv("OVH_LOGIN"), + "password": os.getenv("OVH_PASSWORD"), + "message": f"{lieu}: {message}", + "receivers": os.getenv("SMS_DESTINATAIRES", "").split(","), + "sender": os.getenv("SMS_SENDER", "ALERTE") + } - sender = os.getenv("OVH_SMS_SENDER") - account = os.getenv("OVH_SMS_ACCOUNT") + # Exemple d'envoi avec l'API OVH (à adapter selon ton endpoint exact) + response = requests.post("https://www.ovh.com/cgi-bin/sms/http2sms.cgi", data=sms_data) + print(f"📱 SMS envoyé : {response.text}", flush=True) - result = client.post(f"/sms/{account}/jobs", - sender=sender, - message=message, - receivers=phone_numbers, - priority='high', - noStopClause=True, - charset='UTF-8', - class_='phoneDisplay', - coding='7bit', - senderForResponse=False, - validityPeriod=2880) - - print(f"[✓] SMS envoyé à {phone_numbers} pour {site}") - # Optionnel : journaliser_sms(message, site, phone_numbers) except Exception as e: - print(f"[!] Erreur envoi SMS OVH : {e}") + print(f"Erreur envoi SMS : {e}", flush=True) + +# --- Fonction de surveillance --- +def surveiller(): + global alertes_actives + log_entries = [] + try: + conn = mysql.connector.connect(**config) + cursor = conn.cursor(dictionary=True) + + cursor.execute("SELECT DISTINCT Lieu FROM Chambres_froides") + lieux = [row['Lieu'] for row in cursor.fetchall()] + + for lieu in lieux: + table_temp = lieu + table_alertes = f"Alertes_{lieu}" + + cursor.execute("SELECT Sonde, Temp_Max FROM Chambres_froides WHERE Lieu=%s AND Etat='ON'", (lieu,)) + sondes = cursor.fetchall() + + for sonde in sondes: + nom_sonde = sonde['Sonde'] + seuil = sonde['Temp_Max'] + + cursor.execute(f""" + SELECT Date, Temperature FROM {table_temp} + WHERE Sonde = %s + ORDER BY Date DESC LIMIT 6 + """, (nom_sonde,)) + releves = cursor.fetchall() + + for r in releves: + log_entries.append({ + "Date": r['Date'], + "Lieu": lieu, + "Sonde": nom_sonde, + "Température": r['Temperature'], + "Seuil": seuil, + "État": "Dépassement" if r['Temperature'] > seuil else "Normal" + }) + + if len(releves) == 6: + toutes_hors_seuil = all(r['Temperature'] > seuil for r in releves) + plus_ancien = releves[-1]['Date'] + maintenant = datetime.now() + + if toutes_hors_seuil and (maintenant - plus_ancien >= timedelta(minutes=30)): + cursor.execute(f""" + SELECT COUNT(*) as total FROM {table_alertes} + WHERE Sonde=%s AND Status='En cours' + """, (nom_sonde,)) + en_cours = cursor.fetchone() + if en_cours['total'] == 0: + cursor.execute( + f"INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) VALUES (%s, NOW(), 'En cours')", + (nom_sonde,) + ) + print(f"🚨 Alerte déclenchée pour {nom_sonde} ({lieu})", flush=True) + + sujet = f"🚨 ALERTE TEMPÉRATURE - {nom_sonde} ({lieu})" + message = ( + f"La sonde '{nom_sonde}' du site '{lieu}' a dépassé le seuil de {seuil}°C " + f"depuis plus de 30 minutes.\nHeure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}" + ) + destinataires_list = os.getenv("EMAIL_DESTINATAIRES", "").split(",") + envoyer_mail(sujet, message, destinataires_list) + if ENVOI_SMS: + envoyer_sms_ovh(message, lieu) + + # Suivi pour rappels + alertes_actives[nom_sonde] = maintenant + + else: + # Alerte déjà en cours : vérifier s'il faut faire un rappel + dernier_envoi = alertes_actives.get(nom_sonde) + if dernier_envoi and (maintenant - dernier_envoi >= timedelta(hours=1)): + sujet = f"🔔 RAPPEL ALERTE TEMPÉRATURE - {nom_sonde} ({lieu})" + message = ( + f"La sonde '{nom_sonde}' du site '{lieu}' est TOUJOURS en dépassement de seuil (>{seuil}°C).\n" + f"Heure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}" + ) + destinataires_list = os.getenv("EMAIL_DESTINATAIRES").split(",") + envoyer_mail(sujet, message, destinataires_list) + if ENVOI_SMS: + envoyer_sms_ovh(message, lieu) + alertes_actives[nom_sonde] = maintenant + + # Vérifier retour à la normale (Acquittement) + cursor.execute(f""" + SELECT Temperature FROM {table_temp} + WHERE Sonde = %s + ORDER BY Date DESC LIMIT 1 + """, (nom_sonde,)) + derniere = cursor.fetchone() + if derniere and derniere['Temperature'] <= seuil: + cursor.execute(f""" + UPDATE {table_alertes} + SET Status = 'Acquitté' + WHERE Sonde = %s AND Status IN ('En cours', 'Test') + """, (nom_sonde,)) + + # Nettoyage du suivi si normalisé + if nom_sonde in alertes_actives: + del alertes_actives[nom_sonde] + + conn.commit() + cursor.close() + conn.close() + + if log_entries: + import pandas as pd + df_logs = pd.DataFrame(log_entries) + + # Sauvegarde principale + df_logs.to_csv(log_dir / "monitor.csv", sep=";", index=False) + + # Sauvegarde secondaire (Linux uniquement) + if os.name != 'nt': + df_logs.to_csv("/var/log/monitor.csv", sep=";", index=False) + + except Exception as e: + print(f"Erreur : {e}", flush=True) + +# --- Boucle principale --- +while True: + print(f"📡 Vérification à {datetime.now()}", flush=True) + surveiller() + time.sleep(300) # 5 minutes +