You are not logged in.
Is there a way to make laptop hibernate automatically when battery falls under, say, 3% capacity WITHOUT laptop-mode or pm-utils?
So, basically, I need to run 'hibernate' command with root privileges when battery state is X or lower.
How would I go about it?
Offline
Assuming you have a 'hibernate' command installed, you could use acpid to do that.
Offline
There's a handy tool found here that could be what you're looking for (though you'll have to change the command used to hibernate).
Offline
@alienated_humour,
So, this creates a python daemon regularly checking on battery status? This could work but I would like to avoid running extra processes if possible.
@tomk,
Can you elaborate on how exactly could it be done? I checked acpid wiki, but it mentions nothing about battery.
Offline
I found a solution here: http://archive09.linux.com/feature/114220.html
/etc/acpi/actions/battery.sh
#!/bin/sh
if grep -q on-line /proc/acpi/ac_adapter/AC/state; then
exit 0
fi
BAT_DIR=/proc/acpi/battery/BAT0
FULL_BAT=`grep 'last full capacity' ${BAT_DIR}/info | awk '{ print $4 }'`
CUR_BAT=`grep 'remaining capacity' ${BAT_DIR}/state | awk '{ print $3 }'`
AVG=`expr $(expr ${CUR_BAT} \* 100) / ${FULL_BAT}`
if [ "$AVG" -le "4" ]; then
/usr/local/sbin/suspend.sh hibernate
fi
The problem is the article is quite old and the only things I have in /proc/acpi/ are:
$ ls -R
.:
button ibm wakeup./button:
lid./button/lid:
LID./button/lid/LID:
state./ibm:
beep bluetooth cmos driver fan hotkey led light thermal video volume
Offline
So you have to modify it to use they /sys directory. So start perusing /sys and find the relevant info. I think (not certain, from memory only) it can be foudn at /sys/class/power_supply/BAT0. You could also simply use the acpi command (not acpid) with some piping to cut to determine the remaining percentage.
$ acpi | cut -d, -f2
then use that info to rework the script. I am unsure how you think that running a cron job with that script is any better than having a service intermittently poll for the same info. Seems to me that either way would be a more or less equal draw on power and system resources.
Offline
I need acpid for other things, too, so I think I will go the daemon way.
Anyway, I found the missing files and I modified the script, but it does not work and I am not sure why. Here it is:
#!/bin/sh
if grep -q 1 /sys/class/power_supply/AC/online; then
exit 0
fi
AVG=`cat /sys/devices/platform/smapi/BAT0/remaining_percent`
if [ "$AVG" -le "92" ]; then
hibernate
fi
I put % at 92 so that I don't have to wait to test it.
Offline
I wasn't saying to do away with acpid, but if you just throw an acpi into your command prompt it gives you battery info. The pipe to cut give you just the percentage. But you figured it out with /sys, so good job.
Offline
I'm using this in my handler.sh:
#!/bin/sh
STATE=`cat /sys/class/power_supply/BAT0/status`
NOW=`cat /sys/class/power_supply/BAT0/energy_now`
FULL=`cat /sys/class/power_supply/BAT0/energy_full`
FLOAT=`awk "BEGIN { print $NOW/$FULL*100 }" ;`
INT=${FLOAT/\.*}
set $*
case "$1" in
battery)
if [ "$STATE" = "Discharging" ] && [ $INT -le 3 ]; then
#dbus-send --system --print-reply --dest="org.freedesktop.UPower" /org/freedesktop/UPower org.freedesktop.UPower.Hibernate
systemctl hibernate
fi
;;
It works because my laptop periodically, when on battery, sends an event that is used to check if the battery is below 3%.
Offline
I tried putting it in /etc/acpi/actions/battery.sh but it does not work.
#!/bin/sh
STATE=`cat /sys/class/power_supply/AC/online`
FULL_BAT=`cat /sys/devices/platform/smapi/BAT0/energy_full`
CUR_BAT=`cat /sys/devices/platform/smapi/BAT0/energy_now`
AVG=`expr $(expr ${CUR_BAT} \* 100) / ${FULL_BAT}`
if [ "$STATE" = "0" ] && [ $AVG -le 97 ]; then
#dbus-send --system --print-reply --dest="org.freedesktop.UPower" /org/freedesktop/UPower org.freedesktop.UPower.Hibernate
hibernate
fi
I don't think I need to reboot before it can take action, right?
My handler.sh is already full with some stuff so I am not sure how to modify it without screwing it up:
#!/bin/sh
# Default acpi script that takes an entry for all actions
minspeed=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq`
maxspeed=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq`
setspeed="/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed"
set $*
case "$1" in
button/power)
case "$2" in
PBTN|PWRF)
logger "PowerButton pressed: $2"
poweroff
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
button/sleep)
case "$2" in
SLPB|SBTN)
echo -n mem >/sys/power/state
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
ac_adapter)
case "$2" in
AC|ACAD|ADP0)
case "$4" in
00000000)
echo -n $minspeed >$setspeed
#/etc/laptop-mode/laptop-mode start
;;
00000001)
echo -n $maxspeed >$setspeed
#/etc/laptop-mode/laptop-mode stop
;;
esac
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
battery)
case "$2" in
BAT0)
case "$4" in
00000000)
logger 'Battery online'
;;
00000001)
logger 'Battery offline'
;;
esac
;;
CPU0)
;;
*) logger "ACPI action undefined: $2" ;;
esac
;;
button/lid)
case "$3" in
close)
logger 'LID closed'
;;
open)
logger 'LID opened'
;;
*)
logger "ACPI action undefined: $3"
;;
esac
;;
*)
logger "ACPI group/action undefined: $1 / $2"
;;
esac
# vim:set ts=4 sw=4 ft=sh et:
Offline
I'm lost. I put that in my handler.sh, but it still doesn't work:
#!/bin/sh
# Default acpi script that takes an entry for all actions
#minspeed=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq`
#maxspeed=`cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq`
#setspeed="/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed"
STATE=`cat /sys/class/power_supply/AC/online`
FULL_BAT=`cat /sys/devices/platform/smapi/BAT0/energy_full`
CUR_BAT=`cat /sys/devices/platform/smapi/BAT0/energy_now`
AVG=`expr $(expr ${CUR_BAT} \* 100) / ${FULL_BAT}`
set $*
case "$1" in
battery)
if [ "$STATE" = "0" ] && [ $AVG -le 75 ]; then
dbus-send --system --print-reply --dest="org.freedesktop.UPower" /org/freedesktop/UPower org.freedesktop.UPower.Suspend
fi
;;
button/power)
case "$2" in
PBTN|PWRF)
logger "PowerButton pressed: $2"
poweroff
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
button/sleep)
case "$2" in
SLPB|SBTN)
echo -n mem >/sys/power/state
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
ac_adapter)
case "$2" in
AC|ACAD|ADP0)
case "$4" in
00000000)
echo -n $minspeed >$setspeed
#/etc/laptop-mode/laptop-mode start
;;
00000001)
echo -n $maxspeed >$setspeed
#/etc/laptop-mode/laptop-mode stop
;;
esac
;;
*)
logger "ACPI action undefined: $2"
;;
esac
;;
battery)
case "$2" in
BAT0)
case "$4" in
00000000)
logger 'Battery online'
;;
00000001)
logger 'Battery offline'
;;
esac
;;
CPU0)
;;
*) logger "ACPI action undefined: $2" ;;
esac
;;
button/lid)
case "$3" in
close)
logger 'LID closed'
;;
open)
logger 'LID opened'
;;
*)
logger "ACPI action undefined: $3"
;;
esac
;;
*)
logger "ACPI group/action undefined: $1 / $2"
;;
esac
# vim:set ts=4 sw=4 ft=sh et:
Offline
I'm lost. I put that in my handler.sh, but it still doesn't work:
It doesn't work because there is no event that triggers it.
Launch acpi_listen when on battery for 5 minutes and then paste the output here.
Offline
$ acpi_listen
ac_adapter ACPI0003:00 00000080 00000000
processor LNXCPU:00 00000081 00000000
processor LNXCPU:01 00000081 00000000
thermal_zone LNXTHERM:00 00000081 00000000
battery PNP0C0A:00 00000080 00000001
video/brightnessup BRTUP 00000086 00000000 K
video/brightnessup BRTUP 00000086 00000000
processor LNXCPU:00 00000080 00000004
processor LNXCPU:01 00000080 00000004
video/brightnessup BRTUP 00000086 00000000
video/brightnessup BRTUP 00000086 00000000 K
video/brightnessup BRTUP 00000086 00000000
video/brightnessup BRTUP 00000086 00000000 K
video/brightnessup BRTUP 00000086 00000000 K
Offline
$ acpi_listen ac_adapter ACPI0003:00 00000080 00000000 processor LNXCPU:00 00000081 00000000 processor LNXCPU:01 00000081 00000000 thermal_zone LNXTHERM:00 00000081 00000000 battery PNP0C0A:00 00000080 00000001 video/brightnessup BRTUP 00000086 00000000 K video/brightnessup BRTUP 00000086 00000000 processor LNXCPU:00 00000080 00000004 processor LNXCPU:01 00000080 00000004 video/brightnessup BRTUP 00000086 00000000 video/brightnessup BRTUP 00000086 00000000 K video/brightnessup BRTUP 00000086 00000000 video/brightnessup BRTUP 00000086 00000000 K video/brightnessup BRTUP 00000086 00000000 K
You launched acpi_listen and then remove the ac adapter right? As far as I can read, your laptop doesn't trigger any "periodical" event.
Try pasting the output of at least 10 minutes of acpi_listen.
Offline
Here's the log started immidiatelly before disconnecting AC. It run about 10 minutes:
$ acpi_listen
ac_adapter ACPI0003:00 00000080 00000000
processor LNXCPU:00 00000081 00000000
processor LNXCPU:01 00000081 00000000
thermal_zone LNXTHERM:00 00000081 00000000
battery PNP0C0A:00 00000080 00000001
battery PNP0C0A:00 00000080 00000001
processor LNXCPU:00 00000080 00000004
processor LNXCPU:01 00000080 00000004
processor LNXCPU:00 00000080 00000000
processor LNXCPU:01 00000080 00000000
Last edited by Lockheed (2012-08-29 16:48:34)
Offline
Here's the log started immidiatelly before disconnecting AC. It run about 10 minutes:
$ acpi_listen ac_adapter ACPI0003:00 00000080 00000000 processor LNXCPU:00 00000081 00000000 processor LNXCPU:01 00000081 00000000 thermal_zone LNXTHERM:00 00000081 00000000 battery PNP0C0A:00 00000080 00000001 battery PNP0C0A:00 00000080 00000001 processor LNXCPU:00 00000080 00000004 processor LNXCPU:01 00000080 00000004 processor LNXCPU:00 00000080 00000000 processor LNXCPU:01 00000080 00000000
It's like I said before: there no way to let acpid hibernate your system when battery is low.
Your laptop doesn't trigger any "useful" event.
You need a daemon that checks every n seconds (minute) the battery status and, in case, hibernate your system.
This daemon could simply be written in bash, for example.
Offline
Ha, bummer then.
But I tried this, and it works:
#!/bin/sh
if grep -q 1 /sys/class/power_supply/AC/online; then
exit 0
fi
AVG=`cat /sys/devices/platform/smapi/BAT0/remaining_percent`
if [ "$AVG" -le "92" ]; then
hibernate
fi
But how to I make a daemon out of it?
Offline
Ha, bummer then.
But I tried this, and it works:
#!/bin/sh if grep -q 1 /sys/class/power_supply/AC/online; then exit 0 fi AVG=`cat /sys/devices/platform/smapi/BAT0/remaining_percent` if [ "$AVG" -le "92" ]; then hibernate fi
But how to I make a daemon out of it?
Here is the first result looking for "bash daemon" on google http://blog.apokalyptik.com/2008/05/09/ … ripts-get/
Offline
I haven't found that myself. Looks promissing, but am I supposed to replace "checkforterm" with the script for my battery?
Offline
I haven't found that myself. Looks promissing, but am I supposed to replace "checkforterm" with the script for my battery?
Exactly and, if you want, how frequent the daemon should check the battery (sleep 1).
Offline
sleep 1 = 1 second, right?
So, the final result should be something like that?
#!/bin/sh
function payload() {
while [ true ]; do
if grep -q 1 /sys/class/power_supply/AC/online; then
exit 0
fi
AVG=`cat /sys/devices/platform/smapi/BAT0/remaining_percent`
if [ "$AVG" -le "92" ]; then
hibernate
fi
date
sleep 1
done
}
source path/to/daemon-functions.sh
Where should I place the finished daemon file? I imagine it supposed to be in some specific location if it's to be run from rc.conf
Offline
How complicated all this is.
You could just install "acpi" package and then add this to /etc/acpi/handler.sh:
battery)
if [[ $(acpi|grep "Discharging, 1%") ]]; then
systemctl hibernate
fi
;;
This works perfectly for me. You can replace 1% for any remaining charge level you like.
Offline
You are lucky enough to have a laptop which reports battery level to acpi. I am not that lucky.
Offline
Find power supplies in /sys/class/power_supply, inspect their parameters with udevadm info --attribute-walk and write udev rule then.
This for example:
## SLEEP IF BATTERY IS LOW
SUBSYSTEM=="power_supply", ATTR{type}=="Battery", ATTR{status}=="Discharging", ATTR{capacity}=="1", RUN+="/usr/bin/systemctl hibernate"
That way you even don't need acpid at all.
Last edited by eruditorum (2013-02-13 11:26:59)
Offline
Right, so these are the attributes for /sys/class/power_supply/AC
looking at device '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00/PNP0C09:00/ACPI0003:00/power_supply/AC':
KERNEL=="AC"
SUBSYSTEM=="power_supply"
DRIVER==""
ATTR{type}=="Mains"
ATTR{online}=="1"
looking at parent device '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00/PNP0C09:00/ACPI0003:00':
KERNELS=="ACPI0003:00"
SUBSYSTEMS=="acpi"
DRIVERS=="ac"
ATTRS{hid}=="ACPI0003"
ATTRS{path}=="\_SB_.PCI0.LPC_.EC__.AC__"
looking at parent device '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00/PNP0C09:00':
KERNELS=="PNP0C09:00"
SUBSYSTEMS=="acpi"
DRIVERS=="ec"
ATTRS{hid}=="PNP0C09"
ATTRS{path}=="\_SB_.PCI0.LPC_.EC__"
looking at parent device '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:00':
KERNELS=="device:00"
SUBSYSTEMS=="acpi"
DRIVERS==""
ATTRS{path}=="\_SB_.PCI0.LPC_"
looking at parent device '/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00':
KERNELS=="PNP0A08:00"
SUBSYSTEMS=="acpi"
DRIVERS=="pci_root"
ATTRS{hid}=="PNP0A08"
ATTRS{path}=="\_SB_.PCI0"
looking at parent device '/devices/LNXSYSTM:00/LNXSYBUS:00':
KERNELS=="LNXSYBUS:00"
SUBSYSTEMS=="acpi"
DRIVERS==""
ATTRS{hid}=="LNXSYBUS"
ATTRS{path}=="\_SB_"
looking at parent device '/devices/LNXSYSTM:00':
KERNELS=="LNXSYSTM:00"
SUBSYSTEMS=="acpi"
DRIVERS==""
ATTRS{hid}=="LNXSYSTM"
ATTRS{path}=="\"
The thing is that "ATTR{type}=="Mains" stays as "Mains even on battery, only ATTR{online}=="1" changes to "0".
How would a dev rule look in this case?
Also, I noticed your rule would kick in only if the battery is at 1%, so if I change it to 3%, it will kick in only if the battery value is at 3, but not below.
Last edited by Lockheed (2013-02-13 12:20:35)
Offline