Battery module error
Juliaria08 opened this issue · comments
Describe the bug
The bug seems to ocurr when my battery is fully charged and I'm using the acpi measurement mode (I'd wish to be able to use acpi measuring to get the time that the battery has)
Your py3status version
py3status version 3.49 (python 3.10.9) on sway
To Reproduce
Steps to reproduce the behavior along with your py3status configuration (filter out any private values):
- Add module 'battery_level'
- Configure module like the following:
battery_level {
blocks = ""
#battery_id = all
battery_id = 1
format = "{percent}% {icon}[\?if=time_remainin>
threshold_degraded = 20
hide_when_full = true
hide_seconds = true
notify_low_level = true
#measurement_mode = "sys"
measurement_mode = "acpi"
notification = true
}
- Wait until the battery is at 100% and the error sometimes pops up.
- See error.
Expected behavior
The battery level to continue being hidden and no error to pop up.
Additional context
The error says battery_level: list index out of range
, which I presume means some error with the acpi handling?
The logs say the following:
2023-03-25 10:26:23 WARNING Instance `battery_level`, user method `battery_level` failed.
2023-03-25 10:26:23 INFO Traceback
IndexError: list index out of range
File "/usr/lib/python3.10/site-packages/py3status/module.py", line 949, in run
response = method()
File "/usr/lib/python3.10/site-packages/py3status/modules/battery_level.py", line 198, in battery_level
battery_list = self.get_battery_info()
File "/usr/lib/python3.10/site-packages/py3status/modules/battery_level.py", line 290, in _extract_battery_info_from_acpi
return [_parse_battery_info(battery) for battery in acpi_list]
File "/usr/lib/python3.10/site-packages/py3status/modules/battery_level.py", line 290, in <listcomp>
return [_parse_battery_info(battery) for battery in acpi_list]
File "/usr/lib/python3.10/site-packages/py3status/modules/battery_level.py", line 262, in _parse_battery_info
findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
More information needed. Paste acpi -b -i
output and... maybe again when/if the error occurs.
I think the output hides some values when it's full. The fix might be something like this. Not 0
tho.
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py
index 2a48b83..809e5d5 100644
--- a/py3status/modules/battery_level.py
+++ b/py3status/modules/battery_level.py
@@ -258,13 +258,13 @@ class Py3status:
findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0]
)
battery["charging"] = "Charging" in acpi_battery_lines[0]
- battery["capacity"] = int(
- findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
- )
# ACPI only shows time remaining if battery is discharging or
# charging
try:
+ battery["capacity"] = int(
+ findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
+ )
battery["time_remaining"] = "".join(
findall(
r"(?<=, )(\d+:\d+:\d+)(?= remaining)|"
@@ -274,6 +274,7 @@ class Py3status:
)
except IndexError:
battery["time_remaining"] = FULLY_CHARGED
+ battery["capacity"] = 0
# TODO: Not implemented yet
battery["power"] = 0.0
More information needed. Paste
acpi -b -i
output and... maybe again when/if the error occurs.I think the output hides some values when it's full. The fix might be something like this. Not
0
tho.diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py index 2a48b83..809e5d5 100644 --- a/py3status/modules/battery_level.py +++ b/py3status/modules/battery_level.py @@ -258,13 +258,13 @@ class Py3status: findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0] ) battery["charging"] = "Charging" in acpi_battery_lines[0] - battery["capacity"] = int( - findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] - ) # ACPI only shows time remaining if battery is discharging or # charging try: + battery["capacity"] = int( + findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] + ) battery["time_remaining"] = "".join( findall( r"(?<=, )(\d+:\d+:\d+)(?= remaining)|" @@ -274,6 +274,7 @@ class Py3status: ) except IndexError: battery["time_remaining"] = FULLY_CHARGED + battery["capacity"] = 0 # TODO: Not implemented yet battery["power"] = 0.0
Battery 0: Discharging, 0%, rate information unavailable
Battery 1: Full, 100%
Battery 1: design capacity 3100 mAh, last full capacity 2905 mAh = 93%
The battery 1 is the important one, I don't really know what battery 0 is.
I could probably make a check if "percent" == 100 (pseudocode), and if so not write the module, as making a module with a empty text makes it not show it. (For some reason, I think it's intended tho.)
(The error is ocurring right now)
The fix
More information needed. Paste
acpi -b -i
output and... maybe again when/if the error occurs.
I think the output hides some values when it's full. The fix might be something like this. Not0
tho.diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py index 2a48b83..809e5d5 100644 --- a/py3status/modules/battery_level.py +++ b/py3status/modules/battery_level.py @@ -258,13 +258,13 @@ class Py3status: findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0] ) battery["charging"] = "Charging" in acpi_battery_lines[0] - battery["capacity"] = int( - findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] - ) # ACPI only shows time remaining if battery is discharging or # charging try: + battery["capacity"] = int( + findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] + ) battery["time_remaining"] = "".join( findall( r"(?<=, )(\d+:\d+:\d+)(?= remaining)|" @@ -274,6 +274,7 @@ class Py3status: ) except IndexError: battery["time_remaining"] = FULLY_CHARGED + battery["capacity"] = 0 # TODO: Not implemented yet battery["power"] = 0.0Battery 0: Discharging, 0%, rate information unavailable Battery 1: Full, 100% Battery 1: design capacity 3100 mAh, last full capacity 2905 mAh = 93%
The battery 1 is the important one, I don't really know what battery 0 is. I could probably make a check if "percent" == 100 (pseudocode), and if so not write the module, as making a module with a empty text makes it not show it. (For some reason, I think it's intended tho.) (The error is ocurring right now)
The fix seems to work with battery["capacity"]
being 100
.
@lasers Could it be applied as a pull request?
I can't seem to get my error consistently. I tried to apply the patch but even when unpatching the error works weirdly.
You can keep modified battery_level.py
in ~/.config/py3status/modules/
. I don't have a setup ready to test this module nor have I studied the module code. My trivial concern is that what would happen with multiple working batteries.
Also, yes, you can make a pull request.
You can keep modified
battery_level.py
in~/.config/py3status/modules/
. I don't have a setup ready to test this module nor have I studied the module code. My trivial concern is that what would happen with multiple working batteries.Also, yes, you can make a pull request.
I'll then apply the patch properly as I can have it in my user folder, I dislike overriding python...
But the issue I have is that I can't seem what I should put in the value as if I put 0 it causes a divisionbyzero, but if I put 1 it causes the battery to be at 0%.
Less issue for you, but still not right due to not storing battery index too in the cache.
Use last known capacity. Untested. It should be 1 on first run, then whatever capacity acpi gave.
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py
index 2a48b83..f5cb943 100644
--- a/py3status/modules/battery_level.py
+++ b/py3status/modules/battery_level.py
@@ -178,6 +178,7 @@ class Py3status:
def post_config_hook(self):
self.last_known_status = ""
+ self.last_known_capacity = 1 # fake
# Guess mode if not set
if self.measurement_mode is None:
if Path(self.sys_battery_path).is_dir():
@@ -258,13 +259,14 @@ class Py3status:
findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0]
)
battery["charging"] = "Charging" in acpi_battery_lines[0]
- battery["capacity"] = int(
- findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
- )
# ACPI only shows time remaining if battery is discharging or
# charging
try:
+ battery["capacity"] = int(
+ findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
+ )
+ self.last_known_capacity = battery["capacity"]
battery["time_remaining"] = "".join(
findall(
r"(?<=, )(\d+:\d+:\d+)(?= remaining)|"
@@ -274,6 +276,7 @@ class Py3status:
)
except IndexError:
battery["time_remaining"] = FULLY_CHARGED
+ battery["capacity"] = self.last_known_capacity
# TODO: Not implemented yet
battery["power"] = 0.0
The PR should come from somebody confirming this to be working, not writing untested PRs like me.
Less issue for you, but still not right due to not storing battery index too in the cache.
Use last known capacity. Untested. It should be 1 on first run, then whatever capacity acpi gave.
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py index 2a48b83..f5cb943 100644 --- a/py3status/modules/battery_level.py +++ b/py3status/modules/battery_level.py @@ -178,6 +178,7 @@ class Py3status: def post_config_hook(self): self.last_known_status = "" + self.last_known_capacity = 1 # fake # Guess mode if not set if self.measurement_mode is None: if Path(self.sys_battery_path).is_dir(): @@ -258,13 +259,14 @@ class Py3status: findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0] ) battery["charging"] = "Charging" in acpi_battery_lines[0] - battery["capacity"] = int( - findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] - ) # ACPI only shows time remaining if battery is discharging or # charging try: + battery["capacity"] = int( + findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] + ) + self.last_known_capacity = battery["capacity"] battery["time_remaining"] = "".join( findall( r"(?<=, )(\d+:\d+:\d+)(?= remaining)|" @@ -274,6 +276,7 @@ class Py3status: ) except IndexError: battery["time_remaining"] = FULLY_CHARGED + battery["capacity"] = self.last_known_capacity # TODO: Not implemented yet battery["power"] = 0.0The PR should come from somebody confirming this to be working, not writing untested PRs like me.
Thanks, applied that patch, will wait for a week or two to see if I experience any issues.
Issue: It still thinks the battery doesn't have capacity. It doesn't seem to save the data...
Can you give me the following outputs? Charging, Discharging, and Full.
EDIT: It might work better if it got a charging / discharging output just once.
Supposedly charging:
Battery 0: Not charging, 98%
Battery 1: Discharging, 0%, rate information unavailable
Discharging:
Battery 0: Discharging, 98%, 03:47:31 remaining
Battery 1: Discharging, 0%, rate information unavailable
Charging:
Battery 0: Charging, 98%, 00:02:03 until charged
Battery 1: Discharging, 0%, rate information unavailable
The other battery sometimes appears and disappears randomly, but note that the hardware only has one physical battery.
Taken with acpi -b -i
? If yes, well, it doesn't print capacity for your batteries... It should look something like this.
"""
# Get the battery info from acpi
# Example Discharging
Battery 0: Discharging, 94%, 09:23:28 remaining
Battery 0: design capacity 5703 mAh, last full capacity 5283 mAh = 92%
Battery 1: Unknown, 98%
Battery 1: design capacity 1880 mAh, last full capacity 1370 mAh = 72%
# Example Charging
Battery 0: Charging, 96%, 00:20:40 until charged
Battery 0: design capacity 5566 mAh, last full capacity 5156 mAh = 92%
Battery 1: Unknown, 98%
Battery 1: design capacity 1879 mAh, last full capacity 1370 mAh = 72%
"""
Nothing we can do here. Possibly better to fix up sys
and/or to look into adding tlp
support if there is a good battery statistics output py3status can use on the bar.
Taken with
acpi -b -i
? If yes, well, it doesn't print capacity for your batteries... It should look something like this.""" # Get the battery info from acpi # Example Discharging Battery 0: Discharging, 94%, 09:23:28 remaining Battery 0: design capacity 5703 mAh, last full capacity 5283 mAh = 92% Battery 1: Unknown, 98% Battery 1: design capacity 1880 mAh, last full capacity 1370 mAh = 72% # Example Charging Battery 0: Charging, 96%, 00:20:40 until charged Battery 0: design capacity 5566 mAh, last full capacity 5156 mAh = 92% Battery 1: Unknown, 98% Battery 1: design capacity 1879 mAh, last full capacity 1370 mAh = 72% """Nothing we can do here. Possibly better to fix up
sys
and/or to look into addingtlp
support if there is a good battery statistics output py3status can use on the bar.
Oh, sorry. wait a sec.
# Supposedly charging (it really discharges)
Battery 0: Discharging, 0%, rate information unavailable
Battery 1: Not charging, 99%
Battery 1: design capacity 3100 mAh, last full capacity 2813 mAh = 90%
# Discharging
Battery 0: Discharging, 0%, rate information unavailable
Battery 1: Discharging, 99%, 13:23:49 remaining
Battery 1: design capacity 3100 mAh, last full capacity 2813 mAh = 90%
# Charging
Battery 0: Discharging, 0%, rate information unavailable
Battery 1: Charging, 98%, charging at zero rate - will never fully charge.
Battery 1: design capacity 3100 mAh, last full capacity 2821 mAh = 91%
# Charging (later)
Battery 0: Discharging, 0%, rate information unavailable
Battery 1: Charging, 98%, 00:36:09 until charged
Battery 1: design capacity 3100 mAh, last full capacity 2821 mAh = 91%
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py
index 2a48b83..a1d0210 100644
--- a/py3status/modules/battery_level.py
+++ b/py3status/modules/battery_level.py
@@ -92,7 +92,7 @@ discharging
{'color': '#FF0000', 'full_text': u'\u2340'}
"""
-from re import findall
+from re import findall, search
import itertools
import math
@@ -178,6 +178,7 @@ class Py3status:
def post_config_hook(self):
self.last_known_status = ""
+ self.last_known_capacity = 1 # fake
# Guess mode if not set
if self.measurement_mode is None:
if Path(self.sys_battery_path).is_dir():
@@ -258,13 +259,14 @@ class Py3status:
findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0]
)
battery["charging"] = "Charging" in acpi_battery_lines[0]
- battery["capacity"] = int(
- findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
- )
# ACPI only shows time remaining if battery is discharging or
# charging
try:
+ battery["capacity"] = int(
+ findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
+ )
+ self.last_known_capacity = battery["capacity"]
battery["time_remaining"] = "".join(
findall(
r"(?<=, )(\d+:\d+:\d+)(?= remaining)|"
@@ -273,6 +275,7 @@ class Py3status:
)[0]
)
except IndexError:
+ battery["capacity"] = self.last_known_capacity
battery["time_remaining"] = FULLY_CHARGED
# TODO: Not implemented yet
@@ -285,9 +288,12 @@ class Py3status:
# Separate the output because each pair of lines corresponds to a
# single battery. Now the list index will correspond to the index of
# the battery we want to look at
- acpi_list = [acpi_list[i : i + 2] for i in range(0, len(acpi_list) - 1, 2)]
+ new_acpi_list = {}
+ for line in acpi_list:
+ number = int(search(r"Battery (\d+)", line).group(1))
+ new_acpi_list.setdefault(number, []).append(line)
- return [_parse_battery_info(battery) for battery in acpi_list]
+ return [_parse_battery_info(battery) for battery in new_acpi_list.values()]
def _extract_battery_info_from_sys(self):
"""
new_acpi_list.setdefault(number, []).append(line)
Thanks, will wait a few days to see if this one works properly. :)
I just found a issue, for some weird reason it seems to just out of the blue change the percentage from 99% to 50%, I can prove any debug data needed if I am given the command (as I know py3status -dl file
won't work properly as no exception is ocurring)
I don't remember if this is how it works, but you can try adding self.py3.log(new_acpi_list.values())
line right after that loop. It should show up in file
along with other noises. Check to see if it's working.... and when you ran into that issue again, check the file immediately.
2023-04-03 14:44:37 INFO Module `battery_level`: dict_values([['Battery 0: Not charging, 99%', 'Battery 0: design capacity 3100 mAh, last full capacity 2814 mAh = 90%'], ['Battery 1: Discharging, 0%, rate information unavailable']])
2023-04-03 14:44:37 INFO method battery_level returned {'composite': [{'full_text': '49% \uf242 (None)', 'instance': ' 0', 'name': 'battery_level', 'background': '#282828'}], 'instance': '', 'name': 'battery_level'}.
I think I just found out the error, there's a extra battery (which I can't practically remove), that always has 0% so the avereage of the percentages is 49%.
- Found the device which makes the issue, I'll try to see how can I bodge it to not present a empty battery (it's a wireless mouse receiver) (seems like when I use it via bluetooth it doesn't show it, which is nice but that also can break my workflow once I add more hosts on my kvm (that is, more laptops because I'm not using the mouse on the rare times I switch to the output of my local server)), I think it's a issue with acpi not handling
hidpp_battery_25
properly. (As /sys/class/power_supply/hidpp_battery_25/capacity shows 95, which means the mouse is 95% full)
Yeah. Your Battery 0
gave capacity this time. This wasn't in one of your outputs before. There is a method later down the road that puts all batteries together to make an average percent... thus this issue. It may be ideal to split everything up so we loop through each battery (ie with format_battery
).
Few ways to fix this.
- Rewriting this module from scratch to loop batteries.
- Add
batteries
config to follow certain batteries. If empty, all. - Find a fix via acpi.
Yeah. Your
Battery 0
gave capacity this time. This wasn't in one of your outputs before. There is a method later down the road that puts all batteries together to make an average percent... thus this issue. It may be ideal to split everything up so we loop through each battery (ie withformat_battery
).Few ways to fix this.
- Rewriting this module from scratch to loop batteries.
That'd take a lot of work.- Add
batteries
config to follow certain batteries. If empty, all.
That would have a way to somehow tell the battery without using a identifier as the id changes when the remote thing is unplugged- Find a fix via acpi.
^ That'd be the best solution, as checking the internal files in/sys/class/power_supply
there's the battery and the capacity shows up properly.
I ended up going with the current aproach of using the mouse via bluetooth. I'll just have to remember to disable bluetooth when I go out with my laptop (as that consumes battery).
Currently I will rename the module override file and see if no issues ocurr and if any issues ocurr (the system still giving me some kind of error), I'd make a PR, but even then, it's a bit sad that they don't expose all the proper files so acpi doesn't work.
@lasers Is it possible to detect a invalid battery (by the fact that it doesn't report rated capacity) and remove it from the dictionary of batteries?
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py
index 2a48b83..046e30f 100644
--- a/py3status/modules/battery_level.py
+++ b/py3status/modules/battery_level.py
@@ -92,7 +92,7 @@ discharging
{'color': '#FF0000', 'full_text': u'\u2340'}
"""
-from re import findall
+from re import findall, search
import itertools
import math
@@ -178,6 +178,7 @@ class Py3status:
def post_config_hook(self):
self.last_known_status = ""
+ self.last_known_capacity = 1
# Guess mode if not set
if self.measurement_mode is None:
if Path(self.sys_battery_path).is_dir():
@@ -258,13 +259,14 @@ class Py3status:
findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0]
)
battery["charging"] = "Charging" in acpi_battery_lines[0]
- battery["capacity"] = int(
- findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
- )
# ACPI only shows time remaining if battery is discharging or
# charging
try:
+ battery["capacity"] = int(
+ findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1]
+ )
+ self.last_known_capacity = battery["capacity"]
battery["time_remaining"] = "".join(
findall(
r"(?<=, )(\d+:\d+:\d+)(?= remaining)|"
@@ -273,6 +275,7 @@ class Py3status:
)[0]
)
except IndexError:
+ battery["capacity"] = self.last_known_capacity
battery["time_remaining"] = FULLY_CHARGED
# TODO: Not implemented yet
@@ -285,9 +288,17 @@ class Py3status:
# Separate the output because each pair of lines corresponds to a
# single battery. Now the list index will correspond to the index of
# the battery we want to look at
- acpi_list = [acpi_list[i : i + 2] for i in range(0, len(acpi_list) - 1, 2)]
+ temp_acpi_list = {}
+ for line in acpi_list:
+ number = search(r"Battery (\d+)", line).group(1)
+ temp_acpi_list.setdefault(number, []).append(line)
+
+ new_acpi_list = {}
+ for k, v in temp_acpi_list.items():
+ if "rate information unavailable" not in "|".join(v):
+ new_acpi_list[k] = v
- return [_parse_battery_info(battery) for battery in acpi_list]
+ return [_parse_battery_info(battery) for battery in new_acpi_list.values()]
def _extract_battery_info_from_sys(self):
"""
diff --git a/py3status/modules/battery_level.py b/py3status/modules/battery_level.py index 2a48b83..046e30f 100644 --- a/py3status/modules/battery_level.py +++ b/py3status/modules/battery_level.py @@ -92,7 +92,7 @@ discharging {'color': '#FF0000', 'full_text': u'\u2340'} """ -from re import findall +from re import findall, search import itertools import math @@ -178,6 +178,7 @@ class Py3status: def post_config_hook(self): self.last_known_status = "" + self.last_known_capacity = 1 # Guess mode if not set if self.measurement_mode is None: if Path(self.sys_battery_path).is_dir(): @@ -258,13 +259,14 @@ class Py3status: findall(r"(?<= )(\d+)(?=%)", acpi_battery_lines[0])[0] ) battery["charging"] = "Charging" in acpi_battery_lines[0] - battery["capacity"] = int( - findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] - ) # ACPI only shows time remaining if battery is discharging or # charging try: + battery["capacity"] = int( + findall(r"(?<= )(\d+)(?= mAh)", acpi_battery_lines[1])[1] + ) + self.last_known_capacity = battery["capacity"] battery["time_remaining"] = "".join( findall( r"(?<=, )(\d+:\d+:\d+)(?= remaining)|" @@ -273,6 +275,7 @@ class Py3status: )[0] ) except IndexError: + battery["capacity"] = self.last_known_capacity battery["time_remaining"] = FULLY_CHARGED # TODO: Not implemented yet @@ -285,9 +288,17 @@ class Py3status: # Separate the output because each pair of lines corresponds to a # single battery. Now the list index will correspond to the index of # the battery we want to look at - acpi_list = [acpi_list[i : i + 2] for i in range(0, len(acpi_list) - 1, 2)] + temp_acpi_list = {} + for line in acpi_list: + number = search(r"Battery (\d+)", line).group(1) + temp_acpi_list.setdefault(number, []).append(line) + + new_acpi_list = {} + for k, v in temp_acpi_list.items(): + if "rate information unavailable" not in "|".join(v): + new_acpi_list[k] = v - return [_parse_battery_info(battery) for battery in acpi_list] + return [_parse_battery_info(battery) for battery in new_acpi_list.values()] def _extract_battery_info_from_sys(self): """
Thanks, seems to work properly. Do you think this would negatively affect other users?, if it won't I'm pretty sure a PR can be made.