sane-sysinfo: add a way to render memory use
This commit is contained in:
parent
f4d806c0c2
commit
0f6c9f3cde
|
@ -24,6 +24,7 @@ logger = logging.getLogger(__name__)
|
|||
# these icons may only render in nerdfonts
|
||||
ICON_BAT_CHG = ["", "", "", ""]
|
||||
ICON_BAT_DIS = ["", "", "", ""]
|
||||
ICON_MEM="☵"
|
||||
SUFFIX_ICON = " " # thin space
|
||||
SUFFIX_PERCENT = "%"
|
||||
# SUFFIX_ICON=" "
|
||||
|
@ -68,9 +69,12 @@ class Formatter:
|
|||
suffix_hr: str = SUFFIX_HR
|
||||
suffix_min: str = SUFFIX_MIN
|
||||
|
||||
def render_icon(self, direction: ChargeDirection, percentage: float) -> str:
|
||||
def render_charge_icon(self, direction: ChargeDirection, percentage: float) -> str:
|
||||
return f"{self._choose_icon(direction, percentage)}{self.suffix_icon}"
|
||||
|
||||
def render_mem_icon(self) -> str:
|
||||
return f"{ICON_MEM}{self.suffix_icon}"
|
||||
|
||||
def render_hours_minutes(self, minutes: int) -> str:
|
||||
hr = minutes // 60
|
||||
min = minutes % 60
|
||||
|
@ -84,7 +88,7 @@ class Formatter:
|
|||
level = max(0, min(3, level))
|
||||
level = int(round(level))
|
||||
|
||||
logger.debug(f"render_icon: direction={direction} level={level}")
|
||||
logger.debug(f"render_charge_icon: direction={direction} level={level}")
|
||||
|
||||
if direction == ChargeDirection.Charging:
|
||||
return ICON_BAT_CHG[level]
|
||||
|
@ -93,6 +97,46 @@ class Formatter:
|
|||
|
||||
raise RuntimeError(f"invalid ChargeDirection {direction}")
|
||||
|
||||
class MemInfo:
|
||||
"""
|
||||
reads values from /proc/meminfo
|
||||
"""
|
||||
def __init__(self):
|
||||
try:
|
||||
lines = open("/proc/meminfo").readlines()
|
||||
except Exception as e:
|
||||
logger.info(f"failed to open /proc/meminfo: {e}")
|
||||
lines = []
|
||||
|
||||
# lines are like:
|
||||
# MemTotal: 16262708 kB
|
||||
# HugePages_Total: 0
|
||||
self.entries = {}
|
||||
for l in lines:
|
||||
if ":" not in l: continue
|
||||
key_len = l.index(":")
|
||||
key, value_str = l[:key_len].strip(), l[key_len+1:].strip()
|
||||
|
||||
unit_str = ""
|
||||
if " " in value_str:
|
||||
value_len = value_str.index(" ")
|
||||
value_str, unit_str = value_str[:value_len].strip(), value_str[value_len+1:].strip()
|
||||
|
||||
try:
|
||||
value = int(value_str)
|
||||
except:
|
||||
logger.info(f"unexpected /proc/meminfo line: {l}")
|
||||
continue
|
||||
|
||||
if unit_str == "kB":
|
||||
value = value * 1024
|
||||
self.entries[key] = value
|
||||
|
||||
def get(self, entry):
|
||||
v = self.entries.get(entry)
|
||||
logger.debug(f"/proc/meminfo: {entry}={v}")
|
||||
return v
|
||||
|
||||
class PowerSupply:
|
||||
"""
|
||||
reads values from /sys/class/power_supply/$dev/ API
|
||||
|
@ -186,7 +230,7 @@ class BatteryInfo:
|
|||
)
|
||||
|
||||
|
||||
def try_path(p: str) -> BatteryInfo | None:
|
||||
def try_battery_path(p: str) -> BatteryInfo | None:
|
||||
"""
|
||||
try to read battery information from some p = "/sys/class/power_supply/$node" path
|
||||
"""
|
||||
|
@ -205,10 +249,10 @@ def try_path(p: str) -> BatteryInfo | None:
|
|||
|
||||
return BatteryInfo(ps.capacity, charges_per_hour, ps.status)
|
||||
|
||||
def try_all_paths() -> BatteryInfo | None:
|
||||
p = try_path("/sys/class/power_supply/axp20x-battery") # Pinephone
|
||||
def try_all_batteries() -> BatteryInfo | None:
|
||||
p = try_battery_path("/sys/class/power_supply/axp20x-battery") # Pinephone
|
||||
if p is None:
|
||||
p = try_path("/sys/class/power_supply/BAT0") # Thinkpad
|
||||
p = try_battery_path("/sys/class/power_supply/BAT0") # Thinkpad
|
||||
|
||||
logger.debug(f"perc: {p.percent_charged if p else None}")
|
||||
logger.debug(f"charge: {p.percent_charged if p else None}")
|
||||
|
@ -216,22 +260,52 @@ def try_all_paths() -> BatteryInfo | None:
|
|||
|
||||
return p
|
||||
|
||||
def pretty_output(f: Formatter, p: BatteryInfo) -> str:
|
||||
if p.minutes_to_charged != None:
|
||||
logger.debug("charging")
|
||||
icon = f.render_icon(ChargeDirection.Charging, p.percent_charged)
|
||||
duration = p.minutes_to_charged
|
||||
else:
|
||||
logger.debug("discharging")
|
||||
icon = f.render_icon(ChargeDirection.Discharging, p.percent_charged)
|
||||
duration = p.minutes_to_discharged
|
||||
@dataclass
|
||||
class AllInfo:
|
||||
_fmt: Formatter
|
||||
_mem: MemInfo | None
|
||||
_bat: BatteryInfo | None
|
||||
|
||||
if duration is not None and duration < 1440:
|
||||
details = f.render_hours_minutes(duration)
|
||||
else:
|
||||
details = f.render_percent(p.percent_charged)
|
||||
@property
|
||||
def mem_icon(self) -> str:
|
||||
if self._mem is None: return ""
|
||||
return self._fmt.render_mem_icon()
|
||||
|
||||
return f"{icon}{details}"
|
||||
@property
|
||||
def mem_pct(self) -> str:
|
||||
if self._mem is None: return ""
|
||||
|
||||
total = self._mem.get("MemTotal")
|
||||
free = self._mem.get("MemAvailable")
|
||||
|
||||
if total is None or free is None or free > total:
|
||||
return ""
|
||||
|
||||
mem_use_pct = int((total - free) / total * 100)
|
||||
return self._fmt.render_percent(mem_use_pct)
|
||||
|
||||
@property
|
||||
def bat_icon(self) -> str:
|
||||
if self._bat is None: return ""
|
||||
elif self._bat.minutes_to_charged != None:
|
||||
logger.debug("bat_icon: charging")
|
||||
return self._fmt.render_charge_icon(ChargeDirection.Charging, self._bat.percent_charged)
|
||||
else:
|
||||
logger.debug("bat_icon: discharging")
|
||||
return self._fmt.render_charge_icon(ChargeDirection.Discharging, self._bat.percent_charged)
|
||||
|
||||
@property
|
||||
def bat_time(self) -> str:
|
||||
if self._bat is None: return ""
|
||||
elif self._bat.minutes_to_charged != None:
|
||||
duration = self._bat.minutes_to_charged
|
||||
else:
|
||||
duration = self._bat.minutes_to_discharged
|
||||
|
||||
if duration is not None and duration < 1440:
|
||||
return self._fmt.render_hours_minutes(duration)
|
||||
else:
|
||||
return self._fmt.render_percent(self._bat.percent_charged)
|
||||
|
||||
def main() -> None:
|
||||
logging.basicConfig()
|
||||
|
@ -243,6 +317,8 @@ def main() -> None:
|
|||
parser.add_argument("--hour-suffix", default=SUFFIX_HR)
|
||||
parser.add_argument("--minute-suffix", default=SUFFIX_MIN)
|
||||
parser.add_argument("--percent-suffix", default=SUFFIX_PERCENT)
|
||||
parser.add_argument("--template", default="{_.bat_icon}{_.bat_time}")
|
||||
# parser.add_argument("--template", default="{_.mem_icon}{_.mem_pct}")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.debug:
|
||||
|
@ -254,9 +330,12 @@ def main() -> None:
|
|||
f.suffix_hr = args.hour_suffix
|
||||
f.suffix_min = args.minute_suffix
|
||||
|
||||
p = try_all_paths()
|
||||
if p is not None:
|
||||
print(pretty_output(f, p))
|
||||
info = AllInfo(
|
||||
f,
|
||||
MemInfo(),
|
||||
try_all_batteries(),
|
||||
)
|
||||
print(args.template.format(_=info))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue
Block a user