Files
Nomarchy/modules/home/nightlight.nix
Bernardo Magri 5747dc9839 feat(nightlight): persistent off via state marker
A manual `off` now drops ~/.local/state/nomarchy/nightlight-off and the
hyprsunset unit refuses to (re)start while it exists
(Unit.ConditionPathExists=!%h/...), so the off survives logout/reboot
until toggled back on. `on`/toggle-on remove the marker before starting
(a failed condition would otherwise no-op the start). Without the marker
behaviour is unchanged: the service stays WantedBy=graphical-session.target
and resumes its schedule at login, so persistence is opt-in via the toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:09:22 +01:00

80 lines
3.7 KiB
Nix

# Night light — a scheduled blue-light filter via hyprsunset (Hyprland's own
# gamma/temperature tool). Warm at night, identity (no shift) by day;
# hyprsunset's time-based `profile` entries handle the schedule and pick the
# right state on session start. Opt-in via nomarchy.nightlight.enable.
#
# The hyprsunset HM service module is provided by home-manager; this only
# configures it. Override anything with plain services.hyprsunset.* options.
#
# nomarchy-nightlight is the menu/Waybar toggle: it starts/stops the hyprsunset
# *service* (so `systemctl --user is-active` is the single source of truth and
# the screen restores to true colours when stopped), letting you force the
# filter off for colour-sensitive work and back on without touching the config.
{ config, lib, pkgs, ... }:
let
cfg = config.nomarchy.nightlight;
# Toggle/status helper, on PATH so the menu and both the generated and
# whole-swap Waybars can drive it by name. Self-gates: when the unit doesn't
# exist (night-light not enabled) `status` prints nothing, so the Waybar
# module hides itself — same pattern as the power-profile indicator.
nomarchy-nightlight = pkgs.writeShellScriptBin "nomarchy-nightlight" ''
unit=hyprsunset.service
# Persistent-off marker mirrors the unit's ConditionPathExists below.
# Present => night-light stays off across logout/reboot until toggled on.
# Path is literal (not XDG_STATE_HOME) so it matches the unit's %h check.
marker="$HOME/.local/state/nomarchy/nightlight-off"
mark_off() { mkdir -p "$(dirname "$marker")"; : > "$marker"; }
mark_on() { rm -f "$marker"; }
if ! systemctl --user cat "$unit" >/dev/null 2>&1; then
[ "''${1:-}" = status ] && exit 0
notify-send "Night light" "Not enabled set nomarchy.nightlight.enable."
exit 0
fi
case "''${1:-toggle}" in
status)
if systemctl --user is-active --quiet "$unit"; then
printf '{"text":"󰖔","tooltip":"Night light on warm on schedule (click to disable)","class":"on"}\n'
else
printf '{"text":"󰖙","tooltip":"Night light off (click to enable)","class":"off"}\n'
fi ;;
on) mark_on; systemctl --user start "$unit" ;;
off) mark_off; systemctl --user stop "$unit" ;;
toggle)
if systemctl --user is-active --quiet "$unit"; then
mark_off; systemctl --user stop "$unit"
notify-send "Night light off" "Screen back to true colours (stays off until re-enabled)."
else
mark_on; systemctl --user start "$unit"
notify-send "Night light on" "Warm filter follows your schedule."
fi ;;
*) echo "usage: nomarchy-nightlight [toggle|status|on|off]" >&2; exit 64 ;;
esac
'';
in
{
config = {
services.hyprsunset = lib.mkIf cfg.enable {
enable = true;
settings.profile = [
# Daytime: identity = no colour change.
{ time = cfg.sunrise; identity = true; }
# Night: shift to the warm temperature.
{ time = cfg.sunset; temperature = cfg.temperature; }
];
};
# Persistent off: a manual `off` drops ~/.local/state/nomarchy/nightlight-off,
# and this Condition makes the unit refuse to (re)start while it exists — so
# an off survives logout/reboot. `nomarchy-nightlight on` removes it first
# (a failed condition would otherwise no-op the start). %h = $HOME.
systemd.user.services.hyprsunset.Unit.ConditionPathExists =
lib.mkIf cfg.enable "!%h/.local/state/nomarchy/nightlight-off";
# Always on PATH (it self-gates at runtime), so a static whole-swap Waybar
# can exec it even when night-light is off without the call failing.
home.packages = [ nomarchy-nightlight ];
};
}