"""Per-enclosure temperature aggregation (read-only consumer). Reads the poller's data out of Redis — never touches hardware. Each named SES temperature sensor plus a derived per-enclosure hotspot (the hottest reading across SES sensors *and* the drives housed in that enclosure — drive temps are usually the real hotspot signal). """ import logging import os import socket from services import store logger = logging.getLogger(__name__) def get_node_id() -> str: """Stable identifier for this monitor instance (defaults to hostname).""" return os.environ.get("MQTT_NODE_ID") or socket.gethostname() or "jbod-monitor" async def build_temp_snapshot() -> dict: """Collect per-enclosure temperature metrics from the store.""" inventory = await store.get_inventory() out_enclosures: list[dict] = [] for enc in inventory.get("enclosures", []): ses = await store.get_ses(enc["id"]) or {} sensors: list[dict] = [] sensor_temps: list[float] = [] for t in ses.get("temps", []): temp = t.get("temperature_c") sensors.append({ "index": t["index"], "name": t.get("name"), "status": t.get("status", "Unknown"), "temperature_c": temp, }) if isinstance(temp, (int, float)) and temp > 0: sensor_temps.append(float(temp)) # Drive temperatures for drives housed in this enclosure. drive_temps: list[float] = [] for slot in enc.get("slots", []): dev = slot.get("device") if not dev: continue sm = await store.get_smart(dev) if not sm: continue temp = sm.get("temperature_c") if isinstance(temp, (int, float)) and temp > 0: drive_temps.append(float(temp)) all_temps = sensor_temps + drive_temps out_enclosures.append({ "id": enc["id"], "vendor": enc.get("vendor", ""), "model": enc.get("model", ""), "hotspot_c": max(all_temps) if all_temps else None, "drive_max_c": max(drive_temps) if drive_temps else None, "drive_temp_count": len(drive_temps), "sensors": sensors, }) return {"node": get_node_id(), "enclosures": out_enclosures}