diff options
| -rw-r--r-- | src/Xmobar/Plugins/Monitors/Batt/Linux.hs | 155 | 
1 files changed, 79 insertions, 76 deletions
| diff --git a/src/Xmobar/Plugins/Monitors/Batt/Linux.hs b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs index 9b53f65..5389be0 100644 --- a/src/Xmobar/Plugins/Monitors/Batt/Linux.hs +++ b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs @@ -1,7 +1,7 @@  -----------------------------------------------------------------------------  -- |  -- Module      :  Plugins.Monitors.Batt.Linux --- Copyright   :  (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega +-- Copyright   :  (c) 2010-2013, 2015, 2016, 2018, 2019, 2022 Jose A Ortega  --                (c) 2010 Andrea Rossato, Petr Rockai  -- License     :  BSD-style (see LICENSE)  -- @@ -15,7 +15,7 @@  module Xmobar.Plugins.Monitors.Batt.Linux (readBatteries) where -import Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..) +import Xmobar.Plugins.Monitors.Batt.Common ( BattOpts(..)                                             , Result(..)                                             , Status(..)                                             , maybeAlert) @@ -41,27 +41,54 @@ data Files = Files    , fCurrent :: String    , fPower :: String    , fStatus :: String +  , fBat :: String    } deriving Eq --- the default basenames of the possibly available attributes exposed by the kernel -defFileBasenames :: Files -defFileBasenames = Files { -    fEFull = "energy_full"   -  , fCFull = "charge_full"   -  , fEFullDesign = "energy_full_design"   -  , fCFullDesign = "charge_full_design"   -  , fENow = "energy_now"   -  , fCNow = "charge_now"   -  , fVoltage = "voltage_now"   -  , fVoltageMin = "voltage_min_design"   -  , fCurrent = "current_now"   -  , fPower = "power_now"   -  , fStatus = "status" } - --- prefix all files in a Files object by a given prefix --- I couldn't find a better way to do this -prefixFiles :: String -> Files -> Files -prefixFiles p (Files a b c d e f g h i j k) = Files (p </> a) (p </> b) (p </> c) (p </> d) (p </> e) (p </> f) (p </> g) (p </> h) (p </> i) (p </> j) (p </> k)  +-- the default basenames of the possibly available attributes exposed +-- by the kernel +defaultFiles :: Files +defaultFiles = Files +  { fEFull = "energy_full" +  , fCFull = "charge_full" +  , fEFullDesign = "energy_full_design" +  , fCFullDesign = "charge_full_design" +  , fENow = "energy_now" +  , fCNow = "charge_now" +  , fVoltage = "voltage_now" +  , fVoltageMin = "voltage_min_design" +  , fCurrent = "current_now" +  , fPower = "power_now" +  , fStatus = "status" +  , fBat = "BAT0" +  } + +type FilesAccessor = Files -> String + +sysDir :: FilePath +sysDir = "/sys/class/power_supply" + +battFile :: FilesAccessor -> Files -> FilePath +battFile accessor files = sysDir </> fBat files </> accessor files + +grabNumber :: (Num a, Read a) => FilesAccessor -> Files -> IO (Maybe a) +grabNumber = grabFile (fmap read . hGetLine) + +grabString :: FilesAccessor -> Files -> IO (Maybe String) +grabString = grabFile hGetLine + +-- grab file contents returning Nothing if the file doesn't exist or +-- any other error occurs +grabFile :: (Handle -> IO a) -> FilesAccessor -> Files -> IO (Maybe a) +grabFile readMode accessor files = +  handle (onFileError Nothing) (withFile f ReadMode (fmap Just . readMode)) +  where f = battFile accessor files + +onFileError :: a -> SomeException -> IO a +onFileError returnOnError = const (return returnOnError) + +-- get the filenames for a given battery name +batteryFiles :: String -> Files +batteryFiles bat = defaultFiles { fBat = bat }  data Battery = Battery    { full :: !Float @@ -70,103 +97,79 @@ data Battery = Battery    , status :: !String    } -sysDir :: FilePath -sysDir = "/sys/class/power_supply" - --- get the filenames for a given battery name -batteryFiles :: String -> Files  -batteryFiles bat = prefixFiles (sysDir </> bat) defFileBasenames -  haveAc :: FilePath -> IO Bool  haveAc f = -  handle (onFileError False) $ withFile (sysDir </> f) ReadMode (fmap (== "1") . hGetLine) +  handle (onFileError False) $ +    withFile (sysDir </> f) ReadMode (fmap (== "1") . hGetLine)  -- retrieve the currently drawn power in Watt  -- sc is a scaling factor which by kernel documentation must be 1e6  readBatPower :: Float -> Files -> IO (Maybe Float)  readBatPower sc f = -    do pM <- grabNumber $ fPower f -       cM <- grabNumber $ fCurrent f -       vM <- grabNumber $ fVoltage f +    do pM <- grabNumber fPower f +       cM <- grabNumber fCurrent f +       vM <- grabNumber fVoltage f         return $ case (pM, cM, vM) of             (Just pVal, _, _) -> Just $ pVal / sc             (_, Just cVal, Just vVal) -> Just $ cVal * vVal / (sc * sc)             (_, _, _) -> Nothing --- retrieve the maximum capacity in Watt hours  +-- retrieve the maximum capacity in Watt hours  -- sc is a scaling factor which by kernel documentation must be 1e6 --- on getting the voltage: using voltage_min_design will probably underestimate  +-- on getting the voltage: using voltage_min_design will probably underestimate  -- the actual energy content of the battery and using voltage_now will probably  -- overestimate it.  readBatCapacityFull :: Float -> Files -> IO (Maybe Float)  readBatCapacityFull sc f = -    do cM  <- grabNumber $ fCFull f -       eM  <- grabNumber $ fEFull f -       cdM <- grabNumber $ fCFullDesign f -       edM <- grabNumber $ fEFullDesign f -       vM  <- grabNumber $ fVoltageMin f -- not sure if Voltage or VoltageMin is more accurate and if both are always available -       return $ case (eM, cM, edM, cdM, vM) of  +    do cM  <- grabNumber fCFull f +       eM  <- grabNumber fEFull f +       cdM <- grabNumber fCFullDesign f +       edM <- grabNumber fEFullDesign f +       -- not sure if Voltage or VoltageMin is more accurate and if both +       -- are always available +       vM  <- grabNumber fVoltageMin f +       return $ case (eM, cM, edM, cdM, vM) of             (Just eVal, _, _, _, _)         -> Just $ eVal        / sc             (_, Just cVal, _, _, Just vVal) -> Just $ cVal * vVal / (sc * sc)             (_, _, Just eVal, _, _)         -> Just $ eVal        / sc             (_, _, _, Just cVal, Just vVal) -> Just $ cVal * vVal / (sc * sc)             (_, _, _, _, _) -> Nothing --- retrieve the current capacity in Watt hours  +-- retrieve the current capacity in Watt hours  -- sc is a scaling factor which by kernel documentation must be 1e6 --- on getting the voltage: using voltage_min_design will probably underestimate  +-- on getting the voltage: using voltage_min_design will probably underestimate  -- the actual energy content of the battery and using voltage_now will probably  -- overestimate it.  readBatCapacityNow :: Float -> Files -> IO (Maybe Float)  readBatCapacityNow sc f = -    do cM  <- grabNumber $ fCNow f -       eM  <- grabNumber $ fENow f -       vM  <- grabNumber $ fVoltageMin f -- not sure if Voltage or VoltageMin is more accurate and if both are always available +    do cM  <- grabNumber fCNow f +       eM  <- grabNumber fENow f +       vM  <- grabNumber fVoltageMin f -- not sure if Voltage or +                                       -- VoltageMin is more accurate +                                       -- and if both are always +                                       -- available         return $ case (eM, cM, vM) of             (Just eVal, _, _)         -> Just $ eVal        / sc             (_, Just cVal, Just vVal) -> Just $ cVal * vVal / (sc * sc)             (_, _, _) -> Nothing  readBatStatus :: Files -> IO (Maybe String) -readBatStatus f = grabString $ fStatus f - --- "resolve" a Maybe to a given default value if it is Nothing -setDefault :: a -> IO (Maybe a) -> IO a -setDefault def ioval = -    do m <- ioval  -       return $ case m of (Just v) -> v -                          Nothing -> def +readBatStatus = grabString fStatus  -- collect all relevant battery values with defaults of not available  readBattery :: Float -> Files -> IO Battery  readBattery sc files = -    do cFull <- setDefault 0 $ readBatCapacityFull sc files -       cNow <- setDefault 0 $ readBatCapacityNow sc files -       pwr <- setDefault 0 $ readBatPower sc files -       s <- setDefault "Unknown" $ readBatStatus files -       let cFull' = max cFull cNow -- sometimes the reported max charge is lower than +    do cFull <- withDef 0 readBatCapacityFull +       cNow <- withDef 0 readBatCapacityNow +       pwr <- withDef 0 readBatPower +       s <- withDef "Unknown" (const readBatStatus) +       let cFull' = max cFull cNow -- sometimes the reported max +                                   -- charge is lower than         return $ Battery (3600 * cFull') -- wattseconds                          (3600 * cNow) -- wattseconds                          (abs pwr) -- watts                          s -- string: Discharging/Charging/Full - -grabNumber :: (Num a, Read a) => FilePath -> IO (Maybe a) -grabNumber = grabFile (fmap read . hGetLine) - -grabString :: FilePath -> IO (Maybe String) -grabString = grabFile hGetLine - --- grab file contents returning Nothing if the file doesn't exist or any other error occurs -grabFile :: (Handle -> IO a) -> FilePath -> IO (Maybe a) -grabFile readMode f = handle (onFileError Nothing) (withFile f ReadMode (doJust . readMode)) - -onFileError :: a -> SomeException -> IO a -onFileError returnOnError = const (return returnOnError)  - -doJust :: IO a -> IO (Maybe a) -doJust a = -    do v <- a -       return $ Just v +         where withDef d reader = fromMaybe d `fmap` reader sc files  -- sortOn is only available starting at ghc 7.10  sortOn :: Ord b => (a -> b) -> [a] -> [a] | 
