Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

battery2: change battery symbol when discharging #524

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions battery2/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,46 @@
# battery2

Show the current status of your battery.
## Show the current status of your battery

### Plugged and full
![](images/full.png)

### Charging
![](images/charging.png)

![](images/unplugged.png)
### Discharging
![](images/unplugged_full.png)

![](images/unplugged_75.png)

![](images/unplugged_50.png)

![](images/unplugged_25.png)

![](images/unplugged_empty.png)

### Unknown status
![](images/unknown.png)

### No battery is present
![](images/nobattery.png)

# Dependencies
## Show the notification when below threshold

![](images/warning_threshold.png)

# Dependencies (Debian like)

fonts-font-awesome, acpi, python3
- `fonts-font-awesome`,
- `libnotify-bin`,
- `acpi`,
- `python3`

# Installation

To use with i3blocks, copy the blocklet configuration in the given `i3blocks.conf` into your i3blocks configuration file, the recommended config is
To use with `i3blocks`, copy the blocklet configuration in the given
`i3blocks.conf` into your i3blocks configuration file, the recommended
config is:

```INI
[battery2]
Expand Down
201 changes: 124 additions & 77 deletions battery2/battery2
Original file line number Diff line number Diff line change
@@ -1,106 +1,153 @@
#!/usr/bin/env python3
#
# Copyright (C) 2016 James Murphy
# 2024 Gregory David
# Licensed under the GPL version 2 only
#
# A battery indicator blocklet script for i3blocks

from subprocess import check_output
import os
import re
import sys
from subprocess import CalledProcessError, check_output
from typing import Dict

config = dict(os.environ)
status = check_output(['acpi'], universal_newlines=True)

if not status:
# stands for no battery found
color = config.get("color_10", "red")
fulltext = "<span color='{}'><span font='FontAwesome'>\uf00d \uf240</span></span>".format(color)
percentleft = 100
else:
# if there is more than one battery in one laptop, the percentage left is
# available for each battery separately, although state and remaining
# time for overall block is shown in the status of the first battery
batteries = status.split("\n")
state_batteries=[]
commasplitstatus_batteries=[]
percentleft_batteries=[]
time = ""
for battery in batteries:
if battery!='':
CONFIG = dict(os.environ)
DISCHARGE_LEVEL = {
90: CONFIG.get("discharge_90", "\uf240"),
75: CONFIG.get("discharge_75", "\uf241"),
50: CONFIG.get("discharge_50", "\uf242"),
25: CONFIG.get("discharge_25", "\uf243"),
10: CONFIG.get("discharge_10", "\uf244"),
}
COLOR_LEVEL = {
90: CONFIG.get("color_90", "#FFFFFF"),
80: CONFIG.get("color_80", "#FFFF66"),
70: CONFIG.get("color_70", "#FFFF33"),
60: CONFIG.get("color_60", "#FFFF00"),
50: CONFIG.get("color_50", "#FFCC00"),
40: CONFIG.get("color_40", "#FF9900"),
30: CONFIG.get("color_30", "#FF6600"),
20: CONFIG.get("color_20", "#FF3300"),
10: CONFIG.get("color_10", "#FFFFFF"),
}
WARNING_THRESHOLD = int(CONFIG.get("warning_threshold", 10))
COLOR_CHARGING = CONFIG.get("color_charging", "yellow")
COLOR_NO_BATTERY = CONFIG.get("color_no_battery", "red")


def value_for_level(percent: int, level_mapping: Dict[int, str]):
"""Get symbol associated to a level percentage."""
levels = sorted(level_mapping.items(), reverse=True)
for lvl, sym in levels:
if lvl <= percent:
return sym
return levels[-1][1]


def discharge_level(percent: int):
"""Get battery symbol for given level percentage."""
return value_for_level(percent, DISCHARGE_LEVEL)


def color_level(percent: int):
"""Get color for given level percentage."""
return value_for_level(percent, COLOR_LEVEL)


def battery2():
"""Main entry point."""
try:
status = check_output(["acpi", "-b"], universal_newlines=True)
except (FileNotFoundError, CalledProcessError):
status = None

if not status:
percentleft = 100
content = discharge_level(percentleft)
if status is None:
content = "no ACPI process"
fulltext = f"<span color='{COLOR_NO_BATTERY}' font='FontAwesome'>\uf00d {content}</span>"
shorttext = fulltext
else:
# if there is more than one battery in one laptop, the percentage left is
# available for each battery separately, although state and remaining
# time for overall block is shown in the status of the first battery
batteries = status.split("\n")
state_batteries = []
commasplitstatus_batteries = []
percentleft_batteries = []
time = ""
for battery in batteries:
if not battery:
continue
state_batteries.append(battery.split(": ")[1].split(", ")[0])
commasplitstatus = battery.split(", ")
if not time:
time = commasplitstatus[-1].strip()
# check if it matches a time
time = re.match(r"(\d+):(\d+)", time)
timeleft = ""
if time:
time = ":".join(time.groups())
timeleft = " ({})".format(time)
else:
timeleft = ""
timeleft = f"({time})"

p = int(commasplitstatus[1].rstrip("%\n"))
if p>0:
if p > 0:
percentleft_batteries.append(p)
commasplitstatus_batteries.append(commasplitstatus)
state = state_batteries[0]
commasplitstatus = commasplitstatus_batteries[0]
if percentleft_batteries:
percentleft = int(sum(percentleft_batteries)/len(percentleft_batteries))
else:
state = state_batteries[0]
commasplitstatus = commasplitstatus_batteries[0]
percentleft = 0
if percentleft_batteries:
percentleft = int(sum(percentleft_batteries) / len(percentleft_batteries))

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

# stands for plugged in
FA_PLUG = "<span font='FontAwesome'>\uf1e6</span>"
output = []
if state == "Discharging":
output.append(f"{fa_battery}")
elif state in ["Full", "Not charging"]:
timeleft = ""
output.append(f"{fa_plugged_in}")
elif state == "Charging":
output.append(
f"<span color='{COLOR_CHARGING}' font='FontAwesome'>\uf0e7</span>"
)
output.append(fa_plugged_in)
else:
# stands for unknown status of battery
timeleft = ""
output.append("<span font='FontAwesome'>\uf128</span>")
output.append(fa_battery)

# stands for using battery
FA_BATTERY = "<span font='FontAwesome'>\uf240</span>"
output.append(f"<span color='{color_level(percentleft)}'>{percentleft}%</span>")
shorttext = " ".join(output)
output.append(timeleft)
fulltext = " ".join(output)

# stands for unknown status of battery
FA_QUESTION = "<span font='FontAwesome'>\uf128</span>"
print(fulltext)
print(shorttext)

if percentleft < WARNING_THRESHOLD and state != "Charging":
check_output(
[
"notify-send",
"-u",
"critical",
"-a",
"i3blocks.battery2",
"-t",
"0",
"Warning",
f"Battery level <b>{percentleft}%</b> is below <b>{WARNING_THRESHOLD}%</b>, plug power or suspend system",
]
)
# exit code 33 will turn background red
return 33
return 0

if state == "Discharging":
fulltext = FA_BATTERY + " "
elif state == "Full":
fulltext = FA_PLUG + " "
timeleft = ""
elif state == "Unknown":
fulltext = FA_QUESTION + " " + FA_BATTERY + " "
timeleft = ""
else:
fulltext = FA_LIGHTNING + " " + FA_PLUG + " "

def color(percent):
if percent < 10:
# exit code 33 will turn background red
return config.get("color_10", "#FFFFFF")
if percent < 20:
return config.get("color_20", "#FF3300")
if percent < 30:
return config.get("color_30", "#FF6600")
if percent < 40:
return config.get("color_40", "#FF9900")
if percent < 50:
return config.get("color_50", "#FFCC00")
if percent < 60:
return config.get("color_60", "#FFFF00")
if percent < 70:
return config.get("color_70", "#FFFF33")
if percent < 80:
return config.get("color_80", "#FFFF66")
return config.get("color_full", "#FFFFFF")

form = '<span color="{}">{}%</span>'
fulltext += form.format(color(percentleft), percentleft)
fulltext += timeleft

print(fulltext)
print(fulltext)
if percentleft < 10:
exit(33)

if __name__ == "__main__":
sys.exit(battery2())
17 changes: 17 additions & 0 deletions battery2/i3blocks.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,20 @@
command=$SCRIPT_DIR/battery2
markup=pango
interval=30
warning_threshold=10
color_charging=yellow
color_no_battery=red
color_90=#FFFFFF
color_80=#FFFF66
color_70=#FFFF33
color_60=#FFFF00
color_50=#FFCC00
color_40=#FF9900
color_30=#FF6600
color_20=#FF3300
color_10=#FFFFFF
discharge_90=
discharge_75=
discharge_50=
discharge_25=
discharge_10=
Binary file removed battery2/images/unplugged.png
Binary file not shown.
Binary file added battery2/images/unplugged_25.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added battery2/images/unplugged_50.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added battery2/images/unplugged_75.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added battery2/images/unplugged_empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added battery2/images/unplugged_full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added battery2/images/warning_threshold.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.