BMS/simulators/bots/ups.py
2026-03-19 11:32:17 +00:00

54 lines
1.8 KiB
Python

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),
}