55 lines
1.8 KiB
Python
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
|