import random from bots.base import BaseBot class UpsBot(BaseBot): """UPS unit monitor.""" interval = 60 def __init__(self, site_id: str, ups_id: str) -> None: super().__init__() self.site_id = site_id self.ups_id = ups_id self._charge = 94.0 + random.uniform(-3, 3) self._on_battery = False def get_topic(self) -> str: return f"bms/{self.site_id}/power/{self.ups_id}" def set_scenario(self, name: str | None) -> None: super().set_scenario(name) if name in ("UPS_MAINS_FAILURE", "UPS_OVERLOAD"): self._on_battery = True elif name in (None, "RESET"): self._on_battery = False self._charge = 94.0 def get_payload(self) -> dict: overload = (self._scenario == "UPS_OVERLOAD") if self._on_battery: # Overload drains battery ~4x faster — carrying full site load with no mains drain = random.uniform(3.0, 5.0) if overload else random.uniform(0.8, 1.5) self._charge = max(5.0, self._charge - drain) state = "overload" if overload else "on_battery" runtime = max(1, int((self._charge / 100) * (15 if overload else 60))) else: # Trickle charge back up when on mains self._charge = min(100.0, self._charge + random.uniform(0, 0.2)) state = "online" runtime = int((self._charge / 100) * 60) if overload: load_pct = min(99.0, 88.0 + self._scenario_step * 1.2 + random.gauss(0, 1.5)) else: load_pct = 62.0 + random.gauss(0, 2) return { "state": state, "charge_pct": round(self._charge, 1), "load_pct": round(load_pct, 1), "runtime_min": runtime, "voltage": round(229.5 + random.gauss(0, 0.5), 1), }