4

Is there any way?

How to script in /usr/lib/pm-utils/sleep.d to check ${downspeed eth0}, ${upspeed wlan0}, ${upspeed eth0} and ${downspeed wlan0} and to set the system not to suspend while a download in progress but only turn screen off?

My OS is Ubuntu 15.04.

  • 1
    When your system is suspended, it is not working, but keeping its current state stored in RAM (faster than on HDD, but vanishes when power is off). It can't work in that state, it is just listening every now and then for any wakeup events. – Byte Commander Jan 22 '15 at 12:08
  • 1
    @ ByteCommander. Thanks. So is there any way to make the system not to suspend while a download in progress? – dhiya Jan 22 '15 at 12:16
  • 3
    Easiest way would be to manually disable suspending in your system configuration panel when you want to do a big download, and turn it back on afterwards. No idea at the moment on how to automatize this... – Byte Commander Jan 22 '15 at 12:22
  • 1
    Not sure if the question is still "open", but couldn't resist posting an answer :) – Jacob Vlijm Aug 11 '15 at 19:12
  • Just one question, where do I save the file To? – user508952 Feb 19 '16 at 21:01
7

Inspired by @ByteCommanders second comment, the script below does what you describe: assuming the downloads folder is the directory you download into, it disables suspend during download, and subsequently waits for five minutes (arbitrary to set) before re- enabling suspend, to make sure the download is really finished.
You can set any other directory to be watched as download folder.

How it works

In a loop (once per 20 seconds), the script checks the size of the targeted folder with the command:

du -ks ~/Downloads

The script compares each check with the last one, to check for download activity (increase of size). If there is no activity for more then five minutes (but you can set any other "wait" time), the script assumes no download is going in, and "normal" suspend is (re-) enabled.

The other way around: if the script sees increasing size of the ~/Downloadsdirectory, it disables suspend, until no activity was detected for more then five minutes.

Low on juice

The script's commands are extremely low on resources. Since the cycle runs only once per 20 seconds (as it is), The processor load is practically none.

The script

#!/usr/bin/env python3
import subprocess
import time

#--- set suspend time below (seconds)
suspend_wait = 300
#---

#--- you can change values below, but I'd leave them as they are
speed_limit = 0      # set a minimum speed (kb/sec) to be considered a download activity
looptime = 20        # time interval between checks
download_wait = 300  # time (seconds) to wait after the last download activity before suspend is re- activated
#---

t = 0
key = ["gsettings", "get", "org.gnome.settings-daemon.plugins.power", "sleep-inactive-ac-timeout", "set"]

set_suspend = key[0]+" "+key[-1]+" "+(" ").join(key[2:4])
get_suspend = (" ").join(key[0:4])

def get_size():
    return int(subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split()[0])

def get(cmd):
    return subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")

check_1 = int(get("du -ks ~/Downloads").split()[0])

while True:
    time.sleep(looptime)
    try:
        check_2 = int(get("du -ks ~/Downloads").split()[0])
    except subprocess.CalledProcessError:
        pass
    speed = int((check_2 - check_1)/looptime)
    # check current suspend setting
    suspend = get(get_suspend).strip()
    if speed > speed_limit:
        # check/set disable suspend if necessary
        if suspend != "0":
            subprocess.Popen(["/bin/bash", "-c", set_suspend+" 0"])
        t = 0
    else:
        if all([t > download_wait/looptime, suspend != str(download_wait)]):
            # check/enable suspend if necessary
            subprocess.Popen(["/bin/bash", "-c", set_suspend+" "+str(suspend_wait)])
    check_1 = check_2
    t = t+1

How to use

  1. Copy the script below into an empty file, save it as no_suspend.py
  2. In the head section of the script, set the desired "normal" suspend time (since the script will re- enable suspend):

    #--- set suspend time below (seconds)
    suspend_wait = 300
    #---
    
  3. If you want, you can set other values:

    #--- you can change values below, but I'd leave them as they are
    speed_limit = 0      # set a minimum speed (kb/sec) to be considered a download activity
    looptime = 20        # time interval between checks
    download_wait = 300  # time (seconds) to wait after the last download activity before suspend is re- activated
    #---
    
  4. Test- run the script with the command:

    python3 /path/to/no_suspend.py
    
  5. If all works fine, add it to your startup applications: Dash > Startup Applications > add the command:

    python3 /path/to/no_suspend.py
    
4

UPDATE (2016-06-11): Since first writing the script underneath it has grown into a full blown program that determines when to suspend based on

  1. Network Traffic
  2. User Activity
  3. CPU load

I have published it on Launchpad here https://launchpad.net/keep.awake under a GPL license.

