added warning capabilities
This commit is contained in:
parent
396e5916a2
commit
2ca8ad5217
4 changed files with 106 additions and 7 deletions
14
config.yaml
14
config.yaml
|
|
@ -2,12 +2,20 @@ check-interval: 1
|
|||
timers:
|
||||
gaming:
|
||||
apps:
|
||||
- retroarch
|
||||
- minecraft
|
||||
- xmcl
|
||||
- mcpelauncher-ui
|
||||
# how many minutes of usage
|
||||
time-limit: 40
|
||||
time-limit: 5
|
||||
# within how many hours of interval
|
||||
limit-interval: 12
|
||||
limit-interval: 4
|
||||
# warn n minutes before reaching the limit
|
||||
warn-threshold: 4
|
||||
# commands run on GNOME to pre-warn/final-warn
|
||||
warn-command: >
|
||||
runuser -l randogoth -c 'DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send "App Timer" "Gaming time is almost up ({time_left_int} min left)" -u critical && canberra-gtk-play -i complete'
|
||||
final-warn-command: >
|
||||
runuser -l randogoth -c 'DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send "App Timer" "Final minute before shutdown ({time_left_seconds}s)" -u critical && canberra-gtk-play -i alarm-clock-elapsed'
|
||||
websurf:
|
||||
apps:
|
||||
- chrome
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import math
|
||||
import os, time
|
||||
import subprocess
|
||||
|
||||
|
|
@ -27,6 +28,7 @@ class Usage(object):
|
|||
super(Usage, self).__init__()
|
||||
self.timer = timer
|
||||
self.file = '%s/usage/%s' % (CONFIG_PATH, self.timer.name)
|
||||
self.final_warning_sent = False
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
|
|
@ -45,7 +47,9 @@ class Usage(object):
|
|||
|
||||
def release(self):
|
||||
'''Remove the timer usage to restart the counter'''
|
||||
os.remove(self.file)
|
||||
if os.path.exists(self.file):
|
||||
os.remove(self.file)
|
||||
self.final_warning_sent = False
|
||||
|
||||
def isOffLimit(self):
|
||||
'''Is the timer usage off its limits'''
|
||||
|
|
@ -99,18 +103,89 @@ class Timer(object):
|
|||
apps = [app.strip() for app in item_apps]
|
||||
return apps
|
||||
|
||||
@property
|
||||
def warnThreshold(self):
|
||||
threshold = self.item.get('warn-threshold', None)
|
||||
if threshold is None:
|
||||
return None
|
||||
return float(threshold)
|
||||
|
||||
@property
|
||||
def warnCommand(self):
|
||||
return self.item.get('warn-command')
|
||||
|
||||
@property
|
||||
def finalWarnCommand(self):
|
||||
return self.item.get('final-warn-command')
|
||||
|
||||
def _command_context(self, time_left):
|
||||
minutes = max(time_left, 0)
|
||||
seconds = minutes * 60
|
||||
return {
|
||||
'timer_name': self.name,
|
||||
'time_left': minutes,
|
||||
'time_left_int': int(math.ceil(minutes)),
|
||||
'time_left_floor': int(math.floor(minutes)),
|
||||
'time_left_seconds': int(math.ceil(seconds)),
|
||||
}
|
||||
|
||||
def _prepareCommand(self, command, context):
|
||||
if isinstance(command, (list, tuple)):
|
||||
command = ' '.join([str(part) for part in command])
|
||||
if not command:
|
||||
return None
|
||||
context = context or {}
|
||||
try:
|
||||
return command.format(**context)
|
||||
except (KeyError, IndexError, ValueError) as exc:
|
||||
print('Timer %s warning command format error (%s), using raw command' % (self.name, exc))
|
||||
return command
|
||||
|
||||
def _runCommand(self, command, label, context=None):
|
||||
cmd = self._prepareCommand(command, context)
|
||||
if not cmd:
|
||||
return
|
||||
print('Timer %s running %s command: %s' % (self.name, label, cmd))
|
||||
shell(cmd)
|
||||
|
||||
def maybeWarn(self, check_interval):
|
||||
if self.timeLimit < 0:
|
||||
return
|
||||
current_usage = self.usage.current
|
||||
time_left = self.timeLimit - current_usage
|
||||
if time_left <= 0:
|
||||
if self.usage.final_warning_sent:
|
||||
self.usage.final_warning_sent = False
|
||||
return
|
||||
context = self._command_context(time_left)
|
||||
if (self.warnThreshold is not None and self.warnCommand and
|
||||
time_left <= self.warnThreshold):
|
||||
self._runCommand(self.warnCommand, 'warning', context)
|
||||
final_command = self.finalWarnCommand
|
||||
if not final_command:
|
||||
return
|
||||
interval = float(check_interval)
|
||||
if time_left <= interval:
|
||||
if not self.usage.final_warning_sent:
|
||||
self._runCommand(final_command, 'final warning', context)
|
||||
self.usage.final_warning_sent = True
|
||||
else:
|
||||
# reset flag if the next loop is no longer the final one
|
||||
if self.usage.final_warning_sent:
|
||||
self.usage.final_warning_sent = False
|
||||
|
||||
def isRunning(self):
|
||||
running = False
|
||||
for app in self.apps:
|
||||
cmd = 'ps aux | grep -v grep | grep "%s"' % app
|
||||
cmd = 'pgrep -f "%s"' % app
|
||||
res = shell(cmd)
|
||||
if len(res):
|
||||
if res.strip():
|
||||
running = True
|
||||
return running
|
||||
|
||||
def block(self):
|
||||
for app in self.apps:
|
||||
cmd = 'killall "%s"' % app
|
||||
cmd = 'pkill -f "%s"' % app
|
||||
shell(cmd)
|
||||
|
||||
|
||||
|
|
|
|||
15
readme.md
15
readme.md
|
|
@ -23,6 +23,21 @@ timers:
|
|||
time-limit: 60
|
||||
# within how many hours of interval
|
||||
limit-interval: 12
|
||||
# optional: warn N minutes before the limit is reached
|
||||
warn-threshold: 5
|
||||
# run on every loop inside the warning window (anything executable)
|
||||
warn-command: "notify-send 'App Timer' 'Only {time_left_int} minutes left'"
|
||||
# run only on the final loop before apps are blocked
|
||||
final-warn-command: "notify-send 'App Timer' 'Last minute before shutdown'"
|
||||
```
|
||||
|
||||
`warn-threshold` is measured in minutes and triggers the `warn-command` every loop while there is still time remaining. `final-warn-command` fires once on the last loop before the timer exceeds its limit (based on `check-interval`). All commands are executed through the shell, so they can show desktop notifications, play sounds, etc. You can interpolate values in the command string using `{timer_name}`, `{time_left}`, `{time_left_int}`, `{time_left_floor}`, and `{time_left_seconds}` (any Python format specifiers are supported, e.g. `{time_left:.1f}`).
|
||||
|
||||
If the systemd service runs as `root`, wrap desktop commands so they execute inside your session:
|
||||
|
||||
```yaml
|
||||
warn-command: >
|
||||
runuser -l randogoth -c 'DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send "App Timer" "{timer_name}: {time_left_int} minutes left"'
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
|
|
|||
1
timer.py
1
timer.py
|
|
@ -12,6 +12,7 @@ def check_timers(config):
|
|||
if not timer.isRunning():
|
||||
continue
|
||||
print('Timer %s is running' % timer.name)
|
||||
timer.maybeWarn(config.checkInterval)
|
||||
# check for off limit apps
|
||||
if timer.usage.isOffLimit():
|
||||
print('Timer %s is off limit' % timer.name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue