Consolidation de Domo91 et cosmétique

This commit is contained in:
2025-12-15 11:43:33 +01:00
parent a0b6d22727
commit d54832e558
7 changed files with 288 additions and 187 deletions

View File

@@ -9,7 +9,7 @@ Principes :
- DB_HOST / MQTT_HOST / SMTP_HOST : uniques (VPS unique)
- Paramètres par site via env : MAIL_TO_{SITE}, ALERT_SMS_TO_{SITE}, etc.
- Les alertes ne concernent QUE les sondes présentes dans Chambres_froides pour le site
et avec Etat=ON et En_entretien=0.
et avec Etat=ON .
"""
import datetime as dt
@@ -274,7 +274,7 @@ def lire_cfg_chambres(site: str):
"""
dbname = os.getenv("DB_NAME", "Sondes")
sql = f"""
SELECT Sonde, Temp_Max, Etat, En_entretien
SELECT Sonde, Temp_Max, Etat
FROM `{dbname}`.`Chambres_froides`
WHERE Lieu=%s
"""
@@ -283,11 +283,10 @@ def lire_cfg_chambres(site: str):
try:
cur = cnx.cursor()
cur.execute(sql, (site,))
for sonde, temp_max, etat, en_entretien in cur.fetchall():
for sonde, temp_max, etat in cur.fetchall():
cfg[str(sonde)] = {
"temp_max": float(temp_max),
"active": str(etat).upper() == "ON",
"entretien": bool(int(en_entretien or 0)),
}
return cfg
except MySQLError as err:
@@ -307,7 +306,7 @@ def compute_site_alarm(last_values: list[dict], cfg: dict[str, dict], hysteresis
meta = cfg.get(sonde)
if not meta:
continue
if (not meta["active"]) or meta["entretien"]:
if not meta["active"]:
continue
temp = float(row["Temperature"])
seuil = float(meta["temp_max"])
@@ -315,6 +314,70 @@ def compute_site_alarm(last_values: list[dict], cfg: dict[str, dict], hysteresis
return True, (sonde, temp, seuil)
return False, None
def depassement_depuis_2min(site: str, window_min: int = 3, needed_points: int = 2):
"""
Retourne (active: bool, trigger: (sonde, temp, seuil) | None)
active = True si une sonde dépasse son seuil sur au moins 'needed_points' mesures
dans les 'window_min' dernières minutes.
window_min=3 rend le système tolérant aux petits décalages (ex: sonde en retard).
needed_points=2 correspond à votre objectif "2 minutes" (sondes toutes les minutes).
"""
table = safe_site(site)
dbname = os.getenv("DB_NAME", "Sondes")
cnx = get_db()
try:
cur = cnx.cursor()
# 1) Cherche une sonde qui dépasse >=2 fois dans la fenêtre
cur.execute(f"""
SELECT m.Sonde
FROM `{table}` m
JOIN `{dbname}`.`Chambres_froides` c
ON c.Lieu=%s
AND c.Sonde=m.Sonde
AND UPPER(c.Etat)='ON'
WHERE m.Date >= NOW() - INTERVAL %s MINUTE
AND m.Temperature IS NOT NULL
AND m.Temperature > c.Temp_Max
GROUP BY m.Sonde
HAVING COUNT(*) >= %s
LIMIT 1
""", (site, window_min, needed_points))
row = cur.fetchone()
if not row:
return False, None
sonde = str(row[0])
# 2) Récupère dernière température + seuil pour le trigger
cur.execute(f"""
SELECT m.Temperature, c.Temp_Max
FROM `{table}` m
JOIN `{dbname}`.`Chambres_froides` c
ON c.Lieu=%s AND c.Sonde=%s
WHERE m.Sonde=%s
AND m.Temperature IS NOT NULL
ORDER BY m.Date DESC
LIMIT 1
""", (site, sonde, sonde))
trow = cur.fetchone()
if not trow:
return True, (sonde, 0.0, 0.0)
temp = float(trow[0])
seuil = float(trow[1])
return True, (sonde, temp, seuil)
except MySQLError as err:
log.exception("Erreur DB (depassement_depuis_2min): %s", err)
return False, None
finally:
cnx.close()
def depassement_depuis_30min(site: str, sonde: str, seuil: float) -> bool:
"""
@@ -820,9 +883,10 @@ class GyroPulseController:
self._last_trigger = None
def _is_alarm_now(self) -> tuple[bool, tuple[str, float, float] | None]:
last_rows = lire_sondes_depuis_db(self.site)
cfg = lire_cfg_chambres(self.site)
return compute_site_alarm(last_rows, cfg, hysteresis=float(os.getenv("GYRO_HYSTERESIS", "0.0")))
# Déclenchement "2 minutes" (2 points au-dessus du seuil sur ~3 minutes)
window_min = int(os.getenv("GYRO_WINDOW_MIN", "3"))
needed = int(os.getenv("GYRO_NEEDED_POINTS", "2"))
return depassement_depuis_2min(self.site, window_min=window_min, needed_points=needed)
def _run(self):
while not self._stop.is_set():
@@ -944,7 +1008,7 @@ def run_monitor_cycle(site: str, notifier: Notifier):
meta = cfg.get(nom)
if not meta:
continue
if (not meta["active"]) or meta["entretien"]:
if not meta["active"]:
continue
seuil = float(meta["temp_max"])