rewrite battery to simpler code, account for changing sink id in pulseaudio

This commit is contained in:
Dominic 2025-04-13 23:32:12 -04:00
parent 02a46e5c59
commit 27155e5a42
2 changed files with 50 additions and 155 deletions

View File

@ -1,170 +1,62 @@
#!/usr/bin/env python3
"""
poly-battery-status-py: Generates a pretty status-bar string for multi-battery systems on Linux.
Copyright (C) 2020 Falke Carlsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import re
import sys
from enum import Enum
from pathlib import Path
PSEUDO_FS_PATH = "/sys/class/power_supply/"
CURRENT_CHARGE_FILENAME = "energy_now"
MAX_CHARGE_FILENAME = "energy_full"
POWER_DRAW_FILENAME = "power_now"
TLP_THRESHOLD_PERCENTAGE = 1.0
PERCENTAGE_FORMAT = ".2%"
if len(sys.argv) > 1:
# parsing threshold
try:
TLP_THRESHOLD_PERCENTAGE = float(sys.argv[1])
except ValueError:
print(f"[ERROR]: Could not convert '{sys.argv[1]}' into a float.")
if len(sys.argv) > 2:
# parsing formatting
PERCENTAGE_FORMAT = sys.argv[2]
import subprocess
class Status(Enum):
CHARGING = 1
DISCHARGING = 2
PASSIVE = 3
BATTERY_ZERO_DATA = subprocess.check_output(["upower", "-i", "/org/freedesktop/UPower/devices/battery_BAT0"]).decode('utf-8')
BATTERY_ONE_DATA = subprocess.check_output(["upower", "-i", "/org/freedesktop/UPower/devices/battery_BAT1"]).decode('utf-8')
BATTERY_STATE_MAP = {
'charging': "󰂄",
'fully-charged': "󱟢",
'empty': "󰂃",
}
bat_0 = {}
bat_1 = {}
class Configuration:
time_to_completion: int
percentage: float
status: Status
def __init__(self, time_to_completion, percentage, status):
self.time_to_completion = time_to_completion
self.percentage = percentage
self.status = status
def get_upower_value(data, key):
return data.split(key)[-1].split('\n')[0].strip()
class Battery:
status: Status
current_charge: int
max_charge: int
power_draw: int
def parse_battery_data(data, obj):
obj['state'] = get_upower_value(data, 'state:')
obj['percentage'] = get_upower_value(data, 'percentage:')
def __init__(self, status, current_charge, max_charge, power_draw):
self.Status = status
self.current_charge = current_charge
self.max_charge = max_charge
self.power_draw = power_draw
if obj['state'] not in BATTERY_STATE_MAP:
bat_level = int(obj['percentage'].strip('%'))
if bat_level < 10:
obj['icon'] = "󰁺"
elif 20 > bat_level >= 10:
obj['icon'] = "󰁻"
elif 30 > bat_level >= 20:
obj['icon'] = "󰁼"
elif 40 > bat_level >= 30:
obj['icon'] = "󰁽"
elif 50 > bat_level >= 40:
obj['icon'] = "󰁾"
elif 60 > bat_level >= 50:
obj['icon'] = "󰁿"
elif 70 > bat_level >= 60:
obj['icon'] = "󰂀"
elif 80 > bat_level >= 70:
obj['icon'] = "󰂁"
elif 90 > bat_level >= 80:
obj['icon'] = "󰂂"
elif 100 > bat_level >= 90:
obj['icon'] = "󰁹"
else:
obj['icon'] = "󱈑"
def get_configuration() -> Configuration:
# get all batteries on system
batteries = []
for x in Path(PSEUDO_FS_PATH).iterdir():
bat_name = str(x.parts[len(x.parts) - 1])
if re.match("^BAT\d+$", bat_name):
batteries.append(Battery(
get_status(bat_name),
get_current_charge(bat_name),
get_max_charge(bat_name),
get_power_draw(bat_name)))
# calculate global status, assumes that if a battery is not passive, it will be discharging or charging
config_status = Status.PASSIVE
for bat in batteries:
if bat.Status == Status.CHARGING:
config_status = Status.CHARGING
break
elif bat.Status == Status.DISCHARGING:
config_status = Status.DISCHARGING
break
# construct and return configuration
return Configuration(calc_time(batteries, config_status), calc_percentage(batteries), config_status)
def get_status(bat_name: str) -> Status:
raw_status = Path(f"{PSEUDO_FS_PATH}{bat_name}/status").open().read().strip()
if raw_status == "Unknown" or raw_status == "Full":
return Status.PASSIVE
elif raw_status == "Charging":
return Status.CHARGING
elif raw_status == "Discharging":
return Status.DISCHARGING
else:
raise ValueError
obj['icon'] = BATTERY_STATE_MAP.get(obj['state'], "󱃌")
def get_current_charge(bat_name: str) -> int:
return int(Path(f"{PSEUDO_FS_PATH}{bat_name}/{CURRENT_CHARGE_FILENAME}").open().read().strip())
parse_battery_data(BATTERY_ZERO_DATA, bat_0)
parse_battery_data(BATTERY_ONE_DATA, bat_1)
print(f"{bat_0['icon']} {bat_0['percentage']} {bat_1['icon']} {bat_1['percentage']}")
def get_max_charge(bat_name: str) -> int:
return int(Path(f"{PSEUDO_FS_PATH}{bat_name}/{MAX_CHARGE_FILENAME}").open().read().strip())
def get_power_draw(bat_name: str) -> int:
return int(Path(f"{PSEUDO_FS_PATH}{bat_name}/{POWER_DRAW_FILENAME}").open().read().strip())
def calc_time(batteries: list, status: Status) -> int:
if status == Status.PASSIVE:
return 0
# get total metrics on configuration
total_current_charge = sum([bat.current_charge for bat in batteries])
total_max_charge = sum([bat.max_charge for bat in batteries])
total_power_draw = sum([bat.power_draw for bat in batteries])
if total_power_draw == 0:
return 0
if status == Status.DISCHARGING:
# return number of seconds until empty
return (total_current_charge / total_power_draw) * 3600
elif status == Status.CHARGING:
# return number of seconds until (optionally relatively) charged
return (((total_max_charge * TLP_THRESHOLD_PERCENTAGE) - total_current_charge) / total_power_draw) * 3600
def calc_percentage(batteries: list) -> float:
total_max_charge = sum([bat.max_charge for bat in batteries])
total_current_charge = sum([bat.current_charge for bat in batteries])
return total_current_charge / total_max_charge
def calc_display_time(status: Status, seconds: int) -> str:
hours = int(seconds // 3600)
minutes = int((seconds % 3600) / 60)
if status == Status.PASSIVE:
return ""
# assume charging initially if not passive
direction = "+"
if status == Status.DISCHARGING:
direction = "-"
# format output digitally, e.g. (+0:09)
return f" ({direction}{hours}:{minutes:02})"
def print_status(config: Configuration):
print(f"󰄌 {config.percentage:{PERCENTAGE_FORMAT}}{calc_display_time(config.status, config.time_to_completion)}")
def main():
print_status(get_configuration())
if __name__ == '__main__':
main()

View File

@ -1,12 +1,15 @@
#!/bin/bash
mute=$(pulsemixer --get-mute)
sink_id=$(pulsemixer --list-sinks | grep "Name: Built-in Audio Analog Ster
eo" | cut -f 3 | cut -d ' ' -f 3 | sed 's/,//g'
)
if [[ $mute -eq 1 ]]
then
echo "Mute"
else
vol=$(pulsemixer --get-volume --id sink-1206 | cut -d ' ' -f1)
vol=$(pulsemixer --get-volume --id ${sink_id} | cut -d ' ' -f1)
per=%
echo "${vol}${per}"
fi