54 lines
1.8 KiB
Python
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),
|
|
}
|