¿Cómo hago que mi computadora portátil duerma cuando alcanza un umbral de batería baja?

24

Estoy usando Ubuntu, pero tengo i3 como mi administrador de ventanas en lugar de un entorno de escritorio.

Cuando mi batería alcanza el 0%, la computadora se apaga abruptamente, sin advertencia ni nada.

¿Hay una secuencia de comandos o configuración simple que pueda configurar para que duerma, digamos, 4% de la batería?

o_o_o--
fuente

Respuestas:

12

Aquí hay un pequeño script que verifica el nivel de batería y llama a un comando personalizado, aquí pm-hibernate, en caso de que el nivel de batería esté por debajo de un cierto umbral.

#!/bin/sh

###########################################################################
#
# Usage: system-low-battery
#
# Checks if the battery level is low. If “low_threshold” is exceeded
# a system notification is displayed, if “critical_threshold” is exceeded
# a popup window is displayed as well. If “OK” is pressed, the system
# shuts down after “timeout” seconds. If “Cancel” is pressed the script
# does nothing.
#
# This script is supposed to be called from a cron job.
#
###########################################################################

# This is required because the script is invoked by cron. Dbus information
# is stored in a file by the following script when a user logs in. Connect
# it to your autostart mechanism of choice.
#
# #!/bin/sh
# touch $HOME/.dbus/Xdbus
# chmod 600 $HOME/.dbus/Xdbus
# env | grep DBUS_SESSION_BUS_ADDRESS > $HOME/.dbus/Xdbus
# echo 'export DBUS_SESSION_BUS_ADDRESS' >> $HOME/.dbus/Xdbus
# exit 0
#
if [ -r ~/.dbus/Xdbus ]; then
  . ~/.dbus/Xdbus
fi

low_threshold=10
critical_threshold=4
timeout=59
shutdown_cmd='/usr/sbin/pm-hibernate'

level=$(cat /sys/devices/platform/smapi/BAT0/remaining_percent)
state=$(cat /sys/devices/platform/smapi/BAT0/state)

if [ x"$state" != x'discharging' ]; then
  exit 0
fi

do_shutdown() {
  sleep $timeout && kill $zenity_pid 2>/dev/null

  if [ x"$state" != x'discharging' ]; then
    exit 0
  else
    $shutdown_cmd
  fi
}

if [ "$level" -gt $critical_threshold ] && [ "$level" -lt $low_threshold ]; then
  notify-send "Battery level is low: $level%"
fi

if [ "$level" -lt $critical_threshold ]; then

  notify-send -u critical -t 20000 "Battery level is low: $level%" \
    'The system is going to shut down in 1 minute.'

  DISPLAY=:0 zenity --question --ok-label 'OK' --cancel-label 'Cancel' \
    --text "Battery level is low: $level%.\n\n The system is going to shut down in 1 minute." &
  zenity_pid=$!

  do_shutdown &
  shutdown_pid=$!

  trap 'kill $shutdown_pid' 1

  if ! wait $zenity_pid; then
    kill $shutdown_pid 2>/dev/null
  fi

fi

exit 0

Es un script muy simple, pero creo que captas la idea y puedes adaptarla fácilmente a tus necesidades. La ruta al nivel de la batería puede ser diferente en su sistema. Un poco más portátil probablemente sería usar algo como acpi | cut -f2 -d,para obtener el nivel de batería. Cron puede programar este script para que se ejecute cada minuto. Edite su crontab con crontab -ey agregue el script:

*/1 * * * * /home/me/usr/bin/low-battery-shutdown

Otra solución sería instalar un entorno de escritorio como Gnome o Xfce (y cambiar su administrador de ventanas a i3). Ambos entornos destop mencionados cuentan con demonios de administración de energía que se encargan de apagar la computadora. Pero supongo que deliberadamente no los usa y está buscando una solución más minimalista.

