relancement cuisines Saclay et Meudon

This commit is contained in:
2025-09-22 11:11:55 +02:00
parent 90aab548d4
commit bb461a2ed1
10 changed files with 343 additions and 32 deletions

View File

@@ -115,6 +115,49 @@ def lire_sondes_depuis_db(site: str):
finally:
cnx.close()
def lire_cfg_chambres(site: str):
"""
Retourne un dict {sonde: {"temp_max": float, "active": bool, "entretien": bool}}
depuis Chambres_froides pour le site.
"""
sql = """
SELECT Sonde, Temp_Max, Etat, En_entretien
FROM Chambres_froides
WHERE Lieu=%s
"""
cnx = get_db()
cfg: dict[str, dict] = {}
try:
cur = cnx.cursor()
cur.execute(sql, (site,))
for sonde, temp_max, etat, en_entretien 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:
log.exception("Erreur DB (lire_cfg_chambres): %s", err)
return cfg
finally:
cnx.close()
def compute_site_alarm(last_values: list[dict], cfg: dict[str, dict], hysteresis: float = 0.0):
"""
Retourne (is_on: bool, trigger: tuple[str,float,float] | None)
trigger = (sonde, temp, seuil) si dépassement détecté.
"""
for row in last_values:
sonde = str(row["Sonde"])
meta = cfg.get(sonde)
if not meta or not meta["active"] or meta["entretien"]:
continue
temp = float(row["Temperature"])
if temp > float(meta["temp_max"]) + 0.0:
return True, (sonde, temp, float(meta["temp_max"]))
return False, None
def lire_seuils_depuis_db(site: str):
sql = """
SELECT Sonde, Temp_Max
@@ -360,8 +403,12 @@ class MQTTPublisher:
def __init__(self, site: str):
self.enabled = (_mqtt_ok and (os.getenv("GYRO_MODE", "").lower() == "mqtt"))
self.site = site
self.topic = (os.getenv(f"GYRO_MQTT_TOPIC_{site}") or
os.getenv(f"GYRO_MQTT_TOPIC_{site.capitalize()}"))
self.topic = (
os.getenv(f"GYRO_MQTT_TOPIC_{site}") or
os.getenv(f"GYRO_MQTT_TOPIC_{site.upper()}") or
os.getenv("GYRO_MQTT_TOPIC") or
f"Sondes/{site}/Gyro/cmd"
)
self.last_state: bool | None = None
if not self.enabled:
@@ -417,9 +464,9 @@ class MQTTPublisher:
if self.last_state is not None and self.last_state == on:
return
payload = "on" if on else "off"
payload = "ON" if on else "OFF"
try:
r = self.client.publish(self.topic, payload=payload, qos=1, retain=True)
r = self.client.publish(self.topic, payload=payload, qos=2, retain=True)
r.wait_for_publish(timeout=3)
if r.rc != 0:
log.warning("MQTT publish rc=%s (topic=%s)", r.rc, self.topic)
@@ -438,22 +485,34 @@ def notifier_sur_depassement(site: str, sonde: str, temp: float, seuil: float):
subject, sms_text, email_body = build_alert_text(site, sonde, temp, seuil)
notifier.send_sms(sms_text)
notifier.send_email(subject, email_body)
try: beacon.set(True)
except Exception: pass
def notifier_acquittement(site: str, sonde: str, temp: float, seuil: float):
subject, sms_text, _ = build_ok_text(site, sonde, temp, seuil)
notifier.send_sms(sms_text)
try:
if not any_alert_open(site):
beacon.set(False)
except Exception: pass
# ========= Cycle & boucle =========
def run_monitor_cycle(site: str = SITE):
sondes = lire_sondes_depuis_db(site)
seuils = lire_seuils_depuis_db(site)
for r in sondes:
# 1) Lecture dernières mesures + config chambres
last_rows = lire_sondes_depuis_db(site) # [{'Sonde','Temperature','Date'}]
cfg = lire_cfg_chambres(site) # {sonde: {temp_max, active, entretien}}
# 2) Gyro instantané : ON si >=1 sonde active & non en entretien dépasse son seuil
try:
gyro_on, trigger = compute_site_alarm(last_rows, cfg, hysteresis=float(os.getenv("GYRO_HYSTERESIS", "0.0")))
if trigger:
s, t, se = trigger
log.info("Gyro %s => ON (déclenché par %s: %.2f > %.2f)", site, s, t, se)
else:
log.info("Gyro %s => OFF (aucun dépassement)", site)
beacon.set(gyro_on)
except Exception as e:
log.exception("Erreur calcul/publish gyrophare: %s", e)
# 3) Alertes "officielles" (inchangées) avec temporisation 30 min
# On reconstitue un dict seuils pour réutiliser ta logique existante en dessous.
seuils = {s: meta["temp_max"] for s, meta in cfg.items() if meta.get("active", False)}
for r in last_rows:
nom = str(r["Sonde"])
temp = float(r["Temperature"])
seuil = float(seuils.get(nom, 6.0))
@@ -478,6 +537,7 @@ def run_monitor_cycle(site: str = SITE):
conn.close()
def run_monitor_loop(site: str = SITE, period_sec: int = 300):
log.info("%s démarré (site=%s, période=%ss) ✅", PROGRAM_NAME, site, period_sec)
while True: