From 8aaf3b53192e62abfd28eddf79f9a9de9d7834f5 Mon Sep 17 00:00:00 2001 From: kerfuzzle <58907164+kerfuzzle@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:56:44 +0100 Subject: [PATCH] hyprsunset: Update module to use hyprsunset.conf Updates the hyprsunset module so that it creates the hyprsunset.conf file introduced in v0.3.0 This allows hyprsunset to manange the transition timings instead of defining extra systemd services/timers See https://wiki.hypr.land/Hypr-Ecosystem/hyprsunset/#configuration Signed-off-by: Austin Horstman --- modules/services/hyprsunset.nix | 219 +++++++++++++++++++++----------- 1 file changed, 148 insertions(+), 71 deletions(-) diff --git a/modules/services/hyprsunset.nix b/modules/services/hyprsunset.nix index 811d3117d..7c2c62891 100644 --- a/modules/services/hyprsunset.nix +++ b/modules/services/hyprsunset.nix @@ -22,7 +22,7 @@ in default = [ ]; description = "Additional command-line arguments to pass to `hyprsunset`."; example = [ - "--identity" + "--verbose" ]; }; @@ -69,89 +69,166 @@ in } ''; }; + + settings = lib.mkOption { + type = + with lib.types; + let + valueType = + nullOr (oneOf [ + bool + int + float + str + path + (attrsOf valueType) + (listOf valueType) + ]) + // { + description = "Hyprsunset configuration value"; + }; + in + valueType; + default = { }; + description = '' + Hyprsunset configuration written in Nix. Entries with the same key + should be written as lists. Variables' and colors' names should be + quoted. See for more examples. + ''; + example = lib.literalExpression '' + { + max-gamma = 150; + + profile = [ + { + time = "7:30"; + identity = true; + } + { + time = "21:00"; + temperature = 5000; + gamma = 0.8; + } + ]; + }; + ''; + }; + + importantPrefixes = lib.mkOption { + type = with lib.types; listOf str; + default = [ "$" ]; + example = [ "$" ]; + description = '' + List of prefix of attributes to source at the top of the config. + ''; + }; + + systemdTarget = lib.mkOption { + type = lib.types.str; + default = config.wayland.systemd.target; + defaultText = lib.literalExpression "config.wayland.systemd.target"; + example = "hyprland-session.target"; + description = "Systemd target to bind to."; + }; }; config = lib.mkIf cfg.enable { assertions = [ { - assertion = (config.wayland.windowManager.hyprland.package != null); + assertion = (config.wayland.windowManager.hyprland.package != null || cfg.transitions == { }); message = '' - Can't set services.hyprsunset.enable if wayland.windowManager.hyprland.package - is set to null. If you are using Hyprland's upstream flake, see: + Can't set services.hyprsunset.enable when using the deprecated option + services.hyprsunset.transitions if wayland.windowManager.hyprland.package + is set to null. Either migrate your configuration to use services.hyprsunset.settings + or, if you are using Hyprland's upstream flake, see: . ''; } ]; - systemd.user = lib.mkIf (config.wayland.windowManager.hyprland.package != null) ( - let - # Create the main persistent service that maintains the IPC socket - # Create a service for each transition in the transitions configuration - # These services will send requests to the persistent service via IPC - transitionServices = lib.mapAttrs' ( - name: transitionCfg: - lib.nameValuePair "hyprsunset-${name}" { - Install = { }; - Unit = { - ConditionEnvironment = "WAYLAND_DISPLAY"; - Description = "hyprsunset transition for ${name}"; - After = [ "hyprsunset.service" ]; - Requires = [ "hyprsunset.service" ]; - }; + xdg.configFile."hypr/hyprsunset.conf" = lib.mkIf (cfg.settings != { }) { + text = lib.hm.generators.toHyprconf { + attrs = cfg.settings; + inherit (cfg) importantPrefixes; + }; + }; - Service = { - Type = "oneshot"; - # Execute multiple requests sequentially - ExecStart = lib.concatMapStringsSep " && " ( - cmd: - "${lib.getExe' config.wayland.windowManager.hyprland.package "hyprctl"} hyprsunset ${lib.escapeShellArgs cmd}" - ) transitionCfg.requests; - }; + systemd.user = + lib.mkIf (config.wayland.windowManager.hyprland.package != null || cfg.transitions == { }) + ( + let + # Handle deprecated transitions option + # If this is unused, no additional services will be created as transitions will be empty + # Create a service for each transition in the transitions configuration + # These services will send requests to the persistent service via IPC + transitionServices = lib.mapAttrs' ( + name: transitionCfg: + lib.nameValuePair "hyprsunset-${name}" { + Install = { }; + + Unit = { + ConditionEnvironment = "WAYLAND_DISPLAY"; + Description = "hyprsunset transition for ${name}"; + After = [ "hyprsunset.service" ]; + Requires = [ "hyprsunset.service" ]; + }; + + Service = { + Type = "oneshot"; + # Execute multiple requests sequentially + ExecStart = lib.concatMapStringsSep " && " ( + cmd: + "${lib.getExe' config.wayland.windowManager.hyprland.package "hyprctl"} hyprsunset ${lib.escapeShellArgs cmd}" + ) transitionCfg.requests; + }; + } + ) cfg.transitions; + in + { + services = { + hyprsunset = { + Install = { + WantedBy = [ cfg.systemdTarget ]; + }; + + Unit = { + ConditionEnvironment = "WAYLAND_DISPLAY"; + Description = "hyprsunset - Hyprland's blue-light filter"; + After = [ config.wayland.systemd.target ]; + PartOf = [ config.wayland.systemd.target ]; + X-Restart-Triggers = lib.mkIf (cfg.settings != { }) [ + "${config.xdg.configFile."hypr/hyprsunset.conf".source}" + ]; + }; + + Service = { + ExecStart = "${lib.getExe cfg.package} ${lib.escapeShellArgs cfg.extraArgs}"; + Restart = "always"; + RestartSec = "10"; + }; + }; + } + // transitionServices; + + timers = lib.mapAttrs' ( + name: transitionCfg: + lib.nameValuePair "hyprsunset-${name}" { + Install = { + WantedBy = [ config.wayland.systemd.target ]; + }; + + Unit = { + Description = "Timer for hyprsunset transition (${name})"; + }; + + Timer = { + OnCalendar = transitionCfg.calendar; + Persistent = true; + }; + } + ) cfg.transitions; } - ) cfg.transitions; - in - { - services = { - hyprsunset = { - Install = { - WantedBy = [ config.wayland.systemd.target ]; - }; - - Unit = { - ConditionEnvironment = "WAYLAND_DISPLAY"; - Description = "hyprsunset - Hyprland's blue-light filter"; - After = [ config.wayland.systemd.target ]; - PartOf = [ config.wayland.systemd.target ]; - }; - - Service = { - ExecStart = "${lib.getExe cfg.package} ${lib.escapeShellArgs cfg.extraArgs}"; - Restart = "always"; - RestartSec = "10"; - }; - }; - } - // transitionServices; - - timers = lib.mapAttrs' ( - name: transitionCfg: - lib.nameValuePair "hyprsunset-${name}" { - Install = { - WantedBy = [ config.wayland.systemd.target ]; - }; - - Unit = { - Description = "Timer for hyprsunset transition (${name})"; - }; - - Timer = { - OnCalendar = transitionCfg.calendar; - Persistent = true; - }; - } - ) cfg.transitions; - } - ); + ); }; }