diff --git a/.env b/.env
index 9311f3a..cc77ae5 100644
--- a/.env
+++ b/.env
@@ -16,11 +16,11 @@ OVH_APP_KEY=f725d07b2f98a195
OVH_APP_SECRET=5ca392a0a728e2395edd426bb1e11ad6
OVH_CONSUMER_KEY=305f2e8611e58b83930de84ee65c99f9
OVH_ENDPOINT=ovh-eu
-OVH_SMS_ACCOUNT=sms-jm164396-1
OVH_SMS_SENDER=DOMO91FR
OVH_SERVICE_NAME=sms-jm164396-1
-SMS_RECEIVER=+33635164680
+SMS_RECEIVER=0635164680
+OVH_PASSWORD=w*j&A2j*QT^HL6
ENVOI_SMS=1
-PHONE_SACLAY=+33682069405,+33650270939
-PHONE_MEUDON=+33666271128
+PHONE_SACLAY=33682069405,33650270939
+PHONE_MEUDON=33666271128
PHONE_ADMIN=+33635164680
diff --git a/alerte_sms.py b/alerte_sms.py
new file mode 100644
index 0000000..291b9aa
--- /dev/null
+++ b/alerte_sms.py
@@ -0,0 +1,174 @@
+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:
+ import requests
+ sms_data = {
+ "account": os.getenv("OVH_SMS_ACCOUNT"),
+ "login": os.getenv("OVH_SERVICE_NAME"),
+ "password": os.getenv("OVH_PASSWORD"),
+ "message": f"{lieu}: {message}",
+ "receivers": os.getenv("SMS_RECEIVER", "").split(","),
+ "sender": os.getenv("OVH_SMS_SENDER")
+ }
+
+ # 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)
+
+ except Exception as 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 Sondes.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 Sondes.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')}"
+ )
+
+ 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')}"
+ )
+
+ 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
+
diff --git a/app/Interface.py b/app/Interface.py
index 6815123..b687fa1 100644
--- a/app/Interface.py
+++ b/app/Interface.py
@@ -17,7 +17,7 @@ st.set_page_config(page_title="Domo91 - Surveillance", layout="wide")
if "authenticated" not in st.session_state:
st.session_state["authenticated"] = False
st.session_state["role"] = None
- st.session_state["lieu_autorise"] = None
+ st.session_state["site_autorise"] = None
st.title("📡 Supervision Températures")
@@ -154,8 +154,8 @@ if "authenticated" not in st.session_state:
st.session_state["authenticated"] = False
if "role" not in st.session_state:
st.session_state["role"] = None
-if "lieu_autorise" not in st.session_state:
- st.session_state["lieu_autorise"] = None
+if "site_autorise" not in st.session_state:
+ st.session_state["site_autorise"] = None
# --- Connexion utilisateur dans la sidebar ---
st.sidebar.header("🔐 Connexion")
@@ -178,7 +178,7 @@ if not st.session_state.get("authenticated"):
st.stop()
st.session_state["authenticated"] = True
st.session_state["role"] = result["role"]
- st.session_state["lieu_autorise"] = result["Lieu"]
+ st.session_state["site_autorise"] = result["Lieu"]
st.success(f"Connecté comme {result['role']} ({result['Lieu']})")
# Enregistrement de la connexion dans Connexion_Log
try:
@@ -202,31 +202,12 @@ else:
if st.sidebar.button("🔓 Déconnexion", key="logout_sidebar"):
st.session_state["authenticated"] = False
st.session_state["role"] = None
- st.session_state["lieu_autorise"] = None
+ st.session_state["site_autorise"] = None
st.rerun()
def hash_password(password):
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
-def ajouter_utilisateur(utilisateur, mot_de_passe, role, lieu, expiration):
- try:
- conn = mysql.connector.connect(**db_config)
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM MotsDePasse WHERE utilisateur = %s", (utilisateur,))
- if cursor.fetchone():
- return False, "❌ Utilisateur déjà existant."
-
- hash_mdp = hash_password(mot_de_passe)
- cursor.execute("""
- INSERT INTO MotsDePasse (utilisateur, mot_de_passe, role, Lieu, Expiration)
- VALUES (%s, %s, %s, %s, %s)
- """, (utilisateur, hash_mdp, role, lieu, expiration))
- conn.commit()
- cursor.close()
- conn.close()
- return True, "✅ Utilisateur ajouté avec succès."
- except Exception as e:
- return False, f"⚠️ Erreur : {e}"
def afficher_gestion_expiration(conn):
st.subheader("🔐 Gestion des expirations d'accès")
@@ -269,7 +250,7 @@ def afficher_gestion_expiration(conn):
# 📄 Affichage bouton PDF si une date est choisie
site_pdf = (
- st.session_state.get("lieu_autorise")
+ st.session_state.get("site_autorise")
if st.session_state.get("role") != "superviseur"
else st.session_state.get("selected_site")
@@ -285,7 +266,7 @@ if site_pdf and date_pdf:
# --- Forcer une alerte de test dynamique (réservé aux superviseurs)
if st.session_state.get("authenticated") and st.session_state.get("role") == "superviseur":
site_actuel = (
- st.session_state.get("lieu_autorise") or "Saclay"
+ st.session_state.get("site_autorise") or "Saclay"
if st.session_state.get("role") != "superviseur"
else st.session_state.get("selected_site")
)
@@ -338,14 +319,14 @@ if st.session_state.get("authenticated") and st.session_state.get("role") == "su
if st.button("🔓 Déconnexion", key="logout_main"):
st.session_state["authenticated"] = False
st.session_state["role"] = None
- st.session_state["lieu_autorise"] = None
+ st.session_state["site_autorise"] = None
st.rerun()
st.markdown("---")
st.subheader("📄 Rapport PDF")
if "selected_date" in st.session_state:
if st.button("📅 Télécharger l'état du jour (PDF)"):
- site = st.session_state["lieu_autorise"]
+ site = st.session_state["site_autorise"]
date_val = st.session_state["selected_date"].strftime("%Y-%m-%d")
generer_pdf(site, date_val, periode)
else:
@@ -382,10 +363,10 @@ if st.session_state["authenticated"]:
if "role" not in st.session_state:
st.session_state["role"] = None
- if "lieu_autorise" not in st.session_state:
- st.session_state["lieu_autorise"] = None
+ if "site_autorise" not in st.session_state:
+ st.session_state["site_autorise"] = None
site_selectionne = (
- st.session_state["lieu_autorise"]
+ st.session_state["site_autorise"]
if st.session_state["role"] != "superviseur"
else st.session_state.get("selected_site", "Saclay")
)
@@ -421,7 +402,46 @@ if st.session_state["authenticated"]:
onglet = st.sidebar.radio("📁 Navigation", onglets_possibles, index=onglets_possibles.index(onglet))
st.session_state["onglet_actif"] = onglet
-# --- ONGLET ACCUEIL ---
+ # --- Bandeau Alertes ---
+ try:
+ conn = get_connection()
+ cursor = conn.cursor(dictionary=True)
+
+ # Récupérer le site autorisé depuis la session
+ site = st.session_state.get("lieu_autorise")
+
+ if site:
+ # Lecture des alertes non acquittées pour ce site
+ cursor.execute(f"""
+ SELECT Id, Sonde, Debut_defaut, Etat
+ FROM Alertes_{site}
+ WHERE Etat != 'Acquitté'
+ ORDER BY Debut_defaut DESC
+ """)
+ alertes = cursor.fetchall()
+
+ if alertes:
+ st.markdown(
+ f"
"
+ f"🚨 {len(alertes)} alerte(s) non résolue(s) sur {site}"
+ f"
",
+ unsafe_allow_html=True
+ )
+ else:
+ st.markdown(
+ f""
+ f"✅ Aucune alerte en cours sur {site}"
+ f"
",
+ unsafe_allow_html=True
+ )
+
+ cursor.close()
+ conn.close()
+
+ except Exception as e:
+ st.error(f"Erreur lors de la récupération des alertes : {e}")
+
+ # --- ONGLET ACCUEIL ---
if onglet == "Accueil":
st.markdown("## Sélection du site et de la date")
try:
@@ -432,12 +452,12 @@ if st.session_state["authenticated"]:
site_selectionne = st.selectbox("📍 Choisissez un site :", sites_possibles)
st.session_state["selected_site"] = site_selectionne
else:
- site_selectionne = st.session_state["lieu_autorise"]
+ site_selectionne = st.session_state["site_autorise"]
st.info(f"Site imposé : {site_selectionne}")
selected_date = st.date_input("📅 Date du relevé", value=date.today())
st.session_state["selected_date"] = selected_date
- site_selectionne = st.session_state.get("lieu_autorise") or st.session_state.get("selected_site")
+ site_selectionne = st.session_state.get("site_autorise") or st.session_state.get("selected_site")
if not site_selectionne:
st.warning("Aucun site sélectionné.")
@@ -514,7 +534,7 @@ if st.session_state["authenticated"]:
cursor = conn.cursor(dictionary=True)
site = (
- st.session_state["lieu_autorise"]
+ st.session_state["site_autorise"]
if st.session_state["role"] != "superviseur"
else st.session_state.get("selected_site", "Saclay")
)
@@ -589,7 +609,7 @@ if st.session_state["authenticated"]:
st.error(f"Erreur lors de l'ajout de la sonde : {e}")
# --- Affichage automatique des alertes non acquittées ---
site_selectionne = (
- st.session_state.get("lieu_autorise")
+ st.session_state.get("site_autorise")
if st.session_state.get("role") != "superviseur"
else st.session_state.get("selected_site")
)
@@ -662,7 +682,7 @@ if st.session_state["authenticated"]:
st.markdown("
", unsafe_allow_html=True)
role = st.session_state.get("role", "utilisateur")
- lieu = st.session_state.get("lieu_autorise") if role != "superviseur" else st.selectbox("Choisir un lieu :",
+ lieu = st.session_state.get("site_autorise") if role != "superviseur" else st.selectbox("Choisir un lieu :",
["Saclay", "Meudon"])
if not lieu:
st.warning("Aucun site sélectionné.")
diff --git a/app/Logs/monitor.csv b/app/Logs/monitor.csv
index ea3b80b..767af2f 100644
--- a/app/Logs/monitor.csv
+++ b/app/Logs/monitor.csv
@@ -1,49 +1,37 @@
Date;Lieu;Sonde;Température;Seuil;État
-2025-07-26 13:04:30;Saclay;Congelateur;-14.75;-15.0;Dépassement
-2025-07-26 12:59:27;Saclay;Congelateur;-11.25;-15.0;Dépassement
-2025-07-26 12:54:25;Saclay;Congelateur;-6.50;-15.0;Dépassement
-2025-07-26 12:49:22;Saclay;Congelateur;-4.75;-15.0;Dépassement
-2025-07-26 12:44:19;Saclay;Congelateur;-7.75;-15.0;Dépassement
-2025-07-26 12:39:17;Saclay;Congelateur;-11.50;-15.0;Dépassement
-2025-07-26 13:04:30;Saclay;BOF;1.75;8.0;Normal
-2025-07-26 12:59:28;Saclay;BOF;2.25;8.0;Normal
-2025-07-26 12:54:25;Saclay;BOF;1.00;8.0;Normal
-2025-07-26 12:49:22;Saclay;BOF;2.00;8.0;Normal
-2025-07-26 12:44:20;Saclay;BOF;0.75;8.0;Normal
-2025-07-26 12:39:17;Saclay;BOF;2.00;8.0;Normal
-2025-07-26 13:04:31;Saclay;Viandes;21.00;6.0;Dépassement
-2025-07-26 12:59:28;Saclay;Viandes;21.00;6.0;Dépassement
-2025-07-26 12:54:26;Saclay;Viandes;20.75;6.0;Dépassement
-2025-07-26 12:49:23;Saclay;Viandes;21.00;6.0;Dépassement
-2025-07-26 12:44:20;Saclay;Viandes;20.75;6.0;Dépassement
-2025-07-26 12:39:18;Saclay;Viandes;21.00;6.0;Dépassement
-2025-07-26 13:04:31;Saclay;Legumes;5.25;10.0;Normal
-2025-07-26 12:59:29;Saclay;Legumes;4.25;10.0;Normal
-2025-07-26 12:54:26;Saclay;Legumes;2.75;10.0;Normal
-2025-07-26 12:49:23;Saclay;Legumes;5.50;10.0;Normal
-2025-07-26 12:44:21;Saclay;Legumes;5.00;10.0;Normal
-2025-07-26 12:39:18;Saclay;Legumes;3.75;10.0;Normal
-2025-07-26 13:04:32;Saclay;MeP;6.25;8.0;Normal
-2025-07-26 12:59:29;Saclay;MeP;7.75;8.0;Normal
-2025-07-26 12:54:27;Saclay;MeP;7.00;8.0;Normal
-2025-07-26 12:49:24;Saclay;MeP;5.75;8.0;Normal
-2025-07-26 12:44:21;Saclay;MeP;3.25;8.0;Normal
-2025-07-26 12:39:19;Saclay;MeP;4.75;8.0;Normal
+2025-07-28 06:36:01;Saclay;Congelateur;-17.50;-15.0;Normal
+2025-07-28 06:30:58;Saclay;Congelateur;-18.00;-15.0;Normal
+2025-07-28 06:25:56;Saclay;Congelateur;-16.75;-15.0;Normal
+2025-07-28 06:20:53;Saclay;Congelateur;-17.25;-15.0;Normal
+2025-07-28 06:15:51;Saclay;Congelateur;-17.75;-15.0;Normal
+2025-07-28 06:10:48;Saclay;Congelateur;-17.75;-15.0;Normal
+2025-07-28 06:36:01;Saclay;BOF;3.25;8.0;Normal
+2025-07-28 06:30:59;Saclay;BOF;2.75;8.0;Normal
+2025-07-28 06:25:56;Saclay;BOF;2.00;8.0;Normal
+2025-07-28 06:20:54;Saclay;BOF;2.00;8.0;Normal
+2025-07-28 06:15:51;Saclay;BOF;0.25;8.0;Normal
+2025-07-28 06:10:48;Saclay;BOF;2.50;8.0;Normal
+2025-07-28 06:36:03;Saclay;MeP;5.75;8.0;Normal
+2025-07-28 06:31:00;Saclay;MeP;7.50;8.0;Normal
+2025-07-28 06:25:58;Saclay;MeP;6.75;8.0;Normal
+2025-07-28 06:20:55;Saclay;MeP;5.75;8.0;Normal
+2025-07-28 06:15:53;Saclay;MeP;3.00;8.0;Normal
+2025-07-28 06:10:50;Saclay;MeP;5.00;8.0;Normal
+2025-07-27 09:19:44;Meudon;Viandes;3.94;6.0;Normal
+2025-07-26 18:10:43;Meudon;Viandes;3.94;6.0;Normal
+2025-07-26 14:20:01;Meudon;Viandes;3.94;6.0;Normal
+2025-07-26 14:17:35;Meudon;Viandes;3.94;6.0;Normal
2025-07-26 07:09:45;Meudon;Viandes;3.94;6.0;Normal
2025-07-25 14:37:50;Meudon;Viandes;3.94;6.0;Normal
-2025-07-25 14:32:11;Meudon;Viandes;3.94;6.0;Normal
-2025-07-25 14:30:20;Meudon;Viandes;3.94;6.0;Normal
-2025-07-24 11:00:36;Meudon;Viandes;3.94;6.0;Normal
-2025-07-24 10:41:08;Meudon;Viandes;3.94;6.0;Normal
+2025-07-27 09:19:44;Meudon;Poissons;3.94;6.0;Normal
+2025-07-26 18:10:43;Meudon;Poissons;3.94;6.0;Normal
+2025-07-26 14:20:01;Meudon;Poissons;3.94;6.0;Normal
+2025-07-26 14:17:35;Meudon;Poissons;3.94;6.0;Normal
2025-07-26 07:09:45;Meudon;Poissons;3.94;6.0;Normal
2025-07-25 14:37:50;Meudon;Poissons;3.94;6.0;Normal
-2025-07-25 14:32:11;Meudon;Poissons;3.94;6.0;Normal
-2025-07-25 14:30:20;Meudon;Poissons;3.94;6.0;Normal
-2025-07-24 11:00:36;Meudon;Poissons;3.94;6.0;Normal
-2025-07-24 10:41:08;Meudon;Poissons;3.94;6.0;Normal
+2025-07-27 09:19:44;Meudon;BOF;3.00;8.0;Normal
+2025-07-26 18:10:43;Meudon;BOF;3.00;8.0;Normal
+2025-07-26 14:20:01;Meudon;BOF;3.00;8.0;Normal
+2025-07-26 14:17:35;Meudon;BOF;3.00;8.0;Normal
2025-07-26 07:09:45;Meudon;BOF;3.00;8.0;Normal
2025-07-25 14:37:50;Meudon;BOF;3.00;8.0;Normal
-2025-07-25 14:32:11;Meudon;BOF;3.00;8.0;Normal
-2025-07-25 14:30:20;Meudon;BOF;3.00;8.0;Normal
-2025-07-24 11:00:36;Meudon;BOF;3.00;8.0;Normal
-2025-07-24 10:41:08;Meudon;BOF;3.00;8.0;Normal
diff --git a/app/domo91.py b/app/domo91.py
index 4369ebf..00720c7 100644
--- a/app/domo91.py
+++ b/app/domo91.py
@@ -23,7 +23,7 @@ st.write("Bienvenue sur l’application de supervision.")
for key, default in {
"authenticated": False,
"role": None,
- "lieu_autorise": None,
+ "site_autorise": None,
"onglet_actif": "Accueil",
"selected_date": date.today(),
"selected_site": "Saclay",
@@ -53,7 +53,7 @@ def verifier_password(input_password, hash_en_base):
# --- Connexion utilisateur ---
-if not st.session_state["authenticated"]:
+if not st.session_state.get("authenticated", False):
login = st.sidebar.text_input("Nom d'utilisateur")
password = st.sidebar.text_input("Mot de passe", type="password")
@@ -61,27 +61,37 @@ if not st.session_state["authenticated"]:
try:
conn = get_connection()
cursor = conn.cursor(dictionary=True)
- cursor.execute("SELECT * FROM MotsDePasse WHERE utilisateur = %s", (login,))
+
+ # On interroge la bonne table
+ cursor.execute("""
+ SELECT NomUtilisateur, MotDePasseHash, role, Site, DateExpiration
+ FROM Acces.Utilisateurs
+ WHERE NomUtilisateur = %s
+ LIMIT 1
+ """, (login,))
result = cursor.fetchone()
- if result and verifier_password(password, result["mot_de_passe"]):
- if result["Expiration"] and result["Expiration"] < date.today():
- st.sidebar.error("⛔ Accès expiré.")
- cursor.close()
- conn.close()
- st.stop()
+ if not result:
+ st.sidebar.error("Identifiants invalides")
+ elif result["DateExpiration"] and result["DateExpiration"] < date.today():
+ st.sidebar.error("⛔ Accès expiré.")
+ elif not verifier_password(password, result["MotDePasseHash"]):
+ st.sidebar.error("Identifiants invalides")
+ else:
+ # Authentification réussie
st.session_state.update({
"authenticated": True,
"role": result["role"],
- "lieu_autorise": result["Lieu"]
+ "site_autorise": result["Site"]
})
+
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- cursor.execute("INSERT INTO Connexion_Log (Utilisateur, Lieu, Date_Connexion) VALUES (%s, %s, %s)",
- (login, result["Lieu"], now_str))
+ cursor.execute("""
+ INSERT INTO Sondes.Connexion_Log (Utilisateur, Lieu, Date_Connexion)
+ VALUES (%s, %s, %s)
+ """, (result["NomUtilisateur"], result["Site"], now_str))
conn.commit()
st.rerun()
- else:
- st.sidebar.error("Identifiants invalides")
cursor.close()
conn.close()
@@ -89,22 +99,61 @@ if not st.session_state["authenticated"]:
except Exception as e:
st.sidebar.error(f"Erreur connexion : {e}")
else:
- st.sidebar.success(f"Connecté ({st.session_state['role']})")
+ st.sidebar.success(f"Connecté ({st.session_state.get('role')})")
if st.sidebar.button("🔓 Déconnexion"):
- for key in ["authenticated", "role", "lieu_autorise"]:
+ for key in ["authenticated", "role", "site_autorise"]:
st.session_state[key] = False if key == "authenticated" else None
st.rerun()
+# --- Bandeau Alertes ---
+try:
+ conn = get_connection()
+ cursor = conn.cursor(dictionary=True)
+
+ # Récupérer le site autorisé depuis la session
+ site = st.session_state.get("lieu_autorise")
+
+ if site:
+ # Lecture des alertes non acquittées pour ce site
+ cursor.execute(f"""
+ SELECT Id, Sonde, Debut_defaut, Etat
+ FROM Alertes_{site}
+ WHERE Etat != 'Acquitté'
+ ORDER BY Debut_defaut DESC
+ """)
+ alertes = cursor.fetchall()
+
+ if alertes:
+ st.markdown(
+ f""
+ f"🚨 {len(alertes)} alerte(s) non résolue(s) sur {site}"
+ f"
",
+ unsafe_allow_html=True
+ )
+ else:
+ st.markdown(
+ f""
+ f"✅ Aucune alerte en cours sur {site}"
+ f"
",
+ unsafe_allow_html=True
+ )
+
+ cursor.close()
+ conn.close()
+
+except Exception as e:
+ st.error(f"Erreur lors de la récupération des alertes : {e}")
+
+
# --- Navigation ---
if st.session_state["authenticated"]:
onglets = ["Accueil", "Entretien"] if st.session_state["role"] != "superviseur" else ["Accueil", "Statistiques",
- "Entretien", "Traffic",
- "Utilisateurs"]
+ "Entretien", "Traffic"]
onglet_selectionne = st.sidebar.radio("📁 Navigation", onglets,
index=onglets.index(st.session_state["onglet_actif"]))
st.session_state["onglet_actif"] = onglet_selectionne
- site_actuel = st.session_state.get("lieu_autorise") if st.session_state[
+ site_actuel = st.session_state.get("site_autorise") if st.session_state[
"role"] != "superviseur" else st.session_state.get(
"selected_site", "Saclay")
date_selectionnee = st.session_state.get("selected_date", date.today())
@@ -192,7 +241,7 @@ if st.session_state["authenticated"]:
elif onglet_selectionne == "Statistiques":
st.markdown("## 📈 Statistiques de température")
site = (
- st.session_state["lieu_autorise"]
+ st.session_state["site_autorise"]
if st.session_state["role"] != "superviseur"
else st.session_state.get("selected_site", "Saclay")
)
@@ -203,7 +252,7 @@ if st.session_state["authenticated"]:
cursor = conn.cursor(dictionary=True)
site = (
- st.session_state["lieu_autorise"]
+ st.session_state["site_autorise"]
if st.session_state["role"] != "superviseur"
else st.session_state.get("selected_site", "Saclay")
)
@@ -333,26 +382,3 @@ if st.session_state["authenticated"]:
st.error(f"Erreur : {e}")
st.text(traceback.format_exc())
- # --- Onglet Utilisateurs ---
- elif onglet_selectionne == "Utilisateurs":
- st.header("👥 Gestion des utilisateurs")
- with st.form("ajouter_utilisateur"):
- new_user = st.text_input("Nom d'utilisateur")
- new_pass = st.text_input("Mot de passe", type="password")
- new_role = st.selectbox("Rôle", ["utilisateur", "superviseur"])
- new_lieu = st.selectbox("Lieu", ["Saclay", "Meudon", "Roissy"])
- if st.form_submit_button("Ajouter"):
- try:
- conn = get_connection()
- cursor = conn.cursor()
- hash_mdp = hash_password(new_pass)
- cursor.execute(
- "INSERT INTO MotsDePasse (utilisateur, mot_de_passe, role, Lieu) VALUES (%s, %s, %s, %s)",
- (new_user, hash_mdp, new_role, new_lieu))
- conn.commit()
- cursor.close()
- conn.close()
- st.success("Utilisateur ajouté.")
- except Exception as e:
- st.error(f"Erreur : {e}")
- st.text(traceback.format_exc())
\ No newline at end of file
diff --git a/app/utils_sms.py b/app/utils_sms.py
index 19f48e0..ea3a292 100644
--- a/app/utils_sms.py
+++ b/app/utils_sms.py
@@ -19,16 +19,34 @@ def envoyer_sms(message: str, lieu: str = ""):
return
service_name = services[0]
- numero_dest = os.getenv("NUMERO_DESTINATAIRE")
+ numero_dest = os.getenv("SMS_RECEIVER")
+ sender = os.getenv("OVH_SMS_SENDER")
+
+ if numero_dest.startswith('+'):
+ numero_dest = '00' + numero_dest[1:]
+
+ if not numero_dest or not numero_dest.isdigit():
+ print(f"❌ Numéro de téléphone invalide ou manquant : '{numero_dest}'", flush=True)
+ return
+
+ payload = {
+ "sender": sender,
+ "receivers": [numero_dest],
+ "message": message, # Pas d'encodage ni de nettoyage ici
+ "priority": "high",
+ "noStopClause": False
+
+ }
+
+ print("📤 Requête envoyée à OVH :")
+ print(payload)
+
+ result = client.post(f'/sms/{service_name}/jobs', **payload)
- result = client.post(f'/sms/{service_name}/jobs',
- sender='Monitor',
- message=message,
- receivers=[numero_dest],
- noStopClause=True
- )
print(f"📱 SMS envoyé à {numero_dest} pour {lieu}. Job ID : {result['ids']}", flush=True)
except Exception as e:
- print(f"Erreur envoi SMS : {e}", flush=True)
+ print(f"❌ Erreur envoi SMS : {e}", flush=True)
+if __name__ == "__main__":
+ envoyer_sms("Test SMS OVH", lieu="utils_sms")
diff --git a/scripts/envoi_sms.php b/scripts/envoi_sms.php
new file mode 100644
index 0000000..78cd443
--- /dev/null
+++ b/scripts/envoi_sms.php
@@ -0,0 +1,32 @@
+get('/sms/');
+foreach ($smsServices as $smsService) {
+
+ print_r($smsService);
+}
+
+?>
\ No newline at end of file