Skip to content

Commit 8c97d46

Browse files
committed
battery2: change battery symbol when discharging
- according to its level - popup notification when battery level < threshold - formatting
1 parent 9d66d81 commit 8c97d46

10 files changed

+168
-82
lines changed

battery2/README.md

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
11
# battery2
22

3-
Show the current status of your battery.
3+
## Show the current status of your battery
44

5+
### Plugged and full
56
![](images/full.png)
67

8+
### Charging
79
![](images/charging.png)
810

9-
![](images/unplugged.png)
11+
### Discharging
12+
![](images/unplugged_full.png)
1013

14+
![](images/unplugged_75.png)
15+
16+
![](images/unplugged_50.png)
17+
18+
![](images/unplugged_25.png)
19+
20+
![](images/unplugged_empty.png)
21+
22+
### Unknown status
1123
![](images/unknown.png)
1224

25+
### No battery is present
1326
![](images/nobattery.png)
1427

15-
# Dependencies
28+
## Show the notification when below threshold
29+
30+
![](images/warning_threshold.png)
31+
32+
# Dependencies (Debian like)
1633

17-
fonts-font-awesome, acpi, python3
34+
- `fonts-font-awesome`,
35+
- `libnotify-bin`,
36+
- `acpi`,
37+
- `python3`
1838

1939
# Installation
2040

21-
To use with i3blocks, copy the blocklet configuration in the given `i3blocks.conf` into your i3blocks configuration file, the recommended config is
41+
To use with `i3blocks`, copy the blocklet configuration in the given
42+
`i3blocks.conf` into your i3blocks configuration file, the recommended
43+
config is:
2244

