summaryrefslogtreecommitdiffhomepage
path: root/src/Xmobar/Plugins/Monitors/Batt
diff options
context:
space:
mode:
authorMichal Zielonka <michal.zielonka.8001@gmail.com>2021-10-07 23:25:09 +0200
committerMichal Zielonka <michal.zielonka.8001@gmail.com>2021-10-08 11:11:11 +0200
commitb99a8a6833a1b38882b463fd72784cd6d6f91d9e (patch)
tree9537dcfe5eff2108937bf9a2160a5f15a7a266e5 /src/Xmobar/Plugins/Monitors/Batt
parenta845465fec735d9818a61d078337653b5293da5c (diff)
downloadxmobar-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.hs57
-rw-r--r--src/Xmobar/Plugins/Monitors/Batt/FreeBSD.hs46
-rw-r--r--src/Xmobar/Plugins/Monitors/Batt/Linux.hs130
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