There is no snap package or deb yet but I'll get around to it eventually. In the meantime you can download this and uncompress it to use.

It works like a proper command.
Type --help to see a full listing of what can be done. The examples underneath are only a few:
./keepawake.py --help

To run interactively:
./keepawake.py

To run as a background service:
nohup ./keepawake.py -r > /dev/null 2>&1 &

To run as background service and set 15 min (900 sec) as the user activity idle time before it determines that the user is idle:
nohup ./keepawake.py -u 900 -r > /dev/null 2>&1 &

To run as background service and set minimum CPU load as 13%:
nohup ./keepawake.py -c 13 -r > /dev/null 2>&1 &

To run as background service and set minimum network traffic as 5KB (5120 bytes):
nohup ./keepawake.py -s 5120 -r > /dev/null 2>&1 &

To run all three settings above (network, CPU, User idle) in the one go:

nohup ./keepawake.py -s 5120 -c 13 -u 900 -r > /dev/null 2>&1 &


[ORIGINAL POST]:

Based on dhiya's response I have heavily modified the script. It handles both network traffic AND user acitvity!

I had to name it something so I named it "Keep Awake".

Give it a spin and respond back if you have any questions or comments!

Release notes...

  1. Checks both user activity and network traffic based on thresholds
  2. If either or both are above the threshold limits, the OS suspend-power settings are disabled.
  3. My first modification of the code brought it down to Python 2.7.x I had to migrate it back to 3.4.x
  4. Implemented a print that goes both to a log file and standard output using python 3 capabilities.
  5. Added many data prints to determine state. Very helpful for troubleshooting.
  6. In the If-elif-else logic, I added count down counters.
  7. Added many comments, hopefully helps those new to all of this.
  8. Have bundled a logrotate configuration which you can use to manage the output logs indefinitely forever. (See Comments in History)
  9. It is highly recommended that the logrotate cron be moved to cron.hourly
  10. Makes use of xprintidle
  11. logrotate config makes use of xz-utils
  12. Repeated performance test has shown that cpu impact is virtually negligible. Only the xz compression algorithm run by logrotate can impact a single core up to 100%. But it doesn't last long on modern cpus plus it is single threaded. So your other cores can do other work. It will only compress logs that are a couple of days old or if larger than 20MB.
  13. Once you are happy with the config, you can set it up using the distro's built-in Startup Applications launcher which will launch the program upon login to your account. Make sure you have logrotate configured with the script-configuration underneath to manage the log files. You can tweak that to your pleasure as well.
  14. 16/09/2015 - version 2.1 - Added Force Graceful Suspension logic
  15. 16/09/2015 - version 2.1 - Program is now designed to run with sudo privilege and log to /var/log/Keep.Awake/ . logrotate config will now default to multicore processing for compression and only rotate if greater than 1M. This requires xz-utils >= v5.2.0

Sample command to run in background

    $ sudo nohup python3 Keep\ Awake.v2.py > /dev/null 2>&1 &

