Implementing systemd Timers in home-manager

I’m working on setting up systemd/timers using home-manager, but I’m facing a command not found error. It seems like the packages installed by home-manager aren’t available in my system’s $PATH. Is there a way to include these packages explicitly? I’ve tried using things like ${pkgs.bash}, ${pkgs.gawk}, ${pkgs.acpi} in my setup. But I’m unsure about which package I need for the tr command in my bash script. Here’s the content of my battery_status.sh script for reference:

 #!/run/current-system/sw/bin/bash
   2   │
   3   │ # define global variables
   4   │ # BATTINFO store full battery info
   5   │ BATTINFO=$(acpi -b | grep Battery\ 0)
   6   │
   7   │ # to store percentage
   8   │ percentage=$(echo "$BATTINFO" | awk '{print $4}' | tr -d '%,')
   9   │
  10   │ # for Command Substitution backticks(`) are depricated instead use this syntax $()
  11   │ # if battery is discharging and is between 48% and 30%
  12   │ if [[ $(echo $BATTINFO | grep Discharging) && "$percentage" -lt 45 && "$percentage" -gt 15 ]] ; then
  13   │
  14   │     # to refrence a variable use this syntax $varibale_name
  15   │     dunstify "low battery" "$BATTINFO"
  16   │
  17   │ # if battery is discharging and is less than 30%
  18   │ elif [[ $(echo $BATTINFO | grep Discharging) && "$percentage" -lt 15 ]]; then
  19   │
  20   │     # set the emergency level to critical
  21   │     dunstify -u critical "low battery" "$BATTINFO"
  22   │ fi

And my battery_status.timer and battery_status.service in Home-Manager (home.nix):

# This timer runs every 5 minutes to run bash script battery_status.sh
 101   │   systemd.user.services = {
 102   │     battery_status = {
 103   │       Unit = {
 104   │         Description = "low battery notification service";
 105   │       };
 106   │       Service = {
 107   │         Type = "oneshot";
 108   │         ExecStart = toString (
 109   │          pkgs.writeShellScript "battery-status-script" ''
 110   │           set -eou pipefail
 111   │
 112   │           ${pkgs.bash}/bin/bash "/home/sukhman/Documents/sway_related/battery_status.sh";
 113   │      ''
 114   │     );
 115   │       };
 116   │       Install.WantedBy = [ "default.target" ];
 117   │     };
 118   │   };
 119   │
 120   │   systemd.user.timers = {
 121   │     battery_status = {
 122   │       Unit.Description = "timer for battery_status service";
 123   │       Timer = {
 124   │         Unit = "battery_status";
 125   │     OnBootSec = "1m";
 126   │         OnUnitActiveSec = "1m";
 127   │       };
 128   │       Install.WantedBy = [ "timers.target" ];
 129   │     };
 130   │   };

Error: i am getting command not found for acpi, awk, and tr. Thank you.

You could use

PATH=$PATH:${lib.makeBinPath [ pkgs.gawk pkgs.coreutils pkgs.acpi ]}

in your ExecStart script.

1 Like

Thanks a lot for your solution! It really helped. I just wanted to mention that I also needed to include pkgs.dunst for everything to work perfectly. Your idea worked well, and I also tried another way by making a flake to run my script. This made it easy to list all the packages I needed for the script. Just thought I’d share this alternative solution in case it might help someone else:
flake.nix:

{
   2   │   description = "A best script!";
   3   │   inputs.flake-utils.url = "github:numtide/flake-utils";
   4   │
   5   │   outputs = { self, nixpkgs, flake-utils }:
   6   │     flake-utils.lib.eachDefaultSystem (system:
   7   │
   8   │     let
   9   │
  10   │         pkgs = import nixpkgs { inherit system; };
  11   │         my-name = "my-script";
  12   │         my-buildInputs = with pkgs; [ pkgs.gawk pkgs.coreutils pkgs.acpi ];
  13   │         my-script = (pkgs.writeScriptBin my-name (builtins.readFile ./battery_status.sh)).overrideAttrs(old: {
  14   │           buildCommand = "${old.buildCommand}\n patchShebangs $out";
  15   │         });
  16   │
  17   │     in rec {
  18   │
  19   │         defaultPackage = packages.my-script;
  20   │         packages.my-script = pkgs.symlinkJoin {
  21   │           name = my-name;
  22   │           paths = [ my-script ] ++ my-buildInputs;
  23   │           buildInputs = [ pkgs.makeWrapper ];
  24   │           postBuild = "wrapProgram $out/bin/${my-name} --prefix PATH : $out/bin";
  25   │         };
  26   │       }
  27   │     );
  28   │ }

Since you’re using writeScriptBin already, you might as well use writeShellApplication and put all progs it needs in its buildInputs? You can then forget about all the other runtime env management and just exec the realised shellApplication from systemd.

1 Like