All checks were successful
Check / eval (push) Successful in 4m14s
The timer's OnCalendar now bakes the configured times (falling back like _hhmm_to_min on a hand-edited state), so the switch lands at the configured minute instead of up to 15 minutes late. The original poll's robustness case is covered without it: Persistent catches transitions missed while powered off, systemd fires elapsed calendar timers on resume, and OnStartupSec settles the theme after login. Baked times mean a time edit must rebuild: the menu's Sunrise/Sunset writes now run `auto --force` when enabled (one rebuild re-bakes the timer and lands on the right theme, same as the enable flow). Day and night slugs stay live reads — but with no poll to catch them up, editing the slot currently on screen applies immediately via a plain `auto`. New eval check `auto-theme-timer` asserts the bake state-bridges-style: configured times reach OnCalendar, garbage falls back to 07:00/20:00, disabled installs no unit. Docs + hardware-queue entry updated to the new semantics. V2: checks.auto-theme (KVM VM, passed) covers the auto engine the timer ticks; checks.auto-theme-timer (eval, passed) covers the unit; OnCalendar strings systemd-analyze-validated; the rebuilt menu script passes bash -n with the new branches in place. V3 pending: live timer firing at the exact configured minute on hardware (existing "Auto-theme pair flip" queue entry, refreshed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
59 lines
2.7 KiB
Nix
59 lines
2.7 KiB
Nix
# Auto time-of-day theme switch (BACKLOG #79, VISION § D). A user timer
|
|
# runs `nomarchy-state-sync auto` at the configured sunrise and sunset,
|
|
# which reads settings.autoTheme = { enable, day, night, sunrise, sunset }
|
|
# from the state file and applies the day or night preset for the current
|
|
# clock — through the SAME one engine as a manual `apply` (no second
|
|
# pipeline).
|
|
#
|
|
# The trigger times are BAKED into OnCalendar at rebuild (originally this
|
|
# was a 15-min poll; exact times won on wasted wakeups — Bernardo
|
|
# 2026-07-18), so a time edit must rebuild: the menu's Sunrise/Sunset
|
|
# writes run `auto --force`, whose apply is that rebuild. Missed
|
|
# transitions are covered without polling: Persistent=true catches ones
|
|
# that pass while powered off, systemd fires elapsed OnCalendar timers on
|
|
# resume from suspend, and OnStartupSec settles the theme just after
|
|
# login. The command self-gates (no-op when disabled) and is idempotent
|
|
# (it only rebuilds when the active theme actually needs to change), so
|
|
# every extra firing is cheap.
|
|
#
|
|
# Install is gated on the state flag (like nomarchy.updates gates on its
|
|
# enable), so a machine not using the feature carries no timer; enabling
|
|
# it from the menu (slice 3) writes the flag + rebuilds, which is what
|
|
# brings the timer into being.
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
at = config.nomarchy.settings.autoTheme or { };
|
|
# "HH:MM" or the fallback — mirrors _hhmm_to_min in nomarchy-state-sync,
|
|
# so a hand-edited state can't bake an OnCalendar systemd rejects.
|
|
hhmm = v: fallback:
|
|
if builtins.isString v
|
|
&& builtins.match "([01][0-9]|2[0-3]):[0-5][0-9]" v != null
|
|
then v else fallback;
|
|
in
|
|
lib.mkIf (at.enable or false) {
|
|
systemd.user.services.nomarchy-auto-theme = {
|
|
Unit.Description = "Apply the day/night theme for the current time";
|
|
Service = {
|
|
Type = "oneshot";
|
|
# The rebuild (`home-manager switch`) and its tools resolve from the
|
|
# system + per-user profiles — same PATH shape nomarchy-updates uses.
|
|
Environment = "PATH=/run/current-system/sw/bin:/etc/profiles/per-user/${config.home.username}/bin";
|
|
ExecStart = "${pkgs.nomarchy-state-sync}/bin/nomarchy-state-sync auto";
|
|
};
|
|
};
|
|
|
|
systemd.user.timers.nomarchy-auto-theme = {
|
|
Unit.Description = "Day/night theme switch at sunrise and sunset";
|
|
Timer = {
|
|
OnStartupSec = "1min"; # settle on the right theme shortly after login
|
|
OnCalendar = [
|
|
"*-*-* ${hhmm (at.sunrise or null) "07:00"}:00" # sunrise → day
|
|
"*-*-* ${hhmm (at.sunset or null) "20:00"}:00" # sunset → night
|
|
];
|
|
Persistent = true; # catch a transition missed while powered off
|
|
};
|
|
Install.WantedBy = [ "timers.target" ];
|
|
};
|
|
}
|