Initialisation depuis le code en production
This commit is contained in:
@@ -1,74 +0,0 @@
|
|||||||
import paho.mqtt.client as mqttClient
|
|
||||||
import mysql.connector
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
# Charger les variables d'environnement
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
# 📁 Création du dossier de logs s'il n'existe pas
|
|
||||||
dossier_logs = "/var/log/Chaufferie_Saclay"
|
|
||||||
os.makedirs(dossier_logs, exist_ok=True)
|
|
||||||
|
|
||||||
# 📝 Configuration du logger
|
|
||||||
logfile = os.path.join(dossier_logs, "Chaufferie_Saclay.log")
|
|
||||||
logging.basicConfig(
|
|
||||||
filename=logfile,
|
|
||||||
level=logging.INFO,
|
|
||||||
format="%(asctime)s - %(levelname)s - %(message)s"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 🔌 Connexion MySQL
|
|
||||||
try:
|
|
||||||
sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages")
|
|
||||||
mydb = mysql.connector.connect(
|
|
||||||
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:
|
|
||||||
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()
|
|
||||||
|
|
||||||
topic_mqtt = msg.topic.split('/')[1] # ex: 'ECS'
|
|
||||||
frigo_name = msg.topic.split('/')[-1] # ex: 'Dietrich_1'
|
|
||||||
temperature = float(msg.payload.decode())
|
|
||||||
|
|
||||||
sql = "INSERT INTO Chaufferie (Sonde, Temperature, Date, Topic) VALUES (%s, %s, %s, %s)"
|
|
||||||
val = (frigo_name, temperature, datetime.now(), topic_mqtt)
|
|
||||||
|
|
||||||
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("Chaufferie/#")
|
|
||||||
logging.info("Connexion MQTT réussie et abonnement au topic 'Chaufferie/#'.")
|
|
||||||
client.loop_forever()
|
|
||||||
except Exception as err:
|
|
||||||
logging.error(f"Erreur MQTT : {err}")
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import paho.mqtt.client as mqttClient
|
|
||||||
import mysql.connector
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
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"
|
|
||||||
os.makedirs(dossier_logs, exist_ok=True)
|
|
||||||
|
|
||||||
# 📝 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 🔌 Connexion MySQL
|
|
||||||
try:
|
|
||||||
sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages")
|
|
||||||
mydb = mysql.connector.connect(
|
|
||||||
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:
|
|
||||||
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 err:
|
|
||||||
logging.error(f"Erreur MQTT : {err}")
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
import paho.mqtt.client as mqttClient
|
|
||||||
import mysql.connector
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
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"
|
|
||||||
os.makedirs(dossier_logs, exist_ok=True)
|
|
||||||
|
|
||||||
# 📝 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# 🔌 Connexion MySQL
|
|
||||||
try:
|
|
||||||
sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages")
|
|
||||||
mydb = mysql.connector.connect(
|
|
||||||
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:
|
|
||||||
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 err:
|
|
||||||
logging.error(f"Erreur MQTT : {err}")
|
|
||||||
@@ -1,49 +1,49 @@
|
|||||||
Date;Lieu;Sonde;Température;Seuil;État
|
Date;Lieu;Sonde;Température;Seuil;État
|
||||||
2025-07-23 10:15:19;Saclay;Congelateur;-17.50;-15.0;Normal
|
2025-07-26 08:32:09;Saclay;Congelateur;-15.75;-15.0;Normal
|
||||||
2025-07-23 10:10:16;Saclay;Congelateur;-16.50;-15.0;Normal
|
2025-07-26 08:27:06;Saclay;Congelateur;-16.00;-15.0;Normal
|
||||||
2025-07-23 10:05:14;Saclay;Congelateur;-17.00;-15.0;Normal
|
2025-07-26 08:22:04;Saclay;Congelateur;-14.50;-15.0;Dépassement
|
||||||
2025-07-23 10:00:11;Saclay;Congelateur;-17.50;-15.0;Normal
|
2025-07-26 08:17:01;Saclay;Congelateur;-14.25;-15.0;Dépassement
|
||||||
2025-07-23 09:55:09;Saclay;Congelateur;-17.00;-15.0;Normal
|
2025-07-26 08:11:59;Saclay;Congelateur;-13.75;-15.0;Dépassement
|
||||||
2025-07-23 09:50:06;Saclay;Congelateur;-16.75;-15.0;Normal
|
2025-07-26 08:06:56;Saclay;Congelateur;-12.50;-15.0;Dépassement
|
||||||
2025-07-23 10:15:19;Saclay;BOF;1.00;8.0;Normal
|
2025-07-26 08:32:09;Saclay;BOF;1.50;8.0;Normal
|
||||||
2025-07-23 10:10:17;Saclay;BOF;2.25;8.0;Normal
|
2025-07-26 08:27:07;Saclay;BOF;1.00;8.0;Normal
|
||||||
2025-07-23 10:05:14;Saclay;BOF;1.00;8.0;Normal
|
2025-07-26 08:22:04;Saclay;BOF;3.25;8.0;Normal
|
||||||
2025-07-23 10:00:12;Saclay;BOF;2.00;8.0;Normal
|
2025-07-26 08:17:02;Saclay;BOF;3.00;8.0;Normal
|
||||||
2025-07-23 09:55:09;Saclay;BOF;1.00;8.0;Normal
|
2025-07-26 08:11:59;Saclay;BOF;2.00;8.0;Normal
|
||||||
2025-07-23 09:50:07;Saclay;BOF;1.25;8.0;Normal
|
2025-07-26 08:06:56;Saclay;BOF;1.50;8.0;Normal
|
||||||
2025-07-23 10:15:20;Saclay;Viandes;2.50;6.0;Normal
|
2025-07-26 08:32:10;Saclay;Viandes;20.75;6.0;Dépassement
|
||||||
2025-07-23 10:10:17;Saclay;Viandes;4.75;6.0;Normal
|
2025-07-26 08:27:07;Saclay;Viandes;20.50;6.0;Dépassement
|
||||||
2025-07-23 10:05:15;Saclay;Viandes;4.50;6.0;Normal
|
2025-07-26 08:22:05;Saclay;Viandes;21.00;6.0;Dépassement
|
||||||
2025-07-23 10:00:12;Saclay;Viandes;3.75;6.0;Normal
|
2025-07-26 08:17:02;Saclay;Viandes;20.50;6.0;Dépassement
|
||||||
2025-07-23 09:55:10;Saclay;Viandes;2.50;6.0;Normal
|
2025-07-26 08:12:00;Saclay;Viandes;20.75;6.0;Dépassement
|
||||||
2025-07-23 09:50:07;Saclay;Viandes;3.00;6.0;Normal
|
2025-07-26 08:06:57;Saclay;Viandes;20.50;6.0;Dépassement
|
||||||
2025-07-23 10:15:20;Saclay;Legumes;2.75;10.0;Normal
|
2025-07-26 08:32:10;Saclay;Legumes;4.00;10.0;Normal
|
||||||
2025-07-23 10:10:18;Saclay;Legumes;6.00;10.0;Normal
|
2025-07-26 08:27:08;Saclay;Legumes;3.50;10.0;Normal
|
||||||
2025-07-23 10:05:15;Saclay;Legumes;5.25;10.0;Normal
|
2025-07-26 08:22:05;Saclay;Legumes;5.50;10.0;Normal
|
||||||
2025-07-23 10:00:13;Saclay;Legumes;4.50;10.0;Normal
|
2025-07-26 08:17:03;Saclay;Legumes;5.50;10.0;Normal
|
||||||
2025-07-23 09:55:10;Saclay;Legumes;3.00;10.0;Normal
|
2025-07-26 08:12:00;Saclay;Legumes;5.00;10.0;Normal
|
||||||
2025-07-23 09:50:08;Saclay;Legumes;6.00;10.0;Normal
|
2025-07-26 08:06:57;Saclay;Legumes;3.75;10.0;Normal
|
||||||
2025-07-23 10:15:21;Saclay;MeP;4.00;8.0;Normal
|
2025-07-26 08:32:11;Saclay;MeP;5.25;8.0;Normal
|
||||||
2025-07-23 10:10:18;Saclay;MeP;5.50;8.0;Normal
|
2025-07-26 08:27:08;Saclay;MeP;4.00;8.0;Normal
|
||||||
2025-07-23 10:05:16;Saclay;MeP;4.00;8.0;Normal
|
2025-07-26 08:22:06;Saclay;MeP;7.00;8.0;Normal
|
||||||
2025-07-23 10:00:13;Saclay;MeP;6.00;8.0;Normal
|
2025-07-26 08:17:03;Saclay;MeP;7.75;8.0;Normal
|
||||||
2025-07-23 09:55:11;Saclay;MeP;3.00;8.0;Normal
|
2025-07-26 08:12:01;Saclay;MeP;7.00;8.0;Normal
|
||||||
2025-07-23 09:50:08;Saclay;MeP;6.00;8.0;Normal
|
2025-07-26 08:06:58;Saclay;MeP;5.50;8.0;Normal
|
||||||
2025-07-22 10:33:13;Meudon;Viandes;3.94;6.0;Normal
|
2025-07-26 07:09:45;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 09:14:54;Meudon;Viandes;3.94;6.0;Normal
|
2025-07-25 14:37:50;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 08:31:47;Meudon;Viandes;5.25;6.0;Normal
|
2025-07-25 14:32:11;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 08:26:46;Meudon;Viandes;5.06;6.0;Normal
|
2025-07-25 14:30:20;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 08:21:45;Meudon;Viandes;4.88;6.0;Normal
|
2025-07-24 11:00:36;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 08:16:44;Meudon;Viandes;4.75;6.0;Normal
|
2025-07-24 10:41:08;Meudon;Viandes;3.94;6.0;Normal
|
||||||
2025-07-22 10:33:13;Meudon;Poissons;3.94;6.0;Normal
|
2025-07-26 07:09:45;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 09:14:54;Meudon;Poissons;3.94;6.0;Normal
|
2025-07-25 14:37:50;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 08:31:47;Meudon;Poissons;5.75;6.0;Normal
|
2025-07-25 14:32:11;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 08:26:46;Meudon;Poissons;5.25;6.0;Normal
|
2025-07-25 14:30:20;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 08:21:45;Meudon;Poissons;4.56;6.0;Normal
|
2025-07-24 11:00:36;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 08:16:44;Meudon;Poissons;4.63;6.0;Normal
|
2025-07-24 10:41:08;Meudon;Poissons;3.94;6.0;Normal
|
||||||
2025-07-22 10:33:13;Meudon;BOF;3.00;8.0;Normal
|
2025-07-26 07:09:45;Meudon;BOF;3.00;8.0;Normal
|
||||||
2025-07-22 09:14:54;Meudon;BOF;3.00;8.0;Normal
|
2025-07-25 14:37:50;Meudon;BOF;3.00;8.0;Normal
|
||||||
2025-07-22 08:31:47;Meudon;BOF;2.25;8.0;Normal
|
2025-07-25 14:32:11;Meudon;BOF;3.00;8.0;Normal
|
||||||
2025-07-22 08:26:46;Meudon;BOF;1.75;8.0;Normal
|
2025-07-25 14:30:20;Meudon;BOF;3.00;8.0;Normal
|
||||||
2025-07-22 08:21:45;Meudon;BOF;1.25;8.0;Normal
|
2025-07-24 11:00:36;Meudon;BOF;3.00;8.0;Normal
|
||||||
2025-07-22 08:16:44;Meudon;BOF;1.00;8.0;Normal
|
2025-07-24 10:41:08;Meudon;BOF;3.00;8.0;Normal
|
||||||
|
|||||||
|
27417
Michel@78.197.249.156
27417
Michel@78.197.249.156
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
|||||||
from alerte_sms import envoyer_sms_ovh
|
|
||||||
|
|
||||||
site = "Meudon" # ou "Meudon", selon ce que tu veux tester
|
|
||||||
message = "✅ Test SMS depuis le script Monitor - tout fonctionne bien"
|
|
||||||
|
|
||||||
envoyer_sms_ovh(message, site)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import ovh
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
client = ovh.Client(
|
|
||||||
endpoint=os.getenv("OVH_ENDPOINT"),
|
|
||||||
application_key=os.getenv("OVH_APP_KEY"),
|
|
||||||
application_secret=os.getenv("OVH_APP_SECRET"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Demande d'une clé avec accès lecture/écriture sur les SMS
|
|
||||||
ck_request = client.new_consumer_key_request()
|
|
||||||
ck_request.add_rules(ovh.API_READ_WRITE, "/sms/*")
|
|
||||||
|
|
||||||
validation = ck_request.request()
|
|
||||||
|
|
||||||
print("✅ LIEN DE VALIDATION OVH :")
|
|
||||||
print(validation['validationUrl'])
|
|
||||||
print("\n👉 Une fois autorisé, colle cette valeur dans ton fichier .env :")
|
|
||||||
print(f"OVH_CONSUMER_KEY={validation['consumerKey']}")
|
|
||||||
176
alerte_sms.py
176
alerte_sms.py
@@ -1,176 +0,0 @@
|
|||||||
import mysql.connector
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import time
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
if os.name != 'nt':
|
|
||||||
log_dir = Path('/home/debian/Gestion_sondes/Logs')
|
|
||||||
else:
|
|
||||||
log_dir = Path.cwd() / 'Logs'
|
|
||||||
|
|
||||||
log_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
ENVOI_SMS = os.getenv("ENVOI_SMS") == "1"
|
|
||||||
|
|
||||||
# --- Config MySQL ---
|
|
||||||
config = {
|
|
||||||
"host": os.getenv("DB_HOST"),
|
|
||||||
"user": os.getenv("DB_USER"),
|
|
||||||
"password": os.getenv("DB_PASSWORD"),
|
|
||||||
"database": os.getenv("DB_NAME")
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Suivi des alertes actives pour rappels ---
|
|
||||||
alertes_actives = {}
|
|
||||||
|
|
||||||
# --- Fonction d'envoi de mail ---
|
|
||||||
def envoyer_sms_ovh(message, lieu):
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
sms_data = {
|
|
||||||
"account": os.getenv("OVH_ACCOUNT"),
|
|
||||||
"login": os.getenv("OVH_LOGIN"),
|
|
||||||
"password": os.getenv("OVH_PASSWORD"),
|
|
||||||
"message": f"{lieu}: {message}",
|
|
||||||
"receivers": os.getenv("SMS_DESTINATAIRES", "").split(","),
|
|
||||||
"sender": os.getenv("SMS_SENDER", "ALERTE")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Exemple d'envoi avec l'API OVH (à adapter selon ton endpoint exact)
|
|
||||||
response = requests.post("https://www.ovh.com/cgi-bin/sms/http2sms.cgi", data=sms_data)
|
|
||||||
print(f"📱 SMS envoyé : {response.text}", flush=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Erreur envoi SMS : {e}", flush=True)
|
|
||||||
|
|
||||||
# --- Fonction de surveillance ---
|
|
||||||
def surveiller():
|
|
||||||
global alertes_actives
|
|
||||||
log_entries = []
|
|
||||||
try:
|
|
||||||
conn = mysql.connector.connect(**config)
|
|
||||||
cursor = conn.cursor(dictionary=True)
|
|
||||||
|
|
||||||
cursor.execute("SELECT DISTINCT Lieu FROM 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,))
|
|
||||||
releves = cursor.fetchall()
|
|
||||||
|
|
||||||
for r in releves:
|
|
||||||
log_entries.append({
|
|
||||||
"Date": r['Date'],
|
|
||||||
"Lieu": lieu,
|
|
||||||
"Sonde": nom_sonde,
|
|
||||||
"Température": r['Temperature'],
|
|
||||||
"Seuil": seuil,
|
|
||||||
"État": "Dépassement" if r['Temperature'] > seuil else "Normal"
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(releves) == 6:
|
|
||||||
toutes_hors_seuil = all(r['Temperature'] > seuil for r in releves)
|
|
||||||
plus_ancien = releves[-1]['Date']
|
|
||||||
maintenant = datetime.now()
|
|
||||||
|
|
||||||
if toutes_hors_seuil and (maintenant - plus_ancien >= timedelta(minutes=30)):
|
|
||||||
cursor.execute(f"""
|
|
||||||
SELECT COUNT(*) as total FROM {table_alertes}
|
|
||||||
WHERE Sonde=%s AND Status='En cours'
|
|
||||||
""", (nom_sonde,))
|
|
||||||
en_cours = cursor.fetchone()
|
|
||||||
if en_cours['total'] == 0:
|
|
||||||
cursor.execute(
|
|
||||||
f"INSERT INTO {table_alertes} (Sonde, Debut_defaut, Status) VALUES (%s, NOW(), 'En cours')",
|
|
||||||
(nom_sonde,)
|
|
||||||
)
|
|
||||||
print(f"🚨 Alerte déclenchée pour {nom_sonde} ({lieu})", flush=True)
|
|
||||||
|
|
||||||
sujet = f"🚨 ALERTE TEMPÉRATURE - {nom_sonde} ({lieu})"
|
|
||||||
message = (
|
|
||||||
f"La sonde '{nom_sonde}' du site '{lieu}' a dépassé le seuil de {seuil}°C "
|
|
||||||
f"depuis plus de 30 minutes.\nHeure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
)
|
|
||||||
destinataires_list = os.getenv("EMAIL_DESTINATAIRES", "").split(",")
|
|
||||||
envoyer_mail(sujet, message, destinataires_list)
|
|
||||||
if ENVOI_SMS:
|
|
||||||
envoyer_sms_ovh(message, lieu)
|
|
||||||
|
|
||||||
# Suivi pour rappels
|
|
||||||
alertes_actives[nom_sonde] = maintenant
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Alerte déjà en cours : vérifier s'il faut faire un rappel
|
|
||||||
dernier_envoi = alertes_actives.get(nom_sonde)
|
|
||||||
if dernier_envoi and (maintenant - dernier_envoi >= timedelta(hours=1)):
|
|
||||||
sujet = f"🔔 RAPPEL ALERTE TEMPÉRATURE - {nom_sonde} ({lieu})"
|
|
||||||
message = (
|
|
||||||
f"La sonde '{nom_sonde}' du site '{lieu}' est TOUJOURS en dépassement de seuil (>{seuil}°C).\n"
|
|
||||||
f"Heure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
||||||
)
|
|
||||||
destinataires_list = os.getenv("EMAIL_DESTINATAIRES").split(",")
|
|
||||||
envoyer_mail(sujet, message, destinataires_list)
|
|
||||||
if ENVOI_SMS:
|
|
||||||
envoyer_sms_ovh(message, lieu)
|
|
||||||
alertes_actives[nom_sonde] = maintenant
|
|
||||||
|
|
||||||
# Vérifier retour à la normale (Acquittement)
|
|
||||||
cursor.execute(f"""
|
|
||||||
SELECT Temperature FROM {table_temp}
|
|
||||||
WHERE Sonde = %s
|
|
||||||
ORDER BY Date DESC LIMIT 1
|
|
||||||
""", (nom_sonde,))
|
|
||||||
derniere = cursor.fetchone()
|
|
||||||
if derniere and derniere['Temperature'] <= seuil:
|
|
||||||
cursor.execute(f"""
|
|
||||||
UPDATE {table_alertes}
|
|
||||||
SET Status = 'Acquitté'
|
|
||||||
WHERE Sonde = %s AND Status IN ('En cours', 'Test')
|
|
||||||
""", (nom_sonde,))
|
|
||||||
|
|
||||||
# Nettoyage du suivi si normalisé
|
|
||||||
if nom_sonde in alertes_actives:
|
|
||||||
del alertes_actives[nom_sonde]
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
if log_entries:
|
|
||||||
import pandas as pd
|
|
||||||
df_logs = pd.DataFrame(log_entries)
|
|
||||||
|
|
||||||
# Sauvegarde principale
|
|
||||||
df_logs.to_csv(log_dir / "monitor.csv", sep=";", index=False)
|
|
||||||
|
|
||||||
# Sauvegarde secondaire (Linux uniquement)
|
|
||||||
if os.name != 'nt':
|
|
||||||
df_logs.to_csv("/var/log/monitor.csv", sep=";", index=False)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Erreur : {e}", flush=True)
|
|
||||||
|
|
||||||
# --- Boucle principale ---
|
|
||||||
while True:
|
|
||||||
print(f"📡 Vérification à {datetime.now()}", flush=True)
|
|
||||||
surveiller()
|
|
||||||
time.sleep(300) # 5 minutes
|
|
||||||
|
|
||||||
@@ -9,3 +9,4 @@ schedule~=1.2.2
|
|||||||
paramiko~=3.5.1
|
paramiko~=3.5.1
|
||||||
python-dotenv
|
python-dotenv
|
||||||
bcrypt
|
bcrypt
|
||||||
|
ovh
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import mysql.connector
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import time
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import ovh
|
from db_utils import connect_to_mysql
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from utils_sms import envoyer_sms
|
||||||
|
|
||||||
if os.name != 'nt':
|
if os.name != 'nt':
|
||||||
log_dir = Path('/home/debian/Gestion_sondes/Logs')
|
log_dir = Path('/home/debian/Gestion_sondes/Logs')
|
||||||
@@ -16,45 +16,9 @@ log_dir.mkdir(parents=True, exist_ok=True)
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
ENVOI_SMS = os.getenv("ENVOI_SMS") == "1"
|
ENVOI_SMS = os.getenv("ENVOI_SMS") == "1"
|
||||||
|
|
||||||
# --- Config MySQL ---
|
|
||||||
config = {
|
|
||||||
"host": os.getenv("DB_HOST"),
|
|
||||||
"user": os.getenv("DB_USER"),
|
|
||||||
"password": os.getenv("DB_PASSWORD"),
|
|
||||||
"database": os.getenv("DB_NAME")
|
|
||||||
}
|
|
||||||
|
|
||||||
print("▶️ Lancement Monitor.py")
|
print("▶️ Lancement Monitor.py")
|
||||||
|
|
||||||
# --- Fonction d’envoi de SMS ---
|
|
||||||
def envoyer_sms_ovh(message, lieu):
|
|
||||||
client = ovh.Client(
|
|
||||||
endpoint=os.getenv("OVH_ENDPOINT"), # ex : 'ovh-eu'
|
|
||||||
application_key=os.getenv("OVH_APP_KEY"),
|
|
||||||
application_secret=os.getenv("OVH_APP_SECRET"),
|
|
||||||
consumer_key=os.getenv("OVH_CONSUMER_KEY"),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
services = client.get('/sms/')
|
|
||||||
if not services:
|
|
||||||
print("❌ Aucun service SMS OVH trouvé", flush=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
service_name = services[0]
|
|
||||||
numero_dest = os.getenv("NUMERO_DESTINATAIRE")
|
|
||||||
|
|
||||||
result = client.post(f'/sms/{service_name}/jobs',
|
|
||||||
sender='Monitor',
|
|
||||||
message=message,
|
|
||||||
receivers=[numero_dest],
|
|
||||||
noStopClause=True
|
|
||||||
)
|
|
||||||
print(f"📱 SMS envoyé à {numero_dest} pour {lieu}. Job ID : {result['ids']}", flush=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Erreur envoi SMS : {e}", flush=True)
|
|
||||||
|
|
||||||
# --- Suivi des alertes actives pour rappels ---
|
# --- Suivi des alertes actives pour rappels ---
|
||||||
alertes_actives = {}
|
alertes_actives = {}
|
||||||
|
|
||||||
@@ -63,17 +27,17 @@ def surveiller():
|
|||||||
global alertes_actives
|
global alertes_actives
|
||||||
log_entries = []
|
log_entries = []
|
||||||
try:
|
try:
|
||||||
conn = mysql.connector.connect(**config)
|
conn = connect_to_mysql()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor(dictionary=True)
|
||||||
|
|
||||||
cursor.execute("SELECT DISTINCT Lieu FROM 'Chambres_froides'")
|
cursor.execute("SELECT DISTINCT Lieu FROM `Chambres_froides`")
|
||||||
lieux = [row['Lieu'] for row in cursor.fetchall()]
|
lieux = [row['Lieu'] for row in cursor.fetchall()]
|
||||||
|
|
||||||
for lieu in lieux:
|
for lieu in lieux:
|
||||||
table_temp = lieu
|
table_temp = lieu
|
||||||
table_alertes = f"Alertes_{lieu}"
|
table_alertes = f"Alertes_{lieu}"
|
||||||
|
|
||||||
cursor.execute("SELECT Sonde, Temp_Max FROM 'Chambres_froides' WHERE Lieu=%s AND Etat='ON'", (lieu,))
|
cursor.execute("SELECT Sonde, Temp_Max FROM `Chambres_froides` WHERE Lieu=%s AND Etat='ON'", (lieu,))
|
||||||
sondes = cursor.fetchall()
|
sondes = cursor.fetchall()
|
||||||
|
|
||||||
for sonde in sondes:
|
for sonde in sondes:
|
||||||
@@ -120,7 +84,7 @@ def surveiller():
|
|||||||
f"depuis plus de 30 minutes.\nHeure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
f"depuis plus de 30 minutes.\nHeure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
)
|
)
|
||||||
if ENVOI_SMS:
|
if ENVOI_SMS:
|
||||||
envoyer_sms_ovh(message, lieu)
|
envoyer_sms(message, lieu)
|
||||||
|
|
||||||
alertes_actives[nom_sonde] = maintenant
|
alertes_actives[nom_sonde] = maintenant
|
||||||
|
|
||||||
@@ -132,7 +96,7 @@ def surveiller():
|
|||||||
f"Heure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
f"Heure : {maintenant.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
)
|
)
|
||||||
if ENVOI_SMS:
|
if ENVOI_SMS:
|
||||||
envoyer_sms_ovh(message, lieu)
|
envoyer_sms(message, lieu)
|
||||||
alertes_actives[nom_sonde] = maintenant
|
alertes_actives[nom_sonde] = maintenant
|
||||||
|
|
||||||
# Vérifier retour à la normale (Acquittement)
|
# Vérifier retour à la normale (Acquittement)
|
||||||
23
scripts/db_utils.py
Normal file
23
scripts/db_utils.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import mysql.connector
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
def connect_to_mysql(db_name: str = None):
|
||||||
|
"""
|
||||||
|
Se connecte à MySQL. Par défaut, utilise la base spécifiée dans DB_NAME,
|
||||||
|
mais peut être redirigé vers une autre base en passant db_name.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
sys.path.insert(0, "/myenv/lib/python3.11.2/site-packages") # à ajuster si inutile
|
||||||
|
mydb = mysql.connector.connect(
|
||||||
|
host=os.getenv("DB_HOST"),
|
||||||
|
user=os.getenv("DB_USER"),
|
||||||
|
password=os.getenv("DB_PASSWORD"),
|
||||||
|
database=db_name if db_name else os.getenv("DB_NAME")
|
||||||
|
)
|
||||||
|
logging.info(f"Connexion MySQL à la base {mydb.database} réussie.")
|
||||||
|
return mydb
|
||||||
|
except mysql.connector.Error as err:
|
||||||
|
logging.error(f"Erreur de connexion MySQL : {err}")
|
||||||
|
sys.exit(1)
|
||||||
35
scripts/logger_config.py
Normal file
35
scripts/logger_config.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
def setup_logger(log_filename: str, dossier_logs: str = "/var/log/Cuisine_Saclay") -> None:
|
||||||
|
"""
|
||||||
|
Configure le logger pour écrire à la fois dans un fichier et sur la console.
|
||||||
|
|
||||||
|
:param log_filename: Nom du fichier de log (exemple : 'Cuisine_Saclay.log')
|
||||||
|
:param dossier_logs: Dossier où enregistrer les logs (par défaut : /var/log/Cuisine_Saclay)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 📁 Créer le dossier s'il n'existe pas
|
||||||
|
os.makedirs(dossier_logs, exist_ok=True)
|
||||||
|
|
||||||
|
# 📄 Chemin complet du fichier de log
|
||||||
|
logfile = os.path.join(dossier_logs, log_filename)
|
||||||
|
|
||||||
|
# 📝 Configuration de base du logger (fichier)
|
||||||
|
logging.basicConfig(
|
||||||
|
filename=logfile,
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
|
filemode="a" # ajouter au fichier existant
|
||||||
|
)
|
||||||
|
|
||||||
|
# 🔔 Ajout de la sortie console
|
||||||
|
console = logging.StreamHandler()
|
||||||
|
console.setLevel(logging.INFO)
|
||||||
|
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||||
|
console.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 👇 Ajouter le handler console au logger racine
|
||||||
|
logging.getLogger('').addHandler(console)
|
||||||
|
|
||||||
|
logging.info(f"Logger initialisé. Fichier de log : {logfile}")
|
||||||
49
scripts/mqtt_logger.py
Normal file
49
scripts/mqtt_logger.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import argparse
|
||||||
|
import paho.mqtt.client as mqtt_client
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from logger_config import setup_logger
|
||||||
|
from db_utils import connect_to_mysql
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
def on_message(table_sql, _client, _userdata, msg):
|
||||||
|
try:
|
||||||
|
logging.info(f"Message reçu sur {msg.topic}: {msg.payload.decode()}")
|
||||||
|
cursor = mydb.cursor()
|
||||||
|
sonde_name = '/'.join(msg.topic.split('/')[1:])
|
||||||
|
sql = f"INSERT INTO {table_sql} (Sonde, Temperature) VALUES (%s, %s)"
|
||||||
|
val = (sonde_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}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--log", required=True, help="Nom du fichier de log")
|
||||||
|
parser.add_argument("--table", required=True, help="Nom complet de la table SQL")
|
||||||
|
parser.add_argument("--topic", required=True, help="Topic MQTT à écouter")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 📋 Initialiser le logger
|
||||||
|
setup_logger(args.log)
|
||||||
|
|
||||||
|
# 🔑 Charger les variables d'environnement
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# 🔌 Connexion MySQL
|
||||||
|
mydb = connect_to_mysql()
|
||||||
|
|
||||||
|
# 📡 Connexion MQTT
|
||||||
|
try:
|
||||||
|
client = mqtt_client.Client()
|
||||||
|
client.username_pw_set("Bwps", "scJ5ACj2keRfI^")
|
||||||
|
client.on_message = partial(on_message, args.table)
|
||||||
|
client.connect("54.36.188.119", 1883, 60)
|
||||||
|
client.subscribe(args.topic)
|
||||||
|
logging.info(f"Connexion MQTT réussie et abonnement au topic '{args.topic}'.")
|
||||||
|
client.loop_forever()
|
||||||
|
except Exception as err:
|
||||||
|
logging.error(f"Erreur MQTT : {err}")
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
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
|
|
||||||
echo ""
|
|
||||||
echo "===== 🔄 DÉMARRAGE REDÉMARRAGE DES SERVICES ====="
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
SERVICES=("Cuisine_Saclay" "Monitor" "cuisine_meudon" "Interface")
|
|
||||||
ALL_OK=true
|
|
||||||
|
|
||||||
for service in "${SERVICES[@]}"
|
|
||||||
do
|
|
||||||
echo "🔁 Redémarrage de $service..."
|
|
||||||
supervisorctl restart $service >> $LOG 2>&1
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "✅ $service redémarré avec succès."
|
|
||||||
else
|
|
||||||
echo "❌ Échec du redémarrage de $service."
|
|
||||||
ALL_OK=false
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
DATE_END=$(date '+%Y-%m-%d %H:%M:%S')
|
|
||||||
echo "[$DATE_END] ✅ Tous les services ont été traités." >> $LOG
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if $ALL_OK; then
|
|
||||||
echo "🎉 Tous les services ont été redémarrés avec succès !"
|
|
||||||
else
|
|
||||||
echo "⚠️ Certains services n'ont pas pu être redémarrés. Vérifiez le log :"
|
|
||||||
echo " → $LOG"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo "===== ✅ FIN DU SCRIPT ====="
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "===== STATUS DES APPS SUPERVISÉES ====="
|
|
||||||
supervisorctl status
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "===== UTILISATION MÉMOIRE DES PROCESS STREAMLIT ====="
|
|
||||||
ps aux | grep streamlit | grep -v grep | awk '{printf "%-8s %-6s %-6s %-6s %s\n", $1, $2, $4, $3, $11" "$12" "$13" "$14" "$15" "$16" "$17" "$18}' | awk 'BEGIN { print "USER PID %MEM %CPU CMD" } { print }'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "===== PORTS UTILISÉS PAR STREAMLIT ====="
|
|
||||||
ss -tlnp | grep streamlit | awk '{print $4 " <- " $7}' | sed 's/.*://;s/,.*//'
|
|
||||||
@@ -36,7 +36,7 @@ if envoyer_mail:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with smtplib.SMTP_SSL("smtp.mail.ovh.net", 465) as server:
|
with smtplib.SMTP_SSL("smtp.mail.ovh.net", 465) as server:
|
||||||
server.login("supervisor@domo91.fr", "4j%6A63$F##x7h")
|
server.login("services@domo91.fr", "6ZiCsVtSf9@nEHv@$^0")
|
||||||
server.sendmail(msg["From"], [msg["To"]], msg.as_string())
|
server.sendmail(msg["From"], [msg["To"]], msg.as_string())
|
||||||
print("📧 Mail envoyé.")
|
print("📧 Mail envoyé.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1,19 +1,31 @@
|
|||||||
#!/home/debian/Gestion_sondes/myenv/bin/python3
|
#!/home/debian/Gestion_sondes/myenv/bin/python3
|
||||||
import mysql.connector
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import os
|
import os
|
||||||
import ovh
|
import db_utils
|
||||||
|
import logging
|
||||||
|
from utils_sms import envoyer_sms
|
||||||
|
|
||||||
|
# Dossier Logs
|
||||||
|
LOG_DIR = '/home/debian/Gestion_sondes/Logs'
|
||||||
|
os.makedirs(LOG_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
# Fichier de log (nommé par date)
|
||||||
|
log_filename = os.path.join(LOG_DIR, datetime.now().strftime("surveillance_%Y-%m-%d.log"))
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
handlers=[
|
||||||
|
logging.FileHandler(log_filename),
|
||||||
|
logging.StreamHandler() # Affiche aussi les logs dans la console
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# Charger .env
|
# Charger .env
|
||||||
load_dotenv('/home/debian/Gestion_sondes/.env')
|
load_dotenv('/home/debian/Gestion_sondes/.env')
|
||||||
|
|
||||||
# MySQL
|
|
||||||
MYSQL_HOST = os.getenv('DB_HOST')
|
|
||||||
MYSQL_USER = os.getenv('DB_USER')
|
|
||||||
MYSQL_PASSWORD = os.getenv('DB_PASSWORD')
|
|
||||||
MYSQL_DB = os.getenv('DB_NAME')
|
|
||||||
|
|
||||||
# OVH SMS
|
# OVH SMS
|
||||||
APP_KEY = os.getenv('OVH_APP_KEY')
|
APP_KEY = os.getenv('OVH_APP_KEY')
|
||||||
APP_SECRET = os.getenv('OVH_APP_SECRET')
|
APP_SECRET = os.getenv('OVH_APP_SECRET')
|
||||||
@@ -22,34 +34,26 @@ SERVICE_NAME = os.getenv('OVH_SERVICE_NAME')
|
|||||||
SMS_RECEIVER = os.getenv('SMS_RECEIVER')
|
SMS_RECEIVER = os.getenv('SMS_RECEIVER')
|
||||||
SMS_SENDER = os.getenv('OVH_SMS_SENDER')
|
SMS_SENDER = os.getenv('OVH_SMS_SENDER')
|
||||||
|
|
||||||
# Tables à surveiller
|
tables = ['Saclay', 'Meudon', 'Chaufferie']
|
||||||
tables = ['Saclay', 'Meudon'] # ajoute tes sites ici
|
|
||||||
|
|
||||||
# seuils
|
|
||||||
DELAI_MINUTES = 15
|
DELAI_MINUTES = 15
|
||||||
RAPPEL_HEURES = 6
|
RAPPEL_HEURES = 6
|
||||||
|
|
||||||
# Dossier pour mémoriser les états
|
|
||||||
STATE_DIR = '/tmp/surveillance_states'
|
STATE_DIR = '/tmp/surveillance_states'
|
||||||
os.makedirs(STATE_DIR, exist_ok=True)
|
os.makedirs(STATE_DIR, exist_ok=True)
|
||||||
|
|
||||||
def should_send_alert(site):
|
def should_send_alert(site):
|
||||||
state_file = os.path.join(STATE_DIR, f'{site}.state')
|
state_file = os.path.join(STATE_DIR, f'{site}.state')
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
|
||||||
if not os.path.exists(state_file):
|
if not os.path.exists(state_file):
|
||||||
with open(state_file, 'w') as f:
|
with open(state_file, 'w') as f:
|
||||||
f.write(now.isoformat())
|
f.write(now.isoformat())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
with open(state_file, 'r') as f:
|
with open(state_file, 'r') as f:
|
||||||
last_alert = datetime.fromisoformat(f.read().strip())
|
last_alert = datetime.fromisoformat(f.read().strip())
|
||||||
|
|
||||||
if now - last_alert >= timedelta(hours=RAPPEL_HEURES):
|
if now - last_alert >= timedelta(hours=RAPPEL_HEURES):
|
||||||
with open(state_file, 'w') as f:
|
with open(state_file, 'w') as f:
|
||||||
f.write(now.isoformat())
|
f.write(now.isoformat())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def clear_state(site):
|
def clear_state(site):
|
||||||
@@ -57,29 +61,8 @@ def clear_state(site):
|
|||||||
if os.path.exists(state_file):
|
if os.path.exists(state_file):
|
||||||
os.remove(state_file)
|
os.remove(state_file)
|
||||||
|
|
||||||
def envoyer_sms(message):
|
|
||||||
client = ovh.Client(
|
|
||||||
endpoint='ovh-eu',
|
|
||||||
application_key=APP_KEY,
|
|
||||||
application_secret=APP_SECRET,
|
|
||||||
consumer_key=CONSUMER_KEY,
|
|
||||||
)
|
|
||||||
|
|
||||||
result = client.post(f'/sms/{SERVICE_NAME}/jobs',
|
|
||||||
receivers=[SMS_RECEIVER],
|
|
||||||
message=message,
|
|
||||||
priority='high',
|
|
||||||
sender=SMS_SENDER
|
|
||||||
)
|
|
||||||
print("🚨 SMS envoyé :", result)
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cnx = mysql.connector.connect(
|
cnx = db_utils.connect_to_mysql() # ← appel via db_utils
|
||||||
host=MYSQL_HOST,
|
|
||||||
user=MYSQL_USER,
|
|
||||||
password=MYSQL_PASSWORD,
|
|
||||||
database=MYSQL_DB
|
|
||||||
)
|
|
||||||
cursor = cnx.cursor()
|
cursor = cnx.cursor()
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
@@ -95,15 +78,16 @@ def main():
|
|||||||
if should_send_alert(table):
|
if should_send_alert(table):
|
||||||
problemes.append(f"{table} (dernier relevé : {last_update})")
|
problemes.append(f"{table} (dernier relevé : {last_update})")
|
||||||
else:
|
else:
|
||||||
print(f"⏳ Problème déjà signalé pour {table}, attente du délai de rappel.")
|
logging.info(f"⏳ Problème déjà signalé pour {table}, attente du délai de rappel.")
|
||||||
else:
|
else:
|
||||||
if os.path.exists(os.path.join(STATE_DIR, f'{table}.state')):
|
if os.path.exists(os.path.join(STATE_DIR, f'{table}.state')):
|
||||||
message = f"✅ {table} : relevés à nouveau reçus. Situation normale."
|
message = f"✅ {table} : relevés à nouveau reçus. Situation normale."
|
||||||
envoyer_sms(message)
|
envoyer_sms(message)
|
||||||
clear_state(table)
|
clear_state(table)
|
||||||
print(f"📩 SMS de retour à la normale envoyé pour {table}.")
|
logging.info(f"📩 SMS de retour à la normale envoyé pour {table}.")
|
||||||
else:
|
else:
|
||||||
print(f"✅ {table} OK (dernier relevé : {last_update})")
|
logging.info(f"✅ {table} OK (dernier relevé : {last_update})")
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
cnx.close()
|
cnx.close()
|
||||||
|
|
||||||
@@ -111,7 +95,7 @@ def main():
|
|||||||
message = "⚠️ ALERTE : pas de relevés depuis >15min :\n" + "\n".join(problemes)
|
message = "⚠️ ALERTE : pas de relevés depuis >15min :\n" + "\n".join(problemes)
|
||||||
envoyer_sms(message)
|
envoyer_sms(message)
|
||||||
else:
|
else:
|
||||||
print("👍 Tout est OK, aucun SMS envoyé.")
|
logging.info("👍 Tout est OK, aucun SMS envoyé.")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
34
scripts/utils_sms.py
Normal file
34
scripts/utils_sms.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
import ovh
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
def envoyer_sms(message: str, lieu: str = ""):
|
||||||
|
try:
|
||||||
|
client = ovh.Client(
|
||||||
|
endpoint=os.getenv("OVH_ENDPOINT"),
|
||||||
|
application_key=os.getenv("OVH_APP_KEY"),
|
||||||
|
application_secret=os.getenv("OVH_APP_SECRET"),
|
||||||
|
consumer_key=os.getenv("OVH_CONSUMER_KEY"),
|
||||||
|
)
|
||||||
|
|
||||||
|
services = client.get('/sms/')
|
||||||
|
if not services:
|
||||||
|
print("❌ Aucun service SMS OVH trouvé", flush=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
service_name = services[0]
|
||||||
|
numero_dest = os.getenv("NUMERO_DESTINATAIRE")
|
||||||
|
|
||||||
|
result = client.post(f'/sms/{service_name}/jobs',
|
||||||
|
sender='Monitor',
|
||||||
|
message=message,
|
||||||
|
receivers=[numero_dest],
|
||||||
|
noStopClause=True
|
||||||
|
)
|
||||||
|
print(f"📱 SMS envoyé à {numero_dest} pour {lieu}. Job ID : {result['ids']}", flush=True)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erreur envoi SMS : {e}", flush=True)
|
||||||
|
|
||||||
100
utils/db.py
100
utils/db.py
@@ -1,100 +0,0 @@
|
|||||||
import mysql.connector
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
def lire_alertes_sondes():
|
|
||||||
conn = None
|
|
||||||
cursor = None
|
|
||||||
try:
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor(dictionary=True)
|
|
||||||
cursor.execute("SELECT * FROM Alertes_Chaufferie ORDER BY Debut_defaut DESC")
|
|
||||||
return cursor.fetchall()
|
|
||||||
except Exception as e:
|
|
||||||
import streamlit as st
|
|
||||||
st.warning(f"Erreur lecture alertes : {e}")
|
|
||||||
return []
|
|
||||||
finally:
|
|
||||||
if cursor:
|
|
||||||
cursor.close()
|
|
||||||
if conn and conn.is_connected():
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def acquitter_alerte(id_alerte):
|
|
||||||
conn = None
|
|
||||||
cursor = None
|
|
||||||
try:
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("UPDATE Alertes_Chaufferie SET Etat = 'Acquitté' WHERE Id = %s", (id_alerte,))
|
|
||||||
conn.commit()
|
|
||||||
except Exception as e:
|
|
||||||
import streamlit as st
|
|
||||||
st.warning(f"Erreur lors de l'acquittement : {e}")
|
|
||||||
finally:
|
|
||||||
if cursor:
|
|
||||||
cursor.close()
|
|
||||||
if conn and conn.is_connected():
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
|
|
||||||
def verifier_utilisateur_commun(login, password):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor(dictionary=True)
|
|
||||||
|
|
||||||
query = """
|
|
||||||
SELECT NomUtilisateur FROM Commun.AccesUtilisateurs
|
|
||||||
WHERE NomUtilisateur = %s
|
|
||||||
AND MotDePasse = %s
|
|
||||||
AND (DateExpiration IS NULL OR DateExpiration >= CURDATE())
|
|
||||||
"""
|
|
||||||
cursor.execute(query, (login, password))
|
|
||||||
user = cursor.fetchone()
|
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
return user # None si non trouvée
|
|
||||||
|
|
||||||
def get_connection():
|
|
||||||
return mysql.connector.connect(
|
|
||||||
host=os.getenv("DB_HOST"),
|
|
||||||
user=os.getenv("DB_USER"),
|
|
||||||
password=os.getenv("DB_PASSWORD"),
|
|
||||||
database=os.getenv("DB_NAME")
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_latest_chaufferie():
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor(dictionary=True)
|
|
||||||
query = """
|
|
||||||
SELECT c1.Sonde, c1.Temperature, c1.Date, c1.Topic
|
|
||||||
FROM Chaufferie c1
|
|
||||||
INNER JOIN (
|
|
||||||
SELECT Sonde, MAX(Date) AS MaxDate
|
|
||||||
FROM Chaufferie
|
|
||||||
GROUP BY Sonde
|
|
||||||
) c2 ON c1.Sonde = c2.Sonde AND c1.Date = c2.MaxDate
|
|
||||||
ORDER BY c1.Topic, c1.Sonde
|
|
||||||
"""
|
|
||||||
cursor.execute(query)
|
|
||||||
data = cursor.fetchall()
|
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_history_by_sonde(sonde_name):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor(dictionary=True)
|
|
||||||
query = """
|
|
||||||
SELECT Date, Temperature
|
|
||||||
FROM Chaufferie
|
|
||||||
WHERE Sonde = %s AND Date >= NOW() - INTERVAL 1 DAY
|
|
||||||
ORDER BY Date ASC
|
|
||||||
"""
|
|
||||||
cursor.execute(query, (sonde_name,))
|
|
||||||
data = cursor.fetchall()
|
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
return data
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user