Files

137 lines
4.2 KiB
Nix

{
config,
pkgs,
lib,
...
}:
let
# Python with the ICS parsing stack baked in: icalendar + recurring
# recurrence expansion for the reminder scanner.
python = pkgs.python3.withPackages (ps: [
ps.icalendar
ps.recurring-ical-events
]);
in
{
options.services.quickshell-cal = {
enable = lib.mkEnableOption "QuickShell calendar + weather sync (Nextcloud CalDAV via vdirsyncer/khal, Open-Meteo)";
eventDays = lib.mkOption {
type = lib.types.int;
default = 31;
description = "How many days ahead of today khal should expand/return events for the shell popup.";
};
};
config = lib.mkIf config.services.quickshell-cal.enable {
# The one Nextcloud fact the sync pipeline needs beyond the secret: the
# base user-principal CalDAV URL (vdirsyncer discovers every calendar
# under it). The username + password live in the SOPS secret
# "hp-laptop/nextcloud-cal-env".
home.packages = [
python
pkgs.vdirsyncer
pkgs.pipewire
pkgs.libnotify
];
# ---------- sync script ----------
# Store the python in the Nix store with tool paths baked in (user units
# have an unpredictable PATH), then expose it on the QuickShell PATH.
# Substitutions keep the source tree readable.
xdg.configFile."quickshell-cal/qs-cal-sync.py".text =
builtins.replaceStrings
[
"@vdirsyncer@"
"@libnotify@"
"@pipewire@"
]
[
"${pkgs.vdirsyncer}"
"${pkgs.libnotify}"
"${pkgs.pipewire}"
]
(builtins.readFile ./quickshell-cal.py);
home.file.".local/bin/qs-cal-sync" = {
executable = true;
text = ''
#!/bin/sh
exec ${python}/bin/python3 \
${config.home.homeDirectory}/.config/quickshell-cal/qs-cal-sync.py "$@"
'';
};
# ---------- khal config ----------
# Generated at runtime by the sync script from the vdirsyncer discovery:
# one [[calendar]] section per subdir of ~/.local/share/vdirsyncer (khal
# cannot glob, and the calendar set only changes when Nextcloud does).
# Removing this file from HM keeps ~/.config/khal/config a plain user
# file the script may rewrite on every sync.
# ---------- periodic sync ----------
# Runs as the user so caches are written to ~/.cache without privilege
# games, and the SOPS env file is readable because the host config sets
# owner petere / group users / mode 0440 on it.
systemd.user.services.qs-cal-sync = {
Unit = {
Description = "Sync Nextcloud calendar + weather for the QuickShell popup";
After = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${config.home.homeDirectory}/.local/bin/qs-cal-sync sync";
};
};
systemd.user.timers.qs-cal-sync = {
Unit = {
Description = "Periodic Nextcloud calendar + weather sync";
After = [ "network-online.target" ];
};
Install = {
WantedBy = [ "timers.target" ];
};
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "20min";
Persistent = true;
};
};
# ---------- reminders ----------
# Fires VALARM due-tones once per minute: plays the cached chime and
# sends a desktop notification that lands in the QuickShell notification
# center. Reads only the local .ics stores (no network), so it is cheap
# enough to run every 60s.
systemd.user.services.qs-cal-remind = {
Unit = {
Description = "Fire QuickShell calendar reminders";
};
Service = {
Type = "oneshot";
ExecStart = "${config.home.homeDirectory}/.local/bin/qs-cal-sync remind";
};
};
systemd.user.timers.qs-cal-remind = {
Unit = {
Description = "Per-minute QuickShell calendar reminder check";
};
Install = {
WantedBy = [ "timers.target" ];
};
Timer = {
OnBootSec = "1min";
OnUnitActiveSec = "30s";
# default AccuracySec is a whole minute, which (plus polling phase)
# lets alerts land up to ~2min late; 1s keeps current-skew tiny.
AccuracySec = "1s";
Persistent = true;
};
};
};
}