#!/usr/bin/env bash # This file is part of I38. # I38 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, # either version 3 of the License, or (at your option) any later version. # I38 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more details. # You should have received a copy of the GNU General Public License along with I38. If not, see . # 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 deviceName local output="" 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 # 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 # 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 if [[ -n "$manufacturer" ]] && [[ -n "$modelName" ]]; then deviceName="$manufacturer $modelName" elif [[ -n "$modelName" ]]; then deviceName="$modelName" elif [[ -n "$manufacturer" ]]; then deviceName="$manufacturer Battery" else deviceName="${batteryName//BAT/Battery }" deviceName="${deviceName//hid-/}" deviceName="${deviceName//_/ }" fi output="$deviceName" [[ -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" } # Concise one-line report for the simple spoken announcement. get_battery_simple() { local batteryPath="$1" 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 } if [[ $detailedMode -eq 1 ]]; then # Detailed mode: show the full report in a reviewable yad text-info dialog. batteryInfoText="" batteryCount=0 # 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 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 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 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 $?