#!/bin/sh #!/usr/bin/env nix-shell #!nix-shell -i bash usage() { echo "usage: battery_estimate [options...]" echo echo "pretty-prints a battery estimate (icon to indicate state, and a duration estimate)" echo echo "options:" echo " --debug: output additional information, to stderr" echo " --minute-suffix : use the provided string as a minutes suffix" echo " --hour-suffix : use the provided string as an hours suffix" echo " --icon-suffix : use the provided string as an icon suffix" echo " --percent-suffix : use the provided string when displaying percents" } # these icons come from sxmo; they only render in nerdfonts icon_bat_chg=("󰢟" "󱊤" "󱊥" "󰂅") icon_bat_dis=("󰂎" "󱊡" "󱊢" "󱊣") suffix_icon=" " # thin space suffix_percent="%" # suffix_icon=" " # render time like: 2ʰ08ᵐ # unicode sub/super-scripts: # symbol_hr="ʰ" # symbol_min="ᵐ" # render time like: 2ₕ08ₘ # symbol_hr="ₕ" # symbol_min="ₘ" # render time like: 2h08m # symbol_hr="h" # symbol_min="m" # render time like: 2:08 # symbol_hr=":" # symbol_min= # render time like: 2꞉08⧗ symbol_hr="꞉" symbol_min="⧗" # variants: # symbol_hr=":" # symbol_min="⧖" # symbol_min="⌛" # render time like: 2'08" # symbol_hr="'" # symbol_min='"' log() { if [ "$BATTERY_ESTIMATE_DEBUG" = "1" ]; then printf "$@" >&2 echo >&2 fi } render_icon() { # args: # 1: "chg" or "dis" # 2: current battery percentage level=$(($2 / 25)) level=$(($level > 3 ? 3 : $level)) level=$(($level < 0 ? 0 : $level)) log "icon: %s %d" "$1" "$level" if [ "$1" = "dis" ]; then printf "%s" "${icon_bat_dis[$level]}" elif [ "$1" = "chg" ]; then printf "%s" "${icon_bat_chg[$level]}" fi } try_path() { # assigns output variables: # - perc, perc_from_full (0-100) # - full, rate (pos means charging) if [ -f "$1/capacity" ]; then log "perc, perc_from_full from %s" "$1/capacity" perc=$(cat "$1/capacity") perc_from_full=$((100 - $perc)) fi if [ -f "$1/charge_full_design" ] && [ -f "$1/current_now" ]; then log "full, rate from %s and %s" "$1/charge_full_design" "$1/current_now" # current is positive when charging full=$(cat "$1/charge_full_design") rate=$(cat "$1/current_now") elif [ -f "$1/energy_full" ] && [ -f "$1/power_now" ]; then log "full, rate from %s and %s" "$1/energy_full" "$1/power_now" # power_now is positive when discharging full=$(cat "$1/energy_full") rate=-$(cat "$1/power_now") elif [ -f "$1/energy_full" ] && [ -f "$1/energy_now" ]; then log "full, rate from %s and %s" "$1/energy_full" "$1/energy_now" log " this is a compatibility path for legacy Thinkpad batteries which do not populate the 'power_now' field, and incorrectly populate 'energy_now' with power info" # energy_now is positive when discharging full=$(cat "$1/energy_full") rate=-$(cat "$1/energy_now") fi } try_all_paths() { try_path "/sys/class/power_supply/axp20x-battery" # Pinephone try_path "/sys/class/power_supply/BAT0" # Thinkpad log "perc: %d, perc_from_full: %d" "$perc" "$perc_from_full" log "full: %f, rate: %f" "$full" "$rate" log " rate > 0 means charging, else discharging" } fmt_minutes() { # args: # 1: icon to render # 2: string to show if charge/discharge time is indefinite # 3: minutes to stable state (i.e. to full charge or full discharge) # - we work in minutes instead of hours for precision: bash math is integer-only log "charge/discharge time: %f min" "$3" # args: if [ -n "$3" ] && [ "$3" -lt 1440 ]; then hr=$(($3 / 60)) hr_in_min=$(($hr * 60)) min=$(($3 - $hr_in_min)) printf "%s%s%d%s%02d%s" "$1" "$suffix_icon" "$hr" "$symbol_hr" "$min" "$symbol_min" else log "charge/discharge duration > 1d" printf "%s%s%s" "$1" "$suffix_icon" "$2" # more than 1d fi } pretty_output() { if [ -n "$perc" ]; then duration="" if [ "$rate" -gt 0 ]; then log "charging" icon="$(render_icon chg $perc)" duration="$(($full * 60 * $perc_from_full / (100 * $rate)))" else log "discharging" icon="$(render_icon dis $perc)" if [ "$rate" -lt 0 ]; then duration="$(($full * 60 * $perc / (-100 * $rate)))" fi fi fmt_minutes "$icon" "$perc$suffix_percent" "$duration" fi } while [ "$#" -gt 0 ]; do case "$1" in "--debug") shift BATTERY_ESTIMATE_DEBUG=1 ;; "--icon-suffix") shift suffix_icon="$1" shift ;; "--hour-suffix") shift symbol_hr="$1" shift ;; "--minute-suffix") shift symbol_min="$1" shift ;; "--percent-suffix") shift suffix_percent="$1" shift ;; *) usage exit 1 ;; esac done try_all_paths pretty_output