Marco
fuente
Hm, intenté correr sleepd -b 40y no pasó nada después de la marca del 40%. También lo intenté sudo sleepd -b 40 -s pm-suspendy no pasa nada ...
o_o_o--
@NoamGagliardi Confirmado, tampoco funciona aquí. Además, el paquete parece no mantenido. Intento si puedo encontrar una mejor alternativa y actualizar mi respuesta, de lo contrario la eliminaré.
Marco
(TIL " cut".) ¡El guión funciona! Tengo acpi | cut -f2 -d, | cut -f1 d%- Leeré sobre cron para que se ejecute solo. ¡Gracias!
o_o_o--
No tengo /sys/devices/platform/smapi/directorio ¿Dónde puedo encontrar el porcentaje restante de energía de la batería? Estoy usando kernel personalizado 3.10
Martin Vegter
2
@ MartinVegter Depende de tu hardware, puedes intentarlo /sys/class/power_supply/BAT0/capacity. De lo contrario, use el acpicomando.
Marco
6

En lugar de hackear sus propios scripts y si está usando Ubuntu como sugiere la etiqueta, simplemente puede instalar el paquete upower. Debería estar disponible en todos los derivados de Debian, incluido Ubuntu. Por defecto, viene con una configuración en la /etc/UPower/UPower.confque se activa la suspensión híbrida una vez que el nivel de la batería alcanza valores críticos. El valor predeterminado para el nivel crítico es 2%.

Para los usuarios de otras distribuciones, las entradas relevantes para /etc/UPower/UPower.confson:

PercentageAction=2
CriticalPowerAction=HybridSleep

También puede usar TimeActionjunto con UsePercentageForPolicy=falsepara permitir que la acción se lleve a cabo una vez que solo quede el tiempo especificado:

TimeAction=120

Los valores válidos para CriticalPowerAction son PowerOff,Hibernate y HybridSleep. Si HybridSleep está configurado pero no está disponible, se utilizará Hibernate. Si Hibernate está configurado pero no está disponible, se usará PowerOff.

La ventaja de HybridSleep es que, además de escribir memoria en su área de intercambio, suspende el sistema. Suspender aún consumirá algo de batería, pero si regresa antes de que se agote la batería, puede reanudar mucho más rápidamente desde un sistema suspendido que desde uno hibernado. En caso de que la batería se agote antes de volver a una toma de corriente, puede reanudar el sistema desde la hibernación una vez que tenga energía nuevamente.

josch
fuente
Nota: Creo que HybridSleeprequiere tener un espacio de intercambio.
2
@cipricus es correcto pero upower elegirá con gracia apagar la máquina si no puede hibernar.
josch
2

La respuesta actualmente aceptada es excelente, pero un poco desactualizada para Ubuntu 16.04:

  • Los comandos para obtener el estado de la batería han cambiado.
  • Las variables de entorno necesarias para notificar-enviar al trabajo han cambiado .
  • La secuencia de comandos que se proporciona allí ya no funciona desde el cron del usuario, ya que la hibernación requiere root.
  • systemctl hibernatese prefiere sobre pm-hibernate.

Entonces, aquí está el script que uso:

#!/usr/bin/env bash

# Notifies the user if the battery is low.
# Executes some command (like hibernate) on critical battery.
# This script is supposed to be called from a cron job.
# If you change this script's name/path, don't forget to update it in crontab !!

level=$(cat /sys/class/power_supply/BAT1/capacity)
status=$(cat /sys/class/power_supply/BAT1/status)

# Exit if not discharging
if [ "${status}" != "Discharging" ]; then
  exit 0
fi


# Source the environment variables required for notify-send to work.
. /home/anmol/.env_vars

low_notif_percentage=20
critical_notif_percentage=15
critical_action_percentage=10


if [ "${level}" -le ${critical_action_percentage} ]; then
  # sudo is required when running from cron
  sudo systemctl hibernate
  exit 0
fi

if [ "${level}" -le ${critical_notif_percentage} ]; then
  notify-send -i '/usr/share/icons/gnome/256x256/status/battery-caution.png' "Battery critical: ${level}%"
  exit 0
fi

if [ "${level}" -le ${low_notif_percentage} ]; then
  notify-send -i '/usr/share/icons/gnome/256x256/status/battery-low.png' "Battery low: $level%"
  exit 0
