Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alarm user on alarm #148

Open
teto opened this issue Jan 29, 2015 · 51 comments
Open

Alarm user on alarm #148

teto opened this issue Jan 29, 2015 · 51 comments

Comments

@teto
Copy link
Contributor

teto commented Jan 29, 2015

I had looked for a program like khal one year ago without success but I am very glad to have found it. My question is not directly related but might become. I am looking for a daemon able to read my calendar and warn me via popups when an event is closing in. Thunderbird does warn me but only when it's open. So I was wondering if anyone knew of such a tool and if not, would it make sense to divide khal into a daemon and a client application (as urxvt or powerline).

@untitaker
Copy link
Member

Khal could use atd instead of launching its own daemon.

@untitaker
Copy link
Member

Also I think @michaeladler wrote something like this for his own use?

@geier
Copy link
Member

geier commented Jan 29, 2015

Without thinking too much about this, I'm not a fan of writing our own
daemon here. Using either at or cron and any notification
program comes to my mind.

Quoting Markus Unterwaditzer (2015-01-29 18:05:40)

Also I think @michaeladler wrote something like this for his own use?


Reply to this email directly or view it on GitHub:
#148 (comment)

@teto
Copy link
Contributor Author

teto commented Jan 29, 2015

Somehow the workflow would be, write a cronjob that polls every X minutes from khal events within next X minutes events and pass those events to at ?
This should be enough if X is low. Otherwise if the calendar changes (event removed, or close event added) during the next X minutes it may become complex to handle.

@michaeladler
Copy link

@untitaker: Nope, I pretty much wrote the opposite: a tool that removes annoying alerts from ics-files :)

@teto
Copy link
Contributor Author

teto commented Feb 3, 2015

@michaeladler I would like to be annoyed by such a tool, any name in mind ?

@geier
Copy link
Member

geier commented Feb 3, 2015

I'm happy to support any such endeavor with any custom functions needed, but won't get to implement the tool itself for quite some time.

@geier geier mentioned this issue Feb 18, 2015
@geier
Copy link
Member

geier commented Feb 18, 2015

@mbaldessari 's suggestion:

It would be extremely cool to have something (just thinking aloud here) like:
khal alarms --script foo --delta 60min which will call foo for every event that will take place
in the next 60 minutes (passing the subject of the event + time as args). That way it could
be put in a cron job pretty easily.

@geier geier changed the title Caldav daemon to launch popups before events VALARM support Feb 18, 2015
@untitaker
Copy link
Member

I think that's problematic. If the command you posted were to be executed every hour, it could miss events that are happening within 60 mins and were synced recently. If the user tries to mitigate this by calling that command more often, the given script would be executed multiple times for one event.

I think a better approach is a hook in the config file that is called whenever khal pleases to do so. But in that case, the next version of vdirsyncer will include such a hook, it will invoke a script for each new or updated ics file, thanks to @michaeladler.

@teto
Copy link
Contributor Author

teto commented Feb 18, 2015

Commit susmentioned is this one I guess: pimutils/vdirsyncer@2084534 . Cool stuff. The easiest way would to go for the hook would be to remove all the previously registered cronjobs and recreate them.

@untitaker
Copy link
Member

@teto In that case you might as well not rely on any hooks, and traverse the directories yourself.

I suppose we'll need another hook for deleting items?

@teto
Copy link
Contributor Author

teto commented Feb 19, 2015

I was not sure if the hook would react on a delete but it is needed I guess. You say " traverse the directories yourself." but how often ? you can do it every every X min or when the hook triggers. This way you skip useless updates. Tracking changes on a 1 to 1 mapping looks harder to me then just reset cronjobs. I dunno if there is exists a ID/hash per event ?

@WhyNotHugo
Copy link
Member

at is probably better than cron for this since:

  • It provides an id for each job, making cancellation easier.
  • It looks simpler to schedule things for a determined date/time than via cron (from the top of my head: cron has no way of adding something for 2016-04-20 without it executing on 2015-04-20 as well)
  • It's really designed for this sort of one-off thing.

I'm not sure if khal should really be invoking at though. I'd much rather see a command for output similar to khal agenda but with the date included on every line. Something else can then pick that up and manage at event addition/tracking/deletion. Anything more is honestly rather out-of-scope for khal, IMHO.

This external job/notification tracker can either:

  • Run daemonized, detecting changes to the vdir (remember: they're atomic :D ). Guarantees that no events slip past between syncs.
  • As a vdirsyncer hook.

I've no real strong preference.

@mathstuf
Copy link

mathstuf commented Aug 8, 2015

I think a daemon would be much easier to implement and get right. If you have a snooze on events, you'll need to react which means staying around until the user is "done" with the notification anyways, so you may as well just scan the ical files as needed and add timers to an internal event loop.

@untitaker
Copy link
Member

It certainly gives us more flexibility for more sophisticated features.

Perhaps this daemon can also keep the database in memory, so the CLI has nicer
startup performance (similar to evolution-data-server's role). Not sure how
communication between daemon and client should look like.

On Fri, Aug 07, 2015 at 10:22:17PM -0700, Ben Boeckel wrote:

I think a daemon would be much easier to implement and get right. If you have a snooze on events, you'll need to react which means staying around until the user is "done" with the notification anyways, so you may as well just scan the ical files as needed and add timers to an internal event loop.


Reply to this email directly or view it on GitHub:
#148 (comment)

@teto
Copy link
Contributor Author

teto commented Aug 20, 2015

As an alternative to the daemon solution gcalcli (https://github.com/insanum/gcalcli) proposes a remind command as a cronjob. Looks easier to implement ?
(I like the way they output the week schedule too).

@geier geier mentioned this issue Aug 20, 2015
@mathstuf
Copy link

IMO, you're going to want to run every 10 minutes or so anyways, so I see no reason not to just run as a daemon. No daemonization logic is needed either; use systemd --user, shell backgrounding, nohup, or other mechanisms to manage the process.

@waynew
Copy link

waynew commented Sep 27, 2016

I think the most low-hanging fruit for alarms would be to have khal interactive alert every so often. I don't know exactly how the UI is implemented, but it seems like that would be a reasonable approach.

My personal needs would be fine with that approach.

@geier
Copy link
Member

geier commented Sep 28, 2016

feel free to send an PR :)

@teto
Copy link
Contributor Author

teto commented Sep 28, 2016

I don't think it's much easier and also it would mean having the UI open at all times ? not very practical.

@waynew
Copy link

waynew commented Sep 29, 2016

If you use tmux (or screen) like I do then it's perfectly practical. Otherwise I'm not sure precisely what you'd use for notifications.

@mathstuf
Copy link

Have it send a notification over D-Bus and if the required modules aren't available or the config disables it, output to stdout for processing by any tool.

@mathstuf
Copy link

IMO, Todo and calendar apps are completely different things. Calendars document things that happen while Todo lists document things to be done. They require different logic to handle properly. Personally I use taskwarrior since it supports more complex queries and attributes than iCal does. To that end, I don't think these things should be conflated just because they both need to notify a user. An alarm for an event is usually set in time for me to get to an event while a task for that event may require subtasks, a week of lead time, and resulting "products".

@WhyNotHugo
Copy link
Member

@mathstuf I meant that the file format for both alarms is the same (quite literally, by the way, since it's exactly the same spec). So my idea is basically:

Some for of daemon that reads alarms from vdirs:

  • Set a timer up for the alarm.
  • When the timer fires, show a notification.

If you're using a different task management app/format that should be no problem; this proposal has no ties to todoman [nor khal, for that matter], just to vdir+icalendar, which is the format both use.

@mathstuf
Copy link

Ah, that sounds suitable then.

@WhyNotHugo
Copy link
Member

@geier @untitaker How do you feel about this idea? I can get a proof-of-concept at some point. :)

@geier
Copy link
Member

geier commented May 25, 2017

Have at it if you want, I don't see myself using something like that at the moment, but who knows, I also thought colored days were useless and now I use them all the time.

@vodan
Copy link

vodan commented Nov 2, 2017

Hi all, can somebody tell me the status of notifications. Is currently somebody working on this topic?

@untitaker
Copy link
Member

untitaker commented Nov 2, 2017 via email

@nicoe
Copy link

nicoe commented Mar 28, 2019

I have been inspired by vdirsyncer, khal and todoman to create a daemon that notifies about event stored in a vdir: https://github.com/nicoe/remhind there are still some rough edges related to the handling of VTODO but I'm working on it

@stacyharper
Copy link

Hello there,

I'm too trying to get event notification works.

My first goal is to simply have a script that will notify-send if an event start in now + the event before alarm delay.

I'll then cron run this script every minutes.

I got a problem, I canno't search events using event alarm values.

I think an enough use case could be

Swimming 17:30 18:00 alarm 10 minutes before

$ now '+%H:%M'
17:19
$ khal at --willstart
No events

$ now '+%H:%M'
17:20
$ khal at --willstart
17:30-18:00 Swimming

$ now '+%H:%M'
17:26
$ khal at --willstart
17:30-18:00 Swimming

$ now '+%H:%M'
17:26
$ khal at --willstart --notstarted
No events

But we need to find events that want to alarm us at a given time.

What do you thing about this ?

@doronbehar
Copy link

@Eluminae I think you should consider using / improving @nicoe's remhind. The main difference is that (at least in design - I haven't tried it or read the code myself) it's a daemon with automatically updated schedule of notifications. This is much easier to setup / install - from the user's point of view.

I haven't read remhind's implementation details but I tend to think it could be implemented better. Here are my two cents just out of reading the README:

  1. Why is there an additional configuration file? Shouldn't it be possible to read khal's config or even better - import khal somehow and use it's native functions and data types / classes to get the calendars' data?
  2. If this will be implemented so reliably upon khal, perhaps it'll be better to propose this as an additional module / executable and merge it here.

@stacyharper
Copy link

I think the nicoe's implementation is interesting but I got some reasons to not use it

  • It signify another requirement (it is not packaged in lot of my distros so I'll have to package it myself).
  • Its goal is to notify (using gi.repository). I will no be able to broadcast events alarm notifications on all my devices using MQTT (my real long term goal). Or send email warnings or etc. I'm still missing a alarm query feature.
  • Nico re-implemented the event handling logic. If khal provides some advanced alarm specific search filter, it would make the task easier (and safer) for remhind (or others)

Maybe the ikhal could provide a notification daemon but it would still need for that to be able to query with alarms. Don't you think this feature could be in the khal responsibility scope ?

@WhyNotHugo
Copy link
Member

The problem with khal handling alarms (and having some form of alarm-daemon), is that it would only handle calendar events, and a separate daemon would be needed for calendar reminders.

We've discussed this a bit before, and having a separate application that just handles alarms for all ICS files made sense the most.

@stacyharper
Copy link

Huge good point about calendar reminders. I want them too.

I'll reconsider options :)

@Anachron
Copy link

I hope I didn‘t miss anything (didn‘t read the full discussion) but I use vdirsyncer and I convert all my ics files (using awk) to the remind format and restart the remind agent afterwards.

@fourstepper
Copy link

@Anachron could you expand on your method a bit? Do you run the conversion via cron?

@d7415
Copy link
Collaborator

d7415 commented Apr 27, 2021

@Anachron could you expand on your method a bit? Do you run the conversion via cron?

A post_hook in the vdirsyncer config might be easiest.

@Anachron
Copy link

I didn't know about the vdirsyncer hook, so yes, for now I run my vdirsyncer sync and update-remind script once every 15 minutes inside the crontab.

@fourstepper
Copy link

Would you mind sharing the script?

@Anachron
Copy link

Anachron commented Apr 27, 2021

Sure, I am not sure how well it works for your setup though:

update-remind:

ics2rem | LC_ALL=C sort -k2,2M -k3,3n > ~/app/dat/remind/remind.rem

(You'll have to edit the ~/app/dat/remind/remind.rem path)

and ics2rem:

dir=$(realpath $(dirname "${0}"))
find ~/usr/cal -name '*.ics' -exec awk -F: -f "${dir}/ics2rem.awk" {} \;

(You'll have to edit the ~/usr/cal path)

ics2rem.awk:

#!/usr/bin/env awk -F

BEGIN {
  s=""; e=""; u=""; yc="date +%Y"
  yc | getline y;
  close(yc);
}
{
  if (match($1, /DTSTART/)) { s=$2 }
  else if (match($1, /DTEND/)) { e=$2 }
  else if (match($1, /SUMMARY/)) { u=$2 }
}
END {
  split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", month, " ");
  if (match(s, /T/)) {
    split(s,sa,"T");
    sd=sa[1]; st=sa[2];
  } else {
    sd=s; st="";
  }
  if (match(e, /T/)) {
    split(e,ea,"T");
    ed=sa[1]; et=ea[2];
  } else {
    ed=e; et="";
  }
  if (match(sd, y)) {
    split(sd,sda,"");
    split(st,sta,"");
    if (sda[5] == 0) { smon=sda[6] } else { smon=sda[5]""sda[6] }
    smon=month[smon];
    sday=sda[7]""sda[8]; stime=sta[1]""sta[2]":"sta[3]""sta[4];
    if (stime != ":") { sts=" AT "stime } else { sts="" }
    print "REM "smon" "sday""sts" MSG "u;
  }
}

@fourstepper
Copy link

fourstepper commented Jun 10, 2021

I am thinking about having a go to at making remhind good.

I have tested it right now and a few things have popped up:

  1. It doesn't handle if RRULE UNTIL values aren't specified in UTC when DTSTART is timezone-aware - this has been a thing with a few of my events, I think imported from google's calendar some time back - this shouldn't be a major problem
  2. It uses the XDG_CACHE_HOME for storing the DB and from what I've seen, it, for some reason unknown to me so far, doesn't store the events persistently; the events seem to be parsed on every startup. It would be nice to have those persistent for obvious reasons.

I am writing here to understand what the general consesus is regarding the configuration and parsing events. I think it would be perhaps be a good idea to use the vdirsyncer's database for the events, if vdirsyncer would be considered a dependency.

What do you think some important aspects as far as the integration with other projects and architecture are? Thanks

@nicoe
Copy link

nicoe commented Jun 12, 2021 via email

@WhyNotHugo
Copy link
Member

WhyNotHugo commented Jun 12, 2021

I have a couple of tools that listen for changes in my caldav collections, and trigger updates, one in go and one as a simple shell script.

The principle is:

  • Run continuously in the background.
  • Ask the OS to notify when a file inside a collection has change.
  • Do stuff (the first example runs vdirsyncer, the second updates my statusbar with the next event.

I think the same idea can be used for an alarms daemon:

  • Run continuously in the background.
  • When there are changes, run khal alarms to get an updated list of alarms.
  • Somehow queue showing a notification.

Using at is nice and fast, but requires the user to set it up. Simply sleeping until the right time works, but there are issues is the system is suspended. If a process sets a timer for one hour, and the system sleeps 20 minutes, then the timer is "paused" for those 20 minutes and will expire 20 minutes later that the intended wall-clock time. Using CLOCK_BOOTTIME is a good workaround for this. However, that's non-trivial from Python (it requires C-code). I've personally only used go for this.

Note that I mention khal alarms here. I definitely thing it makes sense for khal to do all the ISC parsing and simply print alarms.

Maybe in simple tabular format?

$ khal alarms
Today, Sat 2021-06-12
19:00 19:30-20:30 Have fun

(format is alarm_time event_time event_name)

Or maybe JSON?

$ khal alarms --json
[
  {"eventName": "Have fun", startTime: 1621953998, "endTime": 1621953998, "alarms": [1621953998]}
]

@dorsiflexion
Copy link

I'm dropping my shell script here which gives a big rofi notification pop-up whenever an alarm is coming up. I wrote this since nothing posted here so far fits my needs. Maybe someone will find this useful.

#!/bin/sh

# notify a khal user via rofi about upcoming events
#
# dependencies: ·khal
#               ·rofi >= 1.7.0 (-timeout-delay 60 -timeout-action "kb-cancel")
#               ·column (eye candy only. can be removed without problems)
#
# usage: put it into a cronjob and let it run every 15 min or so
# -only events with reminders will trigger an alarm (shown in khal as: ⏰)
# -snoozed events will show up again on the next run
# -dismissed events will not trigger an alarm again
# -no alarm will be triggered if the script did never run
#  for the duration of an event or in TIMESPAN beforehand
#
# rofi: -Shift+Return toggles item selection
#       -if any item is selected, "cursor" position has no effect
#       -timeout after 60 seconds; this snoozes all events
####################


# adjust cache file location to your liking
cache_file="${XDG_CACHE_HOME:-$HOME/.cache}/khal-events"
# events starting in the next TIMESPAN will trigger an alarm
timespan='60min'
####################

# this is needed for cronjobs and Xorg
export DISPLAY=":0"
snooze_string='💤 Snooze all.'
dismiss_string='✅ Dismiss all.'
# only show events with ⏰ symbol (=has alarm set)
# remove arrows for all-day events to only have one notification
# remove ⏰ symbol because it is obvious
events="$(khal list --format \
  '{calendar}: {cancelled}{start-style}{to-style}{end-style} {title}{description-separator}{description}{location}{repeat-symbol}{alarm-symbol}' \
  now "${timespan}" \
  | sed -ne '/⏰/ p' \
  | sed -e 's/⏰//g' -e 's/\(↦\|↔\|⇥\)[^[:blank:]]*/all-day/g' \
  | column -tl 3)"

# get events that are new compared to cache file
new_events="$(diff -NwU 0 "${cache_file}" - << EOF | sed -ne '/^\+[^+]/ p' \
  | sed -ne 's/^\+\([[:print:]].*\)$/\1/p'
${events}
EOF
)"

[ -z "${new_events}" ] && exit 0

input="$(printf '%s\n%s\n%s' "${snooze_string}" \
                "${dismiss_string}" "${new_events}" \
  | rofi -timeout-delay 60 -timeout-action "kb-cancel" \
          -dmenu -i -u 2: -no-custom -multi-select -p \
  '⏰ Alarm! Select alarms to snooze. Rest will be dismissed.')"

case "${input}" in
  "${snooze_string}")
    ;;
  "${dismiss_string}")
    printf '%s' "${events}" > "${cache_file}"
    ;;
  *)
    printf '%s' "${events}" | grep -Fve "${input}" > "${cache_file}"
    ;;
esac

@m0wer
Copy link

m0wer commented Jun 2, 2022

Here's a Python script to notify for all events in advance according to the set NOTIFY_MINS (list of minutes in advance to get a notification).

#!/usr/bin/env python3

"""Notify for all Khal events in advance.

According to the set NOTIFY_MINS (list of minutes in advance to get a
notification).
"""

import datetime
import re
from typing import List, Pattern

from sh import khal, notify_send

NOTIFY_MINS: List[int] = [1, 5, 15, 60]

events: str = str(khal("list", "--notstarted", "today", "today", _tty_out=False))
regex: Pattern = re.compile("^([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2}) (.*)$")
now: datetime.time = datetime.datetime.now().time()

for line in events.split("\n"):
    matches: List[str] = regex.findall(line)
    if len(matches) != 1:
        continue

    start: datetime.time = datetime.time(*tuple(map(int, matches[0][0].split(":"))))
    end: datetime.time = datetime.time(*tuple(map(int, matches[0][1].split(":"))))
    if " :: " in matches[0][2]:
        title, description = matches[0][2].split(" :: ")
    else:
        title = matches[0][2]
    remaining = datetime.datetime.combine(
        datetime.date.min, start
    ) - datetime.datetime.combine(datetime.date.min, now)
    remaining_minutes: int = round(remaining.total_seconds() / 60)
    print(remaining_minutes)
    if remaining_minutes in NOTIFY_MINS:
        notify_send(
            f"Upcoming calendar event: {title}",
            f"{matches[0][0]}-{matches[0][1]} in {remaining_minutes} mins.",
        )

@primalmotion
Copy link

Thanks for that python script. with a few changes it does exactly what I needed

@DhruvaSambrani
Copy link

khal already does parse alarms. However, I don't see a way to list them. Can't we add {alarms} to the format syntax for list and then use the output for whatever daemon? A similar parsing can be done for todoman if one eventually decides to do it, and the daemon can be changed to also run todoman, without having to change any major logic

@heull001
Copy link
Contributor

heull001 commented Jul 4, 2023

Alarms is a feature which I am missing too. I think, to print them by khal and shedule them by an external program is a good idea. I like way as suggested by @WhyNotHugo:

$ khal alarms
Today, Sat 2021-06-12
19:00 19:30-20:30 Have fun

(format is alarm_time event_time event_name)

Or maybe JSON?

$ khal alarms --json
[
{"eventName": "Have fun", startTime: 1621953998, "endTime": 1621953998, "alarms": [1621953998]}
]

I started working on this, but there are some questions I'd like to discuss:

Format

It is not very simple to add a format-string alarm-time and just call event.format(), because there can be more than one alarm per event, so the event-object would'nt know which alarm to print. I can see two ways to handle this:

  1. add a format-string and replace it manually before or after calling event.format()
  2. don't add a new formatstring and print alarms only in hardcoded format

the output of this command is primarily intended to be processed by other programs, therefore I think, the second method should be okay.

Config

I think we need two timedelta-parameters. One that specifies for which period triggering alarms should be issued and one in which period events with alarms should be searched for. My alarms are mostly not longer than some hours before the event or some hours after the start of the event in case of whole day events like birthdays. However, as far as I know, there is no limit, so we should give users the ability to set the time to search.

I am not sure about the names for the parameters. What do you think about alarm_timedelta and alarm_search_timedelta? I am not realy sattisfied with this names, so if someone has better suggestions ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests