Fusion de product dans develop

This commit is contained in:
2025-05-07 08:57:38 +02:00
12 changed files with 97 additions and 416 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
venv/ venv/
env/ env/
.env/ .env/
.env
# 👉 Fichiers Python compilés # 👉 Fichiers Python compilés
__pycache__/ __pycache__/

View File

@@ -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

View File

@@ -18,7 +18,11 @@ config = {
"database": os.getenv("DB_NAME") "database": os.getenv("DB_NAME")
} }
<<<<<<< HEAD
# Charger les destinataires depuis .env # Charger les destinataires depuis .env
=======
# Charger les destinataires depuis .env
>>>>>>> product
destinataires = os.getenv("DESTINATAIRES_MAIL", "").split(',') destinataires = os.getenv("DESTINATAIRES_MAIL", "").split(',')
# --- Suivi des alertes actives pour rappels --- # --- Suivi des alertes actives pour rappels ---
@@ -32,9 +36,15 @@ def envoyer_mail(sujet, message, destinataires_list):
msg['To'] = ', '.join(destinataires_list) msg['To'] = ', '.join(destinataires_list)
try: try:
with smtplib.SMTP_SSL('smtp.mail.ovh.net', 465) as server: with smtplib.SMTP_SSL('smtp.mail.ovh.net', 465) as server:
<<<<<<< HEAD
server.login('alertes_saclay@domo91.fr', 'Kdpke674y23Feq^H') server.login('alertes_saclay@domo91.fr', 'Kdpke674y23Feq^H')
server.sendmail(msg['From'], destinataires_list, msg.as_string()) server.sendmail(msg['From'], destinataires_list, msg.as_string())
print(f"📧 Mail envoyé à {destinataires_list}", flush=True) print(f"📧 Mail envoyé à {destinataires_list}", flush=True)
=======
server.login('alertes_saclay@domo91.fr','Kdpke674y23Feq^H')
server.sendmail(msg['From'], destinataires, msg.as_string())
print(f"📧 Mail envoyé à {destinataires}", flush=True)
>>>>>>> product
except Exception as e: except Exception as e:
print(f"Erreur envoi mail : {e}", flush=True) print(f"Erreur envoi mail : {e}", flush=True)
@@ -141,7 +151,11 @@ def surveiller():
if log_entries: if log_entries:
df_logs = pd.DataFrame(log_entries) df_logs = pd.DataFrame(log_entries)
<<<<<<< HEAD
df_logs.to_csv("/var/log/monitor.csv", sep=";", index=False) df_logs.to_csv("/var/log/monitor.csv", sep=";", index=False)
=======
df_logs.to_csv("/home/debian/Gestion_sondes/Logs/monitor.csv", sep=";", index=False)
>>>>>>> product
except Exception as e: except Exception as e:
print(f"Erreur : {e}", flush=True) print(f"Erreur : {e}", flush=True)

View File

@@ -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 <nom_sonde>"
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 <nom_sonde>"
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 <nom_sonde>"
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 <nom_sonde>"
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()

View File

@@ -533,6 +533,11 @@ if st.session_state["authenticated"]:
except Exception as e: except Exception as e:
st.error(f"Erreur chargement des connexions : {e}") st.error(f"Erreur chargement des connexions : {e}")
<<<<<<< HEAD
except Exception as err: # petit bug dans ton except ici (tu avais écrit "except exception") except Exception as err: # petit bug dans ton except ici (tu avais écrit "except exception")
st.error(f"Erreur MySQL : {err}") st.error(f"Erreur MySQL : {err}")
=======
except exception as err:
st.error(f"Erreur MySQL : {err}")
>>>>>>> product

View File

@@ -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)

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
altair==5.5.0 altair==5.5.0
attrs==25.3.0 attrs==25.3.0
blinker==1.9.0 blinker==1.9.0
@@ -8,11 +9,30 @@ click==8.1.8
colorama==0.4.6 colorama==0.4.6
contourpy==1.3.2 contourpy==1.3.2
cycler==0.12.1 cycler==0.12.1
=======
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
>>>>>>> product
dotenv==0.9.9 dotenv==0.9.9
fonttools==4.57.0 fonttools==4.57.0
fpdf==1.7.2 fpdf==1.7.2
gitdb==4.0.12 gitdb==4.0.12
GitPython==3.1.44 GitPython==3.1.44
<<<<<<< HEAD
idna==3.10 idna==3.10
Jinja2==3.1.6 Jinja2==3.1.6
jsonschema==4.23.0 jsonschema==4.23.0
@@ -48,4 +68,55 @@ tornado==6.4.2
typing_extensions==4.13.2 typing_extensions==4.13.2
tzdata==2025.2 tzdata==2025.2
urllib3==2.4.0 urllib3==2.4.0
=======
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
>>>>>>> product
watchdog==6.0.0 watchdog==6.0.0

2
scripts/restart_services.sh Normal file → Executable file
View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/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') DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] 🔄 Redémarrage des services Supervisor" >> $LOG echo "[$DATE] 🔄 Redémarrage des services Supervisor" >> $LOG

4
scripts/update_product.sh Normal file → Executable file
View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
LOG="/home/debian/travail/Gestion_sondes/Logs/update_product.log" LOG="/home/debian/Gestion_sondes/Logs/update_product.log"
REPO_PATH="/home/debian/travail/Gestion_sondes" REPO_PATH="/home/debian/Gestion_sondes"
DATE=$(date '+%Y-%m-%d %H:%M:%S') DATE=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$DATE] 🔄 Lancement du script de mise à jour" >> $LOG echo "[$DATE] 🔄 Lancement du script de mise à jour" >> $LOG

4
supervisor_watchdog.py Normal file → Executable file
View File

@@ -1,6 +1,4 @@
# Controle des services supervisor envoi en cas de disfonction et tous les jours à sept heures #!/home/debian/Gestion_sondes/myenv/bin/python
# 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 subprocess
import smtplib import smtplib
from email.mime.text import MIMEText from email.mime.text import MIMEText

View File

@@ -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

View File

@@ -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