fi

Las variables de entorno necesarias para notify-sendtrabajar se crean con este script :

#!/usr/bin/env bash

# Create a new file containing the values of the environment variables
# required for cron scripts to work.
# This script is supposed to be scheduled to run at startup.

env_vars_path="$HOME/.env_vars"

rm -f "${env_vars_path}"
touch "${env_vars_path}"
chmod 600 "${env_vars_path}"

# Array of the environment variables.
env_vars=("DBUS_SESSION_BUS_ADDRESS" "XAUTHORITY" "DISPLAY")

for env_var in "${env_vars[@]}"
do
    echo "$env_var"
    env | grep "${env_var}" >> "${env_vars_path}";
    echo "export ${env_var}" >> "${env_vars_path}";
done

Este archivo debe ejecutarse al inicio (se puede hacer usando cualquier método que elija; uso las aplicaciones de inicio integradas de Ubuntu ).

Nota: sudo systemctl hibernate puede que no funcione desde cron. Sigue esto para resolverlo.

Anmol Singh Jaggi
fuente
0

Hay muchas formas en que podría implementarse, ya que hay muchos esquemas diferentes de administración de energía implementados dependiendo de lo que haya instalado.

Este sencillo funciona para mí en Debian Jessie minimalista sin ningún entorno de escritorio, solo con un pequeño y rápido administrador de ventanas icewm. (Se recorta porque, de lo contrario, es demasiado lento, y de esta manera supera a GNOME en un hardware mucho mejor)

Específicamente, HE instalado los siguientes paquetes: acpi acpi-fakekey acpi-support acpi-support-base acpid pm-utils pero NO tengo NINGUNO de los siguientes (después de haberlos purgado): gnome * kde * systemd * uswsusp upower laptop-mode-tools juego de políticas de hibernación-1

Así que acabo de poner esto /etc/cron.d/battery_low_check(todo en una línea, dividido para facilitar la lectura):

*/5 * * * *   root  acpi --battery | 
   awk -F, '/Discharging/ { if (int($2) < 10) print }' | 
   xargs -ri acpi_fakekey 205

Es rápido, utiliza pocos recursos y no depende de otros demonios (de hecho, se ignorará si están activos; consulte /usr/share/acpi-support/policy-funcs detalles).

Lo que hace: cada 5 minutos ( */5- puede cambiar a cada minuto con solo usarlo *si lo necesita para verificar la batería con más frecuencia) sondeará el estado de la batería (" acpi --battery ") y ejecutará el comando xargs -risolo si la batería está " Descargando "(es decir, no está conectado a la CA) y el estado de la batería es inferior a 10%(" int ($ 2) <10 "; siéntase libre de ajustarlo a sus necesidades)

acpi_fakekey 205enviará por defecto el KEY_SUSPENDevento ACPI (como presionó una tecla en la computadora portátil solicitando la suspensión), que luego hará lo que generalmente hace por usted (configurado en /etc/default/acpi-support) - para mí hiberna en el disco.

Podría usar otro comando en lugar de, acpi_fakekey 205por supuesto: como hibernate(desde el paquete de hibernación) s2disko s2mem(desde el paquete uswsusp),pm-suspend-hybrid (del paquete pm-utils), etc.

Por cierto, los números clave mágicos como KEY_SUSPEND = 205 arriba se definen en /usr/share/acpi-support/key-constants(otro interesante es probablemente KEY_SLEEP = 142 )

Matija Nalis
fuente
eso parece muy lindo! pero ¿podría usarse con el temporizador systemd en lugar de cron? (ejemplo aquí ) Estoy en Solus OS donde cron está ausente.
@cipricus Supongo que sí, pero evito systemd, así que no puedo dar ejemplo. Parece que recuerdo que systemd tiene sus propios controladores de energía ACPI, por lo que si estás atascado con systemd probablemente quieras evitar chocar con eso
Matija Nalis
gracias, he encontrado una alternativa que involucra uname: github.com/jerrinfrncs/batterynotif/blob/master/…