From cb899e91b05a9f2ac495fc71b62dd0c0b3daf4e7 Mon Sep 17 00:00:00 2001 From: Michel Date: Sat, 19 Apr 2025 10:57:51 +0200 Subject: [PATCH 01/25] =?UTF-8?q?=F0=9F=94=A7=20Nettoyage=20de=20product?= =?UTF-8?q?=20:=20retrait=20des=20fichiers=20import=C3=A9s=20par=20erreur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Chaufferie.py | 30 ---- Purge_Alertes_saclay.py | 18 --- Telegram_sondes.py | 324 ---------------------------------------- logs_analyse.py | 39 ----- supervisor_watchdog.py | 47 ------ 5 files changed, 458 deletions(-) delete mode 100644 Chaufferie.py delete mode 100644 Purge_Alertes_saclay.py delete mode 100644 Telegram_sondes.py delete mode 100644 logs_analyse.py delete mode 100644 supervisor_watchdog.py diff --git a/Chaufferie.py b/Chaufferie.py deleted file mode 100644 index 73eab00..0000000 --- a/Chaufferie.py +++ /dev/null @@ -1,30 +0,0 @@ -import paho.mqtt.client as mqtt -import mysql.connector - -# Configuration de la connexion MySQL -mydb = mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" -) - -# Fonction de callback quand un message est reçu -def on_message(_client, _userdata, msg): - print(f"Message reçu sur {msg.topic}: {msg.payload.decode()}") - cursor = mydb.cursor() - frigo_name = msg.topic.split('/')[-1] # Prend la dernière partie après le "/" - sql = "INSERT INTO Chaufferie (Sonde, Temperature) VALUES (%s, %s)" - val = (frigo_name, msg.payload.decode()) - cursor.execute(sql, val) - mydb.commit() - -# Configuration du client MQTT -client = mqtt.Client() -client.username_pw_set("Bwps", "scJ5ACj2keRfI^") -client.on_message = on_message - -client.connect("54.36.188.119", 1883, 60) -client.subscribe("Module_01/#") # S'abonner à tous les topics commençant par Saclay - -client.loop_forever() # Rester connecté en continu pour écouter les messages diff --git a/Purge_Alertes_saclay.py b/Purge_Alertes_saclay.py deleted file mode 100644 index 83f89e2..0000000 --- a/Purge_Alertes_saclay.py +++ /dev/null @@ -1,18 +0,0 @@ -# Purge de la table Sondes.Alertes_Saclay. -import mysql.connector - -config = { - "host": "54.36.188.119", - "user": "michel", - "password": "#SO2&1nf%mZ@jfh", - "database": "Sondes" -} - -conn = mysql.connector.connect(**config) -cursor = conn.cursor() -cursor.execute("DELETE FROM Alertes_Saclay WHERE Debut_defaut < NOW() - INTERVAL 7 DAY") -conn.commit() -cursor.close() -conn.close() - -print("✅ Alertes de plus de 7 jours supprimées.") diff --git a/Telegram_sondes.py b/Telegram_sondes.py deleted file mode 100644 index 3077d92..0000000 --- a/Telegram_sondes.py +++ /dev/null @@ -1,324 +0,0 @@ -# Gestion des chambres froides & alertes Telegram -import requests -import mysql.connector -from datetime import datetime, timedelta -import time -import sys -import os -import schedule - -def connect_db(): - return mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" - ) - -def envoi_etat_quotidien(cursor, site): - token = "8128378340:AAF2sO3gaH1XpMNya_pEslzerqokoCiFRGs" - chat_id = get_chat_id(cursor, site) - etat_sondes(cursor, site, chat_id, token) - print(f"[INFO] État des sondes envoyé pour le site {site}.") - -def get_active_sondes(cursor, site): - query = "SELECT Sonde, Temp_Max FROM Chambres_froides WHERE Lieu = %s AND Etat = 'On';" - cursor.execute(query, (site,)) - result = cursor.fetchall() - return {row[0]: float(row[1]) for row in result} - - -def check_temperature_limits(cursor, table_historique, sonde, limite, duree=30): - temps_limite = datetime.now() - timedelta(minutes=duree) - query = f""" - SELECT Temperature - FROM {table_historique} - WHERE Sonde = %s AND Date >= %s - ORDER BY Date DESC LIMIT 6; - """ - cursor.execute(query, (sonde, temps_limite)) - result = cursor.fetchall() - if len(result) == 6 and all(float(temp[0]) > limite for temp in result): - return True, float(result[0][0]) - return False, None - -def simulate_alert(db, cursor, site, sonde, chat_id, token): - table_alertes = f"Alertes_{site}" - print(f"[TEST] tentative d'insertion de sonde : {sonde} dans table {table_alertes}") - - try: - query = f""" - INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) - VALUES (%s, NOW(), 'test') - ON DUPLICATE KEY UPDATE Debut_defaut = NOW(), Status = 'test'; - """ - cursor.execute(query, (sonde,)) - db.commit() - print(f"[TEST] insertion ou mise à jour réussie pour {sonde}.") - except Exception as e: - print(f"[ERREUR] lors de l'insertion test : {e}") - - message = f"⚠️ TEST ALERTE : Simulation d'une alerte pour la sonde {sonde}." - url = f"https://api.telegram.org/bot{token}/sendMessage" - try: - requests.get(url, params={'chat_id': chat_id, 'text': message}) - except Exception as e: - print(f"[ERREUR] lors de l'envoi Telegram : {e}") - -def create_alert(db, cursor, table_alertes, sonde, temperature): - query = f""" - INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) - VALUES (%s, NOW(), 'en cours'); - """ - cursor.execute(query, (sonde,)) - send_telegram_message(sonde, temperature) - print(f"Alerte créée pour la sonde {sonde}") - db.commit() - -def resolve_alert(cursor, table_alertes, sonde): - query = f""" - UPDATE {table_alertes} - SET Status = 'résolu' - WHERE Sonde = %s AND Status = 'en cours'; - """ - cursor.execute(query, (sonde,)) - print(f"Alerte résolue pour la sonde {sonde}") - -def acquitter_alerte(cursor, site, sonde, chat_id, token): - table_alertes = f"Alertes_{site}" - query = f""" - UPDATE {table_alertes} - SET Status = 'acquittée' - WHERE Sonde = %s AND Status IN ('en cours', 'test'); - """ - cursor.execute(query, (sonde,)) - lignes_modifiees = cursor.rowcount - - if lignes_modifiees > 0: - message = f"✅ Alerte acquittée pour la sonde {sonde} par commande Telegram." - else: - message = f"ℹ️ Aucune alerte active à acquitter pour la sonde {sonde}." - - url = f"https://api.telegram.org/bot{token}/sendMessage" - try: - requests.get(url, params={'chat_id': chat_id, 'text': message}) - except Exception as e: - print(f"[ERREUR] envoi message Telegram : {e}") - -def send_telegram_message(sonde, temperature): - token = "5714323406:AAGSj9jrfBHbfxubz3ooabPEizI8aBOLnvE" - chat_id = "-1002442631825" - message = f"⚠️ Alerte température : La sonde {sonde} dépasse la limite avec une température de {temperature}°C." - url = f"https://api.telegram.org/bot{token}/sendMessage" - params = {'chat_id': chat_id, 'text': message} - try: - response = requests.get(url, params=params) - if response.status_code == 200: - print(f"Message envoyé via Telegram pour la sonde {sonde}.") - else: - print(f"Erreur lors de l'envoi du message Telegram : {response.status_code}") - except Exception as e: - print(f"Erreur lors de l'envoi du message Telegram : {e}") - - -def get_chat_id(cursor, site): - cursor.execute("SELECT Chat_ID FROM Sites WHERE Nom = %s;", (site,)) - result = cursor.fetchone() - if result: - return result[0] - else: - print(f"Aucun Chat_ID trouvé pour le site {site}") - return None - -def passer_en_maintenance(cursor, site, sonde, chat_id, token): - try: - query = """ - UPDATE Chambres_froides - SET Etat = 'Off' - WHERE Lieu = %s AND Sonde = %s; - """ - cursor.execute(query, (site, sonde)) - lignes_modifiees = cursor.rowcount - - if lignes_modifiees > 0: - message = f"🛠️ La sonde {sonde} a été passée en mode maintenance (OFF)." - else: - message = f"⚠️ Sonde {sonde} introuvable pour le site {site}." - - url = f"https://api.telegram.org/bot{token}/sendMessage" - requests.get(url, params={'chat_id': chat_id, 'text': message}) - - except Exception as e: - print(f"[ERREUR] lors du passage en maintenance : {e}") - -def reactiver_sonde(cursor, site, sonde, chat_id, token): - try: - query = """ - UPDATE Chambres_froides - SET Etat = 'On' - WHERE Lieu = %s AND Sonde = %s; - """ - cursor.execute(query, (site, sonde)) - lignes_modifiees = cursor.rowcount - - if lignes_modifiees > 0: - message = f"✅ La sonde {sonde} a été réactivée (ON)." - else: - message = f"⚠️ Sonde {sonde} introuvable pour le site {site}." - - url = f"https://api.telegram.org/bot{token}/sendMessage" - requests.get(url, params={'chat_id': chat_id, 'text': message}) - - except Exception as e: - print(f"[ERREUR] lors de la réactivation : {e}") - -def etat_sondes(cursor, site, chat_id, token): - query = "SELECT Sonde, Etat FROM Chambres_froides WHERE Lieu = %s ORDER BY Sonde;" - cursor.execute(query, (site,)) - result = cursor.fetchall() - - message = f"📊 État des sondes - {site} :\n" - for sonde, etat in result: - symbole = "🟢" if etat.upper() == "ON" else "🔴" - message += f"{symbole} {sonde} ({etat.upper()})\n" - - url = f"https://api.telegram.org/bot{token}/sendMessage" - requests.get(url, params={'chat_id': chat_id, 'text': message}) - -def monitor_temperatures_simple(site, db, cursor): - table_historique = site - table_alertes = f"Alertes_{site}" - sondes = get_active_sondes(cursor, site) - print(f"[MONITORING] Sondes actives pour {site} : {sondes}") - - for sonde, limite in sondes.items(): - alert_needed, temperature = check_temperature_limits(cursor, table_historique, sonde, limite) - if alert_needed: - create_alert(db, cursor, table_alertes, sonde, temperature) - else: - resolve_alert(cursor, table_alertes, sonde) - - db.commit() - print("[MONITORING] Vérification des sondes terminée.") - -def monitor_temperatures(site): - db = connect_db() - cursor = db.cursor() - - table_historique = site # Ex: Saclay - table_alertes = f"Alertes_{site}" # Ex: Alertes_Saclay - - sondes = get_active_sondes(cursor, site) - print(f"Sondes actives pour le site {site} :", sondes) - - for sonde, limite in sondes.items(): - alert_needed, temperature = check_temperature_limits(cursor, table_historique, sonde, limite) - if alert_needed: - create_alert(db, cursor, table_alertes, sonde, temperature) - else: - resolve_alert(cursor, table_alertes, sonde) - - db.commit() - listen_for_commands(db, cursor, site) - cursor.close() - db.close() - -def listen_for_commands(db, cursor, site): - token = "5714323406:AAGSj9jrfBHbfxubz3ooabPEizI8aBOLnvE" - chat_id = get_chat_id(cursor, site) - offset_file = os.path.join(os.path.dirname(__file__), f"last_update_id_{site}.txt") - - try: - with open(offset_file, 'r') as f: - last_update_id = int(f.read().strip()) - except FileNotFoundError: - last_update_id = None - - url = f"https://api.telegram.org/bot{token}/getUpdates" - if last_update_id is not None: - url += f"?offset={last_update_id + 1}" - - try: - response = requests.get(url) - data = response.json() - - if "result" in data: - for update in data["result"]: - update_id = update["update_id"] - - if "message" in update: - message = update["message"]["text"] - print(f"[CMD] Message reçu : {message}") - - if message.lower().startswith("/etat"): - etat_sondes(cursor, site, chat_id, token) - - elif message.lower().startswith("/acquitter"): - parts = message.strip().split(" ", 1) - if len(parts) == 2 and parts[1].strip(): - sonde = parts[1].strip() - acquitter_alerte(cursor, site, sonde, chat_id, token) - db.commit() - else: - erreur_msg = "❌ Utilisation incorrecte. Format attendu : /acquitter " - requests.get(f"https://api.telegram.org/bot{token}/sendMessage", - params={'chat_id': chat_id, 'text': erreur_msg}) - elif message.lower().startswith("/maintenance"): - parts = message.strip().split(" ", 1) - if len(parts) == 2 and parts[1].strip(): - sonde = parts[1].strip() - passer_en_maintenance(cursor, site, sonde, chat_id, token) - db.commit() - else: - erreur_msg = "❌ Utilisation incorrecte. Format attendu : /maintenance " - requests.get(f"https://api.telegram.org/bot{token}/sendMessage", - params={'chat_id': chat_id, 'text': erreur_msg}) - elif message.lower().startswith("/reactiver"): - parts = message.strip().split(" ", 1) - if len(parts) == 2 and parts[1].strip(): - sonde = parts[1].strip() - reactiver_sonde(cursor, site, sonde, chat_id, token) - db.commit() - else: - erreur_msg = "❌ Utilisation incorrecte. Format attendu : /reactiver " - requests.get(f"https://api.telegram.org/bot{token}/sendMessage", - params={'chat_id': chat_id, 'text': erreur_msg}) - elif message.lower().startswith("/test"): - parts = message.strip().split(" ", 1) - if len(parts) == 2 and parts[1].strip(): - sonde = parts[1].strip() - simulate_alert(db, cursor, site, sonde, chat_id, token) - else: - erreur_msg = "❌ Utilisation incorrecte. Format attendu : /test " - requests.get(f"https://api.telegram.org/bot{token}/sendMessage", - params={'chat_id': chat_id, 'text': erreur_msg}) - - with open(offset_file, 'w') as f: - f.write(str(update_id)) - - except Exception as e: - print(f"Erreur lors de la lecture des commandes Telegram : {e}") - - -def main(): - site = sys.argv[1] if len(sys.argv) > 1 else "Saclay" - db = connect_db() - cursor = db.cursor() - - # ✅ Planification : tous les jours à 07:00 → état des sondes - schedule.every().day.at("07:00").do(envoi_etat_quotidien, cursor=cursor, site=site) - - # ✅ Planification : toutes les 2m30 → surveillance - schedule.every(2).minutes.do(monitor_temperatures_simple, site=site, db=db, cursor=cursor) - - try: - while True: - schedule.run_pending() - listen_for_commands(db, cursor, site) - time.sleep(2) # petite pause pour ne pas surcharger Telegram - finally: - cursor.close() - db.close() - -if __name__ == "__main__": - main() diff --git a/logs_analyse.py b/logs_analyse.py deleted file mode 100644 index 5630e30..0000000 --- a/logs_analyse.py +++ /dev/null @@ -1,39 +0,0 @@ -import streamlit as st -import pandas as pd -import matplotlib.pyplot as plt - -# --- Chargement des données --- -try: - df = pd.read_csv("/home/debian/travail/logs_monitor.csv", sep=";", parse_dates=["Date"]) -except FileNotFoundError: - st.error("📂 Le fichier de log n'a pas été trouvé.") - st.stop() - -st.title("🧾 Analyse des scans Monitor.py") - -# --- Filtres --- -df["Date_str"] = df["Date"].dt.date -dates_dispo = sorted(df["Date_str"].unique(), reverse=True) -date_selection = st.selectbox("📅 Sélectionnez une date :", dates_dispo) - -lieux = sorted(df["Lieu"].unique()) -lieu_selection = st.selectbox("📍 Site :", lieux) - -df_filtré = df[(df["Date_str"] == date_selection) & (df["Lieu"] == lieu_selection)] - -sondes = sorted(df_filtré["Sonde"].unique()) -sonde_selection = st.selectbox("🧪 Sonde :", sondes) - -df_sonde = df_filtré[df_filtré["Sonde"] == sonde_selection] - -# --- Graphique température --- -fig, ax = plt.subplots(figsize=(10, 4)) -ax.plot(df_sonde["Date"], df_sonde["Température"], marker='o') -ax.set_title(f"Évolution de la température - {sonde_selection}") -ax.set_xlabel("Heure") -ax.set_ylabel("Température (°C)") -st.pyplot(fig) - -# --- Tableau complet du jour/sonde sélectionnés --- -st.markdown("### 📋 Détail des scans") -st.dataframe(df_sonde[["Date", "Sonde", "Température", "Seuil", "État"]], use_container_width=True) diff --git a/supervisor_watchdog.py b/supervisor_watchdog.py deleted file mode 100644 index 76a7834..0000000 --- a/supervisor_watchdog.py +++ /dev/null @@ -1,47 +0,0 @@ -# Controle des services supervisor envoi en cas de disfonction et tous les jours à sept heures -# a faire tourner avec crontb -e en sudo -# */30 * * * * /home/debian/travail/myenv/bin/python3 /home/debian/travail/Scripts/supervisor_watchdog.py -import subprocess -import smtplib -from email.mime.text import MIMEText -from datetime import datetime - -heure_actuelle = datetime.now().strftime("%H:%M") -etat_services = [] -anomalies = [] - -try: - output = subprocess.check_output("/usr/bin/supervisorctl status", shell=True, text=True) - for line in output.splitlines(): - parts = line.split() - if len(parts) >= 2: - nom, statut = parts[0], parts[1] - etat_services.append(f"{nom} ➤ {statut}") - if statut != "RUNNING": - anomalies.append(f"{nom} ➤ {statut}") -except Exception as e: - etat_services.append("❌ Impossible d'exécuter supervisorctl") - anomalies.append(f"Erreur : {e}") - -# Déclenchement mail si anomalie ou à 07:00 -envoyer_mail = bool(anomalies) or heure_actuelle == "07:00" - -if envoyer_mail: - sujet = "⚠️ Alerte Supervisor" if anomalies else "✅ Rapport quotidien Supervisor" - intro = "🛑 Les services suivants ne sont pas en RUNNING :" if anomalies else "✅ Tous les services supervisés sont en RUNNING." - contenu = f"{intro}\n\n" + "\n".join(etat_services) - - msg = MIMEText(contenu) - msg["Subject"] = sujet - msg["From"] = "alertes_saclay@domo91.fr" - msg["To"] = "services@domo91.fr" - - try: - with smtplib.SMTP_SSL("smtp.mail.ovh.net", 465) as server: - server.login("alertes_saclay@domo91.fr", "Kdpke674y23Feq^H") - server.sendmail(msg["From"], [msg["To"]], msg.as_string()) - print("📧 Mail envoyé.") - except Exception as e: - print(f"Erreur envoi mail : {e}") -else: - print("🕖 Aucun mail envoyé (tout est OK et ce n’est pas l’heure du rapport).") From 1feee47b65be59b2ed4c7b3491f9c101a53d8e84 Mon Sep 17 00:00:00 2001 From: Michel Date: Sat, 19 Apr 2025 11:44:39 +0200 Subject: [PATCH 02/25] =?UTF-8?q?=F0=9F=94=84=20Sync=20partielle=20depuis?= =?UTF-8?q?=20develop=20:=20fichiers=20communs=20uniquement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | Bin 2663 -> 5302 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/README.md b/README.md index 481e80fec79b0d46660adc581601bf1a7f758692..e6fc8ac078c60fa04ac34fff05c087176dc8123a 100644 GIT binary patch literal 5302 zcmbW5-EJFI5XVn%kr0)*>fND{NLq*kg^LoAN}-`2Qq!iylnaV#H*r2_VjJ5DjnoI? zArKN058;cp6bTUKk_RaKe=~b>b{#tfWNos0cFvsn{Ljq({ci>07 zBP;js=vo(bSI=03vyiN2R>YF?_Dgo12{YQUG@N}FB{kmI5HHuQEsjV#&}n^9b;W1b zD0X$vJM5D<%^RQJ#SS$Rb~ zutK(TvXFbfDV}}3&)z3LkORvR+noP_Sqw|GOD8N1zk42)E9Lk-89gbt;8Pb@GBZkt z7(Nv@Jk>e&5v$8uVGof&EM$$AhJVR3w?$9PZ0RazCq7=(g)U9K+q10TMC99+ZLl*^ zywG2=uPv%xv1?s^Td$n;lOf4SR4IeYcoK~tUhquhbkD^YzAiLM5slY9ay`CU#@Q{A zcuvb!&#_H$AWH}0ga6J&VrV1$Vb5d-dJv=d+VhS)J5xjeSoYttykR)$`;HV_eZ^p- z#Q6v5K>T(j#Uq^$`XB+38^c4b5J6sjTR6LsEcsg1pthlI{+A88@=UCQM2gdUlOe;r!(#a#JD?6lm5q0O95L=#MTiU%S zgimL`6}~(n_sP8fN>S<2vmnjNT0STuw|s6*7HDhTGh@2dek^>{lUJ$;c_1ml*N$Qp z98A(a(fT!MwP9KkxA7G}cC3g;UwgnTFgL~Y6XW}&r~zIH{>|Uhb|jur(YPx4$zh_9 zn$uORHKxdmoXeu7jvN)SnIeE#oK&f)w%f}7NeD1&oUjnP$;z^1Ra`zzF7{2X4wDhV zvu?ajx;}SmL-u7vY_e`?MU@_!ulG_d4FG zl&w!pjictY96}oE^J(bpmC8F9$I7bqM1ogK?ot)+%fbWU$e@xB`8;ahrab38tJkh0 zQSVqrr4yzh%a!4usw>y0WCpdV+7V!rDDGbI64rnk-6B@;!nv5yH1|kzq9Bf=@S6&T ztaJ=*TLsnV5WMn7!<(hyP2nE#($-!$)5UrXr<=K=iypjQ$V11H<&Cl-c3~c%uWuPG z9z^anJ@?GY9woDuWnd_hI9?`st6a|?pLbroa#qnjf}3IU%tt9tJj $1#nofprODPGr#bKpevaKW;WOvpn+>gv&wf>E zsKA08=ku~y>IDyVS1bC$L*vLv^fTVA=XVRzlvt$?_o{!WJN%zBeMa}UjOsvnxL{GW z5$*_Wks|ZDU%}sgxPO9Y&FqRTqfJj=7*{IHx3Y!nMBVjnB>Oa~?oz*Bg%EIX|8d7P zX=Xd~ws&A{S%|JVWne-a@0ole#%7t9_Xk*vyo#DsF6nRTsuHH%5iZ{-POo;2mycK1 z<&84>QuXQQ%P2j)FUWJmZHt<={KB+=OxTEt0G!2(-WxqutiZn^$*0X*jnd3;)i_jV zlW6m0-MZX7L*EkRAk6ACpMvO}j&w%u#3uP*X?Rw+yR4W?o&V$>p=|qQ*^GEc&#Gjp zOLNp6$tFEzI@-^PvZ<(Z1n@qK$pge*(;J@{%bJgSCQsc=Wc!j~&rYA$UY}9WoKAcE ztA*z^@sYFKV={)%jmOC^8c^@*6rT&=%&FvNvJ?I#Gx+cNMw5Yl)vaqviMV3 z92|w4cPq)#YdR&(FJ968ixCyozp=d;QzrYHks<(%@P>CBZX=I@n%3&#NWSo%X_cuF lk5qF7oZzj~(O*+b^Oq0*cH`g9h+>ywBszK#&nvIe{{U*xo?rj~ literal 2663 zcmbtW&2HO95We#%rZEsZawW>H4~~HrknPwBAfZlWB!{N3R^&=rgXA*%<63|Y?h~|t zivnp52@3Q_w;p=vp*OyXU!Zw}e!CR)lg0*$@?cBU?99$LGv7CB^zO~;-~ayeTRP_2 z2$@mLHEEf-f3ZxZ5ic!1-dtVNA-+D`3W8RiCn91qYpT_>&AGY|x;k!nnio^Wj8$CM zYQ!#hM0-JYjpGDD~H^X%pw;d!)*~yQ?5i7iJT=| z!GpQ52J=(JGMx%NhckxjVhY&Y9cs1C z)6-LLxC#)@@Y@_4xZPk;#Nnl%aDNieG+ad=ZZ!tV0bEt%lqVb@r4-Vml!*lY*R})n$!s8oc2@i4rUtr{tRo^<0~YZ` zIp4kc;V)Ou7P`6k)ds;wUp~4-D<}M`jgc|A-V4Lnrs-IuK&BDNG~8`AFL#>FF!E2> z^v`|6FU_YjFPrl~oAcs4SdMx9jq7MTYY~g)k>}h5!Gw-b0+;2=GodevKTy&C`%Hof z+V}&{Ew4XddFLzmr{1jH@^No*-HDsZ68gm`FOAH?^3G4*e@9|UrD`k(m{2!TA~&vc zP4QDS@`E=3qfw5|@eNZ<4LaI$#?95MvKjTi!<9L3u*JBcN4 znuYyspL~D=oDdm5(=1BZxXfij2Qo=;M|XJ*N?IaR(#S%C%p9R~{h{0Z(@KDpTTsb= z4AAk1?hc^%OAC&0gsL0n()3_Hp+oR2&tjH=h3I5#Mi3VatjM%s$nCwpz9Yh%Z!w@h zy8(Stx+rNqkr4_Q1ifCb&-5r53JiZDK{D0MrstmW_I7r5!xS^clW;~9XQj|Ja3+Z2 ziausM32ofEw;^pwVR!FTaF+k%bJccEM0D zqBUwm;4Z_IMW1&AZ~vlfSN&qi40g{&7R#@pA8S>*?jAtW^;-pNkc+$ml;PQmrSHF^mNg^PR6q+n)6FI3Z9xyfCw@+>5q@FtAvVx#h#{bLOC5Cmi*L;N0&pKvkE;x^fjB8x{ju6p zT#8P7dzkNv3^sDBM+g8h_v7vqb)P{X9Vb+Z-5GbEwrNyM`& Date: Mon, 21 Apr 2025 20:29:03 +0200 Subject: [PATCH 03/25] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20domo91.py=20d?= =?UTF-8?q?epuis=20develop=20:=20PDF=20+=20d=C3=A9connexion=20+=20droits?= =?UTF-8?q?=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domo91.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/domo91.py b/domo91.py index 2a73efa..e26270b 100644 --- a/domo91.py +++ b/domo91.py @@ -105,6 +105,7 @@ def generer_pdf(site, date_str): except Exception as e: st.error(f"Erreur lors de la génération du PDF : {e}") + # --- Initialisation des variables de session --- if "authenticated" not in st.session_state: st.session_state["authenticated"] = False @@ -113,9 +114,7 @@ if "role" not in st.session_state: if "lieu_autorise" not in st.session_state: st.session_state["lieu_autorise"] = None -# --- Sidebar (connexion + bouton PDF) --- - -# 🔐 Connexion (toujours affichée) +# --- Connexion utilisateur dans la sidebar --- st.sidebar.header("🔐 Connexion") if not st.session_state.get("authenticated"): login = st.sidebar.text_input("Nom d'utilisateur") @@ -131,6 +130,7 @@ if not st.session_state.get("authenticated"): st.session_state["role"] = result["role"] st.session_state["lieu_autorise"] = result["Lieu"] st.success(f"Connecté comme {result['role']} ({result['Lieu']})") + st.rerun() else: st.sidebar.error("Identifiants invalides") cursor.close() @@ -139,14 +139,28 @@ if not st.session_state.get("authenticated"): st.sidebar.error(f"Erreur lors de la connexion à la base : {e}") else: st.sidebar.success(f"Connecté ({st.session_state['role']})") - if st.sidebar.button("🔓 Déconnexion"): + 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.rerun() -# 🔔 Forcer une alerte de test dynamique avec choix de la sonde -if st.session_state.get("authenticated"): +# 📄 Affichage bouton PDF si une date est choisie +site_pdf = ( + st.session_state.get("lieu_autorise") + if st.session_state.get("role") != "superviseur" + else st.session_state.get("selected_site") +) +date_pdf = st.session_state.get("selected_date") +if site_pdf and date_pdf: + st.sidebar.markdown("---") + st.sidebar.subheader("📄 Rapport PDF") + if st.sidebar.button("📥 Télécharger l’état du jour (PDF)", key="pdf_btn"): + generer_pdf(site_pdf, date_pdf.strftime("%Y-%m-%d")) + + +# 🔔 Forcer une alerte de test dynamique avec choix de la sonde (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") if st.session_state.get("role") != "superviseur" @@ -186,7 +200,7 @@ if st.session_state.get("authenticated"): else: st.success(f"Connecté ({st.session_state['role']})") - if st.button("🔓 Déconnexion"): + if st.button("🔓 Déconnexion", key="logout_main"): st.session_state["authenticated"] = False st.session_state["role"] = None st.session_state["lieu_autorise"] = None @@ -307,7 +321,7 @@ if st.session_state["authenticated"]: except Exception as e: st.error(f"Erreur MySQL : {e}") if st.session_state["role"] == "superviseur": - with st.expander("➕ Ajouter une nouvelle chambre froide", expanded=False): + with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): with st.form("ajout_sonde"): nouvelle_sonde = st.text_input("Nom de la sonde") temp_max = st.number_input("Température maximale autorisée (°C)", value=4) @@ -364,13 +378,13 @@ if st.session_state["role"] == "superviseur": temp_max = chambre["Temp_Max"] moins, val, plus = st.columns([1, 2, 1]) with moins: - if st.button("➖", key=f"moins_{chambre['Id']}"): + if st.button("-", key=f"moins_{chambre['Id']}"): temp_max -= 1 with val: st.markdown(f"
{temp_max}°C
", unsafe_allow_html=True) with plus: - if st.button("➕", key=f"plus_{chambre['Id']}"): + if st.button("+", key=f"plus_{chambre['Id']}"): temp_max += 1 if new_etat != chambre["Etat"] or temp_max != chambre["Temp_Max"]: From 4991b6354ccdb78724c3958f026a20be1c938c2d Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 09:55:05 +0200 Subject: [PATCH 04/25] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20domo91.py=20d?= =?UTF-8?q?epuis=20develop=20r=C3=A9vision=205?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domo91.py | 255 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 168 insertions(+), 87 deletions(-) diff --git a/domo91.py b/domo91.py index e26270b..484cfe7 100644 --- a/domo91.py +++ b/domo91.py @@ -34,7 +34,8 @@ def generer_pdf(site, date_str): conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) - cursor.execute(f"SELECT Sonde, Date, Temperature FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", (date_str,)) + cursor.execute(f"SELECT Sonde, Date, Temperature FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", + (date_str,)) rows = cursor.fetchall() df = pd.DataFrame(rows) df["Heure"] = pd.to_datetime(df["Date"]).dt.strftime("%H:%M") @@ -45,7 +46,8 @@ def generer_pdf(site, date_str): releves[sonde] = list(zip(df_sonde["Heure"], df_sonde["Temperature"])) table_alertes = f"Alertes_{site}" - cursor.execute(f"SELECT Sonde, Debut_defaut, Status FROM {table_alertes} WHERE DATE(Debut_defaut) = %s", (date_str,)) + cursor.execute(f"SELECT Sonde, Debut_defaut, Status FROM {table_alertes} WHERE DATE(Debut_defaut) = %s", + (date_str,)) alertes = cursor.fetchall() cursor.close() @@ -106,6 +108,7 @@ def generer_pdf(site, date_str): except Exception as e: st.error(f"Erreur lors de la génération du PDF : {e}") + # --- Initialisation des variables de session --- if "authenticated" not in st.session_state: st.session_state["authenticated"] = False @@ -130,6 +133,16 @@ if not st.session_state.get("authenticated"): st.session_state["role"] = result["role"] st.session_state["lieu_autorise"] = result["Lieu"] st.success(f"Connecté comme {result['role']} ({result['Lieu']})") + # Enregistrement de la connexion dans Connexion_Log + try: + now = datetime.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)) + conn.commit() + except Exception as e: + st.warning(f"⚠️ Connexion enregistrée échouée : {e}") st.rerun() else: st.sidebar.error("Identifiants invalides") @@ -158,7 +171,6 @@ if site_pdf and date_pdf: if st.sidebar.button("📥 Télécharger l’état du jour (PDF)", key="pdf_btn"): generer_pdf(site_pdf, date_pdf.strftime("%Y-%m-%d")) - # 🔔 Forcer une alerte de test dynamique avec choix de la sonde (réservé aux superviseurs) if st.session_state.get("authenticated") and st.session_state.get("role") == "superviseur": site_actuel = ( @@ -218,108 +230,179 @@ if st.session_state.get("authenticated") and st.session_state.get("role") == "su # --- CONTENU PRINCIPAL SI AUTHENTIFIÉ --- if st.session_state["authenticated"]: - st.markdown("## Sélection du site et de la date") + # --- AFFICHAGE GLOBAL DES ALERTES NON ACQUITTÉES --- try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) - sites_possibles = ["Saclay", "Meudon"] - if st.session_state["role"] == "superviseur": - site_selectionne = st.selectbox("📍 Choisissez un site :", sites_possibles) - st.session_state["selected_site"] = site_selectionne - else: - site_selectionne = st.session_state["lieu_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 - - cursor.execute( - f"SELECT * FROM `{site_selectionne}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", - (selected_date.strftime("%Y-%m-%d"),) + site_selectionne = ( + st.session_state["lieu_autorise"] + if st.session_state["role"] != "superviseur" + else st.session_state.get("selected_site", "Saclay") ) - rows = cursor.fetchall() - if rows: + + table_alertes = f"Alertes_{site_selectionne}" + cursor.execute( + f"SELECT Sonde, Debut_defaut, Status FROM `{table_alertes}` WHERE Status != 'Acquitté' ORDER BY Debut_defaut DESC" + ) + alertes = cursor.fetchall() + + if alertes: + df_alertes = pd.DataFrame(alertes) + st.subheader("🚨 Alertes non acquittées") + st.dataframe(df_alertes, use_container_width=True) + else: + st.success("✅ Aucune alerte en cours.") + + 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["role"] == "superviseur": + onglet = st.sidebar.radio("📁 Navigation", ["Accueil", "Statistiques"]) + else: + onglet = "Accueil" + + if onglet == "Accueil": + st.markdown("## Sélection du site et de la date") + try: + conn = mysql.connector.connect(**db_config) + cursor = conn.cursor(dictionary=True) + sites_possibles = ["Saclay", "Meudon"] + if st.session_state["role"] == "superviseur": + site_selectionne = st.selectbox("📍 Choisissez un site :", sites_possibles) + st.session_state["selected_site"] = site_selectionne + else: + site_selectionne = st.session_state["lieu_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 + + cursor.execute( + f"SELECT * FROM `{site_selectionne}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", + (selected_date.strftime("%Y-%m-%d"),) + ) + rows = cursor.fetchall() + if rows: + df = pd.DataFrame(rows) + df["Date"] = pd.to_datetime(df["Date"]) + sondes = sorted(df["Sonde"].unique()) + sonde_choisie = st.selectbox("🧪 Choisissez une sonde :", sondes, key="selectbox_accueil") + df_sonde = df[df["Sonde"] == sonde_choisie] + + df_sonde.loc[:, "Heure"] = df_sonde["Date"].dt.hour + + tranche = st.radio("🕒 Tranche horaire :", + ["Toute la journée", "Matin (6h-12h)", "Après-midi (12h-18h)", "Nuit (18h-6h)"]) + + if tranche == "Matin (6h-12h)": + df_sonde = df_sonde[(df_sonde["Heure"] >= 6) & (df_sonde["Heure"] < 12)] + elif tranche == "Après-midi (12h-18h)": + df_sonde = df_sonde[(df_sonde["Heure"] >= 12) & (df_sonde["Heure"] < 18)] + elif tranche == "Nuit (18h-6h)": + df_sonde = df_sonde[(df_sonde["Heure"] >= 18) | (df_sonde["Heure"] < 6)] + df_sonde = df_sonde.copy() + + cursor.execute("SELECT Temp_Max FROM Chambres_froides WHERE Lieu = %s AND Sonde = %s", + (site_selectionne, sonde_choisie)) + seuil = cursor.fetchone() + seuil_temp = seuil["Temp_Max"] if seuil else 10 + + st.subheader("📊 Tableau des relevés") + df_filtré = df_sonde.copy() + df_filtré = df_filtré.drop(columns="Id", errors="ignore") + + + def surlignage_temp(val): + try: + if float(val) > seuil_temp: + return "color: red; font-weight: bold" + except: + pass + return "" + + + styled_df = df_filtré.style.applymap(surlignage_temp, subset=["Temperature"]) + st.dataframe(styled_df, use_container_width=True) + + st.subheader("📈 Évolution de la température") + fig, ax = plt.subplots(figsize=(10, 4)) + ax.plot(df_filtré["Date"], df_filtré["Temperature"], marker='o', label="Température") + ax.axhline(seuil_temp, color='red', linestyle='--', label=f"Seuil {seuil_temp}°C") + ax.set_xlabel("Heure") + ax.set_ylabel("Température (°C)") + ax.set_title(f"{sonde_choisie} - {selected_date.strftime('%d/%m/%Y')}") + ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) + ax.legend() + st.pyplot(fig) + + cursor.close() + conn.close() + + except Exception as e: + st.error(f"Erreur MySQL : {e}") + + elif onglet == "Statistiques": + st.markdown("## 📈 Statistiques de température") + try: + conn = mysql.connector.connect(**db_config) + cursor = conn.cursor(dictionary=True) + + site = ( + st.session_state["lieu_autorise"] + if st.session_state["role"] != "superviseur" + else st.session_state.get("selected_site", "Saclay") + ) + + date_val = st.session_state.get("selected_date", date.today()) + + cursor.execute( + f"SELECT * FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", + (date_val.strftime("%Y-%m-%d"),) + ) + rows = cursor.fetchall() df = pd.DataFrame(rows) - df["Date"] = pd.to_datetime(df["Date"]) - sondes = sorted(df["Sonde"].unique()) - sonde_choisie = st.selectbox("🧪 Choisissez une sonde :", sondes) - df_sonde = df[df["Sonde"] == sonde_choisie] -# Ajouter une colonne Heure pour faciliter les filtres - df_sonde.loc[:, "Heure"] = df_sonde["Date"].dt.hour + if df.empty: + st.info("Aucune donnée pour cette date.") + else: + df["Date"] = pd.to_datetime(df["Date"]) + sondes = sorted(df["Sonde"].unique()) + sonde = st.selectbox("Choisir une sonde :", sondes, key="selectbox_stats") + df_sonde = df[df["Sonde"] == sonde] -# Filtrage par tranche horaire - tranche = st.radio("🕒 Tranche horaire :", - ["Toute la journée", "Matin (6h-12h)", "Après-midi (12h-18h)", "Nuit (18h-6h)"]) + st.subheader("Évolution journalière") + fig, ax = plt.subplots(figsize=(10, 4)) + ax.plot(df_sonde["Date"], df_sonde["Temperature"], marker='o') + ax.set_title(f"{sonde} - {date_val.strftime('%d/%m/%Y')}") + ax.set_xlabel("Heure") + ax.set_ylabel("Température (°C)") + ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) + st.pyplot(fig) - if tranche == "Matin (6h-12h)": - df_sonde = df_sonde[(df_sonde["Heure"] >= 6) & (df_sonde["Heure"] < 12)] - elif tranche == "Après-midi (12h-18h)": - df_sonde = df_sonde[(df_sonde["Heure"] >= 12) & (df_sonde["Heure"] < 18)] - elif tranche == "Nuit (18h-6h)": - df_sonde = df_sonde[(df_sonde["Heure"] >= 18) | (df_sonde["Heure"] < 6)] - df_sonde = df_sonde.copy() + cursor.close() + conn.close() - cursor.execute("SELECT Temp_Max FROM Chambres_froides WHERE Lieu = %s AND Sonde = %s", (site_selectionne, sonde_choisie)) - seuil = cursor.fetchone() - seuil_temp = seuil["Temp_Max"] if seuil else 10 + except Exception as e: + st.error(f"Erreur chargement statistiques : {e}") - st.subheader("📊 Tableau des relevés") - df_filtré = df_sonde.copy() - df_filtré = df_filtré.drop(columns="Id", errors="ignore") - - - def surlignage_temp(val): - try: - if float(val) > seuil_temp: - return "color: red; font-weight: bold" - except: - pass - return "" - - -# Appliquer le style uniquement à la colonne "Temperature" - - styled_df = df_filtré.style.applymap(surlignage_temp, subset=["Temperature"]) - - st.dataframe(styled_df, use_container_width=True) - - st.subheader("📈 Évolution de la température") - fig, ax = plt.subplots(figsize=(10, 4)) - ax.plot(df_filtré["Date"], df_filtré["Temperature"], marker='o', label="Température") - ax.axhline(seuil_temp, color='red', linestyle='--', label=f"Seuil {seuil_temp}°C") - ax.set_xlabel("Heure") - ax.set_ylabel("Température (°C)") - ax.set_title(f"{sonde_choisie} - {selected_date.strftime('%d/%m/%Y')}") - ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M')) - ax.legend() - st.pyplot(fig) -# --- Affichage automatique des alertes non acquittées --- + # --- Affichage automatique des alertes non acquittées --- try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) table_alertes = f"Alertes_{site_selectionne}" - cursor.execute( - f"SELECT Sonde, Debut_defaut, Status FROM `{table_alertes}` WHERE Status != 'Acquitté' ORDER BY Debut_defaut DESC" - ) - alertes = cursor.fetchall() - - if alertes: - df_alertes = pd.DataFrame(alertes) - st.subheader("🚨 Alertes non acquittées") - st.dataframe(df_alertes, use_container_width=True) - else: - st.success("✅ Aucune alerte en cours.") cursor.close() conn.close() except Exception as e: st.error(f"Erreur lors de la récupération des alertes : {e}") - except Exception as e: - st.error(f"Erreur MySQL : {e}") + except Exception as e: + st.error(f"Erreur MySQL : {e}") if st.session_state["role"] == "superviseur": with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): with st.form("ajout_sonde"): @@ -378,13 +461,13 @@ if st.session_state["role"] == "superviseur": temp_max = chambre["Temp_Max"] moins, val, plus = st.columns([1, 2, 1]) with moins: - if st.button("-", key=f"moins_{chambre['Id']}"): + if st.button("▼", key=f"moins_{chambre['Id']}"): temp_max -= 1 with val: st.markdown(f"
{temp_max}°C
", unsafe_allow_html=True) with plus: - if st.button("+", key=f"plus_{chambre['Id']}"): + if st.button("▲", key=f"plus_{chambre['Id']}"): temp_max += 1 if new_etat != chambre["Etat"] or temp_max != chambre["Temp_Max"]: @@ -400,5 +483,3 @@ if st.session_state["role"] == "superviseur": except Exception as e: st.error(f"Erreur SQL (admin) : {e}") - - From 4dd230f77b594956fa7e444151c44a529eafa18e Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 10:48:58 +0200 Subject: [PATCH 05/25] =?UTF-8?q?=F0=9F=94=92=20S=C3=A9curisation=20via=20?= =?UTF-8?q?.env=20+=20refactorisation=20Monitor=20et=20MQTT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 15 ++++++++++++++- .gitignore | 1 + Cuisine_meudon.py | 39 ++++++++++++++++++++++----------------- Cuisine_saclay.py | 39 ++++++++++++++++++++++----------------- Monitor.py | 22 ++++++++++++++-------- domo91.py | 14 +++++++------- 6 files changed, 80 insertions(+), 50 deletions(-) diff --git a/.env b/.env index 702821a..99cbf1a 100644 --- a/.env +++ b/.env @@ -1,3 +1,16 @@ DB_HOST=54.36.188.119 DB_USER=michel -DB_PASSWORD=#SO2&1nf%mZ@jfh \ No newline at end of file +DB_PASSWORD=#SO2&1nf%mZ@jfh +DB_NAME=Sondes + +MQTT_HOST=54.36.188.119 +MQTT_USER=Bwps +MQTT_PASSWORD=scJ5ACj2keRfI^ + +# === EMAIL SMTP === +SMTP_HOST=smtp.mail.ovh.net +SMTP_PORT=465 +SMTP_USER=alertes_saclay@domo91.fr +SMTP_PASSWORD=Kdpke674y23Feq^H +EMAIL_FROM=alertes_saclay@domo91.fr +EMAIL_DEST=services@domo91.fr diff --git a/.gitignore b/.gitignore index 1a9ae9c..adfc23b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ venv/ env/ .env/ +.env # 👉 Fichiers Python compilés __pycache__/ diff --git a/Cuisine_meudon.py b/Cuisine_meudon.py index e8c1b05..14a3a7a 100644 --- a/Cuisine_meudon.py +++ b/Cuisine_meudon.py @@ -1,30 +1,35 @@ import paho.mqtt.client as mqttClient -client = mqttClient.Client() import mysql.connector -import sys -sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") +import os +from dotenv import load_dotenv -# Configuration de la connexion MySQL +load_dotenv() + +# Site figé ici +site = "Meudon" + +# Connexion MySQL mydb = mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" + host=os.getenv("DB_HOST"), + user=os.getenv("DB_USER"), + password=os.getenv("DB_PASSWORD"), + database=os.getenv("DB_NAME") ) -# Fonction de callback quand un message est reçu +# Callback MQTT def on_message(_client, _userdata, msg): - print(f"Message reçu sur {msg.topic}: {msg.payload.decode()}") + print(f"[{site}] Message reçu sur {msg.topic}: {msg.payload.decode()}") cursor = mydb.cursor() - frigo_name = msg.topic.split('/')[-1] # Prend la dernière partie après le "/" - sql = "INSERT INTO Meudon (Sonde, Temperature) VALUES (%s, %s)" + frigo_name = msg.topic.split('/')[-1] + sql = f"INSERT INTO {site} (Sonde, Temperature) VALUES (%s, %s)" val = (frigo_name, msg.payload.decode()) cursor.execute(sql, val) mydb.commit() -# Configuration du client MQTT -client.username_pw_set("Bwps", "scJ5ACj2keRfI^") +# Client MQTT +client = mqttClient.Client() +client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD")) client.on_message = on_message -client.connect("54.36.188.119", 1883, 60) -client.subscribe("Meudon/#") # S'abonner à tous les topics commençant par Saclay -client.loop_forever() # Rester connecté en continu pour écouter les messages +client.connect(os.getenv("MQTT_HOST"), 1883, 60) +client.subscribe(f"{site}/#") +client.loop_forever() \ No newline at end of file diff --git a/Cuisine_saclay.py b/Cuisine_saclay.py index 4e68428..d8d6814 100644 --- a/Cuisine_saclay.py +++ b/Cuisine_saclay.py @@ -1,30 +1,35 @@ import paho.mqtt.client as mqttClient -client = mqttClient.Client() import mysql.connector -import sys -sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") +import os +from dotenv import load_dotenv -# Configuration de la connexion MySQL +load_dotenv() + +# Site figé ici +site = "Saclay" + +# Connexion MySQL mydb = mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" + host=os.getenv("DB_HOST"), + user=os.getenv("DB_USER"), + password=os.getenv("DB_PASSWORD"), + database=os.getenv("DB_NAME") ) -# Fonction de callback quand un message est reçu +# Callback MQTT def on_message(_client, _userdata, msg): - print(f"Message reçu sur {msg.topic}: {msg.payload.decode()}") + print(f"[{site}] Message reçu sur {msg.topic}: {msg.payload.decode()}") cursor = mydb.cursor() - frigo_name = msg.topic.split('/')[-1] # Prend la dernière partie après le "/" - sql = "INSERT INTO Saclay (Sonde, Temperature) VALUES (%s, %s)" + frigo_name = msg.topic.split('/')[-1] + sql = f"INSERT INTO {site} (Sonde, Temperature) VALUES (%s, %s)" val = (frigo_name, msg.payload.decode()) cursor.execute(sql, val) mydb.commit() -# Configuration du client MQTT -client.username_pw_set("Bwps", "scJ5ACj2keRfI^") +# Client MQTT +client = mqttClient.Client() +client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD")) client.on_message = on_message -client.connect("54.36.188.119", 1883, 60) -client.subscribe("Saclay/#") # S'abonner à tous les topics commençant par Saclay -client.loop_forever() # Rester connecté en continu pour écouter les messages +client.connect(os.getenv("MQTT_HOST"), 1883, 60) +client.subscribe(f"{site}/#") +client.loop_forever() \ No newline at end of file diff --git a/Monitor.py b/Monitor.py index 60d51e4..3f2fd34 100644 --- a/Monitor.py +++ b/Monitor.py @@ -5,32 +5,38 @@ import time import smtplib from email.mime.text import MIMEText import pandas as pd +from dotenv import load_dotenv +import os + +# Charger les variables d'environnement +load_dotenv() # --- Config MySQL --- config = { - "host": "54.36.188.119", - "user": "michel", - "password": "#SO2&1nf%mZ@jfh", - "database": "Sondes" + "host": os.getenv("DB_HOST"), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME") } # --- Destinataires email --- -destinataires = ['services@domo91.fr'] +destinataires = os.getenv("EMAIL_DEST").split(",") # --- Fonction d'envoi de mail --- def envoyer_mail(sujet, message, destinataires): msg = MIMEText(message) msg['Subject'] = sujet - msg['From'] = 'alertes_saclay@domo91.fr' + msg['From'] = os.getenv("EMAIL_FROM") msg['To'] = ', '.join(destinataires) try: - with smtplib.SMTP_SSL('smtp.mail.ovh.net', 465) as server: - server.login('alertes_saclay@domo91.fr', 'Kdpke674y23Feq^H') + with smtplib.SMTP_SSL(os.getenv("SMTP_HOST"), int(os.getenv("SMTP_PORT"))) as server: + server.login(os.getenv("SMTP_USER"), os.getenv("SMTP_PASSWORD")) server.sendmail(msg['From'], destinataires, msg.as_string()) print(f"📧 Mail envoyé à {destinataires}", flush=True) except Exception as e: print(f"Erreur envoi mail : {e}", flush=True) + # --- Fonction de surveillance --- def surveiller(): log_entries = [] diff --git a/domo91.py b/domo91.py index 484cfe7..314c24f 100644 --- a/domo91.py +++ b/domo91.py @@ -6,11 +6,13 @@ from datetime import date import matplotlib.pyplot as plt import matplotlib.dates as mdates from fpdf import FPDF +from dotenv import load_dotenv +load_dotenv() import os import random import datetime -st.set_page_config(page_title="Domo91 - Surveillance", layout="wide") +st.set_page_config(page_title="Domo91 - Gestion sondes", layout="wide") if "authenticated" not in st.session_state: st.session_state["authenticated"] = False st.session_state["role"] = None @@ -20,13 +22,11 @@ st.title("📡 Supervision Températures") # --- Configuration base de données --- db_config = { - "host": "54.36.188.119", - "user": "michel", - "password": "#SO2&1nf%mZ@jfh", - "database": "Sondes" + "host": os.getenv("DB_HOST"), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME") } - - # --- Fonction de génération PDF --- def generer_pdf(site, date_str): st.info(f"Génération du rapport PDF pour {site} à la date {date_str}") From 4c228e8dcfd0b70505d929870ce4f458e9d550b8 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 13:02:18 +0200 Subject: [PATCH 06/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2022/4/25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domo91.py | 14 +++++++------- tools/MAJ_develop_sur_product.txt | 23 ----------------------- tools/update_selected_from_develop.sh | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 30 deletions(-) delete mode 100644 tools/MAJ_develop_sur_product.txt create mode 100644 tools/update_selected_from_develop.sh diff --git a/domo91.py b/domo91.py index 314c24f..484cfe7 100644 --- a/domo91.py +++ b/domo91.py @@ -6,13 +6,11 @@ from datetime import date import matplotlib.pyplot as plt import matplotlib.dates as mdates from fpdf import FPDF -from dotenv import load_dotenv -load_dotenv() import os import random import datetime -st.set_page_config(page_title="Domo91 - Gestion sondes", layout="wide") +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 @@ -22,11 +20,13 @@ st.title("📡 Supervision Températures") # --- Configuration base de données --- db_config = { - "host": os.getenv("DB_HOST"), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME") + "host": "54.36.188.119", + "user": "michel", + "password": "#SO2&1nf%mZ@jfh", + "database": "Sondes" } + + # --- Fonction de génération PDF --- def generer_pdf(site, date_str): st.info(f"Génération du rapport PDF pour {site} à la date {date_str}") diff --git a/tools/MAJ_develop_sur_product.txt b/tools/MAJ_develop_sur_product.txt deleted file mode 100644 index 49ac08a..0000000 --- a/tools/MAJ_develop_sur_product.txt +++ /dev/null @@ -1,23 +0,0 @@ -✅ Objectif : mettre à jour domo91.py de product avec la version de develop -🧭 Étapes simples à suivre : -1. Assure-toi d’être sur la branche product -git checkout product -2. Récupère seulement le fichier domo91.py depuis develop - -git checkout develop -- domo91.py -💡 Cette commande signifie : - -"Depuis la branche develop, prends juste le fichier domo91.py et applique-le sur la branche actuelle (product)". - -3. Vérifie le changement - -git status -Tu devrais voir : -modified: domo91.py -4. Commit et push la mise à jour -git commit -am "MAJ de domo91.py depuis develop" -git push origin product -✅ Résultat : -Seul le fichier domo91.py est mis à jour sur product - -Tu ne touches ni aux autres fichiers, ni à l’historique Git \ No newline at end of file diff --git a/tools/update_selected_from_develop.sh b/tools/update_selected_from_develop.sh new file mode 100644 index 0000000..ebd4143 --- /dev/null +++ b/tools/update_selected_from_develop.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +echo "🔄 Passage à la branche product" +git checkout product || exit 1 + +FILES=("Cuisine_meudon.py" "Cuisine_saclay.py" "Monitor.py" "domo91.py") + +for file in "${FILES[@]}"; do + if [ -f "$file" ]; then + echo "✔️ Mise à jour de $file depuis develop" + git checkout develop -- "$file" + else + echo "⏭️ Ignoré : $file n'existe pas dans product" + fi +done + +echo "✅ Fichiers mis à jour avec succès depuis develop" \ No newline at end of file From 9d84e640e96dcea6ca574364a053f8a4cbb84222 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 13:11:49 +0200 Subject: [PATCH 07/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2022/4/25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Monitor.py b/Monitor.py index 3f2fd34..c63a38b 100644 --- a/Monitor.py +++ b/Monitor.py @@ -5,10 +5,9 @@ import time import smtplib from email.mime.text import MIMEText import pandas as pd -from dotenv import load_dotenv import os +from dotenv import load_dotenv -# Charger les variables d'environnement load_dotenv() # --- Config MySQL --- @@ -19,8 +18,9 @@ config = { "database": os.getenv("DB_NAME") } -# --- Destinataires email --- -destinataires = os.getenv("EMAIL_DEST").split(",") +# --- Fonction de sélection des destinataires selon le site --- +def get_destinataires(lieu): + return os.getenv(f"EMAIL_{lieu.upper()}", os.getenv("EMAIL_DEFAULT")).split(",") # --- Fonction d'envoi de mail --- def envoyer_mail(sujet, message, destinataires): @@ -36,7 +36,6 @@ def envoyer_mail(sujet, message, destinataires): except Exception as e: print(f"Erreur envoi mail : {e}", flush=True) - # --- Fonction de surveillance --- def surveiller(): log_entries = [] @@ -98,7 +97,7 @@ def surveiller(): f"La sonde '{nom_sonde}' du site '{lieu}' a dépassé le seuil de {seuil}°C " f"depuis plus de 30 minutes.\nHeure : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) - envoyer_mail(sujet, message, destinataires) + envoyer_mail(sujet, message, get_destinataires(lieu)) # Acquittement automatique cursor.execute(f""" @@ -128,6 +127,6 @@ def surveiller(): # --- Boucle principale --- while True: - print(f"📡 Vérification à {datetime.now()}", flush=True) + print(f"🛁 Vérification à {datetime.now()}", flush=True) surveiller() time.sleep(300) # 5 minutes From 3b92d6a326b26e3ec417b228409e252a926754fc Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 13:15:56 +0200 Subject: [PATCH 08/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2022/4/25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cuisine_meudon.py | 84 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/Cuisine_meudon.py b/Cuisine_meudon.py index 14a3a7a..90213ac 100644 --- a/Cuisine_meudon.py +++ b/Cuisine_meudon.py @@ -1,35 +1,65 @@ import paho.mqtt.client as mqttClient import mysql.connector +import sys import os -from dotenv import load_dotenv +import logging +from datetime import datetime -load_dotenv() +# 📁 Création du dossier de logs s'il n'existe pas +dossier_logs = "/var/log/Cuisine_Meudon" +os.makedirs(dossier_logs, exist_ok=True) -# Site figé ici -site = "Meudon" - -# Connexion MySQL -mydb = mysql.connector.connect( - host=os.getenv("DB_HOST"), - user=os.getenv("DB_USER"), - password=os.getenv("DB_PASSWORD"), - database=os.getenv("DB_NAME") +# 📝 Configuration du logger +logfile = os.path.join(dossier_logs, "Cuisine_Meudon.log") +logging.basicConfig( + filename=logfile, + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" ) -# Callback MQTT -def on_message(_client, _userdata, msg): - print(f"[{site}] Message reçu sur {msg.topic}: {msg.payload.decode()}") - cursor = mydb.cursor() - frigo_name = msg.topic.split('/')[-1] - sql = f"INSERT INTO {site} (Sonde, Temperature) VALUES (%s, %s)" - val = (frigo_name, msg.payload.decode()) - cursor.execute(sql, val) - mydb.commit() +# Ajoute aussi un affichage console si utile : +console = logging.StreamHandler() +console.setLevel(logging.INFO) +formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") +console.setFormatter(formatter) +logging.getLogger('').addHandler(console) -# Client MQTT -client = mqttClient.Client() -client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD")) -client.on_message = on_message -client.connect(os.getenv("MQTT_HOST"), 1883, 60) -client.subscribe(f"{site}/#") -client.loop_forever() \ No newline at end of file +# 🔌 Connexion MySQL +try: + sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") + mydb = mysql.connector.connect( + host="54.36.188.119", + user="michel", + password="#SO2&1nf%mZ@jfh", + database="Sondes" + ) + logging.info("Connexion MySQL réussie.") +except mysql.connector.Error as err: + logging.error(f"Erreur de connexion MySQL : {err}") + sys.exit(1) + +# 📥 Callback MQTT +def on_message(_client, _userdata, msg): + try: + logging.info(f"Message reçu sur {msg.topic}: {msg.payload.decode()}") + cursor = mydb.cursor() + frigo_name = msg.topic.split('/')[-1] + sql = "INSERT INTO Meudon (Sonde, Temperature) VALUES (%s, %s)" + val = (frigo_name, msg.payload.decode()) + cursor.execute(sql, val) + mydb.commit() + logging.info(f"Insertion réussie : {val}") + except Exception as e: + logging.error(f"Erreur lors de l'insertion du message : {e}") + +# 📡 Connexion MQTT +try: + client = mqttClient.Client() + client.username_pw_set("Bwps", "scJ5ACj2keRfI^") + client.on_message = on_message + client.connect("54.36.188.119", 1883, 60) + client.subscribe("Meudon/#") + logging.info("Connexion MQTT réussie et abonnement au topic 'Meudon/#'.") + client.loop_forever() +except Exception as e: + logging.error(f"Erreur MQTT : {e}") From 7b6996a0aaa599664ff14df4cb2f20bce4bef9c7 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 13:17:33 +0200 Subject: [PATCH 09/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2022/4/25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cuisine_saclay.py | 84 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/Cuisine_saclay.py b/Cuisine_saclay.py index d8d6814..c194121 100644 --- a/Cuisine_saclay.py +++ b/Cuisine_saclay.py @@ -1,35 +1,65 @@ import paho.mqtt.client as mqttClient import mysql.connector +import sys import os -from dotenv import load_dotenv +import logging +from datetime import datetime -load_dotenv() +# 📁 Création du dossier de logs s'il n'existe pas +dossier_logs = "/var/log/Cuisine_Saclay" +os.makedirs(dossier_logs, exist_ok=True) -# Site figé ici -site = "Saclay" - -# Connexion MySQL -mydb = mysql.connector.connect( - host=os.getenv("DB_HOST"), - user=os.getenv("DB_USER"), - password=os.getenv("DB_PASSWORD"), - database=os.getenv("DB_NAME") +# 📝 Configuration du logger +logfile = os.path.join(dossier_logs, "Cuisine_Saclay.log") +logging.basicConfig( + filename=logfile, + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" ) -# Callback MQTT -def on_message(_client, _userdata, msg): - print(f"[{site}] Message reçu sur {msg.topic}: {msg.payload.decode()}") - cursor = mydb.cursor() - frigo_name = msg.topic.split('/')[-1] - sql = f"INSERT INTO {site} (Sonde, Temperature) VALUES (%s, %s)" - val = (frigo_name, msg.payload.decode()) - cursor.execute(sql, val) - mydb.commit() +# Ajoute aussi l'affichage console +console = logging.StreamHandler() +console.setLevel(logging.INFO) +formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") +console.setFormatter(formatter) +logging.getLogger('').addHandler(console) -# Client MQTT -client = mqttClient.Client() -client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASSWORD")) -client.on_message = on_message -client.connect(os.getenv("MQTT_HOST"), 1883, 60) -client.subscribe(f"{site}/#") -client.loop_forever() \ No newline at end of file +# 🔌 Connexion MySQL +try: + sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") + mydb = mysql.connector.connect( + host="54.36.188.119", + user="michel", + password="#SO2&1nf%mZ@jfh", + database="Sondes" + ) + logging.info("Connexion MySQL réussie.") +except mysql.connector.Error as err: + logging.error(f"Erreur de connexion MySQL : {err}") + sys.exit(1) + +# 📥 Callback MQTT +def on_message(_client, _userdata, msg): + try: + logging.info(f"Message reçu sur {msg.topic}: {msg.payload.decode()}") + cursor = mydb.cursor() + frigo_name = msg.topic.split('/')[-1] + sql = "INSERT INTO Saclay (Sonde, Temperature) VALUES (%s, %s)" + val = (frigo_name, msg.payload.decode()) + cursor.execute(sql, val) + mydb.commit() + logging.info(f"Insertion réussie : {val}") + except Exception as e: + logging.error(f"Erreur lors de l'insertion du message : {e}") + +# 📡 Connexion MQTT +try: + client = mqttClient.Client() + client.username_pw_set("Bwps", "scJ5ACj2keRfI^") + client.on_message = on_message + client.connect("54.36.188.119", 1883, 60) + client.subscribe("Saclay/#") + logging.info("Connexion MQTT réussie et abonnement au topic 'Saclay/#'.") + client.loop_forever() +except Exception as e: + logging.error(f"Erreur MQTT : {e}") From 9f048ac6bee65080d5ad48fc353523bc4b487680 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 13:18:55 +0200 Subject: [PATCH 10/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2022/4/25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.env b/.env index 99cbf1a..070422e 100644 --- a/.env +++ b/.env @@ -7,10 +7,12 @@ MQTT_HOST=54.36.188.119 MQTT_USER=Bwps MQTT_PASSWORD=scJ5ACj2keRfI^ -# === EMAIL SMTP === +# === EMAIL SMTP ===# === EMAIL SMTP === SMTP_HOST=smtp.mail.ovh.net SMTP_PORT=465 SMTP_USER=alertes_saclay@domo91.fr SMTP_PASSWORD=Kdpke674y23Feq^H EMAIL_FROM=alertes_saclay@domo91.fr -EMAIL_DEST=services@domo91.fr +EMAIL_SACLAY=Cuisine +EMAIL_MEUDON=meudon@domo91.fr +EMAIL_DEFAULT=admin@domo91.fr From 6a2c1ce5d865d2d51cf8eebbb3ac29e9e28ef82a Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 14:12:25 +0200 Subject: [PATCH 11/25] =?UTF-8?q?R=C3=A9vis.8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Monitor.py b/Monitor.py index c63a38b..0187052 100644 --- a/Monitor.py +++ b/Monitor.py @@ -18,10 +18,12 @@ config = { "database": os.getenv("DB_NAME") } + # --- Fonction de sélection des destinataires selon le site --- def get_destinataires(lieu): return os.getenv(f"EMAIL_{lieu.upper()}", os.getenv("EMAIL_DEFAULT")).split(",") + # --- Fonction d'envoi de mail --- def envoyer_mail(sujet, message, destinataires): msg = MIMEText(message) @@ -36,6 +38,7 @@ def envoyer_mail(sujet, message, destinataires): except Exception as e: print(f"Erreur envoi mail : {e}", flush=True) + # --- Fonction de surveillance --- def surveiller(): log_entries = [] @@ -64,7 +67,6 @@ def surveiller(): """, (nom_sonde,)) relevés = cursor.fetchall() - # Log CSV : tous les relevés analysés for r in relevés: log_entries.append({ "Date": r['Date'], @@ -124,9 +126,3 @@ def surveiller(): 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 From 749f4f05fb7704cda189ded5ec6fe41efdfaaef9 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 14:39:34 +0200 Subject: [PATCH 12/25] =?UTF-8?q?R=C3=A9v.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Monitor.py b/Monitor.py index 0187052..dbad662 100644 --- a/Monitor.py +++ b/Monitor.py @@ -1,7 +1,6 @@ # Surveillance continue avec envoi d'alertes par email + log CSV import mysql.connector from datetime import datetime, timedelta -import time import smtplib from email.mime.text import MIMEText import pandas as pd @@ -65,9 +64,9 @@ def surveiller(): WHERE Sonde = %s ORDER BY Date DESC LIMIT 6 """, (nom_sonde,)) - relevés = cursor.fetchall() + releves = cursor.fetchall() - for r in relevés: + for r in releves: log_entries.append({ "Date": r['Date'], "Lieu": lieu, @@ -77,9 +76,9 @@ def surveiller(): "État": "Dépassement" if r['Temperature'] > seuil else "Normal" }) - if len(relevés) == 6: - toutes_hors_seuil = all(r['Temperature'] > seuil for r in relevés) - plus_ancien = relevés[-1]['Date'] + 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)): @@ -122,7 +121,8 @@ def surveiller(): # Enregistrer le log if log_entries: df_logs = pd.DataFrame(log_entries) - df_logs.to_csv("/home/debian/travail/Logs/monitor.csv", sep=";", index=False) + df_logs.to_csv("/home/debian/travail/Gestion_sondes/logs/monitor.csv", sep=";", index=False) except Exception as e: + print() print(f"Erreur : {e}", flush=True) From fdd5ef5a6796638f51bff2ea246e64bae56254b0 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 14:43:24 +0200 Subject: [PATCH 13/25] =?UTF-8?q?R=C3=A9v.10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Monitor.py b/Monitor.py index dbad662..96376d9 100644 --- a/Monitor.py +++ b/Monitor.py @@ -121,7 +121,7 @@ def surveiller(): # Enregistrer le log if log_entries: df_logs = pd.DataFrame(log_entries) - df_logs.to_csv("/home/debian/travail/Gestion_sondes/logs/monitor.csv", sep=";", index=False) + df_logs.to_csv("/home/debian/travail/Gestion_sondes/Logs/monitor.csv", sep=";", index=False) except Exception as e: print() From 0c042cf87ae0dd60ef51dbb4faf973f11217fccb Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 15:39:32 +0200 Subject: [PATCH 14/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2011?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Monitor.py b/Monitor.py index 96376d9..d387c91 100644 --- a/Monitor.py +++ b/Monitor.py @@ -1,12 +1,14 @@ -# Surveillance continue avec envoi d'alertes par email + log CSV + # Surveillance continue avec envoi d'alertes par email + log CSV import mysql.connector from datetime import datetime, timedelta +import time import smtplib from email.mime.text import MIMEText import pandas as pd -import os from dotenv import load_dotenv +import os +# Charger les variables d'environnement load_dotenv() # --- Config MySQL --- @@ -17,27 +19,23 @@ config = { "database": os.getenv("DB_NAME") } - -# --- Fonction de sélection des destinataires selon le site --- -def get_destinataires(lieu): - return os.getenv(f"EMAIL_{lieu.upper()}", os.getenv("EMAIL_DEFAULT")).split(",") - +# --- Destinataires email --- +destinataires = ['services@domo91.fr'] # --- Fonction d'envoi de mail --- def envoyer_mail(sujet, message, destinataires): msg = MIMEText(message) msg['Subject'] = sujet - msg['From'] = os.getenv("EMAIL_FROM") + msg['From'] = 'alertes_saclay@domo91.fr' msg['To'] = ', '.join(destinataires) try: - with smtplib.SMTP_SSL(os.getenv("SMTP_HOST"), int(os.getenv("SMTP_PORT"))) as server: - server.login(os.getenv("SMTP_USER"), os.getenv("SMTP_PASSWORD")) + with smtplib.SMTP_SSL('smtp.mail.ovh.net', 465) as server: + server.login('alertes_saclay@domo91.fr', 'Kdpke674y23Feq^H') server.sendmail(msg['From'], destinataires, msg.as_string()) print(f"📧 Mail envoyé à {destinataires}", flush=True) except Exception as e: print(f"Erreur envoi mail : {e}", flush=True) - # --- Fonction de surveillance --- def surveiller(): log_entries = [] @@ -64,9 +62,10 @@ def surveiller(): WHERE Sonde = %s ORDER BY Date DESC LIMIT 6 """, (nom_sonde,)) - releves = cursor.fetchall() + relevés = cursor.fetchall() - for r in releves: + # Log CSV : tous les relevés analysés + for r in relevés: log_entries.append({ "Date": r['Date'], "Lieu": lieu, @@ -76,9 +75,9 @@ def surveiller(): "É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'] + if len(relevés) == 6: + toutes_hors_seuil = all(r['Temperature'] > seuil for r in relevés) + plus_ancien = relevés[-1]['Date'] maintenant = datetime.now() if toutes_hors_seuil and (maintenant - plus_ancien >= timedelta(minutes=30)): @@ -98,7 +97,7 @@ def surveiller(): f"La sonde '{nom_sonde}' du site '{lieu}' a dépassé le seuil de {seuil}°C " f"depuis plus de 30 minutes.\nHeure : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) - envoyer_mail(sujet, message, get_destinataires(lieu)) + envoyer_mail(sujet, message, destinataires) # Acquittement automatique cursor.execute(f""" @@ -121,8 +120,13 @@ def surveiller(): # Enregistrer le log if log_entries: df_logs = pd.DataFrame(log_entries) - df_logs.to_csv("/home/debian/travail/Gestion_sondes/Logs/monitor.csv", sep=";", index=False) + df_logs.to_csv("/home/debian/travail/logs/monitor.csv", sep=";", index=False) except Exception as e: - print() print(f"Erreur : {e}", flush=True) + +# --- Boucle principale --- +while True: + print(f"📡 Vérification à {datetime.now()}", flush=True) + surveiller() + time.sleep(300) # 5 minutes \ No newline at end of file From 46566cb873629585fad8697df6d4683a72bb1cfa Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 15:51:00 +0200 Subject: [PATCH 15/25] =?UTF-8?q?Mise=20=C3=A0=20jour=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Monitor.py b/Monitor.py index d387c91..60d51e4 100644 --- a/Monitor.py +++ b/Monitor.py @@ -1,22 +1,17 @@ - # Surveillance continue avec envoi d'alertes par email + log CSV +# Surveillance continue avec envoi d'alertes par email + log CSV import mysql.connector from datetime import datetime, timedelta import time import smtplib from email.mime.text import MIMEText import pandas as pd -from dotenv import load_dotenv -import os - -# Charger les variables d'environnement -load_dotenv() # --- Config MySQL --- config = { - "host": os.getenv("DB_HOST"), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME") + "host": "54.36.188.119", + "user": "michel", + "password": "#SO2&1nf%mZ@jfh", + "database": "Sondes" } # --- Destinataires email --- @@ -120,7 +115,7 @@ def surveiller(): # Enregistrer le log if log_entries: df_logs = pd.DataFrame(log_entries) - df_logs.to_csv("/home/debian/travail/logs/monitor.csv", sep=";", index=False) + df_logs.to_csv("/home/debian/travail/Logs/monitor.csv", sep=";", index=False) except Exception as e: print(f"Erreur : {e}", flush=True) @@ -129,4 +124,4 @@ def surveiller(): while True: print(f"📡 Vérification à {datetime.now()}", flush=True) surveiller() - time.sleep(300) # 5 minutes \ No newline at end of file + time.sleep(300) # 5 minutes From 9f66d166fd9ac5a8afeb1e115c071f1af5455a57 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 22 Apr 2025 17:20:05 +0200 Subject: [PATCH 16/25] =?UTF-8?q?Version=20prot=C3=A9g=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Monitor.py b/Monitor.py index 60d51e4..99a5aed 100644 --- a/Monitor.py +++ b/Monitor.py @@ -5,13 +5,17 @@ import time import smtplib from email.mime.text import MIMEText import pandas as pd +from dotenv import load_dotenv +import os + +load_dotenv() # --- Config MySQL --- config = { - "host": "54.36.188.119", - "user": "michel", - "password": "#SO2&1nf%mZ@jfh", - "database": "Sondes" + "host": os.getenv("DB_HOST"), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME") } # --- Destinataires email --- From 98586ba0e1424d93acc43336726dc1da6400c321 Mon Sep 17 00:00:00 2001 From: Michel Date: Wed, 23 Apr 2025 09:23:20 +0200 Subject: [PATCH 17/25] =?UTF-8?q?=F0=9F=94=84=20Mise=20=C3=A0=20jour=20des?= =?UTF-8?q?=20fichiers=20modifi=C3=A9s=20depuis=20develop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cuisine_meudon.py | 13 ++++++++----- Cuisine_saclay.py | 13 ++++++++----- Monitor.py | 2 +- domo91.py | 25 ++++++++++++++----------- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Cuisine_meudon.py b/Cuisine_meudon.py index 90213ac..9b26b4a 100644 --- a/Cuisine_meudon.py +++ b/Cuisine_meudon.py @@ -3,7 +3,10 @@ import mysql.connector import sys import os import logging -from datetime import datetime +from dotenv import load_dotenv + +# Charger les variables d'environnement +load_dotenv() # 📁 Création du dossier de logs s'il n'existe pas dossier_logs = "/var/log/Cuisine_Meudon" @@ -28,10 +31,10 @@ logging.getLogger('').addHandler(console) try: sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") mydb = mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" + host=os.getenv("DB_HOST"), + user=os.getenv("DB_USER"), + password=os.getenv("DB_PASSWORD"), + database=os.getenv("DB_NAME") ) logging.info("Connexion MySQL réussie.") except mysql.connector.Error as err: diff --git a/Cuisine_saclay.py b/Cuisine_saclay.py index c194121..20cdbb8 100644 --- a/Cuisine_saclay.py +++ b/Cuisine_saclay.py @@ -3,7 +3,10 @@ import mysql.connector import sys import os import logging -from datetime import datetime +from dotenv import load_dotenv + +# Charger les variables d'environnement +load_dotenv() # 📁 Création du dossier de logs s'il n'existe pas dossier_logs = "/var/log/Cuisine_Saclay" @@ -28,10 +31,10 @@ logging.getLogger('').addHandler(console) try: sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") mydb = mysql.connector.connect( - host="54.36.188.119", - user="michel", - password="#SO2&1nf%mZ@jfh", - database="Sondes" + host=os.getenv("DB_HOST"), + user=os.getenv("DB_USER"), + password=os.getenv("DB_PASSWORD"), + database=os.getenv("DB_NAME") ) logging.info("Connexion MySQL réussie.") except mysql.connector.Error as err: diff --git a/Monitor.py b/Monitor.py index 99a5aed..2bc9325 100644 --- a/Monitor.py +++ b/Monitor.py @@ -19,7 +19,7 @@ config = { } # --- Destinataires email --- -destinataires = ['services@domo91.fr'] +destinataires = ['services@domo91.fr,cuisine@bw-paris-saclay.com>'] # --- Fonction d'envoi de mail --- def envoyer_mail(sujet, message, destinataires): diff --git a/domo91.py b/domo91.py index 484cfe7..f243e91 100644 --- a/domo91.py +++ b/domo91.py @@ -1,4 +1,5 @@ # Application Gestion de sondes +# -*- coding: utf-8 -*- import streamlit as st import mysql.connector import pandas as pd @@ -9,6 +10,10 @@ from fpdf import FPDF import os import random import datetime +from dotenv import load_dotenv + +# Charger les variables d'environnement +load_dotenv() st.set_page_config(page_title="Domo91 - Surveillance", layout="wide") if "authenticated" not in st.session_state: @@ -18,21 +23,19 @@ if "authenticated" not in st.session_state: st.title("📡 Supervision Températures") -# --- Configuration base de données --- -db_config = { - "host": "54.36.188.119", - "user": "michel", - "password": "#SO2&1nf%mZ@jfh", - "database": "Sondes" +db_config ={ + "host": os.getenv("DB_HOST"), + "user": os.getenv("DB_USER"), + "password": os.getenv("DB_PASSWORD"), + "database": os.getenv("DB_NAME") } - # --- Fonction de génération PDF --- def generer_pdf(site, date_str): st.info(f"Génération du rapport PDF pour {site} à la date {date_str}") try: conn = mysql.connector.connect(**db_config) - cursor = conn.cursor(dictionary=True) + pdf_cursor = conn.cursor(dictionary=True) cursor.execute(f"SELECT Sonde, Date, Temperature FROM `{site}` WHERE DATE(Date) = %s ORDER BY Sonde, Date", (date_str,)) @@ -105,8 +108,8 @@ def generer_pdf(site, date_str): mime="application/pdf" ) - except Exception as e: - st.error(f"Erreur lors de la génération du PDF : {e}") + except Exception as err1: + st.error(f"Erreur lors de la génération du PDF : {err1}") # --- Initialisation des variables de session --- @@ -401,7 +404,7 @@ if st.session_state["authenticated"]: except Exception as e: st.error(f"Erreur lors de la récupération des alertes : {e}") - except Exception as e: + except Exception as err: st.error(f"Erreur MySQL : {e}") if st.session_state["role"] == "superviseur": with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): From c3ed5e40ad4a543199218131e0b8a48709ee96ce Mon Sep 17 00:00:00 2001 From: Michel Date: Thu, 24 Apr 2025 16:05:16 +0200 Subject: [PATCH 18/25] =?UTF-8?q?24/04/25=F0=9F=94=84=20Mise=20=C3=A0=20jo?= =?UTF-8?q?ur=20des=20fichiers=20modifi=C3=A9s=20depuis=20develop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domo91.py | 160 +++++++++++++++++++++++++++--------------------------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/domo91.py b/domo91.py index f243e91..3ec8497 100644 --- a/domo91.py +++ b/domo91.py @@ -1,5 +1,7 @@ # Application Gestion de sondes # -*- coding: utf-8 -*- +from logging import exception + import streamlit as st import mysql.connector import pandas as pd @@ -23,13 +25,14 @@ if "authenticated" not in st.session_state: st.title("📡 Supervision Températures") -db_config ={ +db_config = { "host": os.getenv("DB_HOST"), "user": os.getenv("DB_USER"), "password": os.getenv("DB_PASSWORD"), "database": os.getenv("DB_NAME") } + # --- Fonction de génération PDF --- def generer_pdf(site, date_str): st.info(f"Génération du rapport PDF pour {site} à la date {date_str}") @@ -237,7 +240,11 @@ if st.session_state["authenticated"]: try: conn = mysql.connector.connect(**db_config) cursor = conn.cursor(dictionary=True) + 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 site_selectionne = ( st.session_state["lieu_autorise"] if st.session_state["role"] != "superviseur" @@ -315,8 +322,8 @@ if st.session_state["authenticated"]: seuil_temp = seuil["Temp_Max"] if seuil else 10 st.subheader("📊 Tableau des relevés") - df_filtré = df_sonde.copy() - df_filtré = df_filtré.drop(columns="Id", errors="ignore") + df_filtre = df_sonde.copy() + df_filtre = df_filtre.drop(columns="Id", errors="ignore") def surlignage_temp(val): @@ -328,12 +335,12 @@ if st.session_state["authenticated"]: return "" - styled_df = df_filtré.style.applymap(surlignage_temp, subset=["Temperature"]) + styled_df = df_filtre.style.applymap(surlignage_temp, subset=["Temperature"]) st.dataframe(styled_df, use_container_width=True) st.subheader("📈 Évolution de la température") fig, ax = plt.subplots(figsize=(10, 4)) - ax.plot(df_filtré["Date"], df_filtré["Temperature"], marker='o', label="Température") + ax.plot(df_filtre["Date"], df_filtre["Temperature"], marker='o', label="Température") ax.axhline(seuil_temp, color='red', linestyle='--', label=f"Seuil {seuil_temp}°C") ax.set_xlabel("Heure") ax.set_ylabel("Température (°C)") @@ -404,85 +411,80 @@ if st.session_state["authenticated"]: except Exception as e: st.error(f"Erreur lors de la récupération des alertes : {e}") - except Exception as err: - st.error(f"Erreur MySQL : {e}") -if st.session_state["role"] == "superviseur": - with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): - with st.form("ajout_sonde"): - nouvelle_sonde = st.text_input("Nom de la sonde") - temp_max = st.number_input("Température maximale autorisée (°C)", value=4) - etat_on = st.checkbox("État actif (ON)", value=True) + # --- Fonctionnalités administrateur : ajout et gestion des chambres froides --- + if st.session_state["role"] == "superviseur": + with st.expander("+ Ajouter une nouvelle chambre froide", expanded=False): + with st.form("ajout_sonde"): + nouvelle_sonde = st.text_input("Nom de la sonde") + temp_max = st.number_input("Température maximale autorisée (°C)", value=4) + etat_on = st.checkbox("État actif (ON)", value=True) + + submitted = st.form_submit_button("✅ Ajouter") + if submitted: + try: + conn_add = mysql.connector.connect(**db_config) + cursor_add = conn_add.cursor() + cursor_add.execute( + "INSERT INTO Chambres_froides (Lieu, Sonde, Temp_Max, Status) VALUES (%s, %s, %s, %s)", + (site, nouvelle_sonde, temp_max, "ON" if etat_on else "OFF") + ) + conn_add.commit() + cursor_add.close() + conn_add.close() + st.success(f"Sonde '{nouvelle_sonde}' ajoutée avec succès au site {site} 🎉") + st.session_state["refresh_admin"] = random.randint(1000, 9999) + except Exception as e: + st.error(f"Erreur lors de l'ajout : {e}") + + with st.expander("🛠️ Gestion des chambres froides (administrateur)", expanded=True): + if st.button("🔄 Actualiser la liste"): + st.session_state["refresh_admin"] = random.randint(0, 9999) - submitted = st.form_submit_button("✅ Ajouter") - if submitted: try: - conn_add = mysql.connector.connect(**db_config) - cursor_add = conn_add.cursor() + conn_admin = mysql.connector.connect(**db_config) + cursor_admin = conn_admin.cursor(dictionary=True) + cursor_admin.execute("SELECT * FROM Chambres_froides WHERE Lieu = %s", (site,)) + chambres = cursor_admin.fetchall() - cursor_add.execute( - "INSERT INTO Chambres_froides (Lieu, Sonde, Temp_Max, Status) VALUES (%s, %s, %s, %s)", - (site_selectionne, nouvelle_sonde, temp_max, "ON" if etat_on else "OFF") - ) - conn_add.commit() - cursor_add.close() - conn_add.close() + if not chambres: + st.warning("Aucune chambre froide pour ce site.") + else: + for chambre in chambres: + col1, col2, col3 = st.columns([3, 1, 2]) + with col1: + st.markdown(f"**{chambre['Sonde']}**") - st.success(f"Sonde '{nouvelle_sonde}' ajoutée avec succès au site {site_selectionne} 🎉") + with col2: + etat = st.checkbox("ON", value=(chambre["Etat"] == "ON"), + key=f"etat_{chambre['Id']}_{st.session_state.get('refresh_admin', 0)}") + new_etat = "ON" if etat else "OFF" - # Refresh immédiat - st.session_state["refresh_admin"] = random.randint(1000, 9999) + with col3: + temp_max = chambre["Temp_Max"] + moins, val, plus = st.columns([1, 2, 1]) + with moins: + if st.button("▼", key=f"moins_{chambre['Id']}"): + temp_max -= 1 + with val: + st.markdown(f"
{temp_max}°C
", + unsafe_allow_html=True) + with plus: + if st.button("▲", key=f"plus_{chambre['Id']}"): + temp_max += 1 + + if new_etat != chambre["Etat"] or temp_max != chambre["Temp_Max"]: + cursor_admin.execute( + "UPDATE Chambres_froides SET Etat = %s, Temp_Max = %s WHERE Id = %s", + (new_etat, temp_max, chambre["Id"]) + ) + conn_admin.commit() + st.success(f"{chambre['Sonde']} mise à jour") + + cursor_admin.close() + conn_admin.close() except Exception as e: - st.error(f"Erreur lors de l'ajout : {e}") - with st.expander("🛠️ Gestion des chambres froides (administrateur)", expanded=True): + st.error(f"Erreur SQL (admin) : {e}") - # Bouton d'actualisation - if st.button("🔄 Actualiser la liste"): - st.session_state["refresh_admin"] = random.randint(0, 9999) # Force le rerun - - try: - conn_admin = mysql.connector.connect(**db_config) - cursor_admin = conn_admin.cursor(dictionary=True) - - cursor_admin.execute("SELECT * FROM Chambres_froides WHERE Lieu = %s", (site_selectionne,)) - chambres = cursor_admin.fetchall() - - if not chambres: - st.warning("Aucune chambre froide pour ce site.") - else: - for chambre in chambres: - col1, col2, col3 = st.columns([3, 1, 2]) - with col1: - st.markdown(f"**{chambre['Sonde']}**") - - with col2: - etat = st.checkbox("ON", value=(chambre["Etat"] == "ON"), - key=f"etat_{chambre['Id']}_{st.session_state.get('refresh_admin', 0)}") - new_etat = "ON" if etat else "OFF" - - with col3: - temp_max = chambre["Temp_Max"] - moins, val, plus = st.columns([1, 2, 1]) - with moins: - if st.button("▼", key=f"moins_{chambre['Id']}"): - temp_max -= 1 - with val: - st.markdown(f"
{temp_max}°C
", - unsafe_allow_html=True) - with plus: - if st.button("▲", key=f"plus_{chambre['Id']}"): - temp_max += 1 - - if new_etat != chambre["Etat"] or temp_max != chambre["Temp_Max"]: - cursor_admin.execute( - "UPDATE Chambres_froides SET Etat = %s, Temp_Max = %s WHERE Id = %s", - (new_etat, temp_max, chambre["Id"]) - ) - conn_admin.commit() - st.success(f"{chambre['Sonde']} mise à jour") - - cursor_admin.close() - conn_admin.close() - - except Exception as e: - st.error(f"Erreur SQL (admin) : {e}") + except exception as err: + st.error(f"Erreur MySQL : {err}") From 0329bf0e45ee57b26b5279030e1801fb6d32f91a Mon Sep 17 00:00:00 2001 From: Michel Date: Fri, 25 Apr 2025 13:05:44 +0000 Subject: [PATCH 19/25] Actualiser domo91.py --- domo91.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/domo91.py b/domo91.py index 46be970..53aaba9 100644 --- a/domo91.py +++ b/domo91.py @@ -123,11 +123,7 @@ if "lieu_autorise" not in st.session_state: st.session_state["lieu_autorise"] = None # --- Connexion utilisateur dans la sidebar --- -<<<<<<< HEAD -st.sidebar.header("🔐 Connexion") -======= st.sidebar.header("🔐 Connexion") ->>>>>>> origin/develop if not st.session_state.get("authenticated"): login = st.sidebar.text_input("Nom d'utilisateur") password = st.sidebar.text_input("Mot de passe", type="password") @@ -274,12 +270,10 @@ if st.session_state["authenticated"]: # --- NAVIGATION --- if st.session_state["role"] == "superviseur": - onglet = st.sidebar.radio("📁 Navigation", ["Accueil", "Statistiques", "Traffic"]) # --- ONGLET ACCUEIL --- else: onglet = "Accueil" ->>>>>>> origin/develop if onglet == "Accueil": st.markdown("## Sélection du site et de la date") try: @@ -359,11 +353,7 @@ if st.session_state["authenticated"]: except Exception as e: st.error(f"Erreur MySQL : {e}") -<<<<<<< HEAD - -======= # ---- ONGLET STATISTIQUES --- ->>>>>>> origin/develop elif onglet == "Statistiques": st.markdown("## 📈 Statistiques de température") try: @@ -494,11 +484,6 @@ if st.session_state["authenticated"]: except Exception as e: st.error(f"Erreur SQL (admin) : {e}") -<<<<<<< HEAD - - except exception as err: - st.error(f"Erreur MySQL : {err}") -======= # --- ONGLET TRAFFIC ------ elif onglet == "Traffic": @@ -521,5 +506,4 @@ if st.session_state["authenticated"]: st.error(f"Erreur chargement des connexions : {e}") except exception as err: - st.error(f"Erreur MySQL : {err}") ->>>>>>> origin/develop + st.error(f"Erreur MySQL : {err}") \ No newline at end of file From 62f9f0aad51648c724d02cf0a31c244cc563889f Mon Sep 17 00:00:00 2001 From: Michel Date: Sat, 26 Apr 2025 11:00:34 +0200 Subject: [PATCH 20/25] =?UTF-8?q?Mise=20=C3=A0=20jour=20Monitor.py=20avec?= =?UTF-8?q?=20rappels=20d'alertes=20+=20chargement=20destinataires=20depui?= =?UTF-8?q?s=20.env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 22 ++++++++-------------- Monitor.py | 4 ++-- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/.env b/.env index 070422e..12a4c52 100644 --- a/.env +++ b/.env @@ -1,18 +1,12 @@ +# connexion run_deploy.py +SSH_HOST=54.36.188.119 +SSH_USER=debian +SSH_KEY=C:/Users/miche/.ssh/PyCharm.pem +SSH_KEY_PASSPHRASE=gaby + +# connexion Cuisine_saclay & cuisine_meudon DB_HOST=54.36.188.119 DB_USER=michel DB_PASSWORD=#SO2&1nf%mZ@jfh DB_NAME=Sondes - -MQTT_HOST=54.36.188.119 -MQTT_USER=Bwps -MQTT_PASSWORD=scJ5ACj2keRfI^ - -# === EMAIL SMTP ===# === EMAIL SMTP === -SMTP_HOST=smtp.mail.ovh.net -SMTP_PORT=465 -SMTP_USER=alertes_saclay@domo91.fr -SMTP_PASSWORD=Kdpke674y23Feq^H -EMAIL_FROM=alertes_saclay@domo91.fr -EMAIL_SACLAY=Cuisine -EMAIL_MEUDON=meudon@domo91.fr -EMAIL_DEFAULT=admin@domo91.fr +DESTINATAIRES_MAIL=services@domo91.fr,michel-68000@hotmail.fr diff --git a/Monitor.py b/Monitor.py index 4eaf18b..7cf98e7 100644 --- a/Monitor.py +++ b/Monitor.py @@ -18,8 +18,8 @@ config = { "database": os.getenv("DB_NAME") } -# --- Destinataires email --- -destinataires = ['services@domo91.fr', 'michel-68000@hotmail.fr'] +# Charger les destinataires depuis .env +destinataires = os.getenv("DESTINATAIRES_MAIL", "").split(',') # --- Suivi des alertes actives pour rappels --- alertes_actives = {} From e1208e4f258bc6dbaa0a6deadbb159adc5151083 Mon Sep 17 00:00:00 2001 From: Michel Date: Sat, 26 Apr 2025 11:48:31 +0200 Subject: [PATCH 21/25] Retrait du fichier .env du suivi Git --- .env | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 12a4c52..0000000 --- a/.env +++ /dev/null @@ -1,12 +0,0 @@ -# connexion run_deploy.py -SSH_HOST=54.36.188.119 -SSH_USER=debian -SSH_KEY=C:/Users/miche/.ssh/PyCharm.pem -SSH_KEY_PASSPHRASE=gaby - -# connexion Cuisine_saclay & cuisine_meudon -DB_HOST=54.36.188.119 -DB_USER=michel -DB_PASSWORD=#SO2&1nf%mZ@jfh -DB_NAME=Sondes -DESTINATAIRES_MAIL=services@domo91.fr,michel-68000@hotmail.fr From 72749ce90158f2dbbfd7c30417b865038fe49883 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 6 May 2025 13:51:58 +0000 Subject: [PATCH 22/25] Supprimer detect_encodage.py --- detect_encodage.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 detect_encodage.py diff --git a/detect_encodage.py b/detect_encodage.py deleted file mode 100644 index 65a0fa1..0000000 --- a/detect_encodage.py +++ /dev/null @@ -1,16 +0,0 @@ -import os - -racine = "." # point de départ = dossier courant - -def detect_fichiers_non_utf8(dossier): - for root, dirs, files in os.walk(dossier): - for file in files: - if file.endswith(".py"): - chemin = os.path.join(root, file) - try: - with open(chemin, encoding="utf-8") as f: - f.read() - except UnicodeDecodeError: - print(f"⚠️ Encodage non UTF-8 : {chemin}") - -detect_fichiers_non_utf8(racine) From eac952e81e9e4f1655d3ad24bbb7c03da3552708 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 6 May 2025 13:52:06 +0000 Subject: [PATCH 23/25] Supprimer Purge_alertes.py --- Purge_alertes.py | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 Purge_alertes.py diff --git a/Purge_alertes.py b/Purge_alertes.py deleted file mode 100644 index 54d83d5..0000000 --- a/Purge_alertes.py +++ /dev/null @@ -1,35 +0,0 @@ -# Purges des entrées de toutes les tables dans la base Sondes qui commencent -# par Alertes_*** et qui sont agées de plus de sept jours. -import mysql.connector -import os -from dotenv import load_dotenv - -# Charger les variables d'environnement -load_dotenv() - -config = { - "host": os.getenv("DB_HOST"), - "user": os.getenv("DB_USER"), - "password": os.getenv("DB_PASSWORD"), - "database": os.getenv("DB_NAME") -} - -conn = mysql.connector.connect(**config) -cursor = conn.cursor() - -# Récupérer toutes les tables d'alertes -cursor.execute("SHOW TABLES") -tables = [t[0] for t in cursor.fetchall()] -alertes_tables = [t for t in tables if t.startswith("Alertes_")] - -# Appliquer la purge à chaque table -for table in alertes_tables: - query = f"DELETE FROM {table} WHERE Debut_defaut < NOW() - INTERVAL 7 DAY" - cursor.execute(query) - print(f"✅ Table {table} purgée.") - -conn.commit() -cursor.close() -conn.close() - -print("🎉 Purge terminée pour toutes les alertes anciennes.") \ No newline at end of file From 1cc6a56dfa49df42ffa80c75a58824c2adbaeec6 Mon Sep 17 00:00:00 2001 From: Michel Date: Tue, 6 May 2025 13:52:13 +0000 Subject: [PATCH 24/25] Supprimer run_deploy.py --- run_deploy.py | 61 --------------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 run_deploy.py diff --git a/run_deploy.py b/run_deploy.py deleted file mode 100644 index 98ccf01..0000000 --- a/run_deploy.py +++ /dev/null @@ -1,61 +0,0 @@ -import paramiko -import os -from dotenv import load_dotenv -import tkinter as tk -from tkinter import messagebox - -load_dotenv() - -HOST = os.getenv("SSH_HOST") -USER = os.getenv("SSH_USER") -KEY = os.getenv("SSH_KEY") -PASSPHRASE = os.getenv("SSH_KEY_PASSPHRASE") - -root = tk.Tk() -root.withdraw() - -# 🛡 Vérifie que le fichier existe -if not os.path.exists(KEY): - messagebox.showerror("❌ Erreur", f"Clé SSH introuvable :\n{KEY}") - exit(1) - -# 🛡 Vérifie que la clé est lisible par paramiko -try: - paramiko.RSAKey.from_private_key_file(KEY, password=PASSPHRASE) -except paramiko.PasswordRequiredException: - messagebox.showerror("❌ Clé protégée", "La clé est protégée par une passphrase, mais aucune n'a été fournie.") - exit(1) -except paramiko.SSHException: - messagebox.showerror("❌ Erreur de format", "Le fichier de clé n’est pas au format OpenSSH.\n\nSolution : Exporter depuis PuTTYgen → Conversions > Export OpenSSH key") - exit(1) -except Exception as e: - messagebox.showerror("❌ Autre erreur", f"{e}") - exit(1) - -# ✅ Si la clé est valide, on continue -client = paramiko.SSHClient() -client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - -try: - client.connect( - hostname=HOST, - username=USER, - key_filename=KEY, - passphrase=PASSPHRASE - ) - - stdin, stdout, stderr = client.exec_command("bash ~/travail/Gestion_sondes/scripts/deploy_all.sh") - - output = stdout.read().decode() - errors = stderr.read().decode() - - if "Déploiement complet terminé avec succès" in output: - messagebox.showinfo("✅ Déploiement réussi", "Le déploiement s'est bien déroulé.") - else: - messagebox.showwarning("⚠️ Déploiement incomplet", f"Vérifie les logs :\n\n{errors}") - -except Exception as e: - messagebox.showerror("❌ Erreur SSH", f"{e}") - -finally: - client.close() From 96ffdc7eb163b9d951fa2c9f0f415bdb26cb5e44 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 7 May 2025 08:15:12 +0200 Subject: [PATCH 25/25] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20la=20branche?= =?UTF-8?q?=20product=20avec=20les=20fichiers=20en=20production=20sur=20le?= =?UTF-8?q?=20VPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Monitor.py | 4 +- requirements.txt | 71 +++++++++++++++++++++++++++ scripts/restart_services.sh | 4 +- scripts/update_product.sh | 6 +-- supervisor_watchdog.py | 45 +++++++++++++++++ tools/backup_mysql.sh | 6 --- tools/requirements.txt | 9 ---- tools/update_selected_from_develop.sh | 17 ------- 8 files changed, 123 insertions(+), 39 deletions(-) create mode 100644 requirements.txt mode change 100644 => 100755 scripts/restart_services.sh mode change 100644 => 100755 scripts/update_product.sh create mode 100755 supervisor_watchdog.py delete mode 100644 tools/backup_mysql.sh delete mode 100644 tools/requirements.txt delete mode 100644 tools/update_selected_from_develop.sh diff --git a/Monitor.py b/Monitor.py index 7cf98e7..feb1594 100644 --- a/Monitor.py +++ b/Monitor.py @@ -32,7 +32,7 @@ def envoyer_mail(sujet, message, destinataires): msg['To'] = ', '.join(destinataires) try: with smtplib.SMTP_SSL('smtp.mail.ovh.net', 465) as server: - server.login('alertes_saclay@domo91.fr', 'Kdpke674y23Feq^H') + server.login('alertes_saclay@domo91.fr','Kdpke674y23Feq^H') server.sendmail(msg['From'], destinataires, msg.as_string()) print(f"📧 Mail envoyé à {destinataires}", flush=True) except Exception as e: @@ -141,7 +141,7 @@ def surveiller(): if log_entries: df_logs = pd.DataFrame(log_entries) - df_logs.to_csv("/home/debian/travail/Gestion_sondes/Logs/monitor.csv", sep=";", index=False) + df_logs.to_csv("/home/debian/Gestion_sondes/Logs/monitor.csv", sep=";", index=False) except Exception as e: print(f"Erreur : {e}", flush=True) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6a202e4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,71 @@ +acme==2.1.0 +altair==5.5.0 +attrs==22.2.0 +blinker==1.5 +cachetools==5.5.2 +certifi==2022.9.24 +cffi==1.17.1 +chardet==5.1.0 +charset-normalizer==2.1.1 +click==8.1.8 +ConfigArgParse==1.5.3 +configobj==5.0.8 +contourpy==1.3.2 +cryptography==38.0.4 +cycler==0.12.1 +distro==1.8.0 +dotenv==0.9.9 +fonttools==4.57.0 +fpdf==1.7.2 +gitdb==4.0.12 +GitPython==3.1.44 +httplib2==0.20.4 +idna==3.3 +Jinja2==3.1.2 +josepy==1.13.0 +jsonpatch==1.32 +jsonpointer==2.3 +jsonschema==4.10.3 +kiwisolver==1.4.8 +markdown-it-py==2.1.0 +MarkupSafe==2.1.2 +matplotlib==3.10.1 +mdurl==0.1.2 +mysql-connector-python==9.3.0 +narwhals==1.33.0 +numpy==2.2.4 +oauthlib==3.2.2 +packaging==24.2 +paho-mqtt==2.1.0 +pandas==2.2.3 +parsedatetime==2.6 +pillow==11.1.0 +protobuf==5.29.4 +pyarrow==19.0.1 +pycparser==2.22 +pydeck==0.9.1 +Pygments==2.14.0 +PyJWT==2.6.0 +pyOpenSSL==23.2.0 +pyparsing==3.0.9 +pyRFC3339==1.1 +pyrsistent==0.18.1 +pyserial==3.5 +PySimpleSOAP==1.16.2 +python-dateutil==2.9.0.post0 +python-dotenv==1.1.0 +pytz==2022.7.1 +PyYAML==6.0 +requests==2.28.1 +rich==13.3.1 +six==1.16.0 +smmap==5.0.2 +streamlit==1.44.1 +supervisor==4.2.5 +tenacity==9.1.2 +toml==0.10.2 +tornado==6.4.2 +typing_extensions==4.13.1 +tzdata==2025.2 +urllib3==1.26.12 +watchdog==6.0.0 diff --git a/scripts/restart_services.sh b/scripts/restart_services.sh old mode 100644 new mode 100755 index 74b42b6..34f53c3 --- a/scripts/restart_services.sh +++ b/scripts/restart_services.sh @@ -1,6 +1,6 @@ #!/bin/bash -LOG="/home/debian/travail/Gestion_sondes/Logs/restart_services.log" +LOG="/home/debian/Gestion_sondes/Logs/restart_services.log" DATE=$(date '+%Y-%m-%d %H:%M:%S') echo "[$DATE] 🔄 Redémarrage des services Supervisor" >> $LOG @@ -34,4 +34,4 @@ else echo " → $LOG" fi echo "" -echo "===== ✅ FIN DU SCRIPT =====" \ No newline at end of file +echo "===== ✅ FIN DU SCRIPT =====" diff --git a/scripts/update_product.sh b/scripts/update_product.sh old mode 100644 new mode 100755 index 162d952..7c7a9a0 --- a/scripts/update_product.sh +++ b/scripts/update_product.sh @@ -1,7 +1,7 @@ #!/bin/bash -LOG="/home/debian/travail/Gestion_sondes/Logs/update_product.log" -REPO_PATH="/home/debian/travail/Gestion_sondes" +LOG="/home/debian/Gestion_sondes/Logs/update_product.log" +REPO_PATH="/home/debian/Gestion_sondes" DATE=$(date '+%Y-%m-%d %H:%M:%S') echo "[$DATE] 🔄 Lancement du script de mise à jour" >> $LOG @@ -29,4 +29,4 @@ git merge origin/develop -m "🧩 Merge auto develop → product" >> $LOG 2>&1 git push origin product >> $LOG 2>&1 DATE_END=$(date '+%Y-%m-%d %H:%M:%S') -echo "[$DATE_END] ✅ Mise à jour terminée avec succès." >> $LOG \ No newline at end of file +echo "[$DATE_END] ✅ Mise à jour terminée avec succès." >> $LOG diff --git a/supervisor_watchdog.py b/supervisor_watchdog.py new file mode 100755 index 0000000..cdaca30 --- /dev/null +++ b/supervisor_watchdog.py @@ -0,0 +1,45 @@ +#!/home/debian/Gestion_sondes/myenv/bin/python +import subprocess +import smtplib +from email.mime.text import MIMEText +from datetime import datetime + +heure_actuelle = datetime.now().strftime("%H:%M") +etat_services = [] +anomalies = [] + +try: + output = subprocess.check_output("/usr/bin/supervisorctl status", shell=True, text=True) + for line in output.splitlines(): + parts = line.split() + if len(parts) >= 2: + nom, statut = parts[0], parts[1] + etat_services.append(f"{nom} ➤ {statut}") + if statut != "RUNNING": + anomalies.append(f"{nom} ➤ {statut}") +except Exception as e: + etat_services.append("❌ Impossible d'exécuter supervisorctl") + anomalies.append(f"Erreur : {e}") + +# Déclenchement mail si anomalie ou à 07:00 +envoyer_mail = bool(anomalies) or heure_actuelle == "07:00" + +if envoyer_mail: + sujet = "⚠️ Alerte Supervisor" if anomalies else "✅ Rapport quotidien Supervisor" + intro = "🛑 Les services suivants ne sont pas en RUNNING :" if anomalies else "✅ Tous les services supervisés sont en RUNNING." + contenu = f"{intro}\n\n" + "\n".join(etat_services) + + msg = MIMEText(contenu) + msg["Subject"] = sujet + msg["From"] = "alertes_saclay@domo91.fr" + msg["To"] = "services@domo91.fr" + + try: + with smtplib.SMTP_SSL("smtp.mail.ovh.net", 465) as server: + server.login("alertes_saclay@domo91.fr", "Kdpke674y23Feq^H") + server.sendmail(msg["From"], [msg["To"]], msg.as_string()) + print("📧 Mail envoyé.") + except Exception as e: + print(f"Erreur envoi mail : {e}") +else: + print("🕖 Aucun mail envoyé (tout est OK et ce n’est pas l’heure du rapport).") diff --git a/tools/backup_mysql.sh b/tools/backup_mysql.sh deleted file mode 100644 index f9ee856..0000000 --- a/tools/backup_mysql.sh +++ /dev/null @@ -1,6 +0,0 @@ -# Fichier de sauvegarde BDD sur Syno en NFS -# Fichier my.cnf -#[mysqldump] -#user=root -#password=4V5t9P!Z2HgT#CLFes4cntEeNBWs -mysqldump --defaults-file=/home/debian/travail/my.cnf -u root --all-databases > /mnt/syno920/all-$(date +%Y%m%d).sql diff --git a/tools/requirements.txt b/tools/requirements.txt deleted file mode 100644 index eee1a33..0000000 --- a/tools/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -mysql~=0.0.3 -mysql-connector-python~=9.2.0 -pandas~=2.2.3 -DateTime~=5.5 -streamlit~=1.44.1 -matplotlib~=3.10.1 -paho-mqtt~=2.1.0 -requests~=2.32.3 -schedule~=1.2.2 \ No newline at end of file diff --git a/tools/update_selected_from_develop.sh b/tools/update_selected_from_develop.sh deleted file mode 100644 index ebd4143..0000000 --- a/tools/update_selected_from_develop.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -echo "🔄 Passage à la branche product" -git checkout product || exit 1 - -FILES=("Cuisine_meudon.py" "Cuisine_saclay.py" "Monitor.py" "domo91.py") - -for file in "${FILES[@]}"; do - if [ -f "$file" ]; then - echo "✔️ Mise à jour de $file depuis develop" - git checkout develop -- "$file" - else - echo "⏭️ Ignoré : $file n'existe pas dans product" - fi -done - -echo "✅ Fichiers mis à jour avec succès depuis develop" \ No newline at end of file