Watch
1
0
Fork
You've already forked pim_waybar
0
forked from xff/pim_waybar
AI-generated PIM and Waybar module
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-03 16:16:26 +03:00
pim_waybar.py Добавил в дефолтный конфиг пропущенный new_event_calendar. Заметил, что new_event_calendar в коде используется, а в конфиге её нет. 2026-08-03 16:09:28 +03:00
README.md Добавлен README.md 2026-07-31 10:22:56 +03:00

pim-waybar

Утилита для интеграции календаря и списка задач в панель Waybar. Работает напрямую с файлами формата VEVENT/VTODO (.ics) в vdir-каталогах (структура, которую использует, например, vdirsyncer), без обращения к внешним программам khal или todo (todoman). Включает фоновый агент синхронизации/уведомлений и полноэкранный текстовый интерфейс (curses) для просмотра и редактирования событий и задач.

Возможности

  • Два модуля для Waybar: календарь и задачи, каждый — отдельный JSON-объект {text, tooltip, class}.
  • Фоновый агент: периодически запускает vdirsyncer sync, перестраивает локальный кэш и отправляет desktop-уведомления по сработавшим напоминаниям.
  • Комбинированный TUI (calendar-tui / tasks-tui) с двумя панелями — календарём (сетка месяца + временная шкала дня) и списком задач.
  • Создание, редактирование, удаление и экспорт событий и задач напрямую в .ics-файлах; чтение и запись реализованы через модуль icalendar, разбор повторов — через dateutil.rrule.
  • Полноценная обработка повторяющихся событий и задач: RRULE, EXDATE, переопределения отдельных вхождений через RECURRENCE-ID, отмена (STATUS:CANCELLED) конкретного вхождения.
  • Иерархия задач «родитель — подзадача» через RELATED-TO, каскадное завершение и удаление поддерева.
  • Напоминания (VALARM) для событий и задач с отправкой через notify-send и защитой от повторной отправки одного и того же напоминания.
  • Разделение статусов «сеть недоступна» и «реальная ошибка синхронизации/чтения» — временная недоступность сети не помечается как ошибка в интерфейсе.

Требования

  • Python 3 с модулем curses (в некоторых дистрибутивах ставится отдельным пакетом python3-curses).
  • Пакет icalendar (python3-icalendar или pip install --user icalendar).
  • Пакет python-dateutil (python3-dateutil или pip install --user python-dateutil).
  • vdirsyncer — опционально, нужен только для синхронизации с сервером CalDAV/CardDAV; без него скрипт продолжает работать с локальными файлами, а в статусе синхронизации фиксируется ошибка «command not found».
  • notify-send — опционально, нужен для всплывающих напоминаний.
  • Шрифт с поддержкой Nerd Font/Font Awesome в Waybar, если используются иконки (calendar_icon, tasks_icon).

Хранение данных

Никакой собственной базы данных нет: TUI читает и правит те же самые .ics-файлы, которые лежат в vdir-каталогах.

  • События (VEVENT) собираются из каталогов, перечисленных в calendar_roots.
  • Задачи (VTODO) — из каталогов в todo_roots.
  • По умолчанию оба списка указывают на ~/.calendars, ~/.local/share/calendars, ~/.local/share/vdirsyncer; внутри каждого корня автоматически обнаруживаются все вложенные vdir-коллекции.
  • Если auto_discover_todoman включён (по умолчанию так), пути коллекций дополнительно подхватываются из существующего конфига todoman (config.py) — это используется только как источник путей, сам бинарник todo нигде не запускается.
  • Если один и тот же UID мастер-компонента найден сразу в нескольких файлах (например, из-за прерванной синхронизации), используется только самая свежая копия — по DTSTAMP, а если его нет, по времени изменения файла. Остальные файлы на диске не трогаются, предупреждение попадает в debug.uid_conflicts (см. команду debug).

Служебные файлы

Каталог $XDG_CACHE_HOME/pim-waybar (по умолчанию ~/.cache/pim-waybar):

