{- 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 . -} {-# 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 (sdivUBound gap (avg `div` 3600) 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"