Skip to content

Instantly share code, notes, and snippets.

@plredmond
Created December 11, 2020 21:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plredmond/96473cdcdff5e7b6d0cf497dab17a102 to your computer and use it in GitHub Desktop.
Save plredmond/96473cdcdff5e7b6d0cf497dab17a102 to your computer and use it in GitHub Desktop.
easily adjust sources of wakeup from sleep in linux using a list of devices to enable/disable and an allow-list
#!/usr/bin/env python3
import sys
import csv,io
import argparse
import collections
class Wakeups:
status_prop = 'Status'
def __init__(self, path):
self.path = path
def parse(self):
with open(self.path, mode='r') as fd:
raw_lines = fd.readlines()
first, rest = raw_lines[0], raw_lines[1:] # uncons the header row
rest = list(map(str.split, rest)) # split each row into columns
cols = len(rest[0])
assert all(cols == len(row) for row in rest), 'all rows have the same number of columns'
headers = collections.OrderedDict((key,index) for index, key in enumerate(first.strip().split(maxsplit=cols - 1))) # split header into correct number of columns
devices = collections.OrderedDict((row[headers['Device']],row) for row in rest)
return headers, devices
@property
def properties(self):
headers, _ = self.parse()
return list(headers.keys())
@property
def devices(self):
_, devices = self.parse()
return list(devices.keys())
def lookup(self, device, prop):
headers, devices = self.parse()
device_row = devices[device]
prop_offset = headers[prop]
return device_row[prop_offset]
def is_enabled(self, device):
return 'enabled' in self.lookup(device, self.status_prop).lower()
def is_disabled(self, device):
return 'disabled' in self.lookup(device, self.status_prop).lower()
def toggle(self, device):
assert device in self.devices, 'can only toggle existing devices'
with open(self.path, mode='w') as fd:
fd.write(device)
def enable(self, device):
self.is_disabled(device) and self.toggle(device)
def disable(self, device):
self.is_enabled(device) and self.toggle(device)
def pprint(self):
headers, devices = self.parse()
strfd = io.StringIO()
formatter = csv.DictWriter(strfd, fieldnames=headers.keys(), dialect=csv.excel_tab)
formatter.writeheader()
for dev in devices.values():
formatter.writerow(collections.OrderedDict(zip(headers.keys(), dev)))
return strfd.getvalue()
def parse(argv):
ap = argparse.ArgumentParser(description='set kernel wakeup devices to a known state')
default = dict(wakeup='/proc/acpi/wakeup')
ap.add_argument('-w', '--wakeup', metavar='PATH', default=default['wakeup'], help=f'path to kernel wakeup file (default: {default["wakeup"]})')
ap.add_argument('-e', '--enable', nargs='+', metavar='DEV', default=list(), help='enable each device listed')
ap.add_argument('-d', '--disable', nargs='+', metavar='DEV', default=list(), help='disable each device listed')
ap.add_argument('--allow-list', nargs='+', metavar='DEV', default=list(), help='disable each device NOT listed (default: all devices except those in -d/--disable)', dest='allow')
return ap.parse_args(argv)
def main(opts):
wakeups = Wakeups(opts.wakeup)
all_devices = set(wakeups.devices)
opts.enable = set(opts.enable)
opts.disable = set(opts.disable)
opts.allow = set(opts.allow) or all_devices - opts.disable
print(opts)
# check error conditions
assert opts.enable.isdisjoint(opts.disable), f'devices in both --enable and --disable: {opts.enable & opts.disable}'
assert opts.allow.isdisjoint(opts.disable), f'devices in both --allow-list and --disable: {opts.allow & opts.disable}'
assert opts.enable <= opts.allow, f'devices in --enable but not in --allow-list: {opts.enable - opts.allow}'
# check more error conditions
assert opts.enable <= all_devices, f'devices in --enable do not exist: {opts.enable - set(wakeups.devices)}'
assert opts.disable <= all_devices, f'devices in --disable do not exist: {opts.disable - set(wakeups.devices)}'
# do things
for dev in opts.enable:
wakeups.enable(dev)
for dev in opts.disable | (all_devices - opts.allow):
wakeups.disable(dev)
print(wakeups.pprint())
if __name__ == '__main__':
sys.exit(main(parse(sys.argv[1:])))
@plredmond
Copy link
Author

plredmond commented Dec 11, 2020

This is an alternative to echo LID0 | sudo tee /proc/acpi/wakeup for use in scripting, because the tee style adjustment only supports toggling. This script allows you to specify desired state and it will take care of parsing and setting things correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment