-----------------------------------------------------------------------------
-- |
-- Module      :  Plugins.Monitors.Batt.Linux
-- Copyright   :  (c) 2010-2013, 2015, 2016, 2018, 2019, 2022 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, Handle)
import Data.List (sort, sortBy, group)
import Data.Maybe (fromMaybe)
import Data.Ord (comparing)
import Text.Read (readMaybe)

data Files = Files
  { Files -> String
fEFull :: String
  , Files -> String
fCFull :: String
  , Files -> String
fEFullDesign :: String
  , Files -> String
fCFullDesign :: String
  , Files -> String
fENow :: String
  , Files -> String
fCNow :: String
  , Files -> String
fVoltage :: String
  , Files -> String
fVoltageMin :: String
  , Files -> String
fCurrent :: String
  , Files -> String
fPower :: String
  , Files -> String
fStatus :: String
  , Files -> String
fBat :: String
  } deriving Files -> Files -> Bool
(Files -> Files -> Bool) -> (Files -> Files -> Bool) -> Eq Files
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Files -> Files -> Bool
$c/= :: Files -> Files -> Bool
== :: Files -> Files -> Bool
$c== :: Files -> Files -> Bool
Eq

-- the default basenames of the possibly available attributes exposed
-- by the kernel
defaultFiles :: Files
defaultFiles :: Files
defaultFiles = Files :: String
-> String
-> String
-> String
-> String
-> String
-> String
-> String
-> String
-> String
-> String
-> String
-> Files
Files
  { fEFull :: String
fEFull = String
"energy_full"
  , fCFull :: String
fCFull = String
"charge_full"
  , fEFullDesign :: String
fEFullDesign = String
"energy_full_design"
  , fCFullDesign :: String
fCFullDesign = String
"charge_full_design"
  , fENow :: String
fENow = String
"energy_now"
  , fCNow :: String
fCNow = String
"charge_now"
  , fVoltage :: String
fVoltage = String
"voltage_now"
  , fVoltageMin :: String
fVoltageMin = String
"voltage_min_design"
  , fCurrent :: String
fCurrent = String
"current_now"
  , fPower :: String
fPower = String
"power_now"
  , fStatus :: String
fStatus = String
"status"
  , fBat :: String
fBat = String
"BAT0"
  }

type FilesAccessor = Files -> String

sysDir :: FilePath
sysDir :: String
sysDir = String
"/sys/class/power_supply"

battFile :: FilesAccessor -> Files -> FilePath
battFile :: (Files -> String) -> Files -> String
battFile Files -> String
accessor Files
files = String
sysDir String -> String -> String
</> Files -> String
fBat Files
files String -> String -> String
</> Files -> String
accessor Files
files

grabNumber :: (Num a, Read a) => FilesAccessor -> Files -> IO (Maybe a)
grabNumber :: (Files -> String) -> Files -> IO (Maybe a)
grabNumber = (Handle -> IO a) -> (Files -> String) -> Files -> IO (Maybe a)
forall a.
(Handle -> IO a) -> (Files -> String) -> Files -> IO (Maybe a)
grabFile ((String -> a) -> IO String -> IO a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> a
forall a. Read a => String -> a
read (IO String -> IO a) -> (Handle -> IO String) -> Handle -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> IO String
hGetLine)

