# pylint: disable=C0111,R0903 """Display and run a Pomodoro timer. Left click to start timer, left click again to pause. Right click will cancel the timer. Parameters: * pomodoro.work: The work duration of timer in minutes (defaults to 25) * pomodoro.break: The break duration of timer in minutes (defaults to 5) * pomodoro.format: Timer display format with '%m' and '%s' for minutes and seconds (defaults to '%m:%s') Examples: '%m min %s sec', '%mm', '', 'timer' * pomodoro.notify: Notification command to run when timer ends/starts (defaults to nothing) Example: 'notify-send 'Time up!''. If you want to chain multiple commands, please use an external wrapper script and invoke that. The module itself does not support command chaining (see https://github.com/tobi-wan-kenobi/bumblebee-status/issues/532 for a detailled explanation) """ from __future__ import absolute_import import datetime from math import ceil import bumblebee.input import bumblebee.output import bumblebee.engine class Module(bumblebee.engine.Module): def __init__(self, engine, config): widgets = bumblebee.output.Widget(full_text=self.text) super(Module, self).__init__(engine, config, widgets) # Parameters self._work_period = int(self.parameter('work', 25)) self._break_period = int(self.parameter('break', 5)) self._time_format = self.parameter('format', '%m:%s') self._notify_cmd = self.parameter('notify', '') # TODO: Handle time formats more gracefully. This is kludge. self.display_seconds_p = False self.display_minutes_p = False if '%s' in self._time_format: self.display_seconds_p = True if '%m' in self._time_format: self.display_minutes_p = True self.remaining_time = datetime.timedelta(minutes=self._work_period) self.time = None self.pomodoro = { 'state':'OFF', 'type': ''} self._text = self.remaining_time_str() + self.pomodoro['type'] engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd=self.timer_play_pause) engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd=self.timer_reset) def remaining_time_str(self): if self.display_seconds_p and self.display_minutes_p: minutes, seconds = divmod(self.remaining_time.seconds, 60) if not self.display_seconds_p: minutes = ceil(self.remaining_time.seconds / 60) seconds = 0 if not self.display_minutes_p: minutes = 0 seconds = self.remaining_time.seconds minutes = '{:2d}'.format(minutes) seconds = '{:02d}'.format(seconds) return self._time_format.replace('%m',minutes).replace('%s',seconds)+' ' def text(self, widget): return '{}'.format(self._text) def update(self, widget): if self.pomodoro['state'] == 'ON': timediff = (datetime.datetime.now() - self.time) if timediff.seconds >= 0: self.remaining_time -= timediff self.time = datetime.datetime.now() if self.remaining_time.total_seconds() <= 0: self.notify() if self.pomodoro['type'] == 'Work': self.pomodoro['type'] = 'Break' self.remaining_time = datetime.timedelta(minutes=self._break_period) elif self.pomodoro['type'] == 'Break': self.pomodoro['type'] = 'Work' self.remaining_time = datetime.timedelta(minutes=self._work_period) self._text = self.remaining_time_str() + self.pomodoro['type'] def notify(self): if self._notify_cmd: bumblebee.util.execute(self._notify_cmd) def timer_play_pause(self, widget): if self.pomodoro['state'] == 'OFF': self.pomodoro = {'state': 'ON', 'type': 'Work'} self.remaining_time = datetime.timedelta(minutes=self._work_period) self.time = datetime.datetime.now() elif self.pomodoro['state'] == 'ON': self.pomodoro['state'] = 'PAUSED' self.remaining_time -= (datetime.datetime.now() - self.time) self.time = datetime.datetime.now() elif self.pomodoro['state'] == 'PAUSED': self.pomodoro['state'] = 'ON' self.time = datetime.datetime.now() def timer_reset(self, widget): if self.pomodoro['state'] == 'ON' or self.pomodoro['state'] == 'PAUSED': self.pomodoro = {'state':'OFF', 'type': '' } self.remaining_time = datetime.timedelta(minutes=self._work_period) def state(self, widget): state = []; state.append(self.pomodoro['state'].lower()) if self.pomodoro['state'] == 'ON' or self.pomodoro['state'] == 'OFF': state.append(self.pomodoro['type'].lower()) return state