monky-2.0.0.0: Monky/Battery.hs
{-
Copyright 2015 Markus Ongyerth, Stephan Guenther
This file is part of Monky.
Monky is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Monky is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Monky. If not, see <http://www.gnu.org/licenses/>.
-}
{-# LANGUAGE CPP #-}
{-|
Module : Monky.Battery
Description : Allows access to information about a battery connected to the system
Maintainer : ongy
Stability : testing
Portability : Linux
This module allows to read information about a battery connected to the system.
This module does not support every setup yet.
The files in /sys/ used by this module are driver dependend.
If it crashes monky for you setup please make a bug report with an
`ls /sys/class/power_supply/" ++ e ++ "/` attached.
-}
module Monky.Battery
( getBatteryHandle
, getBatteryHandle'
, getCurrentStatus
, getCurrentLevel
, BatteryHandle
, getTimeLeft
, getLoading
, BatteryState(..)
)
where
#if MIN_VERSION_base(4,8,0)
#else
import Control.Applicative ((<$>))
#endif
import Data.Maybe (fromMaybe)
import Data.IORef
import System.Directory
import Monky.Utility
-- FilesArray for the files, those have to be handled different so they
-- get their own type for pattern matching
-- Everything needed as state will be carried in the battery handle
-- |The handle exported by this module
data BatteryHandle = BatH FilesArray (IORef Int)
--PowerNow will use: power_now energy_now energy_full
--ChargeNow will use: voltage_now current_now current_avg charge_now charge_full
data FilesArray = PowerNow File File File File |
ChargeNow File File (Maybe File) File File File deriving(Show)
-- |Datatype to represent battery state
data BatteryState = BatFull | BatLoading | BatDraining
pnowPath :: String -> String
pnowPath e = "/sys/class/power_supply/" ++ e ++ "/power_now"
enowPath :: String -> String
enowPath e = "/sys/class/power_supply/" ++ e ++ "/energy_now"
efullPath :: String -> String
efullPath e = "/sys/class/power_supply/" ++ e ++ "/energy_full"
vnowPath :: String -> String
vnowPath e = "/sys/class/power_supply/" ++ e ++ "/voltage_now"
cnowPath :: String -> String
cnowPath e = "/sys/class/power_supply/" ++ e ++ "/current_now"
cavgPath :: String -> String
cavgPath e = "/sys/class/power_supply/" ++ e ++ "/current_avg"
chnowPath :: String -> String
chnowPath e = "/sys/class/power_supply/" ++ e ++ "/charge_now"
chfullPath :: String -> String
chfullPath e = "/sys/class/power_supply/" ++ e ++ "/charge_full"
adpPath :: String -> String
adpPath e = "/sys/class/power_supply/"++ e ++"/online"
-- |Internal function for getCurrentStatus
getCurrentStatusInt :: File -> IO Int
getCurrentStatusInt = readValue
getCurrentStatusM :: BatteryHandle -> IO Int
getCurrentStatusM (BatH (PowerNow _ _ _ adp) _) =
getCurrentStatusInt adp
getCurrentStatusM (BatH (ChargeNow _ _ _ _ _ adp) _) =
getCurrentStatusInt adp
-- |Get the current state of the battery (loading/draining/full)
getCurrentStatus :: BatteryHandle -> IO BatteryState
getCurrentStatus h = do
val <- getCurrentStatusM h
case val of
1 -> return BatLoading
0 -> return BatDraining
_ -> return BatFull
-- |Internal function for getcurrentLevel
getCurrentLevelInt :: File -> File -> IO Int
getCurrentLevelInt n f = do
now <- readValue n
full <- readValue f
return $ now * 100 `div` full
-- |Get the charge left in the battery in percent.
getCurrentLevel :: BatteryHandle -> IO Int
getCurrentLevel (BatH (PowerNow _ now full _) _) =
getCurrentLevelInt now full
getCurrentLevel (BatH (ChargeNow _ _ _ now full _) _) =
getCurrentLevelInt now full
-- |Internal function for getTimeLeft
getTimeLeftInt :: File -> File -> File -> Int -> File -> IO (Int, Int)
getTimeLeftInt n c f s adp = do
online <- getCurrentStatusInt adp
now <- readValue n
full <- readValue f
let gap = if online == 0 then now else full - now
change <- readValue c
let avg = (change * 20 + s * 80) `div` 100
return (if avg >= 3600 then gap `div` (avg `div` 3600) else 0, avg)
-- |Get current loading speed in Watt/s
getLoading :: BatteryHandle -> IO Float
getLoading (BatH (PowerNow pnow _ _ _) _) = do
power <- readLine pnow
return $(read power :: Float) / 1000000
getLoading (BatH (ChargeNow vnow cnow _ _ _ _) _) = do
voltage <- readLine vnow
current <- readLine cnow
let pow = ((read voltage :: Float) * (read current :: Float)) / 1000000000000
return pow
-- |Get an approximated amount of seconds left until the battery runs out
getTimeLeft :: BatteryHandle -> IO Int
getTimeLeft (BatH (PowerNow pnow enow efull adp) s)= do
c <- readIORef s
(t, n) <- getTimeLeftInt enow pnow efull c adp
writeIORef s n
return t
getTimeLeft (BatH (ChargeNow _ cnow cavg chnow chfull adp) s)= do
c <- readIORef s
(t, _) <- getTimeLeftInt chnow (fromMaybe cnow cavg) chfull c adp
return $ (floor . sqrt $(fromIntegral t :: Float)) * 60
-- |Create a power handle that uses the power_now file
createPowerNowHandle :: String -> String -> IO BatteryHandle
createPowerNowHandle e b = do
power_now <- fopen $ pnowPath b
energy_now <- fopen $ enowPath b
energy_full <- fopen $ efullPath b
adp_online <- fopen $ adpPath e
ref <- newIORef (0 :: Int)
return $BatH (PowerNow power_now energy_now energy_full adp_online) ref
-- |Create a power handle that uses the charge_now file
createChargeNowHandle :: String -> String -> IO BatteryHandle
createChargeNowHandle e b = do
voltage_now <- fopen $ vnowPath b
current_now <- fopen $ cnowPath b
exists <- doesFileExist $ cavgPath b
current_avg <- if exists then Just <$> fopen (cavgPath b) else return Nothing
charge_now <- fopen $ chnowPath b
charge_full <- fopen $ chfullPath b
adp_online <- fopen $ adpPath e
ref <- newIORef (0 :: Int)
return $BatH (ChargeNow voltage_now current_now current_avg charge_now charge_full adp_online) ref
-- |Create a 'BatteryHandle'
getBatteryHandle :: String -- ^The name of the wall socket adapter used by the battery
-> String -- ^The name of the battery
-> IO BatteryHandle
getBatteryHandle e b = do
exists <- doesFileExist $ pnowPath b
if exists
then createPowerNowHandle e b
else createChargeNowHandle e b
-- |Version which defaults to "BAT0"
getBatteryHandle' :: String -> IO BatteryHandle
getBatteryHandle' = flip getBatteryHandle "BAT0"