FILENAME: "Keep\ Awake.v2.py"

    #!/usr/bin/env python3

    import subprocess
    import time
    import logging
    import datetime
    import sys


    #--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    # Keep Awake
    # version: 2.1
    #
    # PURPOSE:
    # This program will check 'network traffic' AND 'user activity' and decide whether to enable the timer to suspend or cancel it.
    #
    # METHOD:
    # The program checks the network traffic if it is greater than a certain treshold; if so it keeps the system awake; if not it checks for user activity.
    # If the user activity is idle for a time greater than a treshold then it sets the timer to suspend.
    # 
    # SAMPLE RUN COMMAND:
    # sudo nohup python3 Keep\ Awake.v2.py > /dev/null 2>&1 &
    #
    # History:
    # 2015-08-22    DanglingPointer - Modified from https://askubuntu.com/questions/576525/can-i-prevent-ubuntu-being-suspended-while-a-download-is-in-progress/661085#661085
    # 2015-09-12    DanglingPointer - Modified to version 2 and renamed "Keep Awake".  
    #                               - Version two now prints to standard output as well as a log file information useful for troubleshooting.
    #                               - Comes with a "Kee,Awake.v2.logrotate.config" file which can be used with linux logrotate.d.  It is recommended that logrotate be moved to cron.hourly.
    #                               - Upgraded coded from Python 2 to 3 using 2to3 command.
    #                               - Requires xprintidle linux package.
    # 2015-09-16    DanglingPointer - Modified to version 2.1.  Added logic to "gracefully force suspension" if automatic suspension fails to happen. This has been observed to happen when 
    #                               - the setting to require a password after suspension is enabled locking the desktop.  Keep.Awake.v2.1 will now send a dbus message to force graceful suspension.
    #                               - **NOTE 1** "Force Graceful suspension" will ONLY work in this version if the program is run as root (sudo privilege) after logging into a user account.
    #                               - **NOTE 2** "Force Graceful suspension" will NOT work if the program is sudo-run from command prompt without logging into a user desktop. (i.e. Fails when: run from CTL+ALT+F2 before login OR run from prompt after "switch user" is selected after locking desktop -> in that order)
    #--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


    # CONFIG
    suspend_wait    = 3600                              # set suspend time (seconds); In Ubuntu 14.04 it needs to be the equivalent of one of the options from the Power Settings or else it may not work.
    speed_limit     = 1024                              # set a minimum speed (bytes/second) to be considered a download activity
    looptime        = 20                                # time interval between checks (seconds)
    download_wait   = 300                               # time (seconds) to wait after the last download activity before suspend is re-activated.
    userIdle        = download_wait*1000                # user activity idle time in miliseconds before suspend is re-activated, requires xprintidle linux package
    forceGraceTime  = download_wait                     # time (seconds); in the event that the automatic suspend fails (like locked screen/user is at login screen) The system is given additional grace-time before a suspension is forced.
    logFileLocation = "/var/log/Keep.Awake/"            # Logfile location
    logFileName     = "Keep.Awake.log"                  # Logfile name

    # RATIOS
    download_waitTOlooptimeRatio    = download_wait/looptime
    suspend_waitTOlooptimeRatio     = suspend_wait/looptime
    forceGraceTimeTOlooptimeRatio   = forceGraceTime/looptime

    # STRING CONSTANTS
    key = ["gsettings", "get", "org.gnome.settings-daemon.plugins.power", "sleep-inactive-ac-timeout", "set"]
    dbusForceSuspend = ["dbus-send", "--print-reply", "--system", "--dest=org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower.Suspend"]

    # WHERE TO OUTPUT
    logging.basicConfig(level=logging.INFO, format='%(message)s')
    logger = logging.getLogger()
    logger.addHandler(logging.FileHandler(logFileLocation + logFileName, 'a'))
    print = logger.info

    # VARIALBES
    rx_speed=0                  # receive speed
    tx_speed=0                  # transmit speed
    t = 0                       # time interval interation count
    countDown = 0               # count down integer
    downLoadWaitCountDown = 0   # count down for download_wait
    graceCountDown = 0          # count down for forced grace time. See comments above for forceGraceTime CONFIG
    graceFlag = False           # flag to provide grace period
    set_suspend = key[0]+" "+key[-1]+" "+(" ").join(key[2:4])
    get_suspend = (" ").join(key[0:4])
    force_suspend = (" ").join(dbusForceSuspend)

    print ("")
    print (">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
    print ("KEEP AWAKE v2.1")
    print ("Python version: " + (sys.version))
    print ("")
    print ("Program Date-Time Start: " + datetime.datetime.now().isoformat())
    print ("Configuration Loaded...")
    print ("suspend_wait: "     + str(suspend_wait)     + " - Time in seconds for setting 'Suspend when inactive for' for the OS.")
    print ("speed_limit: "      + str(speed_limit)      + " - Minimum amount of data in bytes to qualify as activity.")
    print ("looptime: "         + str(looptime)         + " - Time interval in seconds between checks for network activity.")
    print ("download_wait: "    + str(download_wait)    + " - Time in seconds to wait after last download activity before 'suspend_wait' is applied to the OS.")
    print ("userIdle: "         + str(userIdle)         + " - Idle time in miliseconds to wait before 'suspend_wait' is applied to the OS.")
    print ("forceGraceTime: "   + str(forceGraceTime)   + " - Time in seconds; in the event that the automatic suspend fails (like locked screen/user is at login screen), the system is given additional grace-time before a suspension is forced.")
    print ("logFileLocation: "  + logFileLocation       + " - Logfile location")
    print ("logFileName: "      + logFileName           + " - Logfile name")
    print ("")
    print ("Variables Loaded...")
    print ("rx_speed: "         + str(rx_speed)         + " - Received bytes in last interval")
    print ("tx_speed: "         + str(tx_speed)         + " - Transmited bytes in last interval")
    print ("set_suspend: "      + set_suspend           + " - String used to construct Shell command to set the 'Suspend when inactive for' for the OS.")
    print ("get_suspend: "      + get_suspend           + " - String used to construct Shell command to get the 'Suspend when inactive for' setting from the OS.")
    print ("force_suspend: "    + force_suspend         + " - String used to construct Shell command to force the 'Suspend.")
    print ("\n\n")


    def get(cmd):
        return subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")

    def get_bytes(t, iface='eth0'):
        with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
            data = f.read();
        return int(data)

    if __name__ == '__main__':
        (tx_prev, rx_prev) = (0, 0)

        while(True):
            print ("Interval Loop Started:\t" + datetime.datetime.now().isoformat())

            tx = get_bytes('tx')
            rx = get_bytes('rx')

            if tx_prev > 0:
                tx_speed = tx - tx_prev


            if rx_prev > 0:
                rx_speed = rx - rx_prev

            print (str(tx_speed) + " bytes TRANSMITTED in since last interval.")
            print (str(rx_speed) + " bytes RECEIVED in since last interval.")
            print ("Starting nap for " + str(looptime) + " seconds...")

            time.sleep(looptime)

            print ("Awaking from nap:\t" + datetime.datetime.now().isoformat())

            tx_prev = tx
            rx_prev = rx

            speedrx =rx_speed/looptime

            print ("RECEIVE speed: " + str(speedrx) + " bytes/second")

            speedtx = tx_speed/looptime

            print ("TRANSMIT speed: " + str(speedtx) + " bytes/second")

            if speedtx > speedrx:
                speed = speedtx
            else:
                speed = speedrx

            print ("Highest speed selected: " + str(speed) + " bytes/second")

            suspend = get(get_suspend).strip()

            print ("Speed Limit configured: " + str(speed_limit) + " bytes/second"   )
            print ("'t'-value loop counter: " + str(t))
            print ("'download_wait'/'looptime': " + str(download_waitTOlooptimeRatio))
            print ("'suspend' value == " + suspend)

            idleTime = int(get("xprintidle"))

            print ("User activity idle time: " + str(idleTime/1000) + " seconds")
            print ("User activity idle threshold limit: " + str(userIdle/1000) + " seconds")

            if speed > speed_limit or userIdle >= idleTime:
                # check/set disable suspend if necessary
                if suspend != "0":
                    subprocess.Popen(["/bin/bash", "-c", set_suspend+" 0"])
                t = 0
                graceFlag = False
                print ("Threshold limits breached... keeping system awake...")

            elif t > download_waitTOlooptimeRatio:
                # check/enable suspend if necessary
                subprocess.Popen(["/bin/bash", "-c", set_suspend+" "+str(suspend_wait)])
                countDown = suspend_waitTOlooptimeRatio - (t - download_waitTOlooptimeRatio)

                # hack logic - if countDown goes below zero, and automatic sleep fails --> force suspend provide grace period.
                if countDown < 0 and not graceFlag:
                    graceCountDown = -(countDown - forceGraceTimeTOlooptimeRatio)
                    graceFlag = True

                    print ("FORCE SUSPEND: Expected automatic sleep did not happen. User desktop session likely locked to login screen.")
                    print ("FORCE SUSPEND: Providing grace time before forcing suspension.")
                    print ("FORCE SUSPEND: Counting-down to FORCED suspend..." + str(graceCountDown))

                    graceCountDown = graceCountDown - 1

                elif countDown < 0 and graceFlag:
                    print ("FORCE SUSPEND: Counting-down to FORCED suspend..." + str(graceCountDown))
                    graceCountDown = graceCountDown - 1

                    if graceCountDown < 0:
                        print ("FORCE SUSPEND: Force Suspending...")

                        # prime graceFlag to False in case it comes back up to the same conditions allowing a new grace time
                        graceFlag = False

                        # execute suspend command
                        subprocess.call(force_suspend, shell=True)
                else:
                    print ("Cumulative activity below threshold limits... Counting-down to suspend..." + str(countDown))

            else:
                downLoadWaitCountDown = download_waitTOlooptimeRatio - t
                print ("Zero activity... Waiting before setting suspension count down..." + str(downLoadWaitCountDown))

            t = t+1
            print ("Interval Loop End:\t" + datetime.datetime.now().isoformat())
            print ("---------------------------------------------------------")

logrotate custom config. Use "$man logrotate" for details of its use. It is very handy!... FILENAME: "Keep.Awake.v2.logrotate.config"

    /var/log/Keep.Awake/Keep.Awake.log
    {
            copytruncate
            size 1M
            rotate 30
            compress
            delaycompress
            compresscmd /usr/bin/xz
            compressext .xz
            compressoptions -9e --threads=0
            missingok
            notifempty
            extension .log
            create 664 root danglingpointer
            su root root
    }
  • Just for the record; the script never was python2, not sure what you mean by "upgraded coded from python 2 to python 3" in the head section of your script. – Jacob Vlijm Sep 12 '15 at 18:14
  • I wrote it in the release notes what had happened. I accidentally brought it down to 2.7 using 2.7 syntax. I had never shown it here. I'm a java guy and wanted to delve into python and had a legitimate need for the above setup for a backup server that needed to sleep for certain parts of the day to save energy. I was going to write it in Java but decided that it was simple enough task to try in python. – DanglingPointer Sep 13 '15 at 1:52
  • Updated program to version 2.1 - Added Force Graceful Suspension logic - Updated logrotate config to rotate based on size only and use multicore processing for xz (requires xz-utils version 5.2.x) – DanglingPointer Sep 16 '15 at 11:48
3

Based on the answer provided here, I have modified @Jacob Vlijm 's script as follows, so as to prevent Ubuntu from suspend while any upload or download in progress. You have to install xprintidle first.

#!/usr/bin/env python3
#install xprintidle first, it is a binary written in C; see  https://packages.debian.org/sid/xprintidle 
#This script is for prevent/supersede suspend while a download/upload in progress.
#Change wlan0 (wifi) to eth0 (ethernet) or use both if its applies. 
#add it to your startup applications: Dash > Startup Applications > add the command: python3 /path/to/delay_suspend.py
import subprocess
import time
time.sleep(15)
subprocess.Popen(['notify-send', "Ubuntu will supersede suspend while a download/upload in progress"])    

#--- set suspend time below (seconds)
suspend_wait = 300
#---

#--- you can change values below, but I'd leave them as they are
speed_limit = 1000      # set a minimum speed (bytes/looptime) to be considered a download activity
looptime = 20       # time interval between checks
download_wait = 300  # time (seconds) to wait after the last download activity before suspend is re- activated
#---

rx_speed=0
tx_speed=0
speed=0
idletime = 2

t = 0
key = ["gsettings", "get", "org.gnome.settings-daemon.plugins.power", "sleep-inactive-ac-timeout", "set"]

set_suspend = key[0]+" "+key[-1]+" "+(" ").join(key[2:4])
get_suspend = (" ").join(key[0:4])

def get_size():
    return int(subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8").split()[0])

def get(cmd):
    return subprocess.check_output(["/bin/bash", "-c", cmd]).decode("utf-8")



def get_bytes(t, iface='wlan0'):
    with open('/sys/class/net/' + iface + '/statistics/' + t + '_bytes', 'r') as f:
        data = f.read();
    return int(data)

if __name__ == '__main__':
    (tx_prev, rx_prev) = (0, 0)
#Rx stand for received (download) and Tx for tranferred (upload).
    while(True):
        tx = get_bytes('tx')
        rx = get_bytes('rx')

        if tx_prev > 0:
            tx_speed = tx - tx_prev


        if rx_prev > 0:
            rx_speed = rx - rx_prev


        time.sleep(looptime)

        tx_prev = tx
        rx_prev = rx

        speedrx =rx_speed/looptime
        #print('RX: ', rx_speed, 'xxxxx')
        speedtx = tx_speed/looptime
        if speedtx > speedrx:
            speed = speedtx
        else:
            speed = speedrx


    # check current suspend setting
        suspend = get(get_suspend).strip()
        idletime = float(subprocess.check_output('xprintidle').strip())
        idletime=idletime/1000
        if speed > speed_limit:
            # check/set disable suspend if necessary
            if suspend != "0":
                subprocess.Popen(["/bin/bash", "-c", set_suspend+" 0"])

            if idletime >  download_wait-2*looptime:
                subprocess.Popen(['notify-send', "Postponed suspend due to active net traffic"]) 
                    #subprocess.Popen(['notify-send', str(speed) +"Postponed suspend for completion of active upload/download"+ str(speed_limit)])  
            t = 0
        else:
            if all([t > download_wait/looptime, suspend != str(download_wait)]):
                # check/enable suspend if necessary
                subprocess.Popen(["/bin/bash", "-c", set_suspend+" "+str(suspend_wait)])

        t = t+1
        #print(idletime)
        #print(speed)
        #print(speed_limit)        
        #subprocess.Popen(['notify-send', str(idletime)])
        #print('speed:', speed)
        #print('speed limit:', speed_limit)
  • The question only mentions download. – Jacob Vlijm Aug 14 '15 at 6:06
  • @JacobVlijm Yes, the question only mention about download. You wonderful script has help me solve the problem. Thankyou very much. I just modified your script to check the upload/download speed instead of checking download folder (for me, some downloads like updates are saved into another folder) Once again thanks for your script. – dhiya Aug 14 '15 at 15:39
  • No problem, glad it works :) – Jacob Vlijm Aug 14 '15 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.