Reworked battery module, should work better now. Or worse. Let's find out...

This commit is contained in:
enkore 2013-03-08 02:53:54 +01:00
parent 8ef891cef6
commit e8191da312

View File

@ -1,28 +1,34 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re
from . import IntervalModule from . import IntervalModule
from .core.util import PrefixedKeyDict from .core.util import PrefixedKeyDict
from .core.desktop import display_notification from .core.desktop import display_notification
class Battery: class UEventParser: # fun-fact: configparser doesn't provide a way to handle files w/o sections
@staticmethod @staticmethod
def lchop(string, prefix="POWER_SUPPLY_"): def lchop(string, prefix):
if string.startswith(prefix): if string.startswith(prefix):
return string[len(prefix):] return string[len(prefix):]
return string return string
@staticmethod def __init__(self, file, prefix="POWER_SUPPLY_"):
def map_key(key): super().__init__()
return Battery.lchop(key).replace("CHARGE", "ENERGY") self.prefix = prefix
self.data = {}
@staticmethod
def convert(value):
return float(value) if value.isdecimal() else value.strip()
def __init__(self, file):
self.parse(file) self.parse(file)
def dict(self):
return self.data
def map_key(self, key):
return self.lchop(key, self.prefix)
def map_value(self, value):
return float(value) if value.isdecimal() else value.strip()
def parse(self, file): def parse(self, file):
with open(file, "r") as file: with open(file, "r") as file:
for line in file: for line in file:
@ -30,23 +36,75 @@ class Battery:
def parse_line(self, line): def parse_line(self, line):
key, value = line.split("=", 2) key, value = line.split("=", 2)
self.data[self.map_key(key)] = self.map_value(value)
setattr(self, self.map_key(key), self.convert(value)) class Battery:
@staticmethod
def create(from_file):
batinfo = UEventParser(from_file).dict()
if "POWER_NOW" in batinfo:
return BatteryEnergy(batinfo)
else:
return BatteryCharge(batinfo)
class RemainingCalculator: def __init__(self, batinfo):
def __init__(self, energy, power): self.bat = batinfo
self.remaining_time = (energy / power) * 60 self.normalize_µ()
self.hours, self.minutes = map(int, divmod(self.remaining_time, 60))
def get_dict(self, prefix): def normalize_µ(self):
d = PrefixedKeyDict(prefix) for key, µvalue in self.bat.items():
d.update({ if re.match(r"(VOLTAGE|CHARGE|CURRENT|POWER|ENERGY)_(NOW|FULL|MIN)(_DESIGN)?", key):
"str": "{}:{:02}".format(self.hours, self.minutes), self.bat[key] = µvalue / 1000000.0
"hm": "{}h:{:02}m".format(self.hours, self.minutes),
"hours": self.hours, def percentage(self, design=False):
"mins": self.minutes, return self._percentage("_DESIGN" if design else "") * 100
})
return d def status(self):
if self.consumption():
if self.bat["STATUS"] == "Discharging":
return "Discharging"
else:
return "Charging"
else:
return "Full"
class BatteryCharge(Battery):
def consumption(self):
return self.bat["VOLTAGE_NOW"] * self.bat["CURRENT_NOW"] # V * A = W
def _percentage(self, design):
return self.bat["CHARGE_NOW"] / self.bat["CHARGE_FULL"+design]
def remaining(self):
if self.status() == "Discharging":
return self.bat["CHARGE_NOW"] / self.bat["CURRENT_NOW"] * 60 # Ah / A = h * 60 min = min
else:
return (self.bat["CHARGE_FULL"] - self.bat["CHARGE_NOW"]) / self.bat["CURRENT_NOW"] * 60
class BatteryEnergy(Battery):
def consumption(self):
return self.bat["POWER_NOW"]
def _percentage(self, design):
return self.bat["ENERGY_NOW"] / self.bat["ENERGY_FULL"+design]
def remaining(self):
if self.status() == "Discharging":
return self.bat["ENERGY_NOW"] / self.bat["POWER_NOW"] * 60 # Wh / W = h * 60 min = min
else:
return (self.bat["ENERGY_FULL"] - self.bat["ENERGY_NOW"]) / self.bat["POWER_NOW"] * 60
def format_remaining(minutes, prefix):
hours, minutes = map(int, divmod(minutes, 60))
d = PrefixedKeyDict(prefix)
d.update({
"str": "{}:{:02}".format(hours, minutes),
"hm": "{}h:{:02}m".format(hours, minutes),
"hours": hours,
"mins": minutes,
})
return d
class BatteryChecker(IntervalModule): class BatteryChecker(IntervalModule):
""" """
@ -63,7 +121,7 @@ class BatteryChecker(IntervalModule):
* status * status
* battery_ident * battery_ident
""" """
interval=1
settings = ( settings = (
"battery_ident", "format", "battery_ident", "format",
("alert", "Display a libnotify-notification on low battery"), ("alert", "Display a libnotify-notification on low battery"),
@ -88,34 +146,26 @@ class BatteryChecker(IntervalModule):
urgent = False urgent = False
color = "#ffffff" color = "#ffffff"
battery = Battery(self.path) battery = Battery.create(self.path)
status = battery.STATUS
energy_now = battery.ENERGY_NOW
energy_full = battery.ENERGY_FULL
if not hasattr(battery, "POWER_NOW"):
battery.POWER_NOW = battery.VOLTAGE_NOW * battery.CURRENT_NOW
power_now = battery.POWER_NOW
fdict = { fdict = {
"battery_ident": self.battery_ident, "battery_ident": self.battery_ident,
"remaining_str": "", "remaining_str": "",
"remaining_hm": "", "remaining_hm": "",
"percentage": (energy_now / energy_full) * 100, "percentage": battery.percentage(),
"percentage_design": (energy_now / battery.ENERGY_FULL_DESIGN) * 100, "percentage_design": battery.percentage(design=True),
"consumption": power_now / 1000000, "consumption": battery.consumption(),
} }
if power_now:
status = battery.status()
if status in ["Discharging", "Charging"]:
remaining = battery.remaining()
fdict.update(format_remaining(remaining, "remaining_"))
if status == "Discharging": if status == "Discharging":
fdict["status"] = "DIS" fdict["status"] = "DIS"
remaining = RemainingCalculator(energy_now, power_now) if remaining < 15:
if remaining.remaining_time < 15:
urgent = True urgent = True
color = "#ff0000" color = "#ff0000"
else: # Charging, Unknown etc. (My thinkpad says Unknown if close to fully charged)
fdict["status"] = "CHR"
remaining = RemainingCalculator(energy_full-energy_now, power_now)
fdict.update(remaining.get_dict("remaining_"))
else: else:
fdict["status"] = "FULL" fdict["status"] = "FULL"