"""Battery presence + percentage via sysfs — no upower dependency (that daemon isn't guaranteed running; sysfs always is on real hardware). Mirrors the icon tiers already established in scripts/batteryperc (the eww bar's battery poll) so a laptop that switches between hyprlua and hyprdrive sees the same glyphs. """ from __future__ import annotations from pathlib import Path from typing import NamedTuple, Optional _POWER_SUPPLY = Path("/sys/class/power_supply") # (floor percentage, glyph) — first match wins, checked high to low. _DISCHARGING_TIERS = [ (95, "󰁹"), (90, "󰂂"), (80, "󰂁"), (70, "󰂀"), (60, "󰁿"), (50, "󰁾"), (40, "󰁽"), (30, "󰁼"), (20, "󰁻"), (10, "󰁺"), (0, "󰂎"), ] _CHARGING_ICON = "󰂄" class BatteryState(NamedTuple): percent: int charging: bool icon: str def _find_battery() -> Optional[Path]: if not _POWER_SUPPLY.is_dir(): return None for entry in sorted(_POWER_SUPPLY.iterdir()): if entry.name.startswith("BAT"): return entry return None def read() -> Optional[BatteryState]: """Returns None when no battery is present (desktop machine) — callers should hide the widget entirely in that case, per spec.""" bat = _find_battery() if bat is None: return None try: percent = int((bat / "capacity").read_text().strip()) status = (bat / "status").read_text().strip().lower() except (OSError, ValueError): return None charging = status == "charging" if charging: icon = _CHARGING_ICON else: icon = next(g for floor, g in _DISCHARGING_TIERS if percent >= floor) return BatteryState(percent=percent, charging=charging, icon=icon)