diff options
author | Michal Zielonka <michal.zielonka.8001@gmail.com> | 2021-10-07 23:25:09 +0200 |
---|---|---|
committer | Michal Zielonka <michal.zielonka.8001@gmail.com> | 2021-10-08 11:11:11 +0200 |
commit | b99a8a6833a1b38882b463fd72784cd6d6f91d9e (patch) | |
tree | 9537dcfe5eff2108937bf9a2160a5f15a7a266e5 /src/Xmobar/Plugins/Monitors/Batt | |
parent | a845465fec735d9818a61d078337653b5293da5c (diff) | |
download | xmobar-b99a8a6833a1b38882b463fd72784cd6d6f91d9e.tar.gz xmobar-b99a8a6833a1b38882b463fd72784cd6d6f91d9e.tar.bz2 |
try to reorganize modules per os
We should make better split os specify code for FreeBSD and Linux.
Idea comes from @liskin.
Diffstat (limited to 'src/Xmobar/Plugins/Monitors/Batt')
-rw-r--r-- | src/Xmobar/Plugins/Monitors/Batt/Common.hs | 57 | ||||
-rw-r--r-- | src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs | 46 | ||||
-rw-r--r-- | src/Xmobar/Plugins/Monitors/Batt/Linux.hs | 130 |
3 files changed, 233 insertions, 0 deletions
diff --git a/src/Xmobar/Plugins/Monitors/Batt/Common.hs b/src/Xmobar/Plugins/Monitors/Batt/Common.hs new file mode 100644 index 0000000..3262b78 --- /dev/null +++ b/src/Xmobar/Plugins/Monitors/Batt/Common.hs @@ -0,0 +1,57 @@ +----------------------------------------------------------------------------- +-- | +-- Module : Plugins.Monitors.Batt.Common +-- Copyright : (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega +-- (c) 2010 Andrea Rossato, Petr Rockai +-- License : BSD-style (see LICENSE) +-- +-- Maintainer : Jose A. Ortega Ruiz <jao@gnu.org> +-- Stability : unstable +-- Portability : unportable +-- +-- A battery monitor for Xmobar +-- +----------------------------------------------------------------------------- + +module Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..) + , Result(..) + , Status(..) + , maybeAlert) where + +import System.Process (system) +import Control.Monad (unless, void) +import Xmobar.Plugins.Monitors.Common + +data Status = Charging | Discharging | Full | Idle | Unknown deriving (Read, Eq) +-- Result perc watts time-seconds Status +data Result = Result Float Float Float Status | NA + +data BattOpts = BattOpts + { onString :: String + , offString :: String + , idleString :: String + , posColor :: Maybe String + , lowWColor :: Maybe String + , mediumWColor :: Maybe String + , highWColor :: Maybe String + , lowThreshold :: Float + , highThreshold :: Float + , onLowAction :: Maybe String + , actionThreshold :: Float + , onlineFile :: FilePath + , scale :: Float + , onIconPattern :: Maybe IconPattern + , offIconPattern :: Maybe IconPattern + , idleIconPattern :: Maybe IconPattern + , lowString :: String + , mediumString :: String + , highString :: String + , incPerc :: Bool + } + +maybeAlert :: BattOpts -> Float -> IO () +maybeAlert opts left = + case onLowAction opts of + Nothing -> return () + Just x -> unless (isNaN left || actionThreshold opts < 100 * left) + $ void $ system x diff --git a/src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs b/src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs new file mode 100644 index 0000000..2bb8618 --- /dev/null +++ b/src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs @@ -0,0 +1,46 @@ +----------------------------------------------------------------------------- +-- | +-- Module : Plugins.Monitors.Batt.FreeBSD +-- Copyright : (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega +-- (c) 2010 Andrea Rossato, Petr Rockai +-- License : BSD-style (see LICENSE) +-- +-- Maintainer : Jose A. Ortega Ruiz <jao@gnu.org> +-- Stability : unstable +-- Portability : unportable +-- +-- A battery monitor for Xmobar +-- +----------------------------------------------------------------------------- + +module Xmobar.Plugins.Monitors.Batt.FreeBSD (readBatteries) where + +import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..) + , Result(..) + , Status(..) + , maybeAlert) + +import Control.Monad (unless) +import System.BSD.Sysctl (sysctlReadInt) + +battStatus :: Int -> Status +battStatus x + | x == 1 = Discharging + | x == 2 = Charging + | otherwise = Unknown + +readBatteries :: BattOpts -> [String] -> IO Result +readBatteries opts _ = do + lf <- sysctlReadInt "hw.acpi.battery.life" + rt <- sysctlReadInt "hw.acpi.battery.rate" + tm <- sysctlReadInt "hw.acpi.battery.time" + st <- sysctlReadInt "hw.acpi.battery.state" + acline <- sysctlReadInt "hw.acpi.acline" + let p = fromIntegral lf / 100 + w = fromIntegral rt + t = fromIntegral tm * 60 + ac = acline == 1 + -- battery full when rate is 0 and on ac. + sts = if w == 0 && ac then Full else battStatus $ fromIntegral st + unless ac (maybeAlert opts p) + return (Result p w t sts) diff --git a/src/Xmobar/Plugins/Monitors/Batt/Linux.hs b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs new file mode 100644 index 0000000..454e7e9 --- /dev/null +++ b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs @@ -0,0 +1,130 @@ +----------------------------------------------------------------------------- +-- | +-- Module : Plugins.Monitors.Batt.Linux +-- Copyright : (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega +-- (c) 2010 Andrea Rossato, Petr Rockai +-- License : BSD-style (see LICENSE) +-- +-- Maintainer : Jose A. Ortega Ruiz <jao@gnu.org> +-- Stability : unstable +-- Portability : unportable +-- +-- A battery monitor for Xmobar +-- +----------------------------------------------------------------------------- + +module Xmobar.Plugins.Monitors.Batt.Linux (readBatteries) where + +import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..) + , Result(..) + , Status(..) + , maybeAlert) + +import Control.Monad (unless) +import Control.Exception (SomeException, handle) +import System.FilePath ((</>)) +import System.IO (IOMode(ReadMode), hGetLine, withFile) +import System.Posix.Files (fileExist) +import Data.List (sort, sortBy, group) +import Data.Maybe (fromMaybe) +import Data.Ord (comparing) +import Text.Read (readMaybe) + +data Files = Files + { fFull :: String + , fNow :: String + , fVoltage :: String + , fCurrent :: String + , fStatus :: String + , isCurrent :: Bool + } | NoFiles deriving Eq + +data Battery = Battery + { full :: !Float + , now :: !Float + , power :: !Float + , status :: !String + } + +sysDir :: FilePath +sysDir = "/sys/class/power_supply" + +safeFileExist :: String -> String -> IO Bool +safeFileExist d f = handle noErrors $ fileExist (d </> f) + where noErrors = const (return False) :: SomeException -> IO Bool + +batteryFiles :: String -> IO Files +batteryFiles bat = + do is_charge <- exists "charge_now" + is_energy <- if is_charge then return False else exists "energy_now" + is_power <- exists "power_now" + plain <- exists (if is_charge then "charge_full" else "energy_full") + let cf = if is_power then "power_now" else "current_now" + sf = if plain then "" else "_design" + return $ case (is_charge, is_energy) of + (True, _) -> files "charge" cf sf is_power + (_, True) -> files "energy" cf sf is_power + _ -> NoFiles + where prefix = sysDir </> bat + exists = safeFileExist prefix + files ch cf sf ip = Files { fFull = prefix </> ch ++ "_full" ++ sf + , fNow = prefix </> ch ++ "_now" + , fCurrent = prefix </> cf + , fVoltage = prefix </> "voltage_now" + , fStatus = prefix </> "status" + , isCurrent = not ip} + +haveAc :: FilePath -> IO Bool +haveAc f = + handle onError $ withFile (sysDir </> f) ReadMode (fmap (== "1") . hGetLine) + where onError = const (return False) :: SomeException -> IO Bool + +readBattery :: Float -> Files -> IO Battery +readBattery _ NoFiles = return $ Battery 0 0 0 "Unknown" +readBattery sc files = + do a <- grab $ fFull files + b <- grab $ fNow files + d <- grab $ fCurrent files + s <- grabs $ fStatus files + let sc' = if isCurrent files then sc / 10 else sc + a' = max a b -- sometimes the reported max charge is lower than + return $ Battery (3600 * a' / sc') -- wattseconds + (3600 * b / sc') -- wattseconds + (abs d / sc') -- watts + s -- string: Discharging/Charging/Full + where grab f = handle onError $ withFile f ReadMode (fmap read . hGetLine) + onError = const (return (-1)) :: SomeException -> IO Float + grabs f = handle onError' $ withFile f ReadMode hGetLine + onError' = const (return "Unknown") :: SomeException -> IO String + +-- sortOn is only available starting at ghc 7.10 +sortOn :: Ord b => (a -> b) -> [a] -> [a] +sortOn f = + map snd . sortBy (comparing fst) . map (\x -> let y = f x in y `seq` (y, x)) + +mostCommonDef :: Eq a => a -> [a] -> a +mostCommonDef x xs = head $ last $ [x] : sortOn length (group xs) + +readBatteries :: BattOpts -> [String] -> IO Result +readBatteries opts bfs = + do bfs' <- mapM batteryFiles bfs + let bfs'' = filter (/= NoFiles) bfs' + bats <- mapM (readBattery (scale opts)) (take 3 bfs'') + ac <- haveAc (onlineFile opts) + let sign = if ac then 1 else -1 + ft = sum (map full bats) + left = if ft > 0 then sum (map now bats) / ft else 0 + watts = sign * sum (map power bats) + time = if watts == 0 then 0 else max 0 (sum $ map time' bats) + mwatts = if watts == 0 then 1 else sign * watts + time' b = (if ac then full b - now b else now b) / mwatts + statuses :: [Status] + statuses = map (fromMaybe Unknown . readMaybe) + (sort (map status bats)) + acst = mostCommonDef Unknown $ filter (Unknown/=) statuses + racst | acst /= Unknown = acst + | time == 0 = Idle + | ac = Charging + | otherwise = Discharging + unless ac (maybeAlert opts left) + return $ if isNaN left then NA else Result left watts time racst |