diff --git a/workrave-auto.py b/workrave-auto.py --- a/workrave-auto.py +++ b/workrave-auto.py @@ -1,337 +1,337 @@ #!/usr/bin/env python from PyQt5.QtCore import QTimer, pyqtSignal, QObject, pyqtSlot, QCoreApplication, QMetaObject, Qt from PyQt5 import QtGui, QtWidgets 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, parent=None): QObject.__init__(self) self.cfg = cfg self.parent = parent 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) self.inhibited = False 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) + time.sleep(1) self.setup_timer() def suspend_handler(self, suspended): if suspended: print('Going to sleep...') self.timer.stop() else: if self.inhibited: return 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): if self.inhibited: return 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) def get_timer_time(self): if self.timer.isActive(): return self.timer.remainingTime() else: return 0 @pyqtSlot() def startup(self): self.init_dbus() self.startup_check() self.setup_timer() def is_inhibited(self): return self.inhibited def inhibit(self, action): if action: self.timer.stop() self.inhibited = True else: self.startup_check() self.setup_timer() self.inhibited = False 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 quit(self): self.timer.stop() QtWidgets.qApp.quit() class SystemTrayIcon(QtWidgets.QSystemTrayIcon): def __init__(self, icon, manager, parent=None): self.parent = parent QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) menu = QtWidgets.QMenu(parent) self.remaining = menu.addAction('Remaining: ') self.presentationAction = menu.addAction("Inhibit") self.presentationAction.triggered.connect(self.inhibit) self.exitAction = menu.addAction("Exit") self.exitAction.triggered.connect(manager.quit) self.setContextMenu(menu) self.manager = manager self.activated.connect(self.tray_activated) @pyqtSlot() def tray_activated(self): rem = self.manager.get_timer_time() / 1000 sec = datetime.timedelta(seconds=int(rem)) d = datetime.datetime(1, 1, 1) + sec if rem == 0: self.remaining.setText("No action planned.") else: if d.day-1 == 0: fmt = "%d:%d:%d" % (d.hour, d.minute, d.second) else: fmt = "%d days %d:%d:%d" % (d.day-1, d.hour, d.minute, d.second) self.remaining.setText("Next Action: " + fmt) @pyqtSlot() def inhibit(self): if self.manager.is_inhibited(): self.manager.inhibit(False) self.presentationAction.setText("Inhibit") else: self.manager.inhibit(True) self.presentationAction.setText("Resume") def main(): signal.signal(signal.SIGINT, signal.SIG_DFL) app = QtWidgets.QApplication(sys.argv) app.setQuitOnLastWindowClosed(False); 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) w = QtWidgets.QWidget() m = workraveManager(cfg, w) QMetaObject.invokeMethod(m, "startup", Qt.QueuedConnection) # Wait for up to 30 seconds for the systemTray to become available (required for, e.g., Xfce) count = 0 while not QtWidgets.QSystemTrayIcon.isSystemTrayAvailable() and count < 30: time.sleep(1) count += 1 trayIcon = SystemTrayIcon(QtGui.QIcon("/usr/share/icons/workrave-auto.png"), m, w) trayIcon.show() sys.exit(app.exec_()) if __name__ == '__main__': main()