Mise à jour du 30/5/2025
This commit is contained in:
@@ -1,49 +1,49 @@
|
|||||||
Date;Lieu;Sonde;Température;Seuil;État
|
Date;Lieu;Sonde;Température;Seuil;État
|
||||||
2025-05-07 15:50:55;Saclay;Congelateur;-18.25;-15.0;Normal
|
2025-05-30 14:40:14;Saclay;Congelateur;-15.00;-15.0;Normal
|
||||||
2025-05-07 15:45:52;Saclay;Congelateur;-18.75;-15.0;Normal
|
2025-05-30 14:35:11;Saclay;Congelateur;-15.00;-15.0;Normal
|
||||||
2025-05-07 15:40:50;Saclay;Congelateur;-18.50;-15.0;Normal
|
2025-05-30 14:30:08;Saclay;Congelateur;-14.75;-15.0;Dépassement
|
||||||
2025-05-07 15:35:47;Saclay;Congelateur;-18.75;-15.0;Normal
|
2025-05-30 14:25:06;Saclay;Congelateur;-14.75;-15.0;Dépassement
|
||||||
2025-05-07 15:30:45;Saclay;Congelateur;-18.50;-15.0;Normal
|
2025-05-30 14:22:11;Saclay;Congelateur;-14.25;-15.0;Dépassement
|
||||||
2025-05-07 15:25:42;Saclay;Congelateur;-18.00;-15.0;Normal
|
2025-05-30 14:20:03;Saclay;Congelateur;-14.25;-15.0;Dépassement
|
||||||
2025-05-07 15:50:56;Saclay;BOF;1.50;8.0;Normal
|
2025-05-30 14:40:14;Saclay;BOF;1.75;8.0;Normal
|
||||||
2025-05-07 15:45:53;Saclay;BOF;1.00;8.0;Normal
|
2025-05-30 14:35:11;Saclay;BOF;2.25;8.0;Normal
|
||||||
2025-05-07 15:40:50;Saclay;BOF;1.50;8.0;Normal
|
2025-05-30 14:30:09;Saclay;BOF;1.25;8.0;Normal
|
||||||
2025-05-07 15:35:48;Saclay;BOF;1.00;8.0;Normal
|
2025-05-30 14:25:06;Saclay;BOF;1.50;8.0;Normal
|
||||||
2025-05-07 15:30:45;Saclay;BOF;2.00;8.0;Normal
|
2025-05-30 14:22:11;Saclay;BOF;1.75;8.0;Normal
|
||||||
2025-05-07 15:25:43;Saclay;BOF;1.25;8.0;Normal
|
2025-05-30 14:20:04;Saclay;BOF;1.75;8.0;Normal
|
||||||
2025-05-07 15:50:56;Saclay;Viandes;3.25;6.0;Normal
|
2025-05-30 14:40:15;Saclay;Viandes;4.50;6.0;Normal
|
||||||
2025-05-07 15:45:53;Saclay;Viandes;4.00;6.0;Normal
|
2025-05-30 14:35:12;Saclay;Viandes;3.75;6.0;Normal
|
||||||
2025-05-07 15:40:51;Saclay;Viandes;3.00;6.0;Normal
|
2025-05-30 14:30:09;Saclay;Viandes;2.25;6.0;Normal
|
||||||
2025-05-07 15:35:48;Saclay;Viandes;3.25;6.0;Normal
|
2025-05-30 14:25:07;Saclay;Viandes;3.75;6.0;Normal
|
||||||
2025-05-07 15:30:46;Saclay;Viandes;3.00;6.0;Normal
|
2025-05-30 14:22:11;Saclay;Viandes;4.50;6.0;Normal
|
||||||
2025-05-07 15:25:43;Saclay;Viandes;4.50;6.0;Normal
|
2025-05-30 14:20:04;Saclay;Viandes;4.50;6.0;Normal
|
||||||
2025-05-07 15:50:57;Saclay;Legumes;4.25;10.0;Normal
|
2025-05-30 14:40:15;Saclay;Legumes;6.00;10.0;Normal
|
||||||
2025-05-07 15:45:54;Saclay;Legumes;4.50;10.0;Normal
|
2025-05-30 14:35:12;Saclay;Legumes;5.50;10.0;Normal
|
||||||
2025-05-07 15:40:51;Saclay;Legumes;5.75;10.0;Normal
|
2025-05-30 14:30:10;Saclay;Legumes;4.75;10.0;Normal
|
||||||
2025-05-07 15:35:49;Saclay;Legumes;5.25;10.0;Normal
|
2025-05-30 14:25:07;Saclay;Legumes;4.25;10.0;Normal
|
||||||
2025-05-07 15:30:46;Saclay;Legumes;4.00;10.0;Normal
|
2025-05-30 14:22:11;Saclay;Legumes;6.00;10.0;Normal
|
||||||
2025-05-07 15:25:44;Saclay;Legumes;5.00;10.0;Normal
|
2025-05-30 14:20:05;Saclay;Legumes;6.00;10.0;Normal
|
||||||
2025-05-07 15:50:57;Saclay;MeP;6.00;8.0;Normal
|
2025-05-30 14:40:16;Saclay;MeP;7.75;8.0;Normal
|
||||||
2025-05-07 15:45:54;Saclay;MeP;3.75;8.0;Normal
|
2025-05-30 14:35:13;Saclay;MeP;6.00;8.0;Normal
|
||||||
2025-05-07 15:40:52;Saclay;MeP;3.75;8.0;Normal
|
2025-05-30 14:30:10;Saclay;MeP;6.25;8.0;Normal
|
||||||
2025-05-07 15:35:49;Saclay;MeP;4.50;8.0;Normal
|
2025-05-30 14:25:08;Saclay;MeP;4.25;8.0;Normal
|
||||||
2025-05-07 15:30:47;Saclay;MeP;4.75;8.0;Normal
|
2025-05-30 14:22:11;Saclay;MeP;4.75;8.0;Normal
|
||||||
2025-05-07 15:25:44;Saclay;MeP;3.50;8.0;Normal
|
2025-05-30 14:20:05;Saclay;MeP;4.75;8.0;Normal
|
||||||
2025-05-07 15:51:21;Meudon;Viandes;2.63;6.0;Normal
|
2025-05-30 14:43:30;Meudon;Viandes;2.44;6.0;Normal
|
||||||
2025-05-07 15:46:17;Meudon;Viandes;2.44;6.0;Normal
|
2025-05-30 14:38:26;Meudon;Viandes;3.00;6.0;Normal
|
||||||
2025-05-07 15:41:13;Meudon;Viandes;4.00;6.0;Normal
|
2025-05-30 14:33:22;Meudon;Viandes;3.25;6.0;Normal
|
||||||
2025-05-07 15:36:09;Meudon;Viandes;3.81;6.0;Normal
|
2025-05-30 14:28:18;Meudon;Viandes;3.75;6.0;Normal
|
||||||
2025-05-07 15:31:05;Meudon;Viandes;3.63;6.0;Normal
|
2025-05-30 14:23:15;Meudon;Viandes;4.63;6.0;Normal
|
||||||
2025-05-07 15:26:02;Meudon;Viandes;3.25;6.0;Normal
|
2025-05-30 14:22:11;Meudon;Viandes;4.50;6.0;Normal
|
||||||
2025-05-07 15:51:21;Meudon;Poissons;3.19;6.0;Normal
|
2025-05-30 14:43:31;Meudon;Poissons;4.88;6.0;Normal
|
||||||
2025-05-07 15:46:18;Meudon;Poissons;3.13;6.0;Normal
|
2025-05-30 14:38:27;Meudon;Poissons;4.81;6.0;Normal
|
||||||
2025-05-07 15:41:14;Meudon;Poissons;2.81;6.0;Normal
|
2025-05-30 14:33:23;Meudon;Poissons;4.50;6.0;Normal
|
||||||
2025-05-07 15:36:10;Meudon;Poissons;2.75;6.0;Normal
|
2025-05-30 14:28:19;Meudon;Poissons;4.50;6.0;Normal
|
||||||
2025-05-07 15:31:06;Meudon;Poissons;3.06;6.0;Normal
|
2025-05-30 14:23:15;Meudon;Poissons;4.63;6.0;Normal
|
||||||
2025-05-07 15:26:02;Meudon;Poissons;3.00;6.0;Normal
|
2025-05-30 14:22:11;Meudon;Poissons;4.50;6.0;Normal
|
||||||
2025-05-07 15:51:22;Meudon;BOF;4.25;8.0;Normal
|
2025-05-30 14:43:31;Meudon;BOF;3.25;8.0;Normal
|
||||||
2025-05-07 15:46:18;Meudon;BOF;4.00;8.0;Normal
|
2025-05-30 14:38:27;Meudon;BOF;3.75;8.0;Normal
|
||||||
2025-05-07 15:41:14;Meudon;BOF;3.25;8.0;Normal
|
2025-05-30 14:33:24;Meudon;BOF;3.50;8.0;Normal
|
||||||
2025-05-07 15:36:11;Meudon;BOF;2.75;8.0;Normal
|
2025-05-30 14:28:20;Meudon;BOF;2.75;8.0;Normal
|
||||||
2025-05-07 15:31:07;Meudon;BOF;1.75;8.0;Normal
|
2025-05-30 14:23:16;Meudon;BOF;2.00;8.0;Normal
|
||||||
2025-05-07 15:26:03;Meudon;BOF;1.00;8.0;Normal
|
2025-05-30 14:22:11;Meudon;BOF;2.50;8.0;Normal
|
||||||
|
|||||||
|
@@ -25,7 +25,7 @@ config = {
|
|||||||
"password": os.getenv("DB_PASSWORD"),
|
"password": os.getenv("DB_PASSWORD"),
|
||||||
"database": os.getenv("DB_NAME")
|
"database": os.getenv("DB_NAME")
|
||||||
}
|
}
|
||||||
|
print("▶️ Lancement Monitor.py")
|
||||||
# --- Suivi des alertes actives pour rappels ---
|
# --- Suivi des alertes actives pour rappels ---
|
||||||
alertes_actives = {}
|
alertes_actives = {}
|
||||||
|
|
||||||
|
|||||||
199
alerte_sms.py
199
alerte_sms.py
@@ -1,33 +1,176 @@
|
|||||||
def send_sms(message: str, site: str) -> None:
|
import mysql.connector
|
||||||
phone_numbers = PHONE_NUMBERS_BY_SITE.get(site)
|
from datetime import datetime, timedelta
|
||||||
if not phone_numbers or phone_numbers == ['']:
|
import time
|
||||||
print(f"[!] Aucun numéro défini pour le site {site}. SMS non envoyé.")
|
from dotenv import load_dotenv
|
||||||
return
|
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:
|
try:
|
||||||
client = ovh.Client(
|
import requests
|
||||||
endpoint=os.getenv("OVH_ENDPOINT"),
|
sms_data = {
|
||||||
application_key=os.getenv("OVH_APP_KEY"),
|
"account": os.getenv("OVH_ACCOUNT"),
|
||||||
application_secret=os.getenv("OVH_APP_SECRET"),
|
"login": os.getenv("OVH_LOGIN"),
|
||||||
consumer_key=os.getenv("OVH_CONSUMER_KEY"),
|
"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")
|
# Exemple d'envoi avec l'API OVH (à adapter selon ton endpoint exact)
|
||||||
account = os.getenv("OVH_SMS_ACCOUNT")
|
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:
|
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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user