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