Refactored python code into library.

This commit is contained in:
Richard Neumann 2020-03-26 11:47:41 +01:00
parent aae65e3825
commit 309bafd5d1
7 changed files with 247 additions and 203 deletions

View file

@ -1,132 +1,7 @@
#! /usr/bin/env python3
"""Sets the system mode."""
from argparse import ArgumentParser
from subprocess import DEVNULL, CalledProcessError, check_call, run
from sys import stderr
DESCRIPTION = 'Sets or toggles the system mode.'
LAPTOP_MODE_SERVICE = 'laptop-mode.service'
TABLET_MODE_SERVICE = 'tablet-mode.service'
def get_arguments():
"""Returns the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-n', '--notify', action='store_true',
help='display an on-screen notification')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('toggle', help='toggles the system mode')
subparsers.add_parser('laptop', help='switch to laptop mode')
subparsers.add_parser('tablet', help='switch to tablet mode')
subparsers.add_parser('default', help='do not disable any input devices')
return parser.parse_args()
def systemctl(action, unit, *, root=False):
"""Runs systemctl."""
command = ['/usr/bin/sudo'] if root else []
command.append('systemctl')
command.append(action)
command.append(unit)
try:
check_call(command, stdout=DEVNULL) # Return 0 on success.
except CalledProcessError:
return False
return True
def notify_send(summary, body=None):
"""Sends the respective message."""
command = ['/usr/bin/notify-send', summary]
if body is not None:
command.append(body)
return run(command, stdout=DEVNULL)
def notify_laptop_mode():
"""Notifies about laptop mode."""
return notify_send('Laptop mode.', 'The system is now in laptop mode.')
def notify_tablet_mode():
"""Notifies about tablet mode."""
return notify_send('Tablet mode.', 'The system is now in tablet mode.')
def toggle_mode(notify):
"""Toggles between laptop and tablet mode."""
if systemctl('status', TABLET_MODE_SERVICE):
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
else:
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def default_mode(notify):
"""Restores all blocked input devices."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('stop', TABLET_MODE_SERVICE, root=True)
if notify:
notify_send('Default mode.', 'The system is now in default mode.')
def laptop_mode(notify):
"""Starts the laptop mode."""
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def tablet_mode(notify):
"""Starts the tablet mode."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
def main():
"""Runs the main program."""
arguments = get_arguments()
if arguments.mode == 'toggle':
toggle_mode(arguments.notify)
elif arguments.mode == 'default':
default_mode(arguments.notify)
elif arguments.mode == 'laptop':
laptop_mode(arguments.notify)
elif arguments.mode == 'tablet':
tablet_mode(arguments.notify)
else:
print('Must specify a mode.', file=stderr, flush=True)
from tabletmode.cli import main
if __name__ == '__main__':

20
setup.py Executable file
View file

@ -0,0 +1,20 @@
#! /usr/bin/env python
from setuptools import setup
setup(
name='tabletmode',
version_format='{tag}',
setup_requires=['setuptools-git-version'],
author='Richard Neumann',
author_email='mail@richard-neumann.de',
python_requires='>=3.8',
packages=['tabletmode'],
scripts=['setsysmode', 'sysmoded'],
url='https://github.com/conqp/tablet-mode',
license='GPLv3',
description='Tablet mode switch for GNOME 3.',
long_description=open('README.md').read(),
long_description_content_type="text/markdown",
keywords='tablet mode tent convertible switch'
)

View file

@ -1,83 +1,7 @@
#! /usr/bin/env python3
"""System mode daemon."""
from argparse import ArgumentParser
from json import load
from logging import DEBUG, INFO, basicConfig, getLogger
from pathlib import Path
from subprocess import Popen
CONFIG_FILE = Path('/etc/tablet-mode.json')
DESCRIPTION = 'Setup system for laptop or tablet mode.'
EVTEST = '/usr/bin/evtest'
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
LOGGER = getLogger(__file__)
def get_arguments():
"""Parses the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='turn on verbose logging')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('laptop', help='enable laptop mode')
subparsers.add_parser('tablet', help='enable tablet mode')
return parser.parse_args()
def load_configuration():
"""Returns the configuration."""
try:
with CONFIG_FILE.open('r') as cfg:
return load(cfg)
except FileNotFoundError:
LOGGER.warning('Config file %s does not exist.', CONFIG_FILE)
return {}
def disable_device(device):
"""Disables the respective device via evtest."""
return Popen((EVTEST, '--grab', device))
def disable_devices(devices):
"""Disables the given devices."""
subprocesses = []
for device in devices:
subprocess = disable_device(device)
subprocesses.append(subprocess)
for subprocess in subprocesses:
subprocess.wait()
def get_devices(mode):
"""Reads the device from the config file."""
configuration = load_configuration()
devices = configuration.get(mode) or ()
if not devices:
LOGGER.info('No devices configured to disable.')
return devices
def main():
"""Runs the main program."""
arguments = get_arguments()
level = DEBUG if arguments.verbose else INFO
basicConfig(level=level, format=LOG_FORMAT)
devices = get_devices(arguments.mode)
disable_devices(devices)
from tabletmode.daemon import main
if __name__ == '__main__':

1
tabletmode/__init__.py Normal file
View file

