dashboard/app/collect/power.py
2026-08-31 00:47:02 +02:00

55 lines
1.8 KiB
Python

import glob
from app.sample import Sample
from app.utils import sysfs
_PS = "/sys/class/power_supply"
def _supplies() -> list[tuple[str, str]]:
"""List power supplies found under /sys/class/power_supply.
Returns:
(type, path) pairs sorted by path, where type is the sysfs type
("battery", "mains", "usb", ...) of each supply.
"""
out: list[tuple[str, str]] = []
for p in sorted(glob.glob(f"{_PS}/*")):
t = sysfs.read_str(f"{p}/type")
if t:
out.append((t.lower(), p))
return out
def fill(s: Sample) -> None:
"""Fill the battery / ac_online fields of a Sample from sysfs.
psutil's battery API is unreliable here (power_plugged can be None),
so /sys/class/power_supply/* is read directly: the first present
battery provides capacity and status, and ac_online becomes True when
any mains — or, failing that, USB — supply reports online. Fields stay
at their Sample defaults on a desktop without these nodes.
Args:
s: sample to fill.
"""
try:
supplies = _supplies()
for t, p in supplies:
if t == "battery" and sysfs.read_str(f"{p}/present") == "1":
cap = sysfs.read_int(f"{p}/capacity")
if cap is not None:
s.battery = cap
s.battery_status = sysfs.read_str(f"{p}/status")
break
for t, p in supplies:
if t == "mains" and sysfs.read_str(f"{p}/online") == "1":
s.ac_online = True
break
if s.ac_online is None:
for t, p in supplies:
if t == "usb" and sysfs.read_str(f"{p}/online") == "1":
s.ac_online = True
break
except OSError:
pass