#!/usr/bin/python3 import signal import subprocess import sys from gi.repository import Gio, GLib VERSION = '0.10.1' def usage_main(): print('Usage:') print(' powerprofilesctl COMMAND [ARGS…]') print('') print('Commands:') print(' help Print help') print(' version Print version') print(' get Print the currently active power profile') print(' set Set the currently active power profile') print(' list List available power profiles') print(' list-holds List current power profile holds') print(' launch Launch a command while holding a power profile') print('') print('Use “powerprofilesctl help COMMAND” to get detailed help.') def usage_version(): print('Usage:') print(' powerprofilesctl version') print('') print('Print version information and exit.') def usage_get(): print('Usage:') print(' powerprofilesctl get') print('') print('Print the currently active power profile.') def usage_set(): print('Usage:') print(' powerprofilesctl set PROFILE') print('') print('Set the currently active power profile. Must be one of the ') print('available profiles.') def usage_list(): print('Usage:') print(' powerprofilesctl list') print('') print('List available power profiles.') def usage_list_holds(): print('Usage:') print(' powerprofilesctl list-holds') print('') print('List current power profile holds.') def usage_launch(): print('Usage:') print(' powerprofilesctl launch [COMMAND…]') print('') print('Launch a command while holding a power profile.') print('') print('Options:') print(' -p, --profile=PROFILE The power profile to hold') print(' -r, --reason=REASON The reason for the profile hold') print(' -i, --appid=APP-ID The application ID for the profile hold') print('') print('Launch the command while holding a power profile, either performance, ') print('or power-saver. By default, the profile hold is for the performance ') print('profile, but it might not be available on all systems. See the list ') print('command for a list of available profiles.') def usage(_command=None): if not _command: usage_main() elif _command == 'get': usage_get() elif _command == 'set': usage_set() elif _command == 'list': usage_list() elif _command == 'list-holds': usage_list_holds() elif _command == 'launch': usage_launch() elif _command == 'version': usage_version() else: usage_main() def version(): print (VERSION) def get_proxy(): try: bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, 'net.hadess.PowerProfiles', '/net/hadess/PowerProfiles', 'org.freedesktop.DBus.Properties', None) except: raise SystemError return proxy def _get(): proxy = get_proxy() profile = proxy.Get('(ss)', 'net.hadess.PowerProfiles', 'ActiveProfile') print(profile) def _set(profile): proxy = get_proxy() proxy.Set('(ssv)', 'net.hadess.PowerProfiles', 'ActiveProfile', GLib.Variant.new_string(profile)) def get_profiles_property(prop): try: proxy = get_proxy() except: raise SystemError profiles = None try: profiles = proxy.Get('(ss)', 'net.hadess.PowerProfiles', prop) except: raise ReferenceError else: return profiles def _list(): try: profiles = get_profiles_property('Profiles') reason = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'PerformanceDegraded') degraded = (reason != '') active = get_proxy().Get('(ss)', 'net.hadess.PowerProfiles', 'ActiveProfile') except: print("Couldn\'t get Profiles: ", sys.exc_info()[0]) raise SystemError else: index = 0 for profile in reversed(profiles): if index > 0: print('') marker = '*' if profile['Profile'] == active else ' ' print(f'{marker} {profile["Profile"]}:') print(' Driver: ', profile['Driver']) if profile['Profile'] == 'performance': print(' Degraded: ', f'yes ({reason})' if degraded else 'no') index += 1 def _list_holds(): try: holds = get_profiles_property('ActiveProfileHolds') except: # print("Couldn\'t get ActiveProfileHolds: ", sys.exc_info()[0]) raise SystemError else: index = 0 for hold in holds: if index > 0: print('') print('Hold:') print(' Profile: ', hold['Profile']) print(' Application ID: ', hold['ApplicationId']) print(' Reason: ', hold['Reason']) index += 1 def _launch(args, profile, appid, reason): try: bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) proxy = Gio.DBusProxy.new_sync(bus, Gio.DBusProxyFlags.NONE, None, 'net.hadess.PowerProfiles', '/net/hadess/PowerProfiles', 'net.hadess.PowerProfiles', None) except: raise SystemError cookie = proxy.HoldProfile('(sss)', profile, reason, appid) # Kill child when we go away def receive_signal(_signum, _stack): launched_app.terminate() signal.signal(signal.SIGTERM, receive_signal) # print (f'Got {cookie} for {profile} hold') with subprocess.Popen(args) as launched_app: launched_app.wait() proxy.ReleaseProfile('(u)', cookie) def main(): # pylint: disable=too-many-branches, disable=too-many-statements args = None if len(sys.argv) == 1: command = 'list' elif len(sys.argv) >= 2: command = sys.argv[1] if command == '--help': command = 'help' if command == '--version': command = 'version' else: args = sys.argv[2:] if command == 'help': if len(args) > 0: usage(args[0]) else: usage(None) elif command == 'version': version() elif command == 'get': _get() elif command == 'set': if len(args) != 1: usage_set() sys.exit(1) _set(args[0]) elif command == 'list': _list() elif command == 'list-holds': _list_holds() elif command == 'launch': if len(args) == 0: sys.exit(0) profile = None reason = None appid = None while True: if args[0] == '--': args = args[1:] break if args[0][:9] == '--profile' or args[0] == '-p': if args[0][:10] == '--profile=': args = args[0].split('=') + args[1:] profile = args[1] args = args[2:] continue if args[0][:8] == '--reason' or args[0] == '-r': if args[0][:9] == '--reason=': args = args[0].split('=') + args[1:] reason = args[1] args = args[2:] continue if args[0][:7] == '--appid' or args[0] == '-i': if args[0][:8] == '--appid=': args = args[0].split('=') + args[1:] appid = args[1] args = args[2:] continue break if len(args) < 1: sys.exit(0) if not appid: appid = args[0] if not reason: reason = 'Running ' + appid if not profile: profile = 'performance' _launch(args, profile, appid, reason) if __name__ == '__main__': main()