How to shutdown automatically when AC power is not available

My old laptop has a faulty battery, which shows 100% when on AC power but when unplugged it dramatically drops to random percentages within seconds and makes the machine turn off brutally. I am afraid of damaging my SSD (and my HDD) on which Ubuntu is loaded.

I wanted to turn the PC off as soon as the AC power is not available. I searched here and found this. But I don't understand the answer for that question or atleast it is relevant to my situation.

Please explain me a way to shut down my laptop automatically as soon as the AC power is unplugged or when there is a power failure.


Solution 1:

Create a new rule in udev by opening the terminal and using:

gksu gedit /etc/udev/rules.d/50-ac-unplugged.rules

(If you are using Ubuntu 18.04 or a newer version gksu will not be available by default. In that situation please refer this question or use the above command as sudo -H gedit /etc/udev/rules.d/50-ac-unplugged.rules)

Put in the following line:

SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="/sbin/shutdown now"

Save the file and then restart udev services with:

sudo udevadm control --reload-rules

Save all your work and unplug your power supply.

Solution 2:

Introduction

The script that has been discussed in the comments was written in bash and a while back. Since then I have made a different implementation of it in Python and using several utility functions which use dbus. All the technical gibberish aside, the script below is basically modified version of that Python script.

The key parts of the process are all done in the main() function. The whole bunch of other lines of code are utility functions, so the code might look slightly intimidating, but it is really not and isn't doing anything spectacular. There's a few extra error-checks made just in case,too.

The idea of how it works is simple:

  1. It starts as soon as you log in.
  2. The first while loop in main waits till AC adapter is plugged in ( which in this particular case is a bit redundant, but just in case is still used). If AC adapter is plugged in we go to next step
  3. Second while loop waits till AC adapter is removed. Once it is removed, we fall through to the final step.
  4. The shutdown_system() function will try to shutdown your computer. If anything goes wrong, you should see a popup with error message.

Setting up the script

First of all, obtain the script source code and save it as a file, preferably in your home folder, in ~/bin to be exact. If you don't have bin/ folder in your home directory, create one.

Save the script as shutdown_monitor.py and make sure it is executable either via right clicking on it in file manager or using chmod +x ~/bin/shutdown_monitor.py command in terminal.

Finally, let's make the script start automatically when you log in. Open Unity Dash and find Startup Applications. Add either full path to the command orpython /home/USERNAME/bin/shutdown_monitor.py` as new command.

That's it !

Script

Also available as gist on GitHub

#!/usr/bin/env python
"""
Author: Serg Kolo <[email protected]>
Date:   Nov 29 , 2016
Purpose:Script for shutting down Ubuntu system
        if AC adapter is removed 
Written for:http://askubuntu.com/q/844193/295286  
"""
import dbus
import time
import subprocess

def get_dbus_property(bus_type, obj, path, iface, prop):
    """ utility:reads properties defined on specific dbus interface"""
    if bus_type == "session":
        bus = dbus.SessionBus()
    if bus_type == "system":
        bus = dbus.SystemBus()
    proxy = bus.get_object(obj, path)
    aux = 'org.freedesktop.DBus.Properties'
    props_iface = dbus.Interface(proxy, aux)
    props = props_iface.Get(iface, prop)
    return props

def get_dbus_method(bus_type, obj, path, interface, method, arg):
    """ utility: executes dbus method on specific interface"""
    if bus_type == "session":
        bus = dbus.SessionBus()
    if bus_type == "system":
        bus = dbus.SystemBus()
    proxy = bus.get_object(obj, path)
    method = proxy.get_dbus_method(method, interface)
    if arg:
        return method(arg)
    else:
        return method()

def on_ac_power():
    adapter = get_adapter_path()
    call = ['system','org.freedesktop.UPower',adapter,
            'org.freedesktop.UPower.Device','Online'
    ]

    if get_dbus_property(*call): return True

def get_adapter_path():
    """ Finds dbus path of the ac adapter device """
    call = ['system', 'org.freedesktop.UPower',
            '/org/freedesktop/UPower','org.freedesktop.UPower',
            'EnumerateDevices',None
    ]
    devices = get_dbus_method(*call)
    for dev in devices:
        call = ['system','org.freedesktop.UPower',dev,
                'org.freedesktop.UPower.Device','Type'
        ]
        if get_dbus_property(*call) == 1:
            return dev

def shutdown_system():
    call = ['session', 'com.canonical.Unity', 
            '/com/canonical/Unity/Session', 'com.canonical.Unity.Session', 
            'Shutdown',None
    ]
    return get_dbus_method(*call)

def main():
    while not on_ac_power():
        time.sleep(1)

    while on_ac_power():
        time.sleep(1)

    try:
        shutdown_system()
    except Exception as e:
        error_msg = 'Ooops,' + __file__ + 'failed to shutdown your system.'
        error_msg = error_msg + 'Please show Serg this error so he can fix it:'
        subprocess.call(['zenity','--error',
                         '--text', error_msg + repr(e)
        ])

if __name__ == "__main__": main()

Additional notes

Please report bugs, if you find any, preferably here in the comments or on github