2345
```INI
2446
[battery2]

battery2/battery2

+124-77
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,153 @@
11
#!/usr/bin/env python3
22
#
33
# Copyright (C) 2016 James Murphy
4+
# 2024 Gregory David
45
# Licensed under the GPL version 2 only
56
#
67
# A battery indicator blocklet script for i3blocks
78

8-
from subprocess import check_output
99
import os
1010
import re
11+
import sys
12+
from subprocess import CalledProcessError, check_output
13+
from typing import Dict
1114

12-
config = dict(os.environ)
13-
status = check_output(['acpi'], universal_newlines=True)
14-
15-
if not status:
16-
# stands for no battery found
17-
color = config.get("color_10", "red")
18-
fulltext = "<span color='{}'><span font='FontAwesome'>\uf00d \uf240</span></span>".format(color)
19-
percentleft = 100
20-
else:
21-
# if there is more than one battery in one laptop, the percentage left is
22-
# available for each battery separately, although state and remaining
23-
# time for overall block is shown in the status of the first battery
24-
batteries = status.split("\n")
25-
state_batteries=[]
26-
commasplitstatus_batteries=[]
27-
percentleft_batteries=[]
28-
time = ""
29-
for battery in batteries:
30-
if battery!='':
15+
CONFIG = dict(os.environ)
16+
DISCHARGE_LEVEL = {
17+
90: CONFIG.get("discharge_90", "\uf240"),
18+
75: CONFIG.get("discharge_75", "\uf241"),
19+
50: CONFIG.get("discharge_50", "\uf242"),
20+
25: CONFIG.get("discharge_25", "\uf243"),
21+
10: CONFIG.get("discharge_10", "\uf244"),
22+
}
23+
COLOR_LEVEL = {
24+
90: CONFIG.get("color_90", "#FFFFFF"),
25+
80: CONFIG.get("color_80", "#FFFF66"),
26+
70: CONFIG.get("color_70", "#FFFF33"),
27+
60: CONFIG.get("color_60", "#FFFF00"),
28+
50: CONFIG.get("color_50", "#FFCC00"),
29+
40: CONFIG.get("color_40", "#FF9900"),
30+
30: CONFIG.get("color_30", "#FF6600"),
31+
20: CONFIG.get("color_20", "#FF3300"),
32+
10: CONFIG.get("color_10", "#FFFFFF"),
33+
}
34+
WARNING_THRESHOLD = int(CONFIG.get("warning_threshold", 10))
35+
COLOR_CHARGING = CONFIG.get("color_charging", "yellow")
36+
COLOR_NO_BATTERY = CONFIG.get("color_no_battery", "red")
37+
38+
39+
def value_for_level(percent: int, level_mapping: Dict[int, str]):
40+
"""Get symbol associated to a level percentage."""
41+
levels = sorted(level_mapping.items(), reverse=True)
42+
for lvl, sym in levels:
43+
if lvl <= percent:
44+
return sym
45+
return levels[-1][1]
46+
47+
48+
def discharge_level(percent: int):
49+
"""Get battery symbol for given level percentage."""
50+
return value_for_level(percent, DISCHARGE_LEVEL)
51+
52+
53+
def color_level(percent: int):
54+
"""Get color for given level percentage."""
55+
return value_for_level(percent, COLOR_LEVEL)
56+
57+
58+
def battery2():
59+
"""Main entry point."""
60+
try:
61+
status = check_output(["acpi", "-b"], universal_newlines=True)
62+
except (FileNotFoundError, CalledProcessError):
63+
status = None
64+
65+
if not status:
66+
percentleft = 100
67+
content = discharge_level(percentleft)
68+
if status is None:
69+
content = "no ACPI process"
70+
fulltext = f"<span color='{COLOR_NO_BATTERY}' font='FontAwesome'>\uf00d {content}</span>"
71+
shorttext = fulltext
72+
else:
73+
# if there is more than one battery in one laptop, the percentage left is
74+
# available for each battery separately, although state and remaining
75+
# time for overall block is shown in the status of the first battery
76+
batteries = status.split("\n")
77+
state_batteries = []
78+
commasplitstatus_batteries = []
79+
percentleft_batteries = []
80+
time = ""
81+
for battery in batteries:
82+
if not battery:
83+
continue
3184
state_batteries.append(battery.split(": ")[1].split(", ")[0])
3285
commasplitstatus = battery.split(", ")
3386
if not time:
3487
time = commasplitstatus[-1].strip()
3588
# check if it matches a time
3689
time = re.match(r"(\d+):(\d+)", time)
90+
timeleft = ""
3791
if time:
3892
time = ":".join(time.groups())
39-
timeleft = " ({})".format(time)
40-
else:
41-
timeleft = ""
93+
timeleft = f"({time})"
4294

4395
p = int(commasplitstatus[1].rstrip("%\n"))
44-
if p>0:
96+
if p > 0:
4597
percentleft_batteries.append(p)
4698
commasplitstatus_batteries.append(commasplitstatus)
47-
state = state_batteries[0]
48-
commasplitstatus = commasplitstatus_batteries[0]
49-
if percentleft_batteries:
50-
percentleft = int(sum(percentleft_batteries)/len(percentleft_batteries))
51-
else:
99+
state = state_batteries[0]
100+
commasplitstatus = commasplitstatus_batteries[0]
52101
percentleft = 0
102+
if percentleft_batteries:
103+
percentleft = int(sum(percentleft_batteries) / len(percentleft_batteries))
53104

54-
# stands for charging
55-
color = config.get("color_charging", "yellow")
56-
FA_LIGHTNING = "<span color='{}'><span font='FontAwesome'>\uf0e7</span></span>".format(color)
105+
fa_plugged_in = "<span font='FontAwesome'>\uf1e6</span>"
106+
fa_battery = f"<span font='FontAwesome'>{discharge_level(percentleft)}</span>"
57107

58-
# stands for plugged in
59-
FA_PLUG = "<span font='FontAwesome'>\uf1e6</span>"
108+
output = []
109+
if state == "Discharging":
110+
output.append(f"{fa_battery}")
111+
elif state in ["Full", "Not charging"]:
112+
timeleft = ""
113+
output.append(f"{fa_plugged_in}")
114+
elif state == "Charging":
115+
output.append(
116+
f"<span color='{COLOR_CHARGING}' font='FontAwesome'>\uf0e7</span>"
117+
)
118+
output.append(fa_plugged_in)
119+
else:
120+
# stands for unknown status of battery
121+
timeleft = ""
122+
output.append("<span font='FontAwesome'>\uf128</span>")
123+
output.append(fa_battery)
60124

61-
# stands for using battery
62-
FA_BATTERY = "<span font='FontAwesome'>\uf240</span>"
125+
output.append(f"<span color='{color_level(percentleft)}'>{percentleft}%</span>")
126+
shorttext = " ".join(output)
127+
output.append(timeleft)
128+
fulltext = " ".join(output)
63129

64-
# stands for unknown status of battery
65-
FA_QUESTION = "<span font='FontAwesome'>\uf128</span>"
130+
print(fulltext)
131+
print(shorttext)
66132

133+
if percentleft < WARNING_THRESHOLD and state != "Charging":
134+
check_output(
135+
[
136+
"notify-send",
137+
"-u",
138+
"critical",
139+
"-a",
140+
"i3blocks.battery2",
141+
"-t",
142+
"0",
143+
"Warning",
144+
f"Battery level <b>{percentleft}%</b> is below <b>{WARNING_THRESHOLD}%</b>, plug power or suspend system",
145+
]
146+
)
147+
# exit code 33 will turn background red
148+
return 33
149+
return 0
67150

68-
if state == "Discharging":
69-
fulltext = FA_BATTERY + " "
70-
elif state == "Full":
71-
fulltext = FA_PLUG + " "
72-
timeleft = ""
73-
elif state == "Unknown":
74-
fulltext = FA_QUESTION + " " + FA_BATTERY + " "
75-
timeleft = ""
76-
else:
77-
fulltext = FA_LIGHTNING + " " + FA_PLUG + " "
78-
79-
def color(percent):
80-
if percent < 10:
81-
# exit code 33 will turn background red
82-
return config.get("color_10", "#FFFFFF")
83-
if percent < 20:
84-
return config.get("color_20", "#FF3300")
85-
if percent < 30:
86-
return config.get("color_30", "#FF6600")
87-
if percent < 40:
88-
return config.get("color_40", "#FF9900")
89-
if percent < 50:
90-
return config.get("color_50", "#FFCC00")
91-
if percent < 60:
92-
return config.get("color_60", "#FFFF00")
93-
if percent < 70:
94-
return config.get("color_70", "#FFFF33")
95-
if percent < 80:
96-
return config.get("color_80", "#FFFF66")
97-
return config.get("color_full", "#FFFFFF")
98-
99-
form = '<span color="{}">{}%</span>'
100-
fulltext += form.format(color(percentleft), percentleft)
101-
fulltext += timeleft
102-
103-
print(fulltext)
104-
print(fulltext)
105-
if percentleft < 10:
106-
exit(33)
151+
152+
if __name__ == "__main__":
153+
sys.exit(battery2())

battery2/i3blocks.conf

+17
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,20 @@
22
command=$SCRIPT_DIR/battery2
33
markup=pango
44
interval=30
5+
warning_threshold=10
6+
color_charging=yellow
7+
color_no_battery=red
8+
color_90=#FFFFFF
9+
color_80=#FFFF66
10+
color_70=#FFFF33
11+
color_60=#FFFF00
12+
color_50=#FFCC00
13+
color_40=#FF9900
14+
color_30=#FF6600
15+
color_20=#FF3300
16+
color_10=#FFFFFF
17+
discharge_90=
18+
discharge_75=
19+
discharge_50=
20+
discharge_25=
21+
discharge_10=

battery2/images/unplugged.png

-1.15 KB
Binary file not shown.

battery2/images/unplugged_25.png

1.74 KB
Loading

battery2/images/unplugged_50.png

1.75 KB
Loading

battery2/images/unplugged_75.png

1.83 KB
Loading

battery2/images/unplugged_empty.png

1.71 KB
Loading

battery2/images/unplugged_full.png

1.82 KB
Loading

battery2/images/warning_threshold.png

6.16 KB
Loading

0 commit comments

Comments
 (0)