Skip to content

Instantly share code, notes, and snippets.

@mattdenner
Last active March 5, 2024 19:33
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattdenner/befcf099f5cfcc06ea04dcdd4969a221 to your computer and use it in GitHub Desktop.
Save mattdenner/befcf099f5cfcc06ea04dcdd4969a221 to your computer and use it in GitHub Desktop.
Suspend and then hibernate after 60 minutes

I found a post about suspending and then going into hibernate that included a really clever script. Turns out that with NixOS this is even esaier to coordinate as you have systemd so can have a before and after service. I just include this in my /etc/nixos/configuration.nix file and nixos-rebuild; then a systemctl suspend or a close of the lid will cause the hibernate timer to be set.

{ config, pkgs, ... }: let
hibernateEnvironment = {
HIBERNATE_SECONDS = "3600";
HIBERNATE_LOCK = "/var/run/autohibernate.lock";
};
in {
systemd.services."awake-after-suspend-for-a-time" = {
description = "Sets up the suspend so that it'll wake for hibernation";
wantedBy = [ "suspend.target" ];
before = [ "systemd-suspend.service" ];
environment = hibernateEnvironment;
script = ''
curtime=$(date +%s)
echo "$curtime $1" >> /tmp/autohibernate.log
echo "$curtime" > $HIBERNATE_LOCK
${pkgs.utillinux}/bin/rtcwake -m no -s $HIBERNATE_SECONDS
'';
serviceConfig.Type = "simple";
};
systemd.services."hibernate-after-recovery" = {
description = "Hibernates after a suspend recovery due to timeout";
wantedBy = [ "suspend.target" ];
after = [ "systemd-suspend.service" ];
environment = hibernateEnvironment;
script = ''
curtime=$(date +%s)
sustime=$(cat $HIBERNATE_LOCK)
rm $HIBERNATE_LOCK
if [ $(($curtime - $sustime)) -ge $HIBERNATE_SECONDS ] ; then
systemctl hibernate
else
${pkgs.utillinux}/bin/rtcwake -m no -s 1
fi
'';
serviceConfig.Type = "simple";
};
}
@aou
Copy link

aou commented Apr 17, 2023

this is awesome! suspend-then-hibernate from systemd was first broken by a weird spat between the maintainer and everyone else, then kinda stopped working entirely for me. This config works, with one minor addition you might want to add at some point -- if you have an nvidia card and use the power management systemd services, then hibernate-after-recovery should come after "nvidia-resume.service".

@HuzaifaTP
Copy link

I tested it on my nixos setup and works as intended. Many thanks 😁. Was curious as to why before it goes to hibernation it needs to wake up first from suspend.

@HuzaifaTP
Copy link

HuzaifaTP commented Dec 16, 2023

A small modification on my end in case anyone is interested, here it will only go into hibernation from suspension IF it is not plugged into power source:

{ config, pkgs, ... }:

let
  hibernateEnvironment = {
    HIBERNATE_SECONDS = "10";
    HIBERNATE_LOCK = "/var/run/autohibernate.lock";
  };
in {

  systemd.services."awake-after-suspend-for-a-time" = {
    description = "Sets up the suspend so that it'll wake for hibernation only if not on AC power";
    wantedBy = [ "suspend.target" ];
    before = [ "systemd-suspend.service" ];
    environment = hibernateEnvironment;
    script = ''
      if [ $(cat /sys/class/power_supply/AC/online) -eq 0 ]; then
        curtime=$(date +%s)
        echo "$curtime $1" >> /tmp/autohibernate.log
        echo "$curtime" > $HIBERNATE_LOCK
        ${pkgs.utillinux}/bin/rtcwake -m no -s $HIBERNATE_SECONDS
      else
        echo "System is on AC power, skipping wake-up scheduling for hibernation." >> /tmp/autohibernate.log
      fi
    '';
    serviceConfig.Type = "simple";
  };

  systemd.services."hibernate-after-recovery" = {
    description = "Hibernates after a suspend recovery due to timeout";
    wantedBy = [ "suspend.target" ];
    after = [ "systemd-suspend.service" ];
    environment = hibernateEnvironment;
    script = ''
      curtime=$(date +%s)
      sustime=$(cat $HIBERNATE_LOCK)
      rm $HIBERNATE_LOCK
      if [ $(($curtime - $sustime)) -ge $HIBERNATE_SECONDS ] ; then
        systemctl hibernate
      else
        ${pkgs.utillinux}/bin/rtcwake -m no -s 1
      fi
    '';
    serviceConfig.Type = "simple";
  };

}

@verymilan
Copy link

Very nice! Note for the 13'' AMD Framework laptop: AC needs to be ACAD :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment