summaryrefslogtreecommitdiffhomepage
path: root/Plugins/Monitors/Batt.hs
blob: bb2027c7dd20243966c6ffc7fa4f422c428a9cc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
-----------------------------------------------------------------------------
-- |
-- Module      :  Plugins.Monitors.Batt
-- Copyright   :  (c) Andrea Rossato
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Andrea Rossato <andrea.rossato@unibz.it>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A battery monitor for Xmobar
--
-----------------------------------------------------------------------------

module Plugins.Monitors.Batt ( battConfig, runBatt, runBatt' ) where

import qualified Data.ByteString.Lazy.Char8 as B
import Plugins.Monitors.Common
import System.Posix.Files (fileExist)

data Result = Result { percent :: Float, watts :: Float, time :: Float, ac :: String }
            | NA

base = "/sys/class/power_supply"

battConfig :: IO MConfig
battConfig = mkMConfig
       "Batt: <watts>, <left> / <timeleft>" -- template
       ["leftbar", "left", "status", "timeleft", "watts"] -- available replacements

data Files = Files { f_full :: String, f_now :: String
                   , f_voltage :: String, f_current :: String } | NoFiles
data Battery = Battery { full :: Float, now :: Float, voltage :: Float, current :: Float }

batteryFiles :: String -> IO Files
batteryFiles bat =
  do is_charge <- fileExist $ prefix ++ "/charge_now"
     is_energy <- fileExist $ prefix ++ "/energy_now"
     return $ case (is_charge, is_energy) of
       (True, _) -> files "/charge"
       (_, True) -> files "/energy"
       _ -> NoFiles
  where prefix = base ++ "/" ++ bat
        files ch = Files { f_full = prefix ++ ch ++ "_full"
                         , f_now = prefix ++ ch ++ "_now"
                         , f_current = prefix ++ "/current_now"
                         , f_voltage = prefix ++ "/voltage_now" }

haveAc :: IO (Maybe Bool)
haveAc = do know <- fileExist $ base ++ "/AC/online"
            if know
               then do s <- B.unpack `fmap` catRead (base ++ "/AC/online")
                       return $ Just $ s == "1\n"
               else return Nothing

readBattery :: Files -> IO Battery
readBattery NoFiles = return $ Battery 0 0 0 0
readBattery files =
    do a <- grab $ f_full files -- microwatthours
       b <- grab $ f_now files
       c <- grab $ f_voltage files -- microvolts
       d <- grab $ f_current files -- microwatts (huh!)
       return $ Battery (3600 * a / 1000000) -- wattseconds
                        (3600 * b / 1000000) -- wattseconds
                        (c / 1000000) -- volts
                        (d / c) -- amperes
    where grab = fmap (read . B.unpack) . catRead

readBatteries :: [Files] -> IO Result
readBatteries bfs =
    do bats <- mapM readBattery (take 3 bfs)
       ac' <- haveAc
       let ac = if ac' == Just True then True else False
           sign = if ac then 1 else -1
           left = (sum $ map now bats) / (sum $ map full bats)
           watts = sign * (sum $ map voltage bats) * (sum $ map current bats)
           time = if watts == 0 then 0 else sum $ map time' bats -- negate sign
           time' b = (if ac then full b - now b else now b) / (sign * watts)
           acstr = case ac' of
             Nothing -> "?"
             Just True -> "<fc=green>On</fc>"
             Just False -> "<fc=red>Off</fc>"
       return $ if isNaN left then NA else Result left watts time acstr

runBatt :: [String] -> Monitor String
runBatt = runBatt' ["BAT0","BAT1","BAT2"]

runBatt' :: [String] -> [String] -> Monitor String
runBatt' bfs _ = do
  c <- io $ readBatteries =<< mapM batteryFiles bfs
  case c of
    Result x w t s -> do l <- fmtPercent x
                         parseTemplate (l ++ [s] ++ [fmtTime $ floor t, fmtWatts w])
    NA -> return "N/A"
 where fmtPercent :: Float -> Monitor [String]
       fmtPercent x = do
         p <- showPercentsWithColors [x]
         b <- showPercentBar (100 * x) x
         return (b:p)
       fmtWatts x = color x $ showDigits 1 x ++ "W"
       fmtTime x = hours ++ ":" ++ if length minutes == 2 then minutes else "0" ++ minutes
         where hours = show (x `div` 3600)
               minutes = show ((x `mod` 3600) `div` 60)
       color x str | x >= 0 = "<fc=orange>" ++ str ++ "</fc>"
                   | x >= -10 = "<fc=green>" ++ str ++ "</fc>"
                   | x >= -12 = str
                   | otherwise = "<fc=red>" ++ str ++ "</fc>"