refactor: sane-sysinfo: split out a BatteryInfo class

This commit is contained in:
Colin 2024-06-15 09:46:58 +00:00
parent a4f5343fb5
commit df0a8cf900

View File

@ -70,6 +70,9 @@ class Formatter:
@dataclass
class ParsedPowerSupply:
"""
near-direct values from /sys/class/power_supply API endpoints
"""
percent_charged: int | None = None
# unitless: could be joules, could be something else
charge_full: int | None = None
@ -81,25 +84,30 @@ class ParsedPowerSupply:
self.charge_full is not None and \
self.charge_rate is not None
@property
def percent_discharged(self) -> int | None:
if self.percent_charged is not None:
return 100 - self.percent_charged
class BatteryInfo:
"""
higher-level battery info derived from the underlying power supply
"""
percent_charged: int #< always available
minutes_to_charged: int | None = None
minutes_to_discharged: int | None = None
@property
def discharge_rate(self) -> int | None:
if self.charge_rate is not None:
return -self.charge_rate
def __init__(self, p: ParsedPowerSupply):
assert p.percent_charged is not None
self.percent_charged = p.percent_charged
@property
def minutes_to_charged(self) -> int | None:
if self.percent_discharged is not None and self.charge_full and self.charge_rate > 0:
return int(self.charge_full * self.percent_discharged/100 / self.charge_rate * 60)
@property
def minutes_to_discharged(self) -> int | None:
if self.percent_charged is not None and self.charge_full and self.charge_rate < 0:
return int(self.charge_full * self.percent_charged/100 / self.discharge_rate * 60)
if p.charge_rate is not None and p.charge_rate < 0:
self.minutes_to_discharged = int(
60
* p.charge_full * self.percent_charged/100
/ -p.charge_rate
)
if p.charge_full is not None and p.charge_rate is not None and p.charge_rate > 0:
self.minutes_to_charged = int(
60
* p.charge_full * (100-self.percent_charged)/100
/ p.charge_rate * 60
)
def render_icon(direction: ChargeDirection, percentage: float) -> str:
@ -167,7 +175,7 @@ def try_path(p: str) -> ParsedPowerSupply | None:
return state if state.percent_charged is not None else None
def try_all_paths() -> ParsedPowerSupply | None:
def try_all_paths() -> BatteryInfo | None:
p = try_path("/sys/class/power_supply/axp20x-battery") # Pinephone
if p is None:
p = try_path("/sys/class/power_supply/BAT0") # Thinkpad
@ -176,7 +184,8 @@ def try_all_paths() -> ParsedPowerSupply | None:
logger.debug(f"full: {p.charge_full if p else None}, rate: {p.charge_rate if p else None}")
logger.debug(" rate > 0 means charging, else discharging")
return p
if p.percent_charged is not None:
return BatteryInfo(p)
def fmt_minutes(f: Formatter, icon: str, if_indefinite: str, minutes: int | None) -> str:
logger.debug(f"charge/discharge time: {minutes} min")
@ -189,19 +198,19 @@ def fmt_minutes(f: Formatter, icon: str, if_indefinite: str, minutes: int | None
logger.debug("charge/discharge duration > 1d")
return f"{icon}{f.suffix_icon}{if_indefinite}"
def pretty_output(f: Formatter, p: ParsedPowerSupply) -> str:
if p.charge_rate > 0:
def pretty_output(f: Formatter, p: BatteryInfo) -> str:
if p.minutes_to_charged != None:
logger.debug("charging")
icon = render_icon(ChargeDirection.Charging, p.percent_charged)
duration = p.minutes_to_charged
else:
logger.debug("discharging")
icon = render_icon(ChargeDirection.Discharging, p.percent_discharged)
icon = render_icon(ChargeDirection.Discharging, p.percent_charged)
duration = p.minutes_to_discharged
return fmt_minutes(f, icon, f"{p.percent_charged}{f.suffix_percent}", duration)
def main():
def main() -> None:
logging.basicConfig()
logging.getLogger().setLevel(logging.INFO)