#!/bin/bash # omarchy:summary=Returns a formatted battery status string with percentage and power draw/charge. # omarchy:args=[--shell] shell_output=false power_supply_path="${OMARCHY_POWER_SUPPLY_PATH:-/sys/class/power_supply}" case "${1:-}" in "") ;; --shell) shell_output=true ;; *) echo "Usage: omarchy-battery-status [--shell]" >&2 exit 2 ;; esac # UPower names the device after the sysfs entry, and only some kernels call # that BAT*: Apple Silicon reports battery_macsmc_battery. Match on the class, # then take the first one that powers the machine: a wireless mouse or keyboard # is a battery_ device too, and UPower marks it "power supply: no". battery="" battery_info="" while read -r candidate; do [[ -n $candidate ]] || continue candidate_info=$(upower -i "$candidate" 2>/dev/null) || continue if grep -qE '^\s*power supply:\s+yes' <<<"$candidate_info"; then battery=$candidate battery_info=$candidate_info break fi done < <(upower -e 2>/dev/null | grep '/battery_') [[ -z $battery ]] && exit 0 percentage=$(awk '/percentage/ { print int($2); exit }' <<<"$battery_info") capacity=$(awk '/energy-full:/ { printf "%d", $2; exit }' <<<"$battery_info") time_remaining=$(awk '/time to (empty|full)/ { value = $4 unit = $5 if (unit ~ /^minute/) { printf "%dm", int(value) } else { hours = int(value) minutes = int((value - hours) * 60) if (minutes > 0) { printf "%dh %dm", hours, minutes } else { printf "%dh", hours } } exit }' <<<"$battery_info") power_rate_raw=$(awk '/energy-rate/ { print $2; exit }' <<<"$battery_info") native_path=$(awk '/native-path/ { print $2; exit }' <<<"$battery_info") battery_path="$power_supply_path/$native_path" # UPower's energy-rate can lag the kernel telemetry by tens of seconds. Use # the instantaneous sysfs reading when available so the open panel stays live. if [[ -r $battery_path/power_now ]]; then power_rate_raw=$(awk -v microwatts="$(<"$battery_path/power_now")" 'BEGIN { print microwatts / 1000000 }') elif [[ -r $battery_path/current_now && -r $battery_path/voltage_now ]]; then power_rate_raw=$(awk \ -v microamps="$(<"$battery_path/current_now")" \ -v microvolts="$(<"$battery_path/voltage_now")" \ 'BEGIN { print microamps * microvolts / 1000000000000 }') fi # Some kernels sign power_now by direction (negative while discharging on # Apple Silicon); the state field already says which way it flows. power_rate=$(awk -v rate="${power_rate_raw:-0}" 'BEGIN { if (rate < 0) rate = -rate rounded = sprintf("%.1f", rate) sub(/\.0$/, "", rounded) print rounded }') state=$(awk '/state/ { print $2; exit }' <<<"$battery_info") threshold_start=$(awk '/charge-start-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info") threshold_end=$(awk '/charge-end-threshold:/ { gsub(/%/, "", $2); print int($2); exit }' <<<"$battery_info") # Read the thresholds from the battery UPower resolved rather than a BAT* glob, # which misses every battery with another name. [[ -n $threshold_end || ! -r $battery_path/charge_control_end_threshold ]] || threshold_end=$(<"$battery_path/charge_control_end_threshold") [[ -n $threshold_start || ! -r $battery_path/charge_control_start_threshold ]] || threshold_start=$(<"$battery_path/charge_control_start_threshold") [[ -n $threshold_end ]] || threshold_end=$(cat "$power_supply_path"/BAT*/charge_control_end_threshold 2>/dev/null | head -1) [[ -n $threshold_start ]] || threshold_start=$(cat "$power_supply_path"/BAT*/charge_control_start_threshold 2>/dev/null | head -1) ac_online=false for supply in "$power_supply_path"/*; do [[ -r $supply/type ]] || continue [[ $(<"$supply/type") == "Mains" ]] || continue [[ -r $supply/online ]] || continue if [[ $(<"$supply/online") == "1" ]]; then ac_online=true break fi done charge_idle=false if awk -v rate="${power_rate_raw:-0}" 'BEGIN { exit !(rate <= 0.2) }'; then charge_idle=true fi charge_holding=false if [[ $ac_online == "true" && -n $threshold_end ]]; then if [[ $state == "pending-charge" ]]; then charge_holding=true elif [[ $state == "fully-charged" ]] && (( percentage < 99 )); then charge_holding=true elif [[ $state == "charging" && $charge_idle == "true" ]] && (( threshold_end < 99 && percentage >= threshold_end )); then charge_holding=true fi fi if [[ $shell_output == "true" ]]; then printf 'percentage\t%s\n' "${percentage}%" if [[ $charge_holding == "true" ]]; then printf 'state\tholding\n' else printf 'state\t%s\n' "$state" fi printf 'rate\t%s\n' "${power_rate}W" printf 'size\t%s\n' "${capacity}Wh" printf 'time\t%s\n' "$time_remaining" cycles="" [[ ! -r $battery_path/cycle_count ]] || cycles=$(<"$battery_path/cycle_count") [[ -n $cycles ]] || cycles=$(cat "$power_supply_path"/BAT*/cycle_count 2>/dev/null | head -1) [[ -n $cycles ]] && printf 'cycles\t%s\n' "$cycles" if [[ -n $threshold_end ]]; then if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then printf 'threshold\t%s-%s%%\n' "$threshold_start" "$threshold_end" else printf 'threshold\t%s%%\n' "$threshold_end" fi fi exit 0 fi if [[ $charge_holding == "true" ]]; then if [[ -n $threshold_start && $threshold_start != $threshold_end ]]; then threshold_label="${threshold_start}-${threshold_end}%" else threshold_label="${threshold_end}%" fi echo "Battery ${percentage}% · Holding at ${threshold_label} · ${power_rate}W / ${capacity}Wh" elif [[ $state == "charging" ]]; then echo "Battery ${percentage}% · ${time_remaining} to full ·  ${power_rate}W / ${capacity}Wh" else echo "Battery ${percentage}% · ${time_remaining} left ·  ${power_rate}W / ${capacity}Wh" fi