@ -0,0 +1 @@
"""Tablet mode library."""

133
tabletmode/cli.py Normal file
View file

@ -0,0 +1,133 @@
"""Sets the system mode."""
from argparse import ArgumentParser
from subprocess import DEVNULL, CalledProcessError, check_call, run
from sys import stderr
from tabletmode.config import load_configuration
DESCRIPTION = 'Sets or toggles the system mode.'
LAPTOP_MODE_SERVICE = 'laptop-mode.service'
TABLET_MODE_SERVICE = 'tablet-mode.service'
def get_arguments():
"""Returns the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-n', '--notify', action='store_true',
help='display an on-screen notification')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('toggle', help='toggles the system mode')
subparsers.add_parser('laptop', help='switch to laptop mode')
subparsers.add_parser('tablet', help='switch to tablet mode')
subparsers.add_parser('default', help='do not disable any input devices')
return parser.parse_args()
def systemctl(action, unit, *, root=False):
"""Runs systemctl."""
command = ['/usr/bin/sudo'] if root else []
command.append('systemctl')
command.append(action)
command.append(unit)
try:
check_call(command, stdout=DEVNULL) # Return 0 on success.
except CalledProcessError:
return False
return True
def notify_send(summary, body=None):
"""Sends the respective message."""
command = ['/usr/bin/notify-send', summary]
if body is not None:
command.append(body)
return run(command, stdout=DEVNULL, check=False)
def notify_laptop_mode():
"""Notifies about laptop mode."""
return notify_send('Laptop mode.', 'The system is now in laptop mode.')
def notify_tablet_mode():
"""Notifies about tablet mode."""
return notify_send('Tablet mode.', 'The system is now in tablet mode.')
def toggle_mode(notify=False):
"""Toggles between laptop and tablet mode."""
if systemctl('status', TABLET_MODE_SERVICE):
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
else:
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def default_mode(notify=False):
"""Restores all blocked input devices."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('stop', TABLET_MODE_SERVICE, root=True)
if notify:
notify_send('Default mode.', 'The system is now in default mode.')
def laptop_mode(notify=False):
"""Starts the laptop mode."""
systemctl('stop', TABLET_MODE_SERVICE, root=True)
systemctl('start', LAPTOP_MODE_SERVICE, root=True)
if notify:
notify_laptop_mode()
def tablet_mode(notify=False):
"""Starts the tablet mode."""
systemctl('stop', LAPTOP_MODE_SERVICE, root=True)
systemctl('start', TABLET_MODE_SERVICE, root=True)
if notify:
notify_tablet_mode()
def main():
"""Runs the main program."""
arguments = get_arguments()
configuration = load_configuration()
notify = configuration.get('notify', False) or arguments.notify
if arguments.mode == 'toggle':
toggle_mode(notify=notify)
elif arguments.mode == 'default':
default_mode(notify=notify)
elif arguments.mode == 'laptop':
laptop_mode(notify=notify)
elif arguments.mode == 'tablet':
tablet_mode(notify=notify)
else:
print('Must specify a mode.', file=stderr, flush=True)

23
tabletmode/config.py Normal file
View file

@ -0,0 +1,23 @@
"""Configuration file parsing."""
from json import load
from logging import getLogger
from pathlib import Path
__all__ = ['load_configuration']
CONFIG_FILE = Path('/etc/tablet-mode.json')
LOGGER = getLogger('tabletmode')
def load_configuration():
"""Returns the configuration."""
try:
with CONFIG_FILE.open('r') as cfg:
return load(cfg)
except FileNotFoundError:
LOGGER.warning('Config file %s does not exist.', CONFIG_FILE)
return {}

68
tabletmode/daemon.py Normal file
View file

@ -0,0 +1,68 @@
"""System mode daemon."""
from argparse import ArgumentParser
from logging import DEBUG, INFO, basicConfig, getLogger
from subprocess import Popen
from tabletmode.config import load_configuration
DESCRIPTION = 'Setup system for laptop or tablet mode.'
EVTEST = '/usr/bin/evtest'
LOG_FORMAT = '[%(levelname)s] %(name)s: %(message)s'
LOGGER = getLogger('sysmoded')
def get_arguments():
"""Parses the CLI arguments."""
parser = ArgumentParser(description=DESCRIPTION)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='turn on verbose logging')
subparsers = parser.add_subparsers(dest='mode')
subparsers.add_parser('laptop', help='enable laptop mode')
subparsers.add_parser('tablet', help='enable tablet mode')
return parser.parse_args()
def disable_device(device):
"""Disables the respective device via evtest."""
return Popen((EVTEST, '--grab', device))
def disable_devices(devices):
"""Disables the given devices."""
subprocesses = []
for device in devices:
subprocess = disable_device(device)
subprocesses.append(subprocess)
for subprocess in subprocesses:
subprocess.wait()
def get_devices(mode):
"""Reads the device from the config file."""
configuration = load_configuration()
devices = configuration.get(mode) or ()
if not devices:
LOGGER.info('No devices configured to disable.')
return devices
def main():
"""Runs the main program."""
arguments = get_arguments()
level = DEBUG if arguments.verbose else INFO
basicConfig(level=level, format=LOG_FORMAT)
devices = get_devices(arguments.mode)
disable_devices(devices)