From 5a8cd6624c61313255b74f3fbdbbf50e79bd85d6 Mon Sep 17 00:00:00 2001 From: Malte Brandy Date: Wed, 8 Jan 2020 01:14:05 +0100 Subject: [PATCH] Change battery watcher to Haskell --- home/battery.nix | 74 ++++++++++++++++++++++++++++++++++++------------ lib/default.nix | 1 + 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/home/battery.nix b/home/battery.nix index a8169cec..aa73f704 100644 --- a/home/battery.nix +++ b/home/battery.nix @@ -1,32 +1,70 @@ { lib, pkgs, config, ... }: let - battery-watch = pkgs.writeScript "battery-watch" '' - #!${pkgs.stdenv.shell} + inherit (import ../lib/default.nix) writeHaskellScript; + battery-watch = writeHaskellScript { + name = "battery-watch"; + libraries = [ + pkgs.haskellPackages.fdo-notify + pkgs.haskellPackages.megaparsec + pkgs.haskellPackages.replace-megaparsec + ]; + bins = [ pkgs.acpi ]; + imports = [ + "DBus.Notify" + "Control.Concurrent" + "Text.Megaparsec" + "Text.Megaparsec.Char" + "Text.Megaparsec.Char.Lexer" + "Replace.Megaparsec" + "Data.Maybe" + ]; + } '' + criticalLevel = 20 -- percent + minutes = 60 * 1000 * 1000 -- threadDelay takes microseconds - critical_level=20 #percent + main = do + client <- connectSession + let loop = \handleMay -> do + batteryStateText <- decodeUtf8 <$> (acpi "-a" |> captureTrim) + batteryLevelText <- decodeUtf8 <$> (acpi "-b" |> captureTrim) + chargerOnline <- maybe (fail "Couldn‘t get charging state") pure $ parseMaybe onlineParser batteryStateText + batteryLevel <- maybe (fail "Couldn‘t get battery level") pure $ parseMaybe levelParser batteryLevelText + let NextAction { note = noteMay , delay = delay } = chooseAction chargerOnline batteryLevel + handle <- if | Just note <- noteMay -> Just <$> maybe (notify client note) (flip (replace client) note) handleMay + | otherwise -> pure handleMay + echo ([i|Waiting for #{delay} minutes until next message.|] :: String) + threadDelay $ delay * minutes + loop handle + loop Nothing - export PATH=$PATH:${pkgs.coreutils}/bin:${pkgs.gnugrep}/bin:${pkgs.gnused}/bin + data NextAction = NextAction { note :: Maybe Note, delay :: Int } - while true - do - if [ "$(${pkgs.acpi}/bin/acpi -a | grep -o off)" == "off" ]; then - battery_level=`${pkgs.acpi}/bin/acpi -b | sed 's/.*[dg], //g;s/\%,.*//g'` - if [ $battery_level -le $critical_level ]; then - ${pkgs.libnotify}/bin/notify-send 'Battery level is low!' "Only $battery_level% of the charge remains." - else - ${pkgs.libnotify}/bin/notify-send 'Battery level is discharging!' "Only $battery_level% of the charge remains." - sleep 18m - fi - fi - sleep 2m - done + type Parser = Parsec Text LT.Text + + onlineParser :: Parser Bool + onlineParser = not . null . rights <$> sepCap (string "on-line") + + levelParser :: Parser Int + levelParser = (maybe (fail "No Number found") pure . listToMaybe . rights) =<< sepCap (decimal <* "%") + + chooseAction :: Bool -> Int -> NextAction + chooseAction chargerOnline batteryLevel + | chargerOnline = NextAction Nothing 1 + | batteryLevel <= criticalLevel = mkMsg $ myNote {summary = "Battery is low!"} + | otherwise = mkMsg $ myNote {summary = "Battery is discharging!"} + where + mkMsg = flip NextAction (max 1 (batteryLevel `div` 5)) . Just + myNote = blankNote { body = Just $ Text [i|Only #{batteryLevel}% remaining.|]} ''; in { systemd.user = { services.battery = { Unit = { Description = "Watch battery state and warn user"; }; - Service = { ExecStart = toString battery-watch; }; + Service = { + ExecStart = "${battery-watch}/bin/battery-watch"; + Restart = "always"; + }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; }; diff --git a/lib/default.nix b/lib/default.nix index 99b99006..e888b532 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -55,6 +55,7 @@ rec { {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ExtendedDefaultRules #-} + {-# LANGUAGE MultiWayIf #-} import Shh import qualified Prelude