Detailed battery status is now presented in a dialogue. Quick battery status should actually work now.

This commit is contained in:
Storm Dragon
2026-06-21 23:28:49 -04:00
parent 65a03eb441
commit d3fb549fd1
4 changed files with 160 additions and 159 deletions
+6 -2
View File
@@ -132,7 +132,8 @@ In Panel Mode, single keypresses launch different information panels:
| `s` | Show system information |
| `r` | Open reminder panel |
| `n` | Launch notes application |
| `b` | Open bluetooth. *requires blueman be installed at the time of your i3 config generation* |
| `b` | Open Bluetooth *(requires blueman during i3 config generation)* |
| `@MODKEY@` + `b` | Speak a quick battery status |
| `Shift` + `b` | Show detailed battery information |
| `m` | Launch password manager |
| `p` | Show power options |
@@ -150,7 +151,10 @@ The system information panel (`s` key in Panel Mode) displays vital system stati
- Memory usage
- Disk space
- Network status
- Battery level (if applicable)
### Battery Information
The battery report has two modes. Press `@MODKEY@` + `b` in Panel Mode for a concise spoken announcement of charge level and charging state. Press `Shift` + `b` in Panel Mode for the detailed report, shown in a reviewable text dialog instead of spoken aloud, so the full details can be navigated with the arrow keys: charge, status, health, chemistry, voltage, current, power draw, and cycle count when the hardware exposes them. The quick report is also available with `Alt` + `b` in Ratpoison Mode. If `acpi` is installed it is used for the report, otherwise I38 reads `/sys/class/power_supply` directly. In the detailed dialog, press `Control` + `Home` to jump back to the top of the text.
### Weather Panel
+13
View File
@@ -116,6 +116,19 @@ You can apply the same configuration to GTK2 apps. Create or edit ~/.gtkrc-2.0
- -x: Generate ~/.xinitrc and ~/.xprofile.
- -X: Generate ~/.xprofile only.
## Dependency Helpers
Distro-specific dependency helpers live in `extra/`. On Arch Linux, run:
```bash
extra/arch-linux.sh
```
The helper checks I38's required packages, asks which missing provider
applications to install, adds lightweight optional feature packages, then
installs the selected package list at the end. Optional packages that need
manual choice, such as Tesseract language data, are skipped.
## Development
Enable the tracked pre-commit hooks once after cloning:
+3
View File
@@ -1360,6 +1360,9 @@ $(if command -v blueman-manager &> /dev/null ; then
echo "bindsym b exec --no-startup-id blueman-manager, mode \"default\""
fi)
# Quick battery status bound to the configured mod key+b
bindsym \$mod+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh, mode "default"
# Detailed battery information bound to Shift+b
bindsym Shift+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh --detailed, mode "default"
+138 -157
View File
@@ -10,91 +10,71 @@
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
# Battery status reporting.
# Simple mode (default, Panel Mode mod+b): speaks a concise status as a quick
# announcement. Detailed mode (-d/--detailed, Panel Mode Shift+b): shows
# the full report in a screen reader accessible yad text-info dialog so it can
# be reviewed with the arrow keys.
# Parse command line arguments
detailedMode=0
if [[ "$1" == "-d" ]] || [[ "$1" == "--detailed" ]]; then
detailedMode=1
fi
# Read a sysfs attribute safely, stripping trailing newlines. Returns empty if
# the attribute file does not exist or cannot be read.
read_attribute() {
local path="$1"
[[ -f "$path" ]] && cat "$path" 2>/dev/null | tr -d '\n'
}
get_battery_details() {
local batteryPath="$1"
local batteryName="${batteryPath##*/}"
local modelName=""
local manufacturer=""
local capacity=""
local status=""
local health=""
local cycleCount=""
local technology=""
local voltage=""
local current=""
local power=""
local modelName
local manufacturer
local capacity
local status
local health
local cycleCount
local technology
local voltage
local current
local power
local deviceName
local output=""
# Get model name if available
if [[ -f "${batteryPath}/model_name" ]]; then
modelName="$(cat "${batteryPath}/model_name" 2>/dev/null | tr -d '\n')"
modelName="$(read_attribute "${batteryPath}/model_name")"
manufacturer="$(read_attribute "${batteryPath}/manufacturer")"
capacity="$(read_attribute "${batteryPath}/capacity")"
status="$(read_attribute "${batteryPath}/status")"
health="$(read_attribute "${batteryPath}/capacity_level")"
cycleCount="$(read_attribute "${batteryPath}/cycle_count")"
technology="$(read_attribute "${batteryPath}/technology")"
# Voltage (microvolts -> volts)
local voltageUv
voltageUv="$(read_attribute "${batteryPath}/voltage_now")"
if [[ -n "$voltageUv" ]] && [[ "$voltageUv" =~ ^[0-9]+$ ]]; then
voltage="$(awk "BEGIN {printf \"%.2f\", $voltageUv/1000000}")"
fi
# Get manufacturer if available
if [[ -f "${batteryPath}/manufacturer" ]]; then
manufacturer="$(cat "${batteryPath}/manufacturer" 2>/dev/null | tr -d '\n')"
# Current (microamps -> amps)
local currentUa
currentUa="$(read_attribute "${batteryPath}/current_now")"
if [[ -n "$currentUa" ]] && [[ "$currentUa" =~ ^[0-9]+$ ]]; then
current="$(awk "BEGIN {printf \"%.2f\", $currentUa/1000000}")"
fi
# Get capacity (charge percentage)
if [[ -f "${batteryPath}/capacity" ]]; then
capacity="$(cat "${batteryPath}/capacity" 2>/dev/null | tr -d '\n')"
fi
# Get status (Charging, Discharging, Full, etc.)
if [[ -f "${batteryPath}/status" ]]; then
status="$(cat "${batteryPath}/status" 2>/dev/null | tr -d '\n')"
fi
# Get health/capacity level if available
if [[ -f "${batteryPath}/capacity_level" ]]; then
health="$(cat "${batteryPath}/capacity_level" 2>/dev/null | tr -d '\n')"
fi
# Get cycle count if available
if [[ -f "${batteryPath}/cycle_count" ]]; then
cycleCount="$(cat "${batteryPath}/cycle_count" 2>/dev/null | tr -d '\n')"
fi
# Get technology (Li-ion, Li-poly, etc.)
if [[ -f "${batteryPath}/technology" ]]; then
technology="$(cat "${batteryPath}/technology" 2>/dev/null | tr -d '\n')"
fi
# Get voltage (in microvolts, convert to volts)
if [[ -f "${batteryPath}/voltage_now" ]]; then
local voltageUv
voltageUv="$(cat "${batteryPath}/voltage_now" 2>/dev/null | tr -d '\n')"
if [[ -n "$voltageUv" ]] && [[ "$voltageUv" =~ ^[0-9]+$ ]]; then
voltage="$(awk "BEGIN {printf \"%.2f\", $voltageUv/1000000}")"
fi
fi
# Get current (in microamps, convert to amps)
if [[ -f "${batteryPath}/current_now" ]]; then
local currentUa
currentUa="$(cat "${batteryPath}/current_now" 2>/dev/null | tr -d '\n')"
if [[ -n "$currentUa" ]] && [[ "$currentUa" =~ ^[0-9]+$ ]]; then
current="$(awk "BEGIN {printf \"%.2f\", $currentUa/1000000}")"
fi
fi
# Get power (in microwatts, convert to watts)
if [[ -f "${batteryPath}/power_now" ]]; then
local powerUw
powerUw="$(cat "${batteryPath}/power_now" 2>/dev/null | tr -d '\n')"
if [[ -n "$powerUw" ]] && [[ "$powerUw" =~ ^[0-9]+$ ]]; then
power="$(awk "BEGIN {printf \"%.2f\", $powerUw/1000000}")"
fi
# Power (microwatts -> watts)
local powerUw
powerUw="$(read_attribute "${batteryPath}/power_now")"
if [[ -n "$powerUw" ]] && [[ "$powerUw" =~ ^[0-9]+$ ]]; then
power="$(awk "BEGIN {printf \"%.2f\", $powerUw/1000000}")"
fi
# Build readable device name
local deviceName="$batteryName"
if [[ -n "$manufacturer" ]] && [[ -n "$modelName" ]]; then
deviceName="$manufacturer $modelName"
elif [[ -n "$modelName" ]]; then
@@ -102,116 +82,117 @@ get_battery_details() {
elif [[ -n "$manufacturer" ]]; then
deviceName="$manufacturer Battery"
else
# Try to make the battery name more readable
deviceName="${batteryName//BAT/Battery }"
deviceName="${deviceName//hid-/}"
deviceName="${deviceName//_/ }"
fi
# Build the output string
output="$deviceName"
if [[ -n "$capacity" ]]; then
output="$output, ${capacity}%"
fi
if [[ -n "$status" ]]; then
output="$output, ${status,,}"
fi
if [[ -n "$health" ]]; then
output="$output, health: ${health,,}"
fi
if [[ -n "$technology" ]]; then
output="$output, type: $technology"
fi
if [[ -n "$voltage" ]]; then
output="$output, voltage: ${voltage}V"
fi
if [[ -n "$current" ]]; then
output="$output, current: ${current}A"
fi
if [[ -n "$power" ]]; then
output="$output, power: ${power}W"
fi
if [[ -n "$cycleCount" ]]; then
output="$output, cycles: $cycleCount"
fi
[[ -n "$capacity" ]] && output="${output}"$'\n'"Charge: ${capacity}%"
[[ -n "$status" ]] && output="${output}"$'\n'"Status: ${status,,}"
[[ -n "$health" ]] && output="${output}"$'\n'"Health: ${health,,}"
[[ -n "$technology" ]] && output="${output}"$'\n'"Type: $technology"
[[ -n "$voltage" ]] && output="${output}"$'\n'"Voltage: ${voltage} V"
[[ -n "$current" ]] && output="${output}"$'\n'"Current: ${current} A"
[[ -n "$power" ]] && output="${output}"$'\n'"Power: ${power} W"
[[ -n "$cycleCount" ]] && output="${output}"$'\n'"Cycles: $cycleCount"
echo "$output"
}
# Simple battery status (default mode)
# Concise one-line report for the simple spoken announcement.
get_battery_simple() {
local batteryPath="$1"
local bat="${batteryPath##*/}"
bat="${bat//BAT/Battery }"
bat="${bat}: $( { cat "${batteryPath}/status";echo -n ", "; cat "${batteryPath}/capacity"; } | tr -d \\n) percent."
echo "$bat"
local batteryName="${batteryPath##*/}"
local status
local capacity
local deviceName
status="$(read_attribute "${batteryPath}/status")"
capacity="$(read_attribute "${batteryPath}/capacity")"
deviceName="${batteryName//BAT/Battery }"
if [[ -n "$status" ]] && [[ -n "$capacity" ]]; then
echo "${deviceName}: ${status,,}, ${capacity}%"
elif [[ -n "$capacity" ]]; then
echo "${deviceName}: ${capacity}%"
fi
}
# Main script
if [[ $detailedMode -eq 1 ]]; then
# Detailed mode
if command -v acpi &> /dev/null; then
# Try acpi first for more detailed info
batteryInfo=""
while IFS= read -r line; do
if [[ -n "$batteryInfo" ]]; then
batteryInfo="${batteryInfo}. "
fi
batteryInfo="${batteryInfo}${line}"
done < <(acpi -V 2>/dev/null | grep -i battery)
if [[ -n "$batteryInfo" ]]; then
spd-say -P important -Cw "$batteryInfo"
exit 0
fi
fi
# Fallback to /sys/class/power_supply for detailed info
# Detailed mode: show the full report in a reviewable yad text-info dialog.
batteryInfoText=""
batteryCount=0
batteryOutput=""
while IFS= read -r batteryPath; do
if [[ -e "${batteryPath}/capacity" ]] || [[ -e "${batteryPath}/status" ]]; then
batteryDetails="$(get_battery_details "$batteryPath")"
if [[ -n "$batteryDetails" ]]; then
((batteryCount++))
if [[ -n "$batteryOutput" ]]; then
batteryOutput="${batteryOutput}. "
fi
batteryOutput="${batteryOutput}${batteryDetails}"
fi
# Append a non-empty battery block, separating batteries with a blank line.
add_battery_block() {
local block="$1"
[[ -z "$block" ]] && return
batteryCount=$((batteryCount + 1))
if [[ -n "$batteryInfoText" ]]; then
batteryInfoText="${batteryInfoText}"$'\n\n'
fi
done < <(find /sys/class/power_supply -type l 2>/dev/null)
batteryInfoText="${batteryInfoText}${block}"
}
# Prefer acpi -V, fall back to /sys/class/power_supply.
if command -v acpi &> /dev/null; then
acpiInfo=""
while IFS= read -r line; do
[[ -z "$line" ]] && continue
if [[ -n "$acpiInfo" ]]; then
acpiInfo="${acpiInfo}"$'\n'
fi
acpiInfo="${acpiInfo}${line}"
done < <(acpi -V 2>/dev/null | grep -i battery)
add_battery_block "$acpiInfo"
fi
if [[ $batteryCount -eq 0 ]]; then
spd-say -P important -Cw "No battery information available"
else
spd-say -P important -Cw "$batteryOutput"
while IFS= read -r batteryPath; do
if [[ -e "${batteryPath}/capacity" ]] || [[ -e "${batteryPath}/status" ]]; then
add_battery_block "$(get_battery_details "$batteryPath")"
fi
done < <(find /sys/class/power_supply -type l 2>/dev/null)
fi
else
# Simple mode (default)
if command -v acpi &> /dev/null; then
bat="$(acpi -b)"
spd-say -P important -Cw "$bat"
else
find /sys/class/power_supply -type l -exec bash -c '
for i ; do
if [[ -e "$i/capacity" ]]; then
bat="${i##*/}"
bat="${bat//BAT/Battery }"
bat="${bat}: $( { cat "${i}/status";echo -n ", "; cat "${i}/capacity"; } | tr -d \\n) percent."
spd-say -P important -Cw "$bat"
fi
done
' _ {} \;
if [[ $batteryCount -eq 0 ]]; then
batteryInfoText="No battery information available."
fi
displayText="Battery Information (Detailed)
${batteryInfoText}
End of text. Press Control+Home to return to the beginning."
echo "$displayText" | yad --pname=I38Battery \
--title="I38 Battery Information" \
--text-info \
--show-cursor \
--width=450 \
--height=400 \
--center \
--button="Close:0"
exit $?
fi
# Simple mode: speak a concise status as a quick announcement.
announcement=""
if command -v acpi &> /dev/null; then
announcement="$(acpi -b 2>/dev/null)"
else
while IFS= read -r batteryPath; do
if [[ -e "${batteryPath}/capacity" ]]; then
block="$(get_battery_simple "$batteryPath")"
[[ -n "$block" ]] && announcement="${announcement:+$announcement. }$block"
fi
done < <(find /sys/class/power_supply -type l 2>/dev/null)
fi
if [[ -z "$announcement" ]]; then
announcement="No battery information available."
fi
spd-say -P important -Cw "$announcement"
exit $?