Файл Назначение
cache.json Данные для Waybar-модулей и текущий статус синхронизации; читается командой bar
notified.json Журнал уже отправленных напоминаний (дедупликация)
agent.log Лог фонового агента, ротация при достижении ~1 МБ
agent.pid PID работающего агента
agent.lock, sync.lock Файловые блокировки: не более одного агента и не более одной синхронизации одновременно

Конфигурация: $XDG_CONFIG_HOME/pim-waybar/config.json (по умолчанию ~/.config/pim-waybar/config.json), формат — JSON. Отсутствующие в файле ключи берутся из значений по умолчанию.

Команды

Команда Действие
bar calendar / bar tasks Печатает в stdout JSON-объект {text, tooltip, class} соответствующего модуля — читается из cache.json, для использования в Waybar (return-type: json)
agent Запускает фоновый процесс: цикл синхронизации, пересборки кэша и уведомлений. Если агент уже запущен, повторный вызов просто завершается
restart Останавливает уже работающий агент (проверяя по /proc, что это действительно процесс pim-waybar agent, а не переиспользованный PID) и запускает новый
refresh-once Одна пересборка кэша из локальных файлов, без синхронизации и без уведомлений
sync-once Один прогон vdirsyncer sync и последующая пересборка кэша, без уведомлений
calendar-tui Комбинированный TUI с начальным фокусом на панели календаря
tasks-tui Комбинированный TUI с начальным фокусом на панели задач
debug JSON-дамп текущей конфигурации, обнаруженных каталогов/коллекций и содержимого кэша
debug-month [YYYY-MM] Пошаговая трассировка обработки одного месяца: какие файлы найдены, какие исключены как дубликаты UID, что дала разворачивание повтора (RRULE) для каждого файла, что в итоге вернула сборка событий. Без аргумента берётся текущий месяц

Настройка модулей Waybar

Пример записи в ~/.config/waybar/config (пути и интервал — под конкретную установку):

"custom/pim-calendar": {
    "exec": "~/scripts/pim_waybar.py bar calendar",
    "return-type": "json",
    "interval": 15,
    "on-click": "~/scripts/pim_waybar.py calendar-tui"
},
"custom/pim-tasks": {
    "exec": "~/scripts/pim_waybar.py bar tasks",
    "return-type": "json",
    "interval": 15,
    "on-click": "~/scripts/pim_waybar.py tasks-tui"
}

Сам bar только читает уже готовый cache.json — данные обновляет фоновый агент, поэтому его нужно запускать при старте сессии (например, exec_always pim_waybar.py restart в конфиге Sway/i3), а не по требованию из Waybar. Если cache.json не обновлялся дольше stale_after_sec (по умолчанию — авто: max(60, 3 × cache_interval_sec)), модуль получает дополнительный CSS-класс stale; класс empty — когда показывать нечего.

Управление в TUI

Общие клавиши

Клавиша Действие
Tab Переключение фокуса между панелями «Календарь» и «Задачи»
F1 / ? Экран со списком клавиш
r Полное обновление обеих панелей с диска
F10 / q / Esc Выход

Панель календаря

Клавиша Действие
F6 Переключение между сеткой месяца (верх) и временной шкалой дня (низ)
F3 / Enter / s Просмотр события (при нескольких событиях в дне — выбор из списка)
F4 / e Редактирование (для повторяющихся — выбор области: только это / это и последующие / вся серия)
F7 / n Новое событие
F8 / x Удаление (с тем же выбором области для повторяющихся)
F9 / X Экспорт события (целиком, вся серия) в отдельный .ics-файл
F5 / t Переход к сегодняшнему дню, шкала центрируется на текущем времени
: Переход к дате (форматы: YYYY-MM-DD, DD.MM.YYYY, today, tomorrow)
F2 Обновление с диска
PgUp / PgDn Предыдущий / следующий месяц
↑/↓, j/k Сетка: на неделю; шкала: перемещение выделенного события
←/→, h/l На день
+ / - В режиме шкалы: масштаб (4ч / 2ч / 1ч / 30мин / 15мин на строку)

Панель задач

Клавиша Действие
F2 / D Редактирование заметок ($EDITOR)
F3 / Enter / s Просмотр деталей задачи
F4 / e Редактирование (название, список, срок, приоритет, теги, повтор, напоминание)
F5 / z Отложить (+1 день, 1ч/3ч/1д/1нед, либо конкретная дата)
F6 / P Назначить/сменить родительскую задачу
F7 / n Новая задача
F8 / x Удаление (вместе с открытыми подзадачами, с подтверждением)
F9 / H История выполненных задач
d Выполнить (вместе с открытыми подзадачами, с подтверждением)
a Редактирование сразу с переходом к полю «Напоминание»
p Редактирование сразу с переходом к полю «Повтор»
R Редактирование сырого .ics в $EDITOR
N Новая подзадача выбранной задачи
u Отвязать от родителя
/ Фильтр по названию/списку/тегу
↑/↓, j/k Перемещение по списку
PgUp/PgDn Перемещение на 10 позиций

Синхронизация

  • Команда синхронизации задаётся vdirsyncer_cmd (по умолчанию ["vdirsyncer", "sync"]).
  • Интервал между синхронизациями — sync_interval_sec (по умолчанию 600 с); тайм-аут одного запуска — sync_timeout_sec (120 с).
  • Если синхронизация не удалась из-за сети (список шаблонов в sync_offline_patterns: недоступность хоста, DNS, тайм-аут соединения и т. п.), статус фиксируется как «офлайн», а не как ошибка — повтор происходит быстрее, через sync_offline_retry_sec (60 с), а не через полный интервал.
  • Любая другая ненулевая ошибка (авторизация, конфигурация и т. д.) фиксируется как реальная ошибка синхронизации.
  • Параллельный запуск синхронизации исключён файловой блокировкой (sync.lock); если она уже занята, попытка просто пропускается.

Уведомления

  • Источник — свойства VALARM внутри самих VEVENT/VTODO; отдельного механизма напоминаний вне ICS нет.
  • Каждое конкретное напоминание отправляется один раз: ключ (UID события/задачи + время срабатывания) сохраняется в notified.json, повторный показ того же напоминания не происходит даже при частых перестройках кэша.
  • Окно проверки — «с последней проверки по текущий момент», а не фиксированная задержка после времени напоминания: это гарантирует доставку даже если предыдущий цикл агента был занят долгой синхронизацией.
  • alarm_grace_sec (90 с) — дополнительное окно «догона» только при самом старте агента, для напоминаний, которые сработали незадолго до запуска.
  • notify_overdue_tasks (по умолчанию выключено) — отдельная ежедневная всплывающая нотификация по просроченным задачам без явного VALARM; счётчик просроченных задач в тултипе Waybar от этой настройки не зависит и показывается всегда.
  • event_urgency / task_urgency — уровень срочности (notify-send -u) для уведомлений по событиям и задачам.

Часовые пояса и формат записи дат

  • По умолчанию (use_tzid_dates: true) дата со временем записывается как ;TZID=<зона>:<локальное время> вместе с минимальным сопутствующим блоком VTIMEZONE — по той же схеме, что используют iOS/2Do. Зона по умолчанию определяется автоматически (/etc/timezone либо /etc/localtime), либо задаётся явно через tzid_name.
  • При use_tzid_dates: false даты пишутся в абсолютном UTC-формате (суффикс Z), без VTIMEZONE.
  • Для повторяющейся задачи с ограничением по дате (RRULE;UNTIL=...) TZID-схема принудительно не используется независимо от настройки — это обход известной проблемы совместимости в python-dateutil при продвижении такой задачи на следующее вхождение.

Полный список параметров конфигурации

Ключ По умолчанию Назначение
calendar_roots ~/.calendars, ~/.local/share/calendars, ~/.local/share/vdirsyncer Корневые каталоги с vdir-коллекциями событий
todo_roots те же три пути Корневые каталоги с vdir-коллекциями задач
auto_discover_todoman true Дополнительно брать пути коллекций из конфига todoman, если он есть
cascade_complete_with_open_children true При завершении задачи с открытыми подзадачами завершать и их (иначе — отказ)
cascade_delete_with_open_children true При удалении задачи с открытыми подзадачами удалять и их (иначе — они остаются, но без родителя)
lookahead_days 7 Горизонт «предстоящих» событий в тултипе и окне уведомлений
cache_interval_sec 30 Период пересборки кэша фоновым агентом
sync_interval_sec 600 Период синхронизации через vdirsyncer
sync_offline_retry_sec 60 Задержка перед повторной попыткой после «офлайн»-результата
sync_timeout_sec 120 Тайм-аут одного запуска vdirsyncer
stale_after_sec 0 (авто) Порог, после которого модуль Waybar помечается классом stale; 0 = max(60, 3 × cache_interval_sec)
sync_offline_patterns список сетевых сообщений об ошибке По каким подстрокам в выводе vdirsyncer ошибка считается временной (сеть), а не реальной
alarm_grace_sec 90 Окно «догона» напоминаний сразу после старта агента
event_default_length_min 60 Длительность нового события по умолчанию, если конец не указан
event_title_max 32 Обрезка названия события в тексте модуля Waybar
task_title_max 48 Обрезка названия задачи в тултипе
calendar_timeline_start_hour / calendar_timeline_end_hour 0 / 24 Границы временной шкалы дня в TUI
waybar_span_size small Размер шрифта (Pango <span size>) основного текста модулей
calendar_icon_span_size / tasks_icon_span_size small Размер шрифта иконок
calendar_icon_rise / tasks_icon_rise 1400 / `` Вертикальное смещение глифа иконки (Pango rise)
calendar_icon / tasks_icon `` Символ иконки перед текстом модуля
new_task_due_today true Новой задаче из TUI сразу ставится срок «сегодня»
new_task_list `` Фиксированный список для новых задач; пусто — определяется автоматически или запрашивается
new_event_calendar не задан Фиксированный календарь для новых событий, создаваемых не через TUI-форму
use_tzid_dates true Схема записи дат: TZID + VTIMEZONE (true) или абсолютный UTC (false)
tzid_name `` (автоопределение) Явное имя часового пояса IANA для схемы TZID
raw_editor `` Редактор для правки сырого .ics; пусто — $VISUAL, затем $EDITOR, затем vi
tooltip_limit 8 Максимум строк-пунктов одной секции тултипа
tooltip_newline \n Символ переноса строк в тултипе
history_limit 50 Максимум строк в истории выполненных задач (H в TUI)
vdirsyncer_cmd ["vdirsyncer", "sync"] Команда синхронизации
notify_cmd ["notify-send"] Команда отправки desktop-уведомлений
event_urgency normal Срочность уведомлений по событиям
task_urgency critical Срочность уведомлений по задачам
notify_overdue_tasks false Отдельное ежедневное всплывающее уведомление по просроченным задачам без явного напоминания

Диагностика

  • debug — сводка по текущей конфигурации, обнаруженным каталогам/коллекциям и итоговому содержимому кэша (модули, статус синхронизации, счётчики загруженных событий/задач, конфликты UID).
  • debug-month [YYYY-MM] — по каждому файлу в calendar_roots: список сырых VEVENT (с признаками RRULE/RECURRENCE-ID), результат разворачивания повторов в окне месяца (включая текст исключения, если разворачивание упало), и итоговый список событий после полной сборки. Полезно, когда конкретное событие не показывается там, где ожидается.
  • agent.log — лог фонового агента: результаты синхронизаций, ошибки одной итерации цикла (одна неудачная итерация не останавливает агент), решения об офлайн-статусе.