Skip to content

Instantly share code, notes, and snippets.

@domenkozar
Last active May 12, 2024 18:07
Show Gist options
  • Save domenkozar/82886ee82efee623cdc0d19eb81c7fb7 to your computer and use it in GitHub Desktop.
Save domenkozar/82886ee82efee623cdc0d19eb81c7fb7 to your computer and use it in GitHub Desktop.
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.services.batteryNotifier;
in {
options = {
services.batteryNotifier = {
enable = mkOption {
default = false;
description = ''
Whether to enable battery notifier.
'';
};
device = mkOption {
default = "BAT0";
description = ''
Device to monitor.
'';
};
notifyCapacity = mkOption {
default = 10;
description = ''
Battery level at which a notification shall be sent.
'';
};
suspendCapacity = mkOption {
default = 5;
description = ''
Battery level at which a suspend unless connected shall be sent.
'';
};
};
};
config = mkIf cfg.enable {
systemd.user.timers."lowbatt" = {
description = "check battery level";
timerConfig.OnBootSec = "1m";
timerConfig.OnUnitInactiveSec = "1m";
timerConfig.Unit = "lowbatt.service";
wantedBy = ["timers.target"];
};
systemd.user.services."lowbatt" = {
description = "battery level notifier";
serviceConfig.PassEnvironment = "DISPLAY";
script = ''
export battery_capacity=$(${pkgs.coreutils}/bin/cat /sys/class/power_supply/${cfg.device}/capacity)
export battery_status=$(${pkgs.coreutils}/bin/cat /sys/class/power_supply/${cfg.device}/status)
if [[ $battery_capacity -le ${builtins.toString cfg.notifyCapacity} && $battery_status = "Discharging" ]]; then
${pkgs.libnotify}/bin/notify-send --urgency=critical --hint=int:transient:1 --icon=battery_empty "Battery Low" "You should probably plug-in."
fi
if [[ $battery_capacity -le ${builtins.toString cfg.suspendCapacity} && $battery_status = "Discharging" ]]; then
${pkgs.libnotify}/bin/notify-send --urgency=critical --hint=int:transient:1 --icon=battery_empty "Battery Critically Low" "Computer will suspend in 60 seconds."
sleep 60s
battery_status=$(${pkgs.coreutils}/bin/cat /sys/class/power_supply/${cfg.device}/status)
if [[ $battery_status = "Discharging" ]]; then
systemctl suspend
fi
fi
'';
};
};
}
@dmvianna
Copy link

How would I include this in my system config?

@AlexChalk
Copy link

AlexChalk commented Feb 11, 2020

Thanks for this @domenkozar, do you have any advice on debugging it if it's not firing?

My WM is Sway and my notification engine is Mako. I've included the file in my config like this:

  imports = [ ./suspend.nix ];
  services.batteryNotifier = {
    enable = true;
    notifyCapacity = 40;
    suspendCapacity = 10;
  };

If a just run a version of the script in my shell it works fine.

@domenkozar
Copy link
Author

Need to check systemd logs, you're probably missing a binary or something.

@AlexChalk
Copy link

AlexChalk commented Feb 12, 2020

Thanks @domenkozar, I had a look with journalctl. Is there something in particular I should be looking for, this is all I could see related to the above script (a lot of these)?

Feb 11 09:09:12 adc-nixos systemd[1]: Started battery level notifier.
Feb 11 09:09:12 adc-nixos systemd[1]: lowbatt.service: Succeeded.

@AlexChalk
Copy link

Managed to narrow it down to this line: ${pkgs.libnotify}/bin/notify-send --urgency=critical --hint=int:transient:1 --icon=battery_empty "Battery Low" "You should probably plug-in."

I added echoes to each line of the script that write to a log. The script is successfully executing every 1-2 minutes, and after the battery level drops below my notify capacity, the echo inside the $battery_capacity -le ${builtins.toString cfg.notifyCapacity} && $battery_status = "Discharging" condition is successfully logged. But I'm not seeing the notification.

Running notify-send directly from my own shell works fine. It makes me wonder if this is user account related? i.e. Because it's a system configuration, the notify-send call in the script isn't pinging the notification server associated with my user's login session?

@domenkozar
Copy link
Author

Do you see it's been executed if you run $ systemctl status --user lowbatt.service as your user?

Systemd should make sure it executes under your user, and the module sets DISPLAY variable to make sure it can find the display. Maybe you have a custom setup? I'd guess Sway is to blame here, probably needs something else than DISPLAY variable.

@AlexChalk
Copy link

the module sets DISPLAY variable to make sure it can find the display

Nice, this was part of the issue: I realized that systemctl --user show-environment | grep DISPLAY actually outputs nothing as Sway doesn't pass those vars to systemd (see here).

I initially tried to fix this by passing DISPLAY, WAYLAND_DISPLAY and SWAYSOCK to systemd with no luck. Then, like this person, I just passed my whole env and the notifications now work 🍾.

tl;dr: If you have the issue I'm describing, you can resolve it by adding this to the end of your sway config: exec systemctl --user import-environment.

Thank you very much for your help! 🙏

@R-s-rawat
Copy link

R-s-rawat commented Nov 16, 2023

Thank you very much for your help

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