diff --git a/workrave-auto.py b/workrave-auto.py new file mode 100755 --- /dev/null +++ b/workrave-auto.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python + +from PyQt5.QtCore import QTimer, pyqtSignal, QObject, pyqtSlot, QCoreApplication, QMetaObject, Qt +from dbus.mainloop.pyqt5 import DBusQtMainLoop +import dbus +import os +import configparser +import datetime +import time +import sys +import signal + +def wk2int(day): + if day == 'mon': + return 0 + elif day == 'tue': + return 1 + elif day == 'wed': + return 2 + elif day == 'thu': + return 3 + elif day == 'fri': + return 4 + elif day == 'sat': + return 5 + elif day == 'sun': + return 6 + else: + return -1 + +def time2diff(now, time): + datetime.datetime.now() + to = time.split(':') + hr = int(to[0]) + mi = 0 + sec = 0 + if len(to) == 2: + mi = int(to[1]) + if len(to) == 3: + sec = int(to[2]) + return datetime.datetime.combine(datetime.date.today(), datetime.time(hr, mi, sec)) - now + +class autoEntry: + def __init__(self, options): + self.options = options + self.next_action = options['default'] + self.next_run = 0 + self.in_interval = False + self.update() + + def print_options(self): + + print(self.options) + + def update(self): + self.in_interval = False + now = datetime.datetime.now() + days = 0 + starttime = time2diff(now, '00:00') + endtime = time2diff(now, '23:59') + if 'weekday' in self.options: + curr_day = now.weekday() + wanted_day = wk2int(self.options['weekday']) + if wanted_day == -1: + print('Error parsing weekday') + return -1 + diff_day = wanted_day - curr_day + if diff_day < 0: + diff_day += 7 + days = diff_day + if 'starttime' in self.options: + starttime = time2diff(now, self.options['starttime']) + if 'endtime' in self.options: + endtime = time2diff(now, self.options['endtime']) + + if starttime.total_seconds() < 0 and endtime.total_seconds() < 0: + # After interval, if it's today, we need to add a week + if days == 0: + starttime = starttime + datetime.timedelta(days=7) + endtime = endtime + datetime.timedelta(days=7) + self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() + self.next_action = self.options['mode'] + elif starttime.total_seconds() < 0 and endtime.total_seconds() >= 0: + # Inside of interval + if days == 0: + self.next_action = self.options['default'] + self.next_run = endtime.total_seconds() + self.in_interval = True + else: + self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() + self.next_action = self.options['mode'] + elif starttime.total_seconds() >= 0 and endtime.total_seconds() >= 0: + # Before interval + self.next_action = self.options['mode'] + self.next_run = days * 24 * 60 * 60 + starttime.total_seconds() + else: + print('Invalid settings detected!') + + def get_next_action(self): + self.update() + return self.next_action + + def get_interval_action(self): + return self.options['mode'] + + def get_next_run(self): + self.update() + return int(self.next_run) + + def is_in_interval(self): + self.update() + return self.in_interval + + +def load_config(cfgFile): + config = configparser.ConfigParser() + config.read(cfgFile) + return config + +def save_config(config, cfgFile): + with open(cfgFile, 'w') as configFile: + config.write(configFile) + +def recreate_config(cfgFile): + config = configparser.ConfigParser() + config['general'] = { + 'mode' : 'normal' + } + config['entry1'] = { + 'name' : 'workrave', + 'mode' : 'suspended', + 'weekday' : 'wed', + 'starttime' : '07:00', + 'endtime' : '15:10' + } + config['entry2'] = { + 'name' : 'workrave', + 'mode' : 'suspended', + 'weekday' : 'thu', + 'starttime' : '07:00', + 'endtime' : '18:00' + } + save_config(config, cfgFile) + +class workraveManager(QObject): + def __init__(self, cfg): + QObject.__init__(self) + self.cfg = cfg + entries = self.parse_config(cfg) + self.entryobjects = [] + + self.default_mode = cfg['general']['mode'] + self.timer = QTimer() + self.timer.setSingleShot(True) + self.timer.timeout.connect(self.run_action) + + for entry in entries: + entryCfg = entries[entry] + entryCfg['default'] = self.default_mode + self.entryobjects.append(autoEntry(entryCfg)) + + @pyqtSlot() + def run_action(self): + print('Timer fired, about to run action: ' + self.action) + self.set_workrave_mode(self.action) + # This prevents the timer from firing several times + time.sleep(1000) + self.setup_timer() + + def suspend_handler(self, suspended): + if suspended: + print('Going to sleep...') + self.timer.stop() + else: + print('Resuming, doing the recalculation math!') + self.startup_check() + self.setup_timer() + + def init_dbus(self): + + dbus_loop = DBusQtMainLoop(set_as_default=True) + self.system_bus = dbus.SystemBus(mainloop = dbus_loop) + + manager_interface = 'org.freedesktop.login1.Manager' + + self.system_bus.add_signal_receiver(self.suspend_handler, 'PrepareForSleep', manager_interface) + + def parse_config(self, cfg): + ret = {} + for sec in cfg.sections(): + if sec != 'general': + ret[sec] = {} + for key in cfg[sec]: + ret[sec][key] = cfg[sec][key] + return ret + + def startup_check(self): + print('Startup check...') + in_interval = False + for entry in self.entryobjects: + if entry.is_in_interval(): + action = entry.get_interval_action() + print('Interval currently running, setting mode: ' + action) + self.set_workrave_mode(action) + in_interval = True + + if not in_interval: + print('Not in interval, setting default workrave mode: ' + self.default_mode) + self.set_workrave_mode(self.default_mode) + + def set_workrave_mode(self, action): + wr = dbus.SessionBus().get_object('org.workrave.Workrave', '/org/workrave/Workrave/Core') + iface = dbus.Interface(wr, 'org.workrave.CoreInterface') + mode = iface.GetOperationMode() + if mode == action: + print('Mode already set, no action') + else: + iface.SetOperationMode(action) + + @pyqtSlot() + def startup(self): + self.init_dbus() + self.startup_check() + self.setup_timer() + + def setup_timer(self): + self.timer.stop() + next_action_list = [] + next_run_list = [] + for entry in self.entryobjects: + next_action_list.append(entry.get_next_action()) + next_run_list.append(entry.get_next_run()) + sleep_time = min(next_run_list) + self.action = next_action_list[next_run_list.index(sleep_time)] + print('Setting up timer for ' + str(sleep_time) + ' seconds and then running action: ' + self.action) + self.timer.start(sleep_time * 1000) + +def main(): + signal.signal(signal.SIGINT, signal.SIG_DFL) + cfgPath = os.path.expanduser("~") + "/.workrave-auto/" + if not os.path.isdir(cfgPath): + os.makedirs(cfgPath) + cfgFile = cfgPath + "config.ini" + if not os.path.exists(cfgFile): + recreate_config(cfgFile) + + cfg = load_config(cfgFile) + + m = workraveManager(cfg) + QMetaObject.invokeMethod(m, "startup", Qt.QueuedConnection) + app = QCoreApplication(sys.argv) + sys.exit(app.exec_()) + +if __name__ == '__main__': + main() +