#!/bin/bash SLEEP_SECS_INIT=30 SLEEP_SECS=$SLEEP_SECS_INIT SLEEP_STEP=180 SLEEP_MAX_SECS=600 WAKE_SECS=35 LOCK_DIR="/tmp/sleepwalk" CPU_STATE=1 THRESHOLD="15.00" set_cpu_offline() { if [ $CPU_STATE -eq 1 ]; then echo "Powering off all CPU cores except cpu0" for i in $(seq 2); do echo 0 > /sys/devices/system/cpu/cpu$i/online done CPU_STATE=0 fi } set_cpu_online() { SLEEP_SECS=$SLEEP_SECS_INIT if [ $CPU_STATE -eq 0 ]; then echo "Powering on all CPU cores except cpu0" for i in $(seq 2); do echo 1 > /sys/devices/system/cpu/cpu$i/online done CPU_STATE=1 fi } start_deep_sleep() { led_sleep if [ $((SLEEP_SECS + $SLEEP_STEP)) -lt $SLEEP_MAX_SECS ]; then SLEEP_SECS=$((SLEEP_SECS + SLEEP_STEP)) else SLEEP_SECS=$SLEEP_MAX_SECS fi sleep 5 # Give some extra time for pending output data if [ can_suspend ]; then WIFI_WAS_ON=$(nmcli r wifi) nmcli r wifi off echo mem > /sys/power/state 2>/dev/null fi return $? } wait_for_notifications() { secs=$WAKE_SECS is_led_on=0 while [ $secs -ge 0 ]; do sleep 1 if [ $is_led_on == 0 ]; then led_wake is_led_on=1 else led_disable is_led_on=0 fi if is_screen_on; then if is_charging; then cpufreq-set -g conservative else cpufreq-set -g powersave fi set_cpu_online break fi set_cpu_offline secs=$(($secs-1)) done led_disable } schedule_wake_time() { echo "Sleeping for ${SLEEP_SECS}s" rtcwake -m no --date "+${SLEEP_SECS}s" 2>&1 >/dev/null } reset_wake_time() { led_wake > /sys/class/rtc/rtc0/wakealarm } is_charging() { [ "$(cat /sys/class/power_supply/axp20x-usb/online)" = "1" ] } is_inhibitor_active() { # INHIBITORS=$(sudo -u "$(cat /proc/$(pidof -s gnome-session-ctl)/environ | grep -z USER | sed 's,\x0,,;s,USER=,,')" DBUS_SESSION_BUS_ADDRESS=$(cat /proc/$(pidof -s gnome-session-ctl)/environ | grep -z DBUS_SESSION_BUS_ADDRESS | sed "s,\x0,,;s,DBUS_SESSION_BUS_ADDRESS=,,") gnome-session-inhibit --list) # printf "%s\n" "$INHIBITORS" INHIBITORS=$(systemd-inhibit --list --what=sleep --mode=block --no-legend \ | grep -e 'sleep') [ -n "$INHIBITORS" ] # if [ "$INHIBITORS" = "No inhibitors" ]; then # return 1 # fi # return 0 } is_screen_on() { return "$(cat /sys/class/backlight/backlight/bl_power)" } network_activity() { for n in $(seq 3) do for i in wwan0 wlan0 do A=$(ifstat -i $i 1 1 | tail -n1) RX=$(echo $A | awk '{print $1}') TX=$(echo $A | awk '{print $2}') if [ "$RX" = "n/a" ] || [ "$TX" = "n/a" ] then return 1 fi for v in $RX $TX do C=$(echo $v $THRESHOLD | awk '{if ($1 > $2) print "y"; else print "n"}') if [ "$C" = "y" ] then return 0 fi done done done return 1 } can_suspend() { ! ( is_inhibitor_active || is_screen_on || is_charging || network_activity ) } led_sleep() { : # echo 1 > /sys/class/leds/green\:indicator/brightness # echo 0 > /sys/class/leds/red\:indicator/brightness # echo 0 > /sys/class/leds/blue\:indicator/brightness } led_wake() { : # echo 0 > /sys/class/leds/green\:indicator/brightness # echo 1 > /sys/class/leds/red\:indicator/brightness # echo 0 > /sys/class/leds/blue\:indicator/brightness } led_disable() { : # echo 0 > /sys/class/leds/green\:indicator/brightness # echo 0 > /sys/class/leds/red\:indicator/brightness # echo 0 > /sys/class/leds/blue\:indicator/brightness } if [ $EUID -ne 0 ]; then echo "Needs root" >&2 exit 1 fi if [ -z "$1" ] || [ "$1" == "-h" ] || [ "$1" == "--help" ]; then cat <<-EOF >&2 Usage: sleepwalk [start/stop] Commands: start Start sleep cycle stop Stop sleep cycle EOF exit fi if [ "$1" == "start" ]; then if [ -d "$LOCK_DIR" ] || ! mkdir "$LOCK_DIR"; then echo "sleepwalk is already running - run 'sleepwalk stop' to stop it" >&2 exit 1 fi trap "$0 stop" EXIT while [ -d "$LOCK_DIR" ]; do while ! can_suspend; do set_cpu_online if is_charging && is_screen_on; then cpufreq-set -g conservative else cpufreq-set -g powersave fi sleep 10 done reset_wake_time schedule_wake_time while ! start_deep_sleep; do reset_wake_time echo "Failed going to sleep, try again in $WAKE_SECS seconds ..." >&2 wait_for_notifications schedule_wake_time if ! can_suspend; then break fi done if [ "$WIFI_WAS_ON" = "enabled" ]; then nmcli r wifi on fi wait_for_notifications done elif [ "$1" == "stop" ]; then reset_wake_time led_disable rmdir "$LOCK_DIR" 2>/dev/null else echo "Unknown command: $1" >&2 exit 1 fi