Branche product nettoyée : uniquement les fichiers de prod

This commit is contained in:
2025-04-10 15:53:17 +02:00
parent cf94108aa6
commit d5a3381292
24 changed files with 0 additions and 1738 deletions

26
.gitignore vendored
View File

@@ -1,26 +0,0 @@
# --- Environnements virtuels ---
.venv/
env/
# --- Python cache & compilés ---
__pycache__/
*.py[cod]
*.log
# --- IDEs (PyCharm, VSCode) ---
.idea/
.vscode/
# --- fichiers système ---
.DS_Store
Thumbs.db
# --- fichiers de test ou temporaires ---
*.bak
*.swp
*.tmp
*.old
# --- Fichiers spécifiques si besoin ---
README.md.md
nv/

View File

@@ -1,66 +0,0 @@
import mysql.connector
from datetime import datetime
import smtplib
from email.mime.text import MIMEText
# --- Config base de données ---
config = {
"host": "54.36.188.119",
"user": "michel",
"password": "#SO2&1nf%mZ@jfh",
"database": "Sondes"
}
# --- Config email ---
def envoyer_mail(sujet, message, destinataires):
msg = MIMEText(message, "plain", "utf-8")
msg['Subject'] = sujet
msg['From'] = 'alertes_saclay@domo91.fr'
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.sendmail(msg['From'], destinataires, msg.as_string())
print("📧 Mail récapitulatif envoyé avec succès.")
except Exception as e:
print(f"Erreur envoi mail : {e}")
# --- Exécution principale ---
try:
conn = mysql.connector.connect(**config)
cursor = conn.cursor(dictionary=True)
# Rechercher toutes les tables d'alertes
cursor.execute("SHOW TABLES LIKE 'Alertes_%'")
tables = [list(row.values())[0] for row in cursor.fetchall()]
message = ""
total_alertes = 0
for table in tables:
lieu = table.replace("Alertes_", "")
cursor.execute(f"SELECT Sonde, Debut_defaut FROM {table} WHERE Status = 'En cours'")
alertes = cursor.fetchall()
if alertes:
message += f"\n📍 **Site : {lieu}**\n"
for alerte in alertes:
debut = alerte['Debut_defaut'].strftime("%Y-%m-%d %H:%M")
message += f"🔔 Sonde : {alerte['Sonde']} | Depuis : {debut}\n"
total_alertes += len(alertes)
if total_alertes > 0:
envoyer_mail(
sujet="🚨 Récapitulatif des alertes en cours - 7h00",
message=message,
destinataires=["services@domo91.fr"]
)
else:
print("✅ Aucune alerte en cours à 7h00.")
cursor.close()
conn.close()
except Exception as e:
print(f"Erreur script alerte journalière : {e}")

View File

@@ -1,36 +0,0 @@
import requests
import json
# Remplace par ton token de bot Telegram
token = "8128378340:AAF2sO3gaH1XpMNya_pEslzerqokoCiFRGs"
url = f"https://api.telegram.org/bot{token}/getUpdates"
try:
response = requests.get(url)
data = response.json()
print("🔍 Récupération des chats Telegram récents...\n")
if "result" in data:
chats = set()
for update in data["result"]:
message = update.get("message") or update.get("edited_message")
if not message:
continue
chat = message["chat"]
chat_id = chat["id"]
chat_title = chat.get("title") or chat.get("username") or chat.get("first_name") or "Inconnu"
if chat_id not in chats:
chats.add(chat_id)
print(f"➡️ Chat : {chat_title} | ID : {chat_id}")
if not chats:
print("⚠️ Aucun chat trouvé. Envoie un message depuis un groupe ou une discussion avec le bot.")
else:
print("❌ Erreur : aucune donnée 'result' trouvée dans la réponse.")
except Exception as e:
print(f"Erreur lors de la récupération des chats : {e}")

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

@@ -1,9 +0,0 @@
import mysql.connector
conn = mysql.connector.connect(
host="54.36.188.119",
user="michel",
password="#SO2&1nf%mZ@jfh",
database="Sondes"
)
print("Connexion OK")

View File

@@ -1,30 +0,0 @@
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")
# 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 Meudon (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.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

View File

@@ -1,30 +0,0 @@
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")
# 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 Saclay (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.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

Binary file not shown.

View File

@@ -1,323 +0,0 @@
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 = "5714323406:AAGSj9jrfBHbfxubz3ooabPEizI8aBOLnvE"
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

@@ -1,129 +0,0 @@
#!/home/debian/travail/myenv/bin/python
# 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
# --- Config MySQL ---
config = {
"host": "54.36.188.119",
"user": "michel",
"password": "#SO2&1nf%mZ@jfh",
"database": "Sondes"
}
# --- 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'] = 'alertes_saclay@domo91.fr'
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.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 = []
try:
conn = mysql.connector.connect(**config)
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT DISTINCT Lieu FROM Chambres_froides")
lieux = [row['Lieu'] for row in cursor.fetchall()]
for lieu in lieux:
table_temp = lieu
table_alertes = f"Alertes_{lieu}"
cursor.execute("SELECT Sonde, Temp_Max FROM Chambres_froides WHERE Lieu=%s AND Etat='ON'", (lieu,))
sondes = cursor.fetchall()
for sonde in sondes:
nom_sonde = sonde['Sonde']
seuil = sonde['Temp_Max']
cursor.execute(f"""
SELECT Date, Temperature FROM {table_temp}
WHERE Sonde = %s
ORDER BY Date DESC LIMIT 6
""", (nom_sonde,))
relevés = cursor.fetchall()
# Log CSV : tous les relevés analysés
for r in relevés:
log_entries.append({
"Date": r['Date'],
"Lieu": lieu,
"Sonde": nom_sonde,
"Température": r['Temperature'],
"Seuil": seuil,
"État": "Dépassement" if r['Temperature'] > seuil else "Normal"
})
if len(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)):
cursor.execute(f"""
SELECT COUNT(*) as total FROM {table_alertes}
WHERE Sonde=%s AND Status='En cours'
""", (nom_sonde,))
en_cours = cursor.fetchone()
if en_cours['total'] == 0:
cursor.execute(
f"INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) VALUES (%s, NOW(), 'En cours')",
(nom_sonde,)
)
print(f"🚨 Alerte déclenchée pour {nom_sonde} ({lieu})", flush=True)
sujet = f"🚨 ALERTE TEMPÉRATURE - {nom_sonde} ({lieu})"
message = (
f"La sonde '{nom_sonde}' du site '{lieu}' a dépassé le seuil de {seuil}°C "
f"depuis plus de 30 minutes.\nHeure : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
envoyer_mail(sujet, message, destinataires)
# Acquittement automatique
cursor.execute(f"""
SELECT Temperature FROM {table_temp}
WHERE Sonde = %s
ORDER BY Date DESC LIMIT 1
""", (nom_sonde,))
derniere = cursor.fetchone()
if derniere and derniere['Temperature'] <= seuil:
cursor.execute(f"""
UPDATE {table_alertes}
SET Status = 'Acquitté'
WHERE Sonde = %s AND Status = 'En cours'
""", (nom_sonde,))
conn.commit()
cursor.close()
conn.close()
# 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)
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

View File

@@ -1,17 +0,0 @@
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.")

103
README.md
View File

@@ -1,103 +0,0 @@
# 📡 Gestion des sondes de température - Domo91
Application de supervision multi-sites pour chambres froides industrielles.
## 🧩 Structure du projet
- `Streamlit.py` : Interface Web (authentification, affichage des relevés, alertes)
- `Monitor.py` : Script de fond qui vérifie les seuils et envoie les alertes email
- `logs_monitor.csv` : Journal des températures (généré par Monitor)
- `Telegram_sondes.py` : (optionnel) Envoi des alertes sur Telegram
- `Chambres_froides` (MySQL) : Table de référence pour les seuils/états des sondes
## ⚙️ Fonctionnalités principales
- Authentification par rôle (utilisateur / superviseur)
- Visualisation interactive des relevés
- Détection automatique des dépassements prolongés
- Envoi dalertes email automatisé
- Analyse graphique des relevés
- Admin : modification des seuils et activation/désactivation des sondes
## 🗺️ Schéma d'architecture
![Schéma d'architecture](A_flowchart_diagram_in_the_image_depicts_an_archit.png)
## 🚀 Installation locale (PyCharm)
1. Cloner le dépôt :
```bash
git clone http://192.168.1.250:3000/nom_du_repo.git
```
2. Créer lenvironnement virtuel Python :
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
3. Lancer l'interface Streamlit :
```bash
streamlit run Streamlit.py
```
4. Lancer le script de fond manuellement :
```bash
python Monitor.py
```
## 🖥️ Installation sur le VPS
1. Cloner le dépôt dans `/home/debian/travail` :
```bash
git clone <url-du-repo> /home/debian/travail
```
2. Créer lenvironnement virtuel :
```bash
cd /home/debian/travail
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
## 🛠️ Supervisor
Les services sont gérés par Supervisor :
- `monitor` → `/home/debian/travail/Monitor.py`
- `streamlit` → `/home/debian/travail/Streamlit.py`
Fichier exemple : `/etc/supervisor/conf.d/monitor.conf`
```ini
[program:monitor]
command=/home/debian/travail/venv/bin/python /home/debian/travail/Monitor.py
directory=/home/debian/travail
autostart=true
autorestart=true
stderr_logfile=/var/log/monitor.err.log
stdout_logfile=/var/log/monitor.out.log
```
Commandes utiles :
```bash
sudo supervisorctl status
sudo supervisorctl restart monitor
```
## 🧪 Vérification / Test
- Tester MQTT : `mosquitto_pub -t test -m "message"`
- Simuler une alerte : forcer une température > seuil pendant 30 minutes
- Vérifier les emails : vérifier logs ou boîte de réception
- Tester Streamlit : se connecter avec différents rôles utilisateur
## 🔒 Accès
- Interface Web : [https://app.domo91.fr](https://app.domo91.fr)
- Administration Git : [http://192.168.1.250:3000](http://192.168.1.250:3000)
## 👤 Auteur
Michel — [michel@domo91.fr](mailto:michel@domo91.fr)

View File

@@ -1,30 +0,0 @@
#!/bin/bash
# Fonction pour vérifier et relancer un processus
check_and_restart() {
process_name="$1"
log_file="$2"
script_path="$3"
# Vérifier si le processus est actif
if pgrep -af "$process_name" > /dev/null; then
echo "Le processus $process_name est actif."
else
echo "Le processus $process_name est mort."
# Demander à l'utilisateur s'il souhaite relancer le processus
read -p "Voulez-vous relancer le processus $process_name ? (O/N) " choice
if [ "$choice" = "O" ] || [ "$choice" = "o" ]; then
echo "Relancer le processus $process_name."
nohup python3 $script_path > $log_file 2>&1 &
else
echo "Ne rien faire pour le processus $process_name."
fi
fi
}
# Afficher les processus Python3 actifs et les vérifier
echo "Vérification des processus Python3 :"
check_and_restart "Cuisine.py" "/root/python/log/cuisine_saclay_output.log" "/root/python/Cuisine_saclay.py"
check_and_restart "Chaufferie.py" "/root/python/log/chaufferie_output.log" "/root/python/Chaufferie.py"
check_and_restart "Alertes_telegram.py" "/root/python/log/Alertes_telegram_output.log" "/root/python/Alertes_telegram.py"

View File

@@ -1,3 +0,0 @@
# mysqldump --defaults-file=/root/my.cnf -u michel Sondes > /root/syno/Sondes-$(date +%Y%m%d).sql
# mysqldump --defaults-file=/root/my.cnf -u michel --databases Sondes Best_Western >/root/syno/BW-$(date +%Y%m%d).sql
mysqldump --defaults-file=/root/my.cnf -u michel --databases Best_Western Sondes >/root/syno/BW-$(date +%Y%m%d).sql

View File

@@ -1,27 +0,0 @@
#!/bin/bash
# Fonction pour vérifier et redémarrer un container Docker
check_and_restart_container() {
container_name="$1"
# Vérifier si le container est en cours d'exécution
if docker ps --format '{{.Names}}' | grep -q "$container_name"; then
echo "Le container $container_name est en cours d'exécution."
else
echo "Le container $container_name n'est pas en cours d'exécution."
# Demander à l'utilisateur s'il souhaite redémarrer le container
read -p "Voulez-vous redémarrer le container $container_name ? (O/N) " choice
if [ "$choice" = "O" ] || [ "$choice" = "o" ]; then
echo "Redémarrer le container $container_name."
docker restart $container_name
else
echo "Ne rien faire pour le container $container_name."
fi
fi
}
# Afficher les containers Docker actifs et les vérifier
echo "Vérification des containers Docker :"
check_and_restart_container "domoticz"
check_and_restart_container "postfix"

View File

@@ -1,29 +0,0 @@
#!/bin/bash
# Fonction pour vérifier et relancer un processus
check_and_restart() {
process_name="$1"
log_file="$2"
# Vérifier si le processus est actif
if pgrep -af "$process_name" > /dev/null; then
echo "Le processus $process_name est actif."
else
echo "Le processus $process_name est mort."
# Demander à l'utilisateur s'il souhaite relancer le processus
read -p "Voulez-vous relancer le processus $process_name ? (O/N) " choice
if [ "$choice" = "O" ] || [ "$choice" = "o" ]; then
echo "Relancer le processus $process_name."
nohup $process_name > $log_file 2>&1 &
else
echo "Ne rien faire pour le processus $process_name."
fi
fi
}
# Afficher les processus Python3 actifs et les vérifier
echo "Vérification des processus Python3 :"
check_and_restart "python3 /root/python/Cuisine_saclay.py" "/root/python/log/cuisine_saclay_output.log"
check_and_restart "python3 /root/python/Chaufferie.py" "/root/python/log/chaufferie_output.log"
check_and_restart "python3 /root/python/Alertes_telegram.py" "/root/python/log/Alertes_telegram_output.log"

View File

@@ -1,237 +0,0 @@
# Application Streamlit avec coloration rouge des températures dépassant les seuils dans logs
import streamlit as st
import mysql.connector
import pandas as pd
from datetime import date
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
st.set_page_config(page_title="Domo91 - Surveillance", layout="wide")
st.title("📡 Supervision Températures Multisites")
# --- Configuration base de données ---
db_config = {
"host": "54.36.188.119",
"user": "michel",
"password": "#SO2&1nf%mZ@jfh",
"database": "Sondes"
}
if "authenticated" not in st.session_state:
st.session_state["authenticated"] = False
st.session_state["role"] = None
st.session_state["lieu_autorise"] = None
# --- Accès aux logs dès connexion superviseur ---
if st.session_state.get("authenticated") and st.session_state["role"] == "superviseur":
st.markdown("### 🔍 Accès à lanalyse des relevés")
if st.button("🧾 Analyse des logs Monitor.py"):
st.session_state["page"] = "analyse_logs"
st.rerun()
# --- Analyse logs si page active ---
if st.session_state.get("page") == "analyse_logs":
st.title("🧾 Analyse des notifications de relevés")
try:
df_logs = pd.read_csv("/home/debian/travail/logs_monitor.csv", sep=";", parse_dates=["Date"])
df_logs["Date_str"] = df_logs["Date"].dt.date
dates_dispo = sorted(df_logs["Date_str"].unique(), reverse=True)
date_selection = st.selectbox("📅 Sélectionnez une date :", dates_dispo)
lieux = sorted(df_logs["Lieu"].unique())
lieu_selection = st.selectbox("📍 Site :", lieux)
df_filtré = df_logs[(df_logs["Date_str"] == date_selection) & (df_logs["Lieu"] == lieu_selection)]
sondes = sorted(df_filtré["Sonde"].unique())
sonde_selection = st.selectbox("🧪 Sonde :", sondes)
df_sonde = df_filtré[df_filtré["Sonde"] == sonde_selection].copy()
st.subheader(f"📈 Évolution de la température - {sonde_selection}")
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(df_sonde["Date"], df_sonde["Température"], marker='o')
ax.set_title(f"{sonde_selection} - {date_selection}")
ax.set_xlabel("Heure")
ax.set_ylabel("Température (°C)")
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
st.pyplot(fig)
st.subheader("📋 Détail des relevés")
# Coloration conditionnelle des températures > seuil
df_sonde["Depassement"] = df_sonde["Température"] > df_sonde["Seuil"]
st.dataframe(df_sonde.style.apply(
lambda row: [
'background-color: red; color: white' if row["Depassement"] and col == "Température" else ''
for col in df_sonde.columns
], axis=1
), use_container_width=True)
st.markdown("---")
if st.button("⬅️ Retour à l'accueil"):
st.session_state["page"] = None
st.rerun()
except Exception as e:
st.error(f"Erreur lecture logs : {e}")
st.stop()
with st.sidebar:
st.markdown("# 🌡️ **Domo91**")
st.markdown("Monitoring chambres froides industrielles")
st.header("🔐 Connexion")
if not st.session_state["authenticated"]:
login = st.text_input("Nom d'utilisateur")
password = st.text_input("Mot de passe", type="password")
if st.button("Se connecter"):
try:
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM MotsDePasse WHERE utilisateur = %s", (login,))
result = cursor.fetchone()
if result and result["mot_de_passe"] == password:
st.session_state["authenticated"] = True
st.session_state["role"] = result["role"]
st.session_state["lieu_autorise"] = result["Lieu"]
st.success(f"Connecté comme {result['role']} ({result['Lieu']})")
else:
st.error("Identifiants invalides")
cursor.close()
conn.close()
except Exception as e:
st.error(f"Erreur lors de la connexion à la base : {e}")
else:
st.success(f"Connecté ({st.session_state['role']})")
if st.button("🔓 Déconnexion"):
st.session_state["authenticated"] = False
st.session_state["role"] = None
st.session_state["lieu_autorise"] = None
st.rerun()
# --- Interface principale
if st.session_state["authenticated"]:
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)
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())
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)
df_sonde = df[df["Sonde"] == sonde_choisie].copy()
df_sonde["Heure"] = df_sonde["Date"].dt.hour
# 🕓 Filtres horaires
st.markdown("### 🕒 Filtrer par moment de la journée")
col1, col2, col3 = st.columns(3)
filtre_matin = col1.checkbox("Matin (06h-12h)", value=True)
filtre_apresmidi = col2.checkbox("Après-midi (12h-18h)", value=True)
filtre_nuit = col3.checkbox("Nuit (18h-06h)", value=True)
# Construction du filtre combiné
filtres = []
if filtre_matin:
filtres.append(df_sonde["Heure"].between(6, 11))
if filtre_apresmidi:
filtres.append(df_sonde["Heure"].between(12, 17))
if filtre_nuit:
filtres.append((df_sonde["Heure"] >= 18) | (df_sonde["Heure"] < 6))
if filtres:
filtre_combiné = filtres[0]
for f in filtres[1:]:
filtre_combiné |= f
df_sonde = df_sonde[filtre_combiné]
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.dataframe(df_sonde, use_container_width=True)
st.subheader("📈 Évolution de la température")
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(df_sonde["Date"], df_sonde["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)
else:
st.warning("Aucune donnée trouvée pour cette date.")
# --- Alertes et bouton vers analyse logs ---
st.markdown("---")
st.subheader("🚨 Alertes de température")
if st.session_state["role"] == "superviseur":
if st.button("🧾 Voir les notifications relevées dans Monitor"):
st.session_state["page"] = "analyse_logs"
st.rerun()
table_alertes = f"Alertes_{site_selectionne}"
voir_toutes = False
if st.session_state["role"] == "superviseur":
voir_toutes = st.toggle("Afficher toutes les alertes (y compris acquittées)", value=False)
if voir_toutes:
cursor.execute(f"SELECT * FROM {table_alertes} ORDER BY Debut_defaut DESC")
else:
cursor.execute(f"SELECT * FROM {table_alertes} WHERE Status = 'En cours' ORDER BY Debut_defaut DESC")
alertes = cursor.fetchall()
if alertes:
for alerte in alertes:
cols = st.columns([3, 3, 2, 2, 2])
cols[0].markdown(f"**Sonde :** {alerte['Sonde']}")
cols[1].markdown(f"**Début :** {alerte['Debut_defaut'].strftime('%Y-%m-%d %H:%M')}")
cols[2].markdown(f"**Statut :** {alerte['Status']}")
if st.session_state["role"] == "superviseur" and alerte['Status'] == 'En cours':
key_btn = f"acq_{alerte['Id']}"
if cols[4].button("✅ Acquitter", key=key_btn):
cursor.execute(f"UPDATE {table_alertes} SET Status = 'Acquitté' WHERE Id = %s", (alerte['Id'],))
conn.commit()
st.success(f"Alerte acquittée pour {alerte['Sonde']}")
st.rerun()
else:
st.info("✅ Aucune alerte en cours.")
# --- Interface admin seuils / ON-OFF ---
if st.session_state["role"] == "superviseur":
st.markdown("---")
st.subheader("🛠️ Paramètres des sondes")
cursor.execute("SELECT * FROM Chambres_froides WHERE Lieu = %s", (site_selectionne,))
sondes_info = cursor.fetchall()
if sondes_info:
for sonde in sondes_info:
cols = st.columns([3, 2, 3])
cols[0].markdown(f"**{sonde['Sonde']}**")
etat_key = f"etat_{sonde['Id']}"
seuil_key = f"seuil_{sonde['Id']}"
etat_actuel = sonde["Etat"] == "ON"
new_etat = cols[1].checkbox("Actif", value=etat_actuel, key=etat_key)
new_seuil = cols[2].number_input("Seuil Max", min_value=-30.0, max_value=30.0, value=float(sonde["Temp_Max"]), step=0.5, key=seuil_key)
sonde["_new_etat"] = "ON" if new_etat else "OFF"
sonde["_new_seuil"] = new_seuil
if st.button("💾 Enregistrer les modifications"):
try:
for sonde in sondes_info:
cursor.execute("UPDATE Chambres_froides SET Etat = %s, Temp_Max = %s WHERE Id = %s", (sonde["_new_etat"], sonde["_new_seuil"], sonde["Id"]))
conn.commit()
st.success("✅ Modifications enregistrées.")
st.rerun()
except Exception as e:
st.error(f"Erreur lors de la mise à jour : {e}")
cursor.close()
conn.close()
except Exception as e:
st.error(f"Erreur MySQL : {e}")

View File

@@ -1,323 +0,0 @@
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

@@ -1,23 +0,0 @@
import smtplib
from email.mime.text import MIMEText
def envoyer_mail(sujet, message, destinataires):
msg = MIMEText(message)
msg['Subject'] = sujet
msg['From'] = 'alertes_saclay@domo91.fr'
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.sendmail(msg['From'], destinataires, msg.as_string())
print(f"📧 Mail envoyé à {destinataires}")
except Exception as e:
print(f"Erreur envoi mail : {e}")
# --- Test d'envoi ---
envoyer_mail(
sujet="🧪 Test d'envoi d'alerte",
message="Ceci est un test de l'envoi d'e-mail via alertes_saclay@domo91.fr.",
destinataires=["services@domo91.fr"]
)

View File

@@ -1,106 +0,0 @@
import paho.mqtt.client as mqtt
import mysql.connector
import re # Import de la bibliothèque des expressions régulières
# Configuration de la base de données MySQL
db_config = {
'user': 'superviseur', # Remplacez par votre utilisateur MySQL
'password': 'Bto7Lm_z]m!BFH!*', # Remplacez par votre mot de passe MySQL
'host': '54.36.188.119', # Adresse de votre serveur MySQL (localhost si en local)
'database': 'Sondes' # Nom de la base de données MySQL
}
def convertir_rom_id_en_hexa(rom_id):
# On découpe la chaîne rom_id en paires de deux caractères
hex_parts = [f"0x{rom_id[i:i + 2]}" for i in range(0, len(rom_id), 2)]
return ",".join(hex_parts)
# Connexion à la base de données MySQL
def connect_db():
return mysql.connector.connect(**db_config)
# Fonction pour vérifier si la sonde est déjà présente dans la base
def sonde_deja_presente(rom_id):
conn = connect_db()
cursor = conn.cursor()
try:
# Vérifier si la sonde existe déjà dans la base
cursor.execute("SELECT COUNT(*) FROM Tracker WHERE rom_id = %s", (rom_id,))
result = cursor.fetchone()
return result[0] > 0 # Si le nombre est supérieur à 0, la sonde est déjà présente
except mysql.connector.Error as err:
print(f"Erreur MySQL lors de la vérification de la sonde : {err}")
return False
finally:
cursor.close()
conn.close()
# Fonction pour insérer les sondes dans la base de données
def inserer_sonde(rom_id):
if sonde_deja_presente(rom_id):
print(f"Sonde {rom_id} déjà présente dans la base de données. Aucune insertion effectuée.")
return
conn = connect_db()
cursor = conn.cursor()
try:
# Conversion en hexadécimal
rom_id_hexa = convertir_rom_id_en_hexa(rom_id)
# Insertion de rom_id et de sa version hexa dans la base
cursor.execute("INSERT INTO Tracker (rom_id, hexa) VALUES (%s, %s)", (rom_id, rom_id_hexa))
conn.commit()
print(f"Sonde {rom_id} insérée dans la base de données avec son format hexa {rom_id_hexa}.")
except mysql.connector.Error as err:
print(f"Erreur MySQL : {err}")
finally:
cursor.close()
conn.close()
# Fonction de callback pour gérer les messages MQTT
def on_message(_client, _userdata, message):
payload = message.payload.decode()
print(f"Message reçu : {payload}")
# Utilisation de re pour extraire les ROM IDs (Sonde [rom_id])
rom_ids = re.findall(r"[0-9a-fA-F]{16}", payload) # Capturer les IDs de 16 caractères
if rom_ids:
for rom_id in rom_ids:
inserer_sonde(rom_id)
else:
print("Aucun ROM ID trouvé dans le message.")
# Configuration du client MQTT
mqtt_broker = "46.105.92.116"
mqtt_port = 1883 # Vérifiez si votre broker utilise un autre port
mqtt_user = "Bwps"
mqtt_password = "scJ5ACj2keRfI^"
mqtt_topic = "Tracker/sondes"
client = mqtt.Client()
# Authentification MQTT
client.username_pw_set(mqtt_user, mqtt_password)
# Configuration du callback pour les messages MQTT
client.on_message = on_message
# Connexion au broker MQTT
client.connect(mqtt_broker, mqtt_port, 60)
# Souscription au topic Tracker/sondes
client.subscribe(mqtt_topic)
# Démarrer la boucle MQTT
client.loop_forever()

View File

@@ -1,56 +0,0 @@
import subprocess
import smtplib
from email.mime.text import MIMEText
def envoyer_mail(sujet, message, destinataires):
msg = MIMEText(message)
msg['Subject'] = sujet
msg['From'] = 'alertes_saclay@domo91.fr'
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.sendmail(msg['From'], destinataires, msg.as_string())
print(f"📧 Mail envoyé à {destinataires}")
except Exception as e:
print(f"Erreur envoi mail : {e}")
# Liste des processus attendus
processus_attendus = [
"Chauffage",
"Cuisine_Saclay",
"Monitor",
"Streamlit",
"Telegram_sondes",
"cuisine_meudon"
]
# Commande supervisor
output = subprocess.getoutput("supervisorctl status")
# Vérification
anomalies = []
for line in output.splitlines():
for process in processus_attendus:
if line.startswith(process) and "RUNNING" not in line:
anomalies.append(line)
# Logs (facultatif)
with open("/var/log/supervisor_alert.log", "a") as log:
if anomalies:
log.write("🚨 Anomalies détectées :\n")
for a in anomalies:
log.write(f"{a}\n")
else:
log.write("✅ Tous les processus sont OK.\n")
# Envoi de mail si nécessaire
if anomalies:
message = "\n".join(anomalies)
envoyer_mail(
sujet="🚨 Alerte Supervisor : processus en erreur",
message=message,
destinataires=["services@domo91.fr"]
)

View File

@@ -1,19 +0,0 @@
#!/bin/bash
echo "🔄 Déploiement depuis Gitea - branche main"
cd /home/debian/travail/Gestion_sondes || exit 1
# Récupération des dernières modifs
git fetch origin
git checkout main
git pull origin main
# Sécurisation en lecture seule (facultatif mais recommandé)
chmod 444 Monitor.py Cuisine_*.py Streamlit.py
# Redémarrage de Supervisor
echo "🔁 Redémarrage des services Supervisor..."
sudo supervisorctl restart all
echo "✅ Déploiement terminé avec succès"

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,47 +0,0 @@
import subprocess
import smtplib
from email.mime.text import MIMEText
from datetime import datetime
# Informations mail
FROM = "alertes_saclay@domo91.fr"
TO = ["services@domo91.fr"]
SUBJECT = f"🟢 Rapport quotidien - Supervisor OK ({datetime.now().strftime('%d/%m/%Y')})"
# Liste des services attendus
processus_attendus = [
"Chauffage",
"Cuisine_Saclay",
"Monitor",
"Streamlit",
"Telegram_sondes",
"cuisine_meudon"
]
# Vérifie les statuts
output = subprocess.getoutput("supervisorctl status")
etat = "\n".join(line for line in output.splitlines() if any(p in line for p in processus_attendus))
# Contenu du mail
message = f"""Bonjour,
Voici l'état des processus supervisés ce jour ({datetime.now().strftime('%Y-%m-%d %H:%M')}).
{etat}
Cordialement,
Domo91
"""
msg = MIMEText(message)
msg["Subject"] = SUBJECT
msg["From"] = FROM
msg["To"] = ", ".join(TO)
try:
with smtplib.SMTP_SSL("smtp.mail.ovh.net", 465) as server:
server.login(FROM, "Kdpke674y23Feq^H")
server.sendmail(FROM, TO, msg.as_string())
print("✅ Rapport envoyé avec succès.")
except Exception as e:
print(f"Erreur envoi mail : {e}")