grabString :: FilesAccessor -> Files -> IO (Maybe String)
grabString :: (Files -> String) -> Files -> IO (Maybe String)
grabString = (Handle -> IO String)
-> (Files -> String) -> Files -> IO (Maybe String)
forall a.
(Handle -> IO a) -> (Files -> String) -> Files -> IO (Maybe a)
grabFile Handle -> IO String
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 :: (Handle -> IO a) -> (Files -> String) -> Files -> IO (Maybe a)
grabFile Handle -> IO a
readMode Files -> String
accessor Files
files =
  (SomeException -> IO (Maybe a)) -> IO (Maybe a) -> IO (Maybe a)
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle (Maybe a -> SomeException -> IO (Maybe a)
forall a. a -> SomeException -> IO a
onFileError Maybe a
forall a. Maybe a
Nothing) (String -> IOMode -> (Handle -> IO (Maybe a)) -> IO (Maybe a)
forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withFile String
f IOMode
ReadMode ((a -> Maybe a) -> IO a -> IO (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just (IO a -> IO (Maybe a))
-> (Handle -> IO a) -> Handle -> IO (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> IO a
readMode))
  where f :: String
f = (Files -> String) -> Files -> String
battFile Files -> String
accessor Files
files

onFileError :: a -> SomeException -> IO a
onFileError :: a -> SomeException -> IO a
onFileError a
returnOnError = IO a -> SomeException -> IO a
forall a b. a -> b -> a
const (a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
returnOnError)

-- get the filenames for a given battery name
batteryFiles :: String -> Files
batteryFiles :: String -> Files
batteryFiles String
bat = Files
defaultFiles { fBat :: String
fBat = String
bat }

data Battery = Battery
  { Battery -> Float
full :: !Float
  , Battery -> Float
now :: !Float
  , Battery -> Float
power :: !Float
  , Battery -> String
status :: !String
  }

haveAc :: FilePath -> IO Bool
haveAc :: String -> IO Bool
haveAc String
f =
  (SomeException -> IO Bool) -> IO Bool -> IO Bool
forall e a. Exception e => (e -> IO a) -> IO a -> IO a
handle (Bool -> SomeException -> IO Bool
forall a. a -> SomeException -> IO a
onFileError Bool
False) (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$
    String -> IOMode -> (Handle -> IO Bool) -> IO Bool
forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withFile (String
sysDir String -> String -> String
</> String
f) IOMode
ReadMode ((String -> Bool) -> IO String -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"1") (IO String -> IO Bool)
-> (Handle -> IO String) -> Handle -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handle -> IO String
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 :: Float -> Files -> IO (Maybe Float)
readBatPower Float
sc Files
f =
    do Maybe Float
pM <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fPower Files
f
       Maybe Float
cM <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fCurrent Files
f
       Maybe Float
vM <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fVoltage Files
f
       Maybe Float -> IO (Maybe Float)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Float -> IO (Maybe Float))
-> Maybe Float -> IO (Maybe Float)
forall a b. (a -> b) -> a -> b
$ case (Maybe Float
pM, Maybe Float
cM, Maybe Float
vM) of
           (Just Float
pVal, Maybe Float
_, Maybe Float
_) -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
pVal Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
sc
           (Maybe Float
_, Just Float
cVal, Just Float
vVal) -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
cVal Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
vVal Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
sc Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
sc)
           (Maybe Float
_, Maybe Float
_, Maybe Float
_) -> Maybe Float
forall a. Maybe a
Nothing

-- 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
-- the actual energy content of the battery and using voltage_now will probably
-- overestimate it.
readBatCapacityFull :: Float -> Files -> IO (Maybe Float)
readBatCapacityFull :: Float -> Files -> IO (Maybe Float)
readBatCapacityFull Float
sc Files
f =
    do Maybe Float
cM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fCFull Files
f
       Maybe Float
eM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fEFull Files
f
       Maybe Float
cdM <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fCFullDesign Files
f
       Maybe Float
edM <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fEFullDesign Files
f
       -- not sure if Voltage or VoltageMin is more accurate and if both
       -- are always available
       Maybe Float
vM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fVoltageMin Files
f
       Maybe Float -> IO (Maybe Float)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Float -> IO (Maybe Float))
-> Maybe Float -> IO (Maybe Float)
forall a b. (a -> b) -> a -> b
$ case (Maybe Float
eM, Maybe Float
cM, Maybe Float
edM, Maybe Float
cdM, Maybe Float
vM) of
           (Just Float
eVal, Maybe Float
_, Maybe Float
_, Maybe Float
_, Maybe Float
_)         -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
eVal        Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
sc
           (Maybe Float
_, Just Float
cVal, Maybe Float
_, Maybe Float
_, Just Float
vVal) -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
cVal Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
vVal Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
sc Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
sc)
           (Maybe Float
_, Maybe Float
_, Just Float
eVal, Maybe Float
_, Maybe Float
_)         -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
eVal        Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
sc
           (Maybe Float
_, Maybe Float
_, Maybe Float
_, Just Float
cVal, Just Float
vVal) -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
cVal Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
vVal Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
sc Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
sc)
           (Maybe Float
_, Maybe Float
_, Maybe Float
_, Maybe Float
_, Maybe Float
_) -> Maybe Float
forall a. Maybe a
Nothing

-- 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
-- the actual energy content of the battery and using voltage_now will probably
-- overestimate it.
readBatCapacityNow :: Float -> Files -> IO (Maybe Float)
readBatCapacityNow :: Float -> Files -> IO (Maybe Float)
readBatCapacityNow Float
sc Files
f =
    do Maybe Float
cM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fCNow Files
f
       Maybe Float
eM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fENow Files
f
       Maybe Float
vM  <- (Files -> String) -> Files -> IO (Maybe Float)
forall a.
(Num a, Read a) =>
(Files -> String) -> Files -> IO (Maybe a)
grabNumber Files -> String
fVoltageMin Files
f -- not sure if Voltage or
                                       -- VoltageMin is more accurate
                                       -- and if both are always
                                       -- available
       Maybe Float -> IO (Maybe Float)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Float -> IO (Maybe Float))
-> Maybe Float -> IO (Maybe Float)
forall a b. (a -> b) -> a -> b
$ case (Maybe Float
eM, Maybe Float
cM, Maybe Float
vM) of
           (Just Float
eVal, Maybe Float
_, Maybe Float
_)         -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
eVal        Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
sc
           (Maybe Float
_, Just Float
cVal, Just Float
vVal) -> Float -> Maybe Float
forall a. a -> Maybe a
Just (Float -> Maybe Float) -> Float -> Maybe Float
forall a b. (a -> b) -> a -> b
$ Float
cVal Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
vVal Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
sc Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
sc)
           (Maybe Float
_, Maybe Float
_, Maybe Float
_) -> Maybe Float
forall a. Maybe a
Nothing

readBatStatus :: Files -> IO (Maybe String)
readBatStatus :: Files -> IO (Maybe String)
readBatStatus = (Files -> String) -> Files -> IO (Maybe String)
grabString Files -> String
fStatus

-- collect all relevant battery values with defaults of not available
readBattery :: Float -> Files -> IO Battery
readBattery :: Float -> Files -> IO Battery
readBattery Float
sc Files
files =
    do Float
cFull <- Float -> (Float -> Files -> IO (Maybe Float)) -> IO Float
forall (f :: * -> *) b.
Functor f =>
b -> (Float -> Files -> f (Maybe b)) -> f b
withDef Float
0 Float -> Files -> IO (Maybe Float)
readBatCapacityFull
       Float
cNow <- Float -> (Float -> Files -> IO (Maybe Float)) -> IO Float
forall (f :: * -> *) b.
Functor f =>
b -> (Float -> Files -> f (Maybe b)) -> f b
withDef Float
0 Float -> Files -> IO (Maybe Float)
readBatCapacityNow
       Float
pwr <- Float -> (Float -> Files -> IO (Maybe Float)) -> IO Float
forall (f :: * -> *) b.
Functor f =>
b -> (Float -> Files -> f (Maybe b)) -> f b
withDef Float
0 Float -> Files -> IO (Maybe Float)
readBatPower
       String
s <- String -> (Float -> Files -> IO (Maybe String)) -> IO String
forall (f :: * -> *) b.
Functor f =>
b -> (Float -> Files -> f (Maybe b)) -> f b
withDef String
"Unknown" ((Files -> IO (Maybe String)) -> Float -> Files -> IO (Maybe String)
forall a b. a -> b -> a
const Files -> IO (Maybe String)
readBatStatus)
       let cFull' :: Float
cFull' = Float -> Float -> Float
forall a. Ord a => a -> a -> a
max Float
cFull Float
cNow -- sometimes the reported max
                                   -- charge is lower than
       Battery -> IO Battery
forall (m :: * -> *) a. Monad m => a -> m a
return (Battery -> IO Battery) -> Battery -> IO Battery
forall a b. (a -> b) -> a -> b
$ Float -> Float -> Float -> String -> Battery
Battery (Float
3600 Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
cFull') -- wattseconds
                        (Float
3600 Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
cNow) -- wattseconds
                        (Float -> Float
forall a. Num a => a -> a
abs Float
pwr) -- watts
                        String
s -- string: Discharging/Charging/Full
         where withDef :: b -> (Float -> Files -> f (Maybe b)) -> f b
withDef b
d Float -> Files -> f (Maybe b)
reader = b -> Maybe b -> b
forall a. a -> Maybe a -> a
fromMaybe b
d (Maybe b -> b) -> f (Maybe b) -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Float -> Files -> f (Maybe b)
reader Float
sc Files
files

-- sortOn is only available starting at ghc 7.10
sortOn :: Ord b => (a -> b) -> [a] -> [a]
sortOn :: (a -> b) -> [a] -> [a]
sortOn a -> b
f =
  ((b, a) -> a) -> [(b, a)] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map (b, a) -> a
forall a b. (a, b) -> b
snd ([(b, a)] -> [a]) -> ([a] -> [(b, a)]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((b, a) -> (b, a) -> Ordering) -> [(b, a)] -> [(b, a)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((b, a) -> b) -> (b, a) -> (b, a) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (b, a) -> b
forall a b. (a, b) -> a
fst) ([(b, a)] -> [(b, a)]) -> ([a] -> [(b, a)]) -> [a] -> [(b, a)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> (b, a)) -> [a] -> [(b, a)]
forall a b. (a -> b) -> [a] -> [b]
map (\a
x -> let y :: b
y = a -> b
f a
x in b
y b -> (b, a) -> (b, a)
`seq` (b
y, a
x))

mostCommonDef :: Eq a => a -> [a] -> a
mostCommonDef :: a -> [a] -> a
mostCommonDef a
x [a]
xs = [a] -> a
forall a. [a] -> a
head ([a] -> a) -> [a] -> a
forall a b. (a -> b) -> a -> b
$ [[a]] -> [a]
forall a. [a] -> a
last ([[a]] -> [a]) -> [[a]] -> [a]
forall a b. (a -> b) -> a -> b
$ [a
x] [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: ([a] -> Int) -> [[a]] -> [[a]]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([a] -> [[a]]
forall a. Eq a => [a] -> [[a]]
group [a]
xs)

readBatteries :: BattOpts -> [String] -> IO Result
readBatteries :: BattOpts -> [String] -> IO Result
readBatteries BattOpts
opts [String]
bfs =
    do let bfs'' :: [Files]
bfs'' = (String -> Files) -> [String] -> [Files]
forall a b. (a -> b) -> [a] -> [b]
map String -> Files
batteryFiles [String]
bfs
       [Battery]
bats <- (Files -> IO Battery) -> [Files] -> IO [Battery]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Float -> Files -> IO Battery
readBattery (BattOpts -> Float
scale BattOpts
opts)) (Int -> [Files] -> [Files]
forall a. Int -> [a] -> [a]
take Int
3 [Files]
bfs'')
       Bool
ac <- String -> IO Bool
haveAc (BattOpts -> String
onlineFile BattOpts
opts)
       let sign :: Float
sign = if Bool
ac then Float
1 else -Float
1
           ft :: Float
ft = [Float] -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Battery -> Float) -> [Battery] -> [Float]
forall a b. (a -> b) -> [a] -> [b]
map Battery -> Float
full [Battery]
bats) -- total capacity when full
           left :: Float
left = if Float
ft Float -> Float -> Bool
forall a. Ord a => a -> a -> Bool
> Float
0 then [Float] -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Battery -> Float) -> [Battery] -> [Float]
forall a b. (a -> b) -> [a] -> [b]
map Battery -> Float
now [Battery]
bats) Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
ft else Float
0
           watts :: Float
watts = Float
sign Float -> Float -> Float
forall a. Num a => a -> a -> a
* [Float] -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Battery -> Float) -> [Battery] -> [Float]
forall a b. (a -> b) -> [a] -> [b]
map Battery -> Float
power [Battery]
bats)
           time :: Float
time = if Float
watts Float -> Float -> Bool
forall a. Eq a => a -> a -> Bool
== Float
0 then Float
0 else Float -> Float -> Float
forall a. Ord a => a -> a -> a
max Float
0 ([Float] -> Float
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([Float] -> Float) -> [Float] -> Float
forall a b. (a -> b) -> a -> b
$ (Battery -> Float) -> [Battery] -> [Float]
forall a b. (a -> b) -> [a] -> [b]
map Battery -> Float
time' [Battery]
bats)
           mwatts :: Float
mwatts = if Float
watts Float -> Float -> Bool
forall a. Eq a => a -> a -> Bool
== Float
0 then Float
1 else Float
sign Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
watts
           time' :: Battery -> Float
time' Battery
b = (if Bool
ac then Battery -> Float
full Battery
b Float -> Float -> Float
forall a. Num a => a -> a -> a
- Battery -> Float
now Battery
b else Battery -> Float
now Battery
b) Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
mwatts
           statuses :: [Status]
           statuses :: [Status]
statuses = (String -> Status) -> [String] -> [Status]
forall a b. (a -> b) -> [a] -> [b]
map (Status -> Maybe Status -> Status
forall a. a -> Maybe a -> a
fromMaybe Status
Unknown (Maybe Status -> Status)
-> (String -> Maybe Status) -> String -> Status
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Status
forall a. Read a => String -> Maybe a
readMaybe)
                          ([String] -> [String]
forall a. Ord a => [a] -> [a]
sort ((Battery -> String) -> [Battery] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Battery -> String
status [Battery]
bats))
           acst :: Status
acst = Status -> [Status] -> Status
forall a. Eq a => a -> [a] -> a
mostCommonDef Status
Unknown ([Status] -> Status) -> [Status] -> Status
forall a b. (a -> b) -> a -> b
$ (Status -> Bool) -> [Status] -> [Status]
forall a. (a -> Bool) -> [a] -> [a]
filter (Status
UnknownStatus -> Status -> Bool
forall a. Eq a => a -> a -> Bool
/=) [Status]
statuses
           racst :: Status
racst | Status
acst Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
/= Status
Unknown = Status
acst
                 | Float
time Float -> Float -> Bool
forall a. Eq a => a -> a -> Bool
== Float
0 = Status
Idle
                 | Bool
ac = Status
Charging
                 | Bool
otherwise = Status
Discharging
       Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
ac (BattOpts -> Float -> IO ()
maybeAlert BattOpts
opts Float
left)
       Result -> IO Result
forall (m :: * -> *) a. Monad m => a -> m a
return (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ if Float -> Bool
forall a. RealFloat a => a -> Bool
isNaN Float
left then Result
NA else Float -> Float -> Float -> Status -> Result
Result Float
left Float
watts Float
time Status
racst