diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,29 @@
-## Version 0.32
+## Version 0.33 (February, 2020)
+
+_New features_
+
+  - New template parameter `<weather>` for the `Weather` plugin, potentially
+    displaying specific weather conditions that are occurring near the
+    station (thanks to *slotThe*).
+  - New option `--weathers`, for `Weather` to display a default string in
+    case the `weather` field is not reported (thanks to *slotThe*).
+  - New template parameter `<volumestatus>` for the `Volume` plugin, combining
+    the effects of `<volume>` and `<status>`.  This will show the volume
+    (possibly prefixed by `onString` or a percentage-based string) if and only
+    if the volume is not muted.  Otherwise it will show the
+    `offString` (thanks to *slotThe*).
+  - `Battery` and `BatteryN` now support FreeBSD (thanks to Dhananjay
+    Balan).
+  - New option `--useManager` for `Weather` and `UVMeter` to decide whether to
+    use one single manager per monitor for managing network connections or
+    create a new one every time a connection is made.
+  - New more efficient time coalescing strategy for monitor updates,
+    available with the threaded runtime: use the `with_threaded` flag
+    to enable it (see #410; thanks to Tomáš Janoušek).
+  - `Wireless` supports current nl80211 API on Linux now, old Wext ioctls still
+    available as compile-time option (thanks to Paul Fertser).
+
+## Version 0.32 (December, 2019)
 
 _New features_
 
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -117,12 +117,16 @@
   option is needed for the MBox and Mail plugins to work. Requires the
   [hinotify] package.
 
-- `with_iwlib` Support for wireless cards. Enables the Wireless
-   plugin. No Haskell library is required, but you will need the
-   [iwlib] C library and headers in your system (e.g., install
-   `libiw-dev` in Debian-based systems or `wireless_tools` on Arch
-   Linux).
+- `with_nl80211` Support for wireless cards on Linux via nl80211 (all
+   upstream drivers). Enables the Wireless plugin. Requires [netlink]
+   and [cereal] packages.
 
+- `with_iwlib` Support for wireless cards via Wext ioctls
+   (deprecated). Enables the Wireless plugin. No Haskell library is
+   required, but you will need the [iwlib] C library and headers in your
+   system (e.g., install `libiw-dev` in Debian-based systems or
+   `wireless_tools` on Arch Linux). Conflicts with `with_nl80211`.
+
 - `with_alsa` Support for ALSA sound cards. Enables the Volume
    plugin. Requires the [alsa-mixer] package.  To install the latter,
    you'll need the [libasound] C library and headers in your system
@@ -735,11 +739,20 @@
 - Aliases to the Station ID: so `Weather "LIPB" []` can be used in
   template as `%LIPB%`
 - Thresholds refer to temperature in the selected units
-- Args: default monitor arguments
+- Args: default monitor arguments, plus:
+  - `--weathers` _string_ : display a default string when the `weather`
+    variable is not reported.
+    - short option: `-w`
+    - Default: ""
+  - `--useManager` _bool_ : Whether to use one single manager per monitor for
+    managing network connections or create a new one every time a connection is
+    made.
+    - Short option: `-m`
+    - Default: True
 - Variables that can be used with the `-t`/`--template` argument:
 	    `station`, `stationState`, `year`, `month`, `day`, `hour`,
 	    `windCardinal`, `windAzimuth`, `windMph`, `windKnots`, `windMs`, `windKmh`
-        `visibility`, `skyCondition`, `tempC`, `tempF`,
+        `visibility`, `skyCondition`, `weather`, `tempC`, `tempF`,
 	    `dewPointC`, `dewPointF`, `rh`, `pressure`
 - Default template: `<station>: <tempC>C, rh <rh>% (<hour>)`
 - Retrieves weather information from http://tgftp.nws.noaa.gov.
@@ -811,18 +824,21 @@
 
 ### `Wireless Interface Args RefreshRate`
 
-- If set to "", the interface is looked up in /proc/net/wireless.
+- If set to "", first suitable wireless interface is used.
 - Aliases to the interface name with the suffix "wi": thus, `Wireless
   "wlan0" []` can be used as `%wlan0wi%`, and `Wireless "" []` as `%wi%`.
 - Args: default monitor arguments, plus:
   - `--quality-icon-pattern`: dynamic string for connection quality in `qualityipat`.
 - Variables that can be used with the `-t`/`--template` argument:
-            `essid`, `quality`, `qualitybar`, `qualityvbar`, `qualityipat`
-- Thresholds refer to link quality in a `[0, 100]` scale
-- Default template: `<essid> <quality>`
-- Requires the C library [iwlib] (part of the wireless tools suite)
-  installed in your system. In addition, to activate this plugin you
-  must pass `--flags="with_iwlib"` during compilation
+            `ssid`, `signal`, `quality`, `qualitybar`, `qualityvbar`, `qualityipat`
+- Thresholds refer to link quality on a `[0, 100]` scale. Note that
+  `quality` is calculated from `signal` (in dBm) by a possibly lossy
+  conversion. It is also not taking into account many factors such as
+  noise level, air busy time, transcievers' capabilities and the
+  others which can have drastic impact on the link performance.
+- Default template: `<ssid> <quality>`
+- To activate this plugin you must pass `--flags="with_nl80211"` or
+  `--flags="with_iwlib"` during compilation
 
 ### `Memory Args RefreshRate`
 
@@ -924,7 +940,7 @@
     threshold (default: "")
   - `--mediums`: string for AC "off" status and power lower than the `-H`
     threshold (default: "")
-  - `--high`: string for AC "off" status and power higher than the `-H`
+  - `--highs`: string for AC "off" status and power higher than the `-H`
     threshold (default: "")
 
 
@@ -1178,7 +1194,8 @@
           Defaults to "".
         - Long option: `--lows`
 - Variables that can be used with the `-t`/`--template` argument:
-            `volume`, `volumebar`, `volumevbar`, `volumeipat`, `dB`, `status`
+            `volume`, `volumebar`, `volumevbar`, `volumeipat`, `dB`, `status`,
+            `volumestatus`
 - Note that `dB` might only return 0 on your system. This is known
   to happen on systems with a pulseaudio backend.
 - Default template: `Vol: <volume>% <status>`
@@ -1409,9 +1426,14 @@
 
 ### `UVMeter`
 
-- Aliases to "uv " + station id. For example: `%uv brisbane%` or `%uv
-  alice springs%`
-- Args: default monitor arguments.
+- Aliases to "uv " + station id. For example: `%uv Brisbane%` or `%uv
+  Alice Springs%`
+- Args: default monitor arguments, plus:
+  - `--useManager` _bool_ : Whether to use one single manager per monitor for
+    managing network connections or create a new one every time a connection is
+    made.
+    - Short option: `-m`
+    - Default: True
 
 - *Reminder:* Keep the refresh rate high, to avoid making unnecessary
   requests every time the plug-in is run.
@@ -1419,7 +1441,7 @@
   http://www.arpansa.gov.au/uvindex/realtime/xml/uvvalues.xml
 - Example:
 
-        Run UVMeter "brisbane" ["-H", "3", "-L", "3", "--low", "green", "--high", "red"] 900
+        Run UVMeter "Brisbane" ["-H", "3", "-L", "3", "--low", "green", "--high", "red"] 900
 
 ## Executing External Commands
 
@@ -1667,20 +1689,20 @@
 with the help of the greater xmobar and Haskell communities.
 
 In particular, xmobar [incorporates patches] by Mohammed Alshiekh,
-Alex Ameen, Axel Angel, Claudio Bley, Dragos Boca, Ben Boeckel, Duncan
-Burke, Roman Cheplyaka, Patrick Chilton, Antoine Eiche, Nathaniel
-Wesley Filardo, John Goerzen, Reto Hablützel, Juraj Hercek, Tomáš
-Janoušek, Ada Joule, Spencer Janssen, Roman Joost, Jochen Keil,
-Lennart Kolmodin, Krzysztof Kosciuszkiewicz, Dmitry Kurochkin, Todd
-Lunter, Vanessa McHale, Robert J. Macomber, Dmitry Malikov, David
-McLean, Marcin Mikołajczyk, Dino Morelli, Tony Morris, Eric Mrak,
-Thiago Negri, Edward O'Callaghan, Svein Ove, Martin Perner, Jens
-Petersen, Alexander Polakov, Pavan Rikhi, Petr Rockai, Andrew Emmanuel
-Rosa, Sackville-West, Markus Scherer, Daniel Schüssler, Olivier
-Schneider, Alexander Shabalin, Valentin Shirokov, Peter Simons,
-Alexander Solovyov, Will Song, John Soros, Felix Springer, Travis
-Staton, Artem Tarasov, Samuli Thomasson, Edward Tjörnhammar, Sergei
-Trofimovich, Thomas Tuegel, John Tyree, Jan Vornberger, Anton
+Alex Ameen, Axel Angel, Dhananjay Balan, Claudio Bley, Dragos Boca,
+Ben Boeckel, Duncan Burke, Roman Cheplyaka, Patrick Chilton, Antoine
+Eiche, Nathaniel Wesley Filardo, John Goerzen, Reto Hablützel, Juraj
+Hercek, Tomáš Janoušek, Ada Joule, Spencer Janssen, Roman Joost,
+Jochen Keil, Lennart Kolmodin, Krzysztof Kosciuszkiewicz, Dmitry
+Kurochkin, Todd Lunter, Vanessa McHale, Robert J. Macomber, Dmitry
+Malikov, David McLean, Marcin Mikołajczyk, Dino Morelli, Tony Morris,
+Eric Mrak, Thiago Negri, Edward O'Callaghan, Svein Ove, Martin Perner,
+Jens Petersen, Alexander Polakov, Pavan Rikhi, Petr Rockai, Andrew
+Emmanuel Rosa, Sackville-West, Markus Scherer, Daniel Schüssler,
+Olivier Schneider, Alexander Shabalin, Valentin Shirokov, Peter
+Simons, Alexander Solovyov, Will Song, John Soros, Felix Springer,
+Travis Staton, Artem Tarasov, Samuli Thomasson, Edward Tjörnhammar,
+Sergei Trofimovich, Thomas Tuegel, John Tyree, Jan Vornberger, Anton
 Vorontsov, Daniel Wagner, Zev Weiss, Phil Xiaojun Hu, Edward Z. Yang
 and Norbert Zeh.
 
diff --git a/src/Xmobar/App/EventLoop.hs b/src/Xmobar/App/EventLoop.hs
--- a/src/Xmobar/App/EventLoop.hs
+++ b/src/Xmobar/App/EventLoop.hs
@@ -17,7 +17,12 @@
 ------------------------------------------------------------------------------
 
 
-module Xmobar.App.EventLoop (startLoop, startCommand) where
+module Xmobar.App.EventLoop
+    ( startLoop
+    , startCommand
+    , newRefreshLock
+    , refreshLock
+    ) where
 
 import Prelude hiding (lookup)
 import Graphics.X11.Xlib hiding (textExtents, textWidth)
@@ -31,7 +36,7 @@
 import Control.Concurrent
 import Control.Concurrent.Async (Async, async)
 import Control.Concurrent.STM
-import Control.Exception (handle, SomeException(..))
+import Control.Exception (bracket_, handle, SomeException(..))
 import Data.Bits
 import Data.Map hiding (foldr, map, filter)
 import Data.Maybe (fromJust, isJust)
@@ -47,7 +52,10 @@
 import Xmobar.X11.Draw
 import Xmobar.X11.Bitmap as Bitmap
 import Xmobar.X11.Types
+
+#ifndef THREADED_RUNTIME
 import Xmobar.X11.Events(nextEvent')
+#endif
 
 #ifdef XFT
 import Graphics.X11.Xft
@@ -60,15 +68,34 @@
 runX :: XConf -> X () -> IO ()
 runX xc f = runReaderT f xc
 
+newRefreshLock :: IO (TMVar ())
+newRefreshLock = atomically $ newTMVar ()
+
+refreshLock :: TMVar () -> IO a -> IO a
+refreshLock var = bracket_ lock unlock
+    where
+        lock = atomically $ takeTMVar var
+        unlock = atomically $ putTMVar var ()
+
+refreshLockT :: TMVar () -> STM a -> STM a
+refreshLockT var action = do
+    takeTMVar var
+    r <- action
+    putTMVar var ()
+    return r
+
 -- | Starts the main event loop and threads
-startLoop :: XConf -> TMVar SignalType -> [[([Async ()], TVar String)]]
-             -> IO ()
-startLoop xcfg@(XConf _ _ w _ _ _ _) sig vs = do
+startLoop :: XConf
+          -> TMVar SignalType
+          -> TMVar ()
+          -> [[([Async ()], TVar String)]]
+          -> IO ()
+startLoop xcfg@(XConf _ _ w _ _ _ _) sig pauser vs = do
 #ifdef XFT
     xftInitFtLibrary
 #endif
     tv <- atomically $ newTVar []
-    _ <- forkIO (handle (handler "checker") (checker tv [] vs sig))
+    _ <- forkIO (handle (handler "checker") (checker tv [] vs sig pauser))
 #ifdef THREADED_RUNTIME
     _ <- forkOS (handle (handler "eventer") (eventer sig))
 #else
@@ -108,15 +135,16 @@
            -> [String]
            -> [[([Async ()], TVar String)]]
            -> TMVar SignalType
+           -> TMVar ()
            -> IO ()
-checker tvar ov vs signal = do
-      nval <- atomically $ do
+checker tvar ov vs signal pauser = do
+      nval <- atomically $ refreshLockT pauser $ do
               nv <- mapM concatV vs
               guard (nv /= ov)
               writeTVar tvar nv
               return nv
       atomically $ putTMVar signal Wakeup
-      checker tvar nval vs signal
+      checker tvar nval vs signal pauser
     where
       concatV = fmap concat . mapM (readTVar . snd)
 
diff --git a/src/Xmobar/App/Main.hs b/src/Xmobar/App/Main.hs
--- a/src/Xmobar/App/Main.hs
+++ b/src/Xmobar/App/Main.hs
@@ -40,9 +40,10 @@
 import Xmobar.X11.Text
 import Xmobar.X11.Window
 import Xmobar.App.Opts (recompileFlag, verboseFlag, getOpts, doOpts)
-import Xmobar.App.EventLoop (startLoop, startCommand)
+import Xmobar.App.EventLoop (startLoop, startCommand, newRefreshLock, refreshLock)
 import Xmobar.App.Compile (recompile, trace)
 import Xmobar.App.Config
+import Xmobar.App.Timer (withTimer)
 
 xmobar :: Config -> IO ()
 xmobar conf = withDeferSignals $ do
@@ -53,14 +54,16 @@
   cls   <- mapM (parseTemplate (commands conf) (sepChar conf))
                 (splitTemplate (alignSep conf) (template conf))
   sig   <- setupSignalHandler
-  bracket (mapM (mapM $ startCommand sig) cls)
-          cleanupThreads
-          $ \vars -> do
-    (r,w) <- createWin d fs conf
-    let ic = Map.empty
-        to = textOffset conf
-        ts = textOffsets conf ++ replicate (length fl) (-1)
-    startLoop (XConf d r w (fs:fl) (to:ts) ic conf) sig vars
+  refLock <- newRefreshLock
+  withTimer (refreshLock refLock) $
+    bracket (mapM (mapM $ startCommand sig) cls)
+            cleanupThreads
+            $ \vars -> do
+      (r,w) <- createWin d fs conf
+      let ic = Map.empty
+          to = textOffset conf
+          ts = textOffsets conf ++ replicate (length fl) (-1)
+      startLoop (XConf d r w (fs:fl) (to:ts) ic conf) sig refLock vars
 
 configFromArgs :: Config -> IO Config
 configFromArgs cfg = getArgs >>= getOpts >>= doOpts cfg . fst
diff --git a/src/Xmobar/App/Timer.hs b/src/Xmobar/App/Timer.hs
new file mode 100644
--- /dev/null
+++ b/src/Xmobar/App/Timer.hs
@@ -0,0 +1,221 @@
+{-# LANGUAGE LambdaCase #-}
+------------------------------------------------------------------------------
+-- |
+-- Module: Xmobar.App.Timer
+-- Copyright: (c) 2019 Tomáš Janoušek
+-- License: BSD3-style (see LICENSE)
+--
+-- Maintainer: Tomáš Janoušek <tomi@nomi.cz>
+-- Stability: unstable
+--
+-- Timer coalescing for recurring actions.
+--
+------------------------------------------------------------------------------
+
+module Xmobar.App.Timer
+    ( doEveryTenthSeconds
+    , tenthSeconds
+    , withTimer
+    ) where
+
+import Control.Concurrent (threadDelay)
+import Control.Concurrent.Async (withAsync)
+import Control.Concurrent.STM
+import Control.Exception
+import Control.Monad (forever, forM, guard)
+import Data.Foldable (foldrM, for_)
+import Data.Int (Int64)
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Maybe (isJust, fromJust)
+import Data.Time.Clock.POSIX (getPOSIXTime)
+import Data.Unique
+import System.IO.Unsafe (unsafePerformIO)
+
+type Periods = Map Unique Period
+
+data Tick = Tick (TMVar ()) | UnCoalesce
+
+data Period = Period { rate :: Int64, next :: Int64, tick :: TMVar Tick }
+
+data UnCoalesceException = UnCoalesceException deriving Show
+instance Exception UnCoalesceException
+
+{-# NOINLINE periodsVar #-}
+periodsVar :: TVar (Maybe Periods)
+periodsVar = unsafePerformIO $ newTVarIO Nothing
+
+now :: IO Int64
+now = do
+    posix <- getPOSIXTime
+    return $ floor (10 * posix)
+
+newPeriod :: Int64 -> IO (Unique, Period)
+newPeriod r = do
+    u <- newUnique
+    t <- now
+    v <- atomically newEmptyTMVar
+    let t' = t - t `mod` r
+    return (u, Period { rate = r, next = t', tick = v })
+
+-- | Perform a given action every N tenths of a second.
+--
+-- The timer is aligned (coalesced) with other timers to minimize the number
+-- of wakeups and unnecessary redraws. If the action takes too long (one
+-- second or when the next timer is due), coalescing is disabled for it and it
+-- falls back to periodic sleep.
+doEveryTenthSeconds :: Int -> IO () -> IO ()
+doEveryTenthSeconds r action =
+    doEveryTenthSecondsCoalesced r action `catch` \UnCoalesceException ->
+        doEveryTenthSecondsSleeping r action
+
+-- | Perform a given action every N tenths of a second,
+-- coalesce with other timers using a given Timer instance.
+doEveryTenthSecondsCoalesced :: Int -> IO () -> IO ()
+doEveryTenthSecondsCoalesced r action = do
+    (u, p) <- newPeriod (fromIntegral r)
+    bracket_ (push u p) (pop u) $ forever $ bracket (wait p) done $ const action
+    where
+        push u p = atomically $ modifyTVar' periodsVar $ \case
+            Just periods -> Just $ M.insert u p periods
+            Nothing -> throw UnCoalesceException
+        pop u = atomically $ modifyTVar' periodsVar $ \case
+            Just periods -> Just $ M.delete u periods
+            Nothing -> Nothing
+
+        wait p = atomically (takeTMVar $ tick p) >>= \case
+            Tick doneVar -> return doneVar
+            UnCoalesce -> throwIO UnCoalesceException
+        done doneVar = atomically $ putTMVar doneVar ()
+
+-- | Perform a given action every N tenths of a second,
+-- making no attempt to synchronize with other timers.
+doEveryTenthSecondsSleeping :: Int -> IO () -> IO ()
+doEveryTenthSecondsSleeping r action = go
+    where go = action >> tenthSeconds r >> go
+
+-- | Sleep for a given amount of tenths of a second.
+--
+-- (Work around the Int max bound: since threadDelay takes an Int, it
+-- is not possible to set a thread delay grater than about 45 minutes.
+-- With a little recursion we solve the problem.)
+tenthSeconds :: Int -> IO ()
+tenthSeconds s | s >= x = do threadDelay (x * 100000)
+                             tenthSeconds (s - x)
+               | otherwise = threadDelay (s * 100000)
+               where x = (maxBound :: Int) `div` 100000
+
+-- | Start the timer coordination thread and perform a given IO action (this
+-- is meant to surround the entire xmobar execution), terminating the timer
+-- thread afterwards.
+--
+-- Additionally, if the timer thread fails, individual
+-- 'doEveryTenthSecondsCoalesced' invocations that are waiting to be
+-- coordinated by it are notified to fall back to periodic sleeping.
+--
+-- The timer thread _will_ fail immediately when running in a non-threaded
+-- RTS.
+withTimer :: (IO () -> IO ()) -> IO a -> IO a
+withTimer pauseRefresh action =
+    withAsync (timerThread `finally` cleanup) $ const action
+    where
+        timerThread = do
+            atomically $ writeTVar periodsVar $ Just M.empty
+            timerLoop pauseRefresh
+
+        cleanup = atomically $ readTVar periodsVar >>= \case
+            Just periods -> do
+                for_ periods unCoalesceTimer'
+                writeTVar periodsVar Nothing
+            Nothing -> return ()
+
+timerLoop :: (IO () -> IO ()) -> IO ()
+timerLoop pauseRefresh = forever $ do
+    tNow <- now
+    (toFire, tMaybeNext) <- atomically $ do
+        periods <- fromJust <$> readTVar periodsVar
+        let toFire = timersToFire tNow periods
+        let periods' = advanceTimers tNow periods
+        let tMaybeNext = nextFireTime periods'
+        writeTVar periodsVar $ Just periods'
+        return (toFire, tMaybeNext)
+    pauseRefresh $ do
+        -- To avoid multiple refreshes, pause refreshing for up to 1 second,
+        -- fire timers and wait for them to finish (update their text).
+        -- Those that need more time (e.g. weather monitors) will be dropped
+        -- from timer coalescing and will fall back to periodic sleep.
+        timeoutVar <- registerDelay $ case tMaybeNext of
+            Just tNext -> fromIntegral ((tNext - tNow) `max` 10) * 100000
+            Nothing -> 1000000
+        fired <- fireTimers toFire
+        timeouted <- waitForTimers timeoutVar fired
+        unCoalesceTimers timeouted
+    delayUntilNextFire
+
+advanceTimers :: Int64 -> Periods -> Periods
+advanceTimers t = M.map advance
+    where
+        advance p | next p <= t = p { next = t - t `mod` rate p + rate p }
+                  | otherwise = p
+
+timersToFire :: Int64 -> Periods -> [(Unique, Period)]
+timersToFire t periods = [ (u, p) | (u, p) <- M.toList periods, next p <= t ]
+
+nextFireTime :: Periods -> Maybe Int64
+nextFireTime periods
+    | M.null periods = Nothing
+    | otherwise = Just $ minimum [ next p | p <- M.elems periods ]
+
+fireTimers :: [(Unique, Period)] -> IO [(Unique, TMVar ())]
+fireTimers toFire = atomically $ forM toFire $ \(u, p) -> do
+    doneVar <- newEmptyTMVar
+    putTMVar (tick p) (Tick doneVar)
+    return (u, doneVar)
+
+waitForTimers :: TVar Bool -> [(Unique, TMVar ())] -> IO [Unique]
+waitForTimers timeoutVar fired = atomically $ do
+    timeoutOver <- readTVar timeoutVar
+    dones <- forM fired $ \(u, doneVar) -> do
+        done <- isJust <$> tryReadTMVar doneVar
+        return (u, done)
+    guard $ timeoutOver || all snd dones
+    return [u | (u, False) <- dones]
+
+-- | Handle slow timers (drop and signal them to stop coalescing).
+unCoalesceTimers :: [Unique] -> IO ()
+unCoalesceTimers timers = atomically $ do
+    periods <- fromJust <$> readTVar periodsVar
+    periods' <- foldrM unCoalesceTimer periods timers
+    writeTVar periodsVar $ Just periods'
+
+unCoalesceTimer :: Unique -> Periods -> STM Periods
+unCoalesceTimer u periods = do
+    unCoalesceTimer' (periods M.! u)
+    return $ u `M.delete` periods
+
+unCoalesceTimer' :: Period -> STM ()
+unCoalesceTimer' p = do
+    _ <- tryTakeTMVar (tick p)
+    putTMVar (tick p) UnCoalesce
+
+delayUntilNextFire :: IO ()
+delayUntilNextFire = do
+    Just periods <- readTVarIO periodsVar
+    let tMaybeNext = nextFireTime periods
+    tNow <- now
+    delayVar <- case tMaybeNext of
+        Just tNext -> do
+            -- Work around the Int max bound: threadDelay takes an Int, we can
+            -- only sleep for so long, which is okay, we'll just check timers
+            -- sooner and sleep again.
+            let maxDelay = (maxBound :: Int) `div` 100000
+                delay = (tNext - tNow) `min` fromIntegral maxDelay
+                delayUsec = fromIntegral delay * 100000
+            registerDelay delayUsec
+        Nothing -> atomically $ newTVar False
+    atomically $ do
+        delayOver <- readTVar delayVar
+        periods' <- fromJust <$> readTVar periodsVar
+        let tMaybeNext' = nextFireTime periods'
+        -- Return also if a new period is added (it may fire sooner).
+        guard $ delayOver || tMaybeNext /= tMaybeNext'
diff --git a/src/Xmobar/Plugins/DateZone.hs b/src/Xmobar/Plugins/DateZone.hs
--- a/src/Xmobar/Plugins/DateZone.hs
+++ b/src/Xmobar/Plugins/DateZone.hs
@@ -72,7 +72,7 @@
        else
         go (date f locale)
 
-      where go func = func >>= cb >> tenthSeconds r >> go func
+      where go func = doEveryTenthSeconds r $ func >>= cb
 
 {-# NOINLINE localeLock #-}
 -- ensures that only one plugin instance sets the locale
diff --git a/src/Xmobar/Plugins/MBox.hs b/src/Xmobar/Plugins/MBox.hs
--- a/src/Xmobar/Plugins/MBox.hs
+++ b/src/Xmobar/Plugins/MBox.hs
@@ -19,6 +19,7 @@
 import Xmobar.Run.Exec
 #ifdef INOTIFY
 
+import Xmobar.Plugins.Monitors.Common (parseOptsWith)
 import Xmobar.System.Utils (changeLoop, expandHome)
 
 import Control.Monad (when)
@@ -63,12 +64,6 @@
   , Option "s" ["suffix"] (ReqArg (\x o -> o { oSuffix = x }) "") ""
   ]
 
-parseOptions :: [String] -> IO Options
-parseOptions args =
-  case getOpt Permute options args of
-    (o, _, []) -> return $ foldr id defaults o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 #else
 import System.IO
 #endif
@@ -86,7 +81,7 @@
           " but the MBox plugin requires it"
 #else
   start (MBox boxes args _) cb = do
-    opts <- parseOptions args
+    opts <- parseOptsWith options defaults args
     let showAll = oAll opts
         prefix = oPrefix opts
         suffix = oSuffix opts
diff --git a/src/Xmobar/Plugins/Mail.hs b/src/Xmobar/Plugins/Mail.hs
--- a/src/Xmobar/Plugins/Mail.hs
+++ b/src/Xmobar/Plugins/Mail.hs
@@ -18,6 +18,7 @@
 import Xmobar.Run.Exec
 #ifdef INOTIFY
 
+import Xmobar.Plugins.Monitors.Common (parseOptsWith)
 import Xmobar.System.Utils (expandHome, changeLoop)
 
 import Control.Monad
@@ -64,13 +65,6 @@
   , Option "s" ["suffix"] (ReqArg (\x o -> o { oSuffix = x }) "") ""
   ]
 
-parseOptions :: [String] -> IO MOptions
-parseOptions args =
-  case getOpt Permute options args of
-    (o, _, []) -> return $ foldr id defaults o
-    (_, _, errs) -> ioError . userError $ concat errs
-
-
 -- | A list of mail box names and paths to maildirs.
 data Mail = Mail [(String, FilePath)] String
     deriving (Read, Show)
@@ -92,7 +86,7 @@
 #else
     start (MailX ms args _) cb = do
         vs <- mapM (const $ newTVarIO S.empty) ms
-        opts <- parseOptions args
+        opts <- parseOptsWith options defaults args
         let prefix = oPrefix opts
             suffix = oSuffix opts
             dir = oDir opts
diff --git a/src/Xmobar/Plugins/Monitors.hs b/src/Xmobar/Plugins/Monitors.hs
--- a/src/Xmobar/Plugins/Monitors.hs
+++ b/src/Xmobar/Plugins/Monitors.hs
@@ -19,7 +19,7 @@
 
 import Xmobar.Run.Exec
 
-import Xmobar.Plugins.Monitors.Common (runM, runMD)
+import Xmobar.Plugins.Monitors.Common (runM)
 #ifdef WEATHER
 import Xmobar.Plugins.Monitors.Weather
 #endif
@@ -42,12 +42,12 @@
 #ifdef UVMETER
 import Xmobar.Plugins.Monitors.UVMeter
 #endif
-#ifdef IWLIB
+#if defined IWLIB || defined USE_NL80211
 import Xmobar.Plugins.Monitors.Wireless
 #endif
 #ifdef LIBMPD
 import Xmobar.Plugins.Monitors.MPD
-import Xmobar.Plugins.Monitors.Common (runMBD)
+import Xmobar.Plugins.Monitors.Common (runMBD, runMD)
 #endif
 #ifdef ALSA
 import Xmobar.Plugins.Monitors.Volume
@@ -85,7 +85,7 @@
 #ifdef UVMETER
               | UVMeter      Station     Args Rate
 #endif
-#ifdef IWLIB
+#if defined IWLIB || defined USE_NL80211
               | Wireless Interface  Args Rate
 #endif
 #ifdef LIBMPD
@@ -142,7 +142,7 @@
 #ifdef UVMETER
     alias (UVMeter s _ _) = "uv " ++ s
 #endif
-#ifdef IWLIB
+#if defined IWLIB || defined USE_NL80211
     alias (Wireless i _ _) = i ++ "wi"
 #endif
 #ifdef LIBMPD
@@ -164,8 +164,8 @@
     start (TopProc a r) = startTop a r
     start (TopMem a r) = runM a topMemConfig runTopMem r
 #ifdef WEATHER
-    start (Weather s a r) = runMD (a ++ [s]) weatherConfig runWeather r weatherReady
-    start (WeatherX s c a r) = runMD (a ++ [s]) weatherConfig (runWeather' c) r weatherReady
+    start (Weather  s   a r) = startWeather    s a r
+    start (WeatherX s c a r) = startWeather' c s a r
 #endif
     start (Thermal z a r) = runM (a ++ [z]) thermalConfig runThermal r
     start (ThermalZone z a r) =
@@ -184,9 +184,9 @@
     start (Uptime a r) = runM a uptimeConfig runUptime r
     start (CatInt _ s a r) = runM a catIntConfig (runCatInt s) r
 #ifdef UVMETER
-    start (UVMeter s a r) = runM (a ++ [s]) uvConfig runUVMeter r
+    start (UVMeter s a r) = startUVMeter s a r
 #endif
-#ifdef IWLIB
+#if defined IWLIB || defined USE_NL80211
     start (Wireless i a r) = runM a wirelessConfig (runWireless i) r
 #endif
 #ifdef LIBMPD
diff --git a/src/Xmobar/Plugins/Monitors/Alsa.hs b/src/Xmobar/Plugins/Monitors/Alsa.hs
--- a/src/Xmobar/Plugins/Monitors/Alsa.hs
+++ b/src/Xmobar/Plugins/Monitors/Alsa.hs
@@ -57,17 +57,12 @@
   where
     modifyVolumeOpts f o = o { aoVolumeOpts = f (aoVolumeOpts o) }
 
-parseOpts :: [String] -> IO AlsaOpts
-parseOpts argv =
-    case getOpt Permute options argv of
-        (o, _, []) -> return $ foldr id defaultOpts o
-        (_, _, errs) -> ioError . userError $ concat errs
-
+-- | Drop generic Monitor args first, then apply 'parseOptsWith' in order to
+-- parse everything.
 parseOptsIncludingMonitorArgs :: [String] -> IO AlsaOpts
 parseOptsIncludingMonitorArgs args =
-    -- Drop generic Monitor args first
     case getOpt Permute [] args of
-      (_, args', _) -> parseOpts args'
+        (_, args', _) -> parseOptsWith options defaultOpts args'
 
 startAlsaPlugin :: String -> String -> [String] -> (String -> IO ()) -> IO ()
 startAlsaPlugin mixerName controlName args cb = do
@@ -80,7 +75,7 @@
         -- it would have to inline 'runMBD', 'doArgs' and 'parseOpts' to see
         -- it, which probably isn't going to happen with the default
         -- optimization settings).
-        opts2 <- io $ parseOpts args2
+        opts2 <- io $ parseOptsWith options defaultOpts args2
         Volume.runVolumeWith (aoVolumeOpts opts2) mixerName controlName
 
   withMonitorWaiter mixerName (aoAlsaCtlPath opts) cb $ \wait_ ->
diff --git a/src/Xmobar/Plugins/Monitors/Batt.hs b/src/Xmobar/Plugins/Monitors/Batt.hs
--- a/src/Xmobar/Plugins/Monitors/Batt.hs
+++ b/src/Xmobar/Plugins/Monitors/Batt.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE CPP #-}
+
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Plugins.Monitors.Batt
@@ -17,11 +19,14 @@
 
 import System.Process (system)
 import Control.Monad (void, unless)
-import Control.Exception (SomeException, handle)
 import Xmobar.Plugins.Monitors.Common
+import Control.Exception (SomeException, handle)
 import System.FilePath ((</>))
 import System.IO (IOMode(ReadMode), hGetLine, withFile)
 import System.Posix.Files (fileExist)
+#ifdef FREEBSD
+import System.BSD.Sysctl (sysctlReadInt)
+#endif
 import System.Console.GetOpt
 import Data.List (sort, sortBy, group)
 import Data.Maybe (fromMaybe)
@@ -103,14 +108,8 @@
   , Option "" ["highs"] (ReqArg (\x o -> o { highString = x }) "") ""
   ]
 
-parseOpts :: [String] -> IO BattOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 data Status = Charging | Discharging | Full | Idle | Unknown deriving (Read, Eq)
-
+-- Result perc watts time-seconds Status
 data Result = Result Float Float Float Status | NA
 
 sysDir :: FilePath
@@ -154,6 +153,39 @@
  where
    c = 100 * min 1 charge
 
+maybeAlert :: BattOpts -> Float -> IO ()
+maybeAlert opts left =
+  case onLowAction opts of
+    Nothing -> return ()
+    Just x -> unless (isNaN left || actionThreshold opts < 100 * left)
+                $ void $ system x
+
+-- | FreeBSD battery query
+#ifdef FREEBSD
+battStatusFbsd :: Int -> Status
+battStatusFbsd x
+  | x == 1 = Discharging
+  | x == 2 = Charging
+  | otherwise = Unknown
+
+readBatteriesFbsd :: BattOpts -> IO Result
+readBatteriesFbsd opts = do
+  lf <- sysctlReadInt "hw.acpi.battery.life"
+  rt <- sysctlReadInt "hw.acpi.battery.rate"
+  tm <- sysctlReadInt "hw.acpi.battery.time"
+  st <- sysctlReadInt "hw.acpi.battery.state"
+  acline <- sysctlReadInt "hw.acpi.acline"
+  let p = fromIntegral lf / 100
+      w = fromIntegral rt
+      t = fromIntegral tm * 60
+      ac = acline == 1
+      -- battery full when rate is 0 and on ac.
+      sts = if (w == 0 && ac) then Full else (battStatusFbsd $ fromIntegral st)
+  unless ac (maybeAlert opts p)
+  return (Result p w t sts)
+
+#else
+-- | query linux battery
 safeFileExist :: String -> String -> IO Bool
 safeFileExist d f = handle noErrors $ fileExist (d </> f)
   where noErrors = const (return False) :: SomeException -> IO Bool
@@ -195,7 +227,7 @@
            a' = max a b -- sometimes the reported max charge is lower than
        return $ Battery (3600 * a' / sc') -- wattseconds
                         (3600 * b / sc') -- wattseconds
-                        (d / sc') -- watts
+                        (abs d / sc') -- watts
                         s -- string: Discharging/Charging/Full
     where grab f = handle onError $ withFile f ReadMode (fmap read . hGetLine)
           onError = const (return (-1)) :: SomeException -> IO Float
@@ -210,15 +242,8 @@
 mostCommonDef :: Eq a => a -> [a] -> a
 mostCommonDef x xs = head $ last $ [x] : sortOn length (group xs)
 
-maybeAlert :: BattOpts -> Float -> IO ()
-maybeAlert opts left =
-  case onLowAction opts of
-    Nothing -> return ()
-    Just x -> unless (isNaN left || actionThreshold opts < 100 * left)
-                $ void $ system x
-
-readBatteries :: BattOpts -> [Files] -> IO Result
-readBatteries opts bfs =
+readBatteriesLinux :: BattOpts -> [Files] -> IO Result
+readBatteriesLinux opts bfs =
     do let bfs' = filter (/= NoFiles) bfs
        bats <- mapM (readBattery (scale opts)) (take 3 bfs')
        ac <- haveAc (onlineFile opts)
@@ -239,25 +264,34 @@
                  | otherwise = Discharging
        unless ac (maybeAlert opts left)
        return $ if isNaN left then NA else Result left watts time racst
+#endif
 
 runBatt :: [String] -> Monitor String
 runBatt = runBatt' ["BAT", "BAT0", "BAT1", "BAT2"]
 
 runBatt' :: [String] -> [String] -> Monitor String
 runBatt' bfs args = do
-  opts <- io $ parseOpts args
-  let sp = incPerc opts
-  c <- io $ readBatteries opts =<< mapM batteryFiles bfs
+  opts <- io $ parseOptsWith options defaultOpts args
+#ifdef FREEBSD
+  c <- io $ readBatteriesFbsd opts
+#else
+  c <- io $ readBatteriesLinux opts =<< mapM batteryFiles bfs
+#endif
+  formatResult c opts
+
+formatResult :: Result -> BattOpts -> Monitor String
+formatResult res bopt = do
+  let sp = incPerc bopt
   suffix <- getConfigValue useSuffix
   d <- getConfigValue decDigits
   nas <- getConfigValue naString
-  case c of
+  case res of
     Result x w t s ->
       do l <- fmtPercent x sp
-         ws <- fmtWatts w opts suffix d
-         si <- getIconPattern opts s x
+         ws <- fmtWatts w bopt suffix d
+         si <- getIconPattern bopt s x
          st <- showWithColors'
-                 (fmtStatus opts s nas (getBattStatus x opts))
+                 (fmtStatus bopt s nas (getBattStatus x bopt))
                  (100 * x)
          parseTemplate (l ++ [st, fmtTime $ floor t, ws, si])
     NA -> getConfigValue naString
diff --git a/src/Xmobar/Plugins/Monitors/Bright.hs b/src/Xmobar/Plugins/Monitors/Bright.hs
--- a/src/Xmobar/Plugins/Monitors/Bright.hs
+++ b/src/Xmobar/Plugins/Monitors/Bright.hs
@@ -44,13 +44,6 @@
              o { curBrightIconPattern = Just $ parseIconPattern x }) "") ""
           ]
 
--- from Batt.hs
-parseOpts :: [String] -> IO BrightOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 sysDir :: FilePath
 sysDir = "/sys/class/backlight/"
 
@@ -75,7 +68,7 @@
 
 runBright :: [String] ->  Monitor String
 runBright args = do
-  opts <- io $ parseOpts args
+  opts <- io $ parseOptsWith options defaultOpts args
   f <- io $ brightFiles opts
   c <- io $ readBright f
   case f of
diff --git a/src/Xmobar/Plugins/Monitors/Common/Parsers.hs b/src/Xmobar/Plugins/Monitors/Common/Parsers.hs
--- a/src/Xmobar/Plugins/Monitors/Common/Parsers.hs
+++ b/src/Xmobar/Plugins/Monitors/Common/Parsers.hs
@@ -24,12 +24,14 @@
                                               , skipTillString
                                               , parseTemplate
                                               , parseTemplate'
+                                              , parseOptsWith
                                               ) where
 
 import Xmobar.Plugins.Monitors.Common.Types
 
 import Control.Applicative ((<$>))
 import qualified Data.Map as Map
+import System.Console.GetOpt (ArgOrder(Permute), OptDescr, getOpt)
 import Text.ParserCombinators.Parsec
 
 runP :: Parser [a] -> String -> IO [a]
@@ -150,3 +152,14 @@
          Nothing -> return $ "<" ++ ts ++ ">"
          Just  r -> let f "" = r; f n = n; in f <$> parseTemplate' r m
        return $ s ++ str ++ ss ++ next
+
+-- | Try to parse arguments from the config file and apply them to Options.
+parseOptsWith
+    :: [OptDescr (opts -> opts)]  -- ^ Options that are specifiable
+    -> opts                       -- ^ Default options to use as a fallback
+    -> [String]                   -- ^ Actual arguments given
+    -> IO opts
+parseOptsWith options defaultOpts argv =
+    case getOpt Permute options argv of
+        (o, _, []  ) -> pure $ foldr id defaultOpts o
+        (_, _, errs) -> ioError . userError $ concat errs
diff --git a/src/Xmobar/Plugins/Monitors/Common/Run.hs b/src/Xmobar/Plugins/Monitors/Common/Run.hs
--- a/src/Xmobar/Plugins/Monitors/Common/Run.hs
+++ b/src/Xmobar/Plugins/Monitors/Common/Run.hs
@@ -19,6 +19,9 @@
                                           , runMD
                                           , runMB
                                           , runMBD
+                                          , runML
+                                          , runMLD
+                                          , getArgvs
                                           ) where
 
 import Control.Exception (SomeException,handle)
@@ -27,7 +30,7 @@
 import System.Console.GetOpt
 
 import Xmobar.Plugins.Monitors.Common.Types
-import Xmobar.Run.Exec (tenthSeconds)
+import Xmobar.Run.Exec (doEveryTenthSeconds)
 
 options :: [OptDescr Opts]
 options =
@@ -55,6 +58,13 @@
     , Option "E" ["maxtwidthellipsis"] (ReqArg MaxTotalWidthEllipsis "Maximum total width ellipsis") "Ellipsis to be added to the total text when it has reached its max width."
     ]
 
+-- | Get all argument values out of a list of arguments.
+getArgvs :: [String] -> [String]
+getArgvs args =
+    case getOpt Permute options args of
+        (_, n, []  ) -> n
+        (_, _, errs) -> errs
+
 doArgs :: [String]
        -> ([String] -> Monitor String)
        -> ([String] -> Monitor Bool)
@@ -100,11 +110,11 @@
 
 runM :: [String] -> IO MConfig -> ([String] -> Monitor String) -> Int
         -> (String -> IO ()) -> IO ()
-runM args conf action r = runMB args conf action (tenthSeconds r)
+runM args conf action r = runML args conf action (doEveryTenthSeconds r)
 
 runMD :: [String] -> IO MConfig -> ([String] -> Monitor String) -> Int
         -> ([String] -> Monitor Bool) -> (String -> IO ()) -> IO ()
-runMD args conf action r = runMBD args conf action (tenthSeconds r)
+runMD args conf action r = runMLD args conf action (doEveryTenthSeconds r)
 
 runMB :: [String] -> IO MConfig -> ([String] -> Monitor String) -> IO ()
         -> (String -> IO ()) -> IO ()
@@ -115,6 +125,17 @@
 runMBD args conf action wait detect cb = handle (cb . showException) loop
   where ac = doArgs args action detect
         loop = conf >>= runReaderT ac >>= cb >> wait >> loop
+
+runML :: [String] -> IO MConfig -> ([String] -> Monitor String)
+      -> (IO () -> IO ()) -> (String -> IO ()) -> IO ()
+runML args conf action looper = runMLD args conf action looper (\_ -> return True)
+
+runMLD :: [String] -> IO MConfig -> ([String] -> Monitor String)
+       -> (IO () -> IO ()) -> ([String] -> Monitor Bool) -> (String -> IO ())
+       -> IO ()
+runMLD args conf action looper detect cb = handle (cb . showException) loop
+  where ac = doArgs args action detect
+        loop = looper $ conf >>= runReaderT ac >>= cb
 
 showException :: SomeException -> String
 showException = ("error: "++) . show . flip asTypeOf undefined
diff --git a/src/Xmobar/Plugins/Monitors/Common/Types.hs b/src/Xmobar/Plugins/Monitors/Common/Types.hs
--- a/src/Xmobar/Plugins/Monitors/Common/Types.hs
+++ b/src/Xmobar/Plugins/Monitors/Common/Types.hs
@@ -25,8 +25,9 @@
                                             , io
                                             ) where
 
-import Data.IORef
-import Control.Monad.Reader
+import Control.Monad.Reader (ReaderT, ask, liftIO)
+import Data.IORef (IORef, modifyIORef, newIORef, readIORef)
+
 
 type Monitor a = ReaderT MConfig IO a
 
diff --git a/src/Xmobar/Plugins/Monitors/CoreTemp.hs b/src/Xmobar/Plugins/Monitors/CoreTemp.hs
--- a/src/Xmobar/Plugins/Monitors/CoreTemp.hs
+++ b/src/Xmobar/Plugins/Monitors/CoreTemp.hs
@@ -25,8 +25,7 @@
 coreTempConfig :: IO MConfig
 coreTempConfig = mkMConfig
        "Temp: <core0>C" -- template
-       (map ((++) "core" . show) [0 :: Int ..]) -- available
-                                                -- replacements
+       (map ((++) "core" . show) [0 :: Int ..]) -- available replacements
 
 -- |
 -- Function retrieves monitor string holding the core temperature
diff --git a/src/Xmobar/Plugins/Monitors/Cpu.hs b/src/Xmobar/Plugins/Monitors/Cpu.hs
--- a/src/Xmobar/Plugins/Monitors/Cpu.hs
+++ b/src/Xmobar/Plugins/Monitors/Cpu.hs
@@ -35,12 +35,6 @@
      o { loadIconPattern = Just $ parseIconPattern x }) "") ""
   ]
 
-parseOpts :: [String] -> IO CpuOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 cpuConfig :: IO MConfig
 cpuConfig = mkMConfig
        "Cpu: <total>%"
@@ -77,7 +71,7 @@
 runCpu :: CpuDataRef -> [String] -> Monitor String
 runCpu cref argv =
     do c <- io (parseCpu cref)
-       opts <- io $ parseOpts argv
+       opts <- io $ parseOptsWith options defaultOpts argv
        l <- formatCpu opts c
        parseTemplate l
 
diff --git a/src/Xmobar/Plugins/Monitors/Disk.hs b/src/Xmobar/Plugins/Monitors/Disk.hs
--- a/src/Xmobar/Plugins/Monitors/Disk.hs
+++ b/src/Xmobar/Plugins/Monitors/Disk.hs
@@ -34,27 +34,25 @@
   , contiguous :: Bool
   }
 
-parseDiskIOOpts :: [String] -> IO DiskIOOpts
-parseDiskIOOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
- where defaultOpts = DiskIOOpts
-          { totalIconPattern = Nothing
-          , writeIconPattern = Nothing
-          , readIconPattern = Nothing
-          , contiguous = False
-          }
-       options =
-          [ Option "" ["total-icon-pattern"] (ReqArg (\x o ->
-             o { totalIconPattern = Just $ parseIconPattern x}) "") ""
-          , Option "" ["write-icon-pattern"] (ReqArg (\x o ->
-             o { writeIconPattern = Just $ parseIconPattern x}) "") ""
-          , Option "" ["read-icon-pattern"] (ReqArg (\x o ->
-             o { readIconPattern = Just $ parseIconPattern x}) "") ""
-          , Option "c" ["contiguous"] (NoArg (\o -> o {contiguous = True})) ""
-          ]
+dioDefaultOpts :: DiskIOOpts
+dioDefaultOpts = DiskIOOpts
+   { totalIconPattern = Nothing
+   , writeIconPattern = Nothing
+   , readIconPattern = Nothing
+   , contiguous = False
+   }
 
+dioOptions :: [OptDescr (DiskIOOpts -> DiskIOOpts)]
+dioOptions =
+   [ Option "" ["total-icon-pattern"] (ReqArg (\x o ->
+      o { totalIconPattern = Just $ parseIconPattern x}) "") ""
+   , Option "" ["write-icon-pattern"] (ReqArg (\x o ->
+      o { writeIconPattern = Just $ parseIconPattern x}) "") ""
+   , Option "" ["read-icon-pattern"] (ReqArg (\x o ->
+      o { readIconPattern = Just $ parseIconPattern x}) "") ""
+   , Option "c" ["contiguous"] (NoArg (\o -> o {contiguous = True})) ""
+   ]
+
 diskIOConfig :: IO MConfig
 diskIOConfig = mkMConfig "" ["total", "read", "write"
                             ,"totalb", "readb", "writeb"
@@ -72,24 +70,22 @@
   , contiguousU :: Bool
   }
 
-parseDiskUOpts :: [String] -> IO DiskUOpts
-parseDiskUOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
- where defaultOpts = DiskUOpts
-          { freeIconPattern = Nothing
-          , usedIconPattern = Nothing
-          , contiguousU = False
-          }
-       options =
-          [ Option "" ["free-icon-pattern"] (ReqArg (\x o ->
-             o { freeIconPattern = Just $ parseIconPattern x}) "") ""
-          , Option "" ["used-icon-pattern"] (ReqArg (\x o ->
-             o { usedIconPattern = Just $ parseIconPattern x}) "") ""
-          , Option "c" ["contiguous"] (NoArg (\o -> o {contiguousU = True})) ""
-          ]
+duDefaultOpts :: DiskUOpts
+duDefaultOpts = DiskUOpts
+   { freeIconPattern = Nothing
+   , usedIconPattern = Nothing
+   , contiguousU = False
+   }
 
+duOptions :: [OptDescr (DiskUOpts -> DiskUOpts)]
+duOptions =
+   [ Option "" ["free-icon-pattern"] (ReqArg (\x o ->
+      o { freeIconPattern = Just $ parseIconPattern x}) "") ""
+   , Option "" ["used-icon-pattern"] (ReqArg (\x o ->
+      o { usedIconPattern = Just $ parseIconPattern x}) "") ""
+   , Option "c" ["contiguous"] (NoArg (\o -> o {contiguousU = True})) ""
+   ]
+
 diskUConfig :: IO MConfig
 diskUConfig = mkMConfig ""
               [ "size", "free", "used", "freep", "usedp"
@@ -208,7 +204,7 @@
 
 runDiskIO :: DevDataRef -> [(String, String)] -> [String] -> Monitor String
 runDiskIO dref disks argv = do
-  opts <- io $ parseDiskIOOpts argv
+  opts <- io $ parseOptsWith dioOptions dioDefaultOpts argv
   dev <- io $ mountedOrDiskDevices (map fst disks)
   dat <- io $ mountedData dref (map fst dev)
   strs <- mapM (runDiskIO' opts) $ devTemplates disks dev dat
@@ -254,6 +250,6 @@
 runDiskU :: [(String, String)] -> [String] -> Monitor String
 runDiskU disks argv = do
   devs <- io $ mountedDevices (map fst disks)
-  opts <- io $ parseDiskUOpts argv
+  opts <- io $ parseOptsWith duOptions duDefaultOpts argv
   strs <- mapM (\(d, p) -> runDiskU' opts (findTempl d p disks) p) devs
   return $ (if contiguousU opts then concat else unwords) strs
diff --git a/src/Xmobar/Plugins/Monitors/MPD.hs b/src/Xmobar/Plugins/Monitors/MPD.hs
--- a/src/Xmobar/Plugins/Monitors/MPD.hs
+++ b/src/Xmobar/Plugins/Monitors/MPD.hs
@@ -64,7 +64,7 @@
 
 runMPD :: [String] -> Monitor String
 runMPD args = do
-  opts <- io $ mopts args
+  opts <- io $ parseOptsWith options defaultOpts args
   status <- io $ withMPD opts M.status
   song <- io $ withMPD opts M.currentSong
   s <- parseMPD status song opts
@@ -79,7 +79,7 @@
 
 mpdReady :: [String] -> Monitor Bool
 mpdReady args = do
-  opts <- io $ mopts args
+  opts <- io $ parseOptsWith options defaultOpts args
   response <- io $ withMPD opts M.ping
   case response of
     Right _         -> return True
@@ -88,12 +88,6 @@
     Left M.NoMPD    -> return False
     Left (M.ConnectionError _) -> return False
     Left _          -> return True
-
-mopts :: [String] -> IO MOpts
-mopts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
 
 parseMPD :: M.Response M.Status -> M.Response (Maybe M.Song) -> MOpts
             -> Monitor [String]
diff --git a/src/Xmobar/Plugins/Monitors/Mem.hs b/src/Xmobar/Plugins/Monitors/Mem.hs
--- a/src/Xmobar/Plugins/Monitors/Mem.hs
+++ b/src/Xmobar/Plugins/Monitors/Mem.hs
@@ -41,12 +41,6 @@
      o { availableIconPattern = Just $ parseIconPattern x }) "") ""
   ]
 
-parseOpts :: [String] -> IO MemOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 memConfig :: IO MConfig
 memConfig = mkMConfig
        "Mem: <usedratio>% (<cache>M)" -- template
@@ -91,6 +85,6 @@
 runMem :: [String] -> Monitor String
 runMem argv =
     do m <- io parseMEM
-       opts <- io $ parseOpts argv
+       opts <- io $ parseOptsWith options defaultOpts argv
        l <- formatMem opts m
        parseTemplate l
diff --git a/src/Xmobar/Plugins/Monitors/MultiCoreTemp.hs b/src/Xmobar/Plugins/Monitors/MultiCoreTemp.hs
--- a/src/Xmobar/Plugins/Monitors/MultiCoreTemp.hs
+++ b/src/Xmobar/Plugins/Monitors/MultiCoreTemp.hs
@@ -60,12 +60,6 @@
               ""
           ]
 
--- | Parse Arguments and apply them to Options.
-parseOpts :: [String] -> IO CTOpts
-parseOpts argv = case getOpt Permute options argv of
-                   (opts , _ , []  ) -> return $ foldr id defaultOpts opts
-                   (_    , _ , errs) -> ioError . userError $ concat errs
-
 -- | Generate Config with a default template and options.
 cTConfig :: IO MConfig
 cTConfig = mkMConfig cTTemplate cTOptions
@@ -157,7 +151,7 @@
 
 runCT :: [String] -> Monitor String
 runCT argv = do cTs <- io parseCT
-                opts <- io $ parseOpts argv
+                opts <- io $ parseOptsWith options defaultOpts argv
                 l <- formatCT opts cTs
                 parseTemplate l
 
diff --git a/src/Xmobar/Plugins/Monitors/MultiCpu.hs b/src/Xmobar/Plugins/Monitors/MultiCpu.hs
--- a/src/Xmobar/Plugins/Monitors/MultiCpu.hs
+++ b/src/Xmobar/Plugins/Monitors/MultiCpu.hs
@@ -47,12 +47,6 @@
   , Option "" ["contiguous-icons"] (NoArg (\o -> o {contiguous = True})) ""
   ]
 
-parseOpts :: [String] -> IO MultiCpuOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 variables :: [String]
 variables = ["bar", "vbar","ipat","total","user","nice","system","idle"]
 vNum :: Int
@@ -122,7 +116,7 @@
 runMultiCpu :: CpuDataRef -> [String] -> Monitor String
 runMultiCpu cref argv =
   do c <- io $ parseCpuData cref
-     opts <- io $ parseOpts argv
+     opts <- io $ parseOptsWith options defaultOpts argv
      l <- formatMultiCpus opts c
      a <- formatAutoCpus opts l
      parseTemplate $ a ++ l
diff --git a/src/Xmobar/Plugins/Monitors/Net.hs b/src/Xmobar/Plugins/Monitors/Net.hs
--- a/src/Xmobar/Plugins/Monitors/Net.hs
+++ b/src/Xmobar/Plugins/Monitors/Net.hs
@@ -68,12 +68,6 @@
      o { onlyDevList = Just $ parseDevList x }) "") ""
   ]
 
-parseOpts :: [String] -> IO NetOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-    (o, _, []) -> return $ foldr id defaultOpts o
-    (_, _, errs) -> ioError . userError $ concat errs
-
 data UnitPerSec = Bs | KBs | MBs | GBs deriving (Eq,Enum,Ord)
 data NetValue = NetValue Float UnitPerSec deriving (Eq,Show)
 
@@ -188,7 +182,7 @@
 runNet :: NetDevRef -> String -> [String] -> Monitor String
 runNet nref i argv = do
   dev <- io $ parseNet nref i
-  opts <- io $ parseOpts argv
+  opts <- io $ parseOptsWith options defaultOpts argv
   printNet opts dev
 
 parseNets :: [(NetDevRef, String)] -> IO [NetDevRate]
@@ -196,7 +190,7 @@
 
 runNets :: [(NetDevRef, String)] -> [String] -> Monitor String
 runNets refs argv = do
-  opts <- io $ parseOpts argv
+  opts <- io $ parseOptsWith options defaultOpts argv
   dev <- io $ parseActive $ filterRefs opts refs
   printNet opts dev
     where parseActive refs' = fmap selectActive (parseNets refs')
diff --git a/src/Xmobar/Plugins/Monitors/UVMeter.hs b/src/Xmobar/Plugins/Monitors/UVMeter.hs
--- a/src/Xmobar/Plugins/Monitors/UVMeter.hs
+++ b/src/Xmobar/Plugins/Monitors/UVMeter.hs
@@ -19,15 +19,40 @@
 
 import qualified Control.Exception as CE
 import Network.HTTP.Conduit
-       (parseRequest, newManager, tlsManagerSettings, httpLbs,
-        responseBody)
+    ( Manager
+    , httpLbs
+    , managerConnCount
+    , newManager
+    , parseRequest
+    , responseBody
+    , tlsManagerSettings
+    )
 import Data.ByteString.Lazy.Char8 as B
+import Data.Maybe (fromMaybe)
+import System.Console.GetOpt (ArgDescr(ReqArg), OptDescr(Option))
 import Text.Read (readMaybe)
 import Text.Parsec
 import Text.Parsec.String
 import Control.Monad (void)
 
 
+-- | Options the user may specify.
+newtype UVMeterOpts = UVMeterOpts
+  { useManager :: Bool
+  }
+
+-- | Default values for options.
+defaultOpts :: UVMeterOpts
+defaultOpts = UVMeterOpts
+  { useManager = True
+  }
+
+-- | Apply options.
+options :: [OptDescr (UVMeterOpts -> UVMeterOpts)]
+options =
+  [ Option "m" ["useManager"] (ReqArg (\m o -> o { useManager = read m }) "") ""
+  ]
+
 uvConfig :: IO MConfig
 uvConfig = mkMConfig
        "<station>" -- template
@@ -40,16 +65,19 @@
 uvURL :: String
 uvURL = "https://uvdata.arpansa.gov.au/xml/uvvalues.xml"
 
-getData :: IO String
-getData =
-  CE.catch (do request <- parseRequest uvURL
-               manager <- newManager tlsManagerSettings
-               res <- httpLbs request manager
-               return $ B.unpack $ responseBody res)
-           errHandler
-  where errHandler
-          :: CE.SomeException -> IO String
-        errHandler _ = return "<Could not retrieve data>"
+-- | Get the UV data from the given url.
+getData :: Maybe Manager -> IO String
+getData uvMan = CE.catch
+    (do man <- flip fromMaybe uvMan <$> mkManager
+        -- Create a new manager if none was present or the user does not want to
+        -- use one, otherwise use the provided manager.
+        request <- parseRequest uvURL
+        res <- httpLbs request man
+        return $ B.unpack $ responseBody res)
+    errHandler
+  where
+    errHandler :: CE.SomeException -> IO String
+    errHandler _ = return "<Could not retrieve data>"
 
 textToXMLDocument :: String -> Either ParseError [XML]
 textToXMLDocument = parse document ""
@@ -69,11 +97,23 @@
 getUVRating locID (_:xs) = getUVRating locID xs
 getUVRating _ [] = Nothing
 
+-- | Start the uvmeter monitor, create a new 'Maybe Manager', should the user have
+-- chosen to use one.
+startUVMeter
+    :: String    -- ^ Station
+    -> [String]  -- ^ User supplied arguments
+    -> Int       -- ^ Update rate
+    -> (String -> IO ())
+    -> IO ()
+startUVMeter station args rate cb = do
+    opts  <- parseOptsWith options defaultOpts (getArgvs args)
+    uvMan <- tryMakeManager opts
+    runM (station : args) uvConfig (runUVMeter uvMan) rate cb
 
-runUVMeter :: [String] -> Monitor String
-runUVMeter [] = return "N.A."
-runUVMeter (s:_) = do
-    resp <- io getData
+runUVMeter :: Maybe Manager -> [String] -> Monitor String
+runUVMeter _ [] = return "N.A."
+runUVMeter uvMan (s:_) = do
+    resp <- io $ getData uvMan
     case textToXMLDocument resp of
         Right doc -> formatUVRating (getUVRating s doc)
         Left _ -> getConfigValue naString
@@ -155,3 +195,15 @@
     char '"'
     spaces
     return (Attribute (name, value))
+
+-- | Possibly create a new 'Manager', based upon the users preference.  If one
+-- is created, this 'Manager' will be used throughout the monitor.
+tryMakeManager :: UVMeterOpts -> IO (Maybe Manager)
+tryMakeManager opts =
+    if useManager opts
+        then Just <$> mkManager
+        else pure Nothing
+
+-- | Create a new 'Manager' for managing network connections.
+mkManager :: IO Manager
+mkManager = newManager $ tlsManagerSettings {managerConnCount = 1}
diff --git a/src/Xmobar/Plugins/Monitors/Volume.hs b/src/Xmobar/Plugins/Monitors/Volume.hs
--- a/src/Xmobar/Plugins/Monitors/Volume.hs
+++ b/src/Xmobar/Plugins/Monitors/Volume.hs
@@ -23,16 +23,26 @@
 
 import Control.Applicative ( (<$>), liftA3 )
 import Control.Monad ( liftM2, liftM3, mplus )
+import Data.Maybe (fromMaybe)
 import Data.Traversable (sequenceA)
 import Xmobar.Plugins.Monitors.Common
 import Sound.ALSA.Mixer
 import qualified Sound.ALSA.Exception as AE
 import System.Console.GetOpt
 
-volumeConfig :: IO MConfig
-volumeConfig = mkMConfig "Vol: <volume>% <status>"
-                         ["volume", "volumebar", "volumevbar", "dB","status", "volumeipat"]
 
+volumeConfig :: IO MConfig
+volumeConfig =
+    mkMConfig
+        "Vol: <volume>% <status>"
+        [ "volume"
+        , "volumebar"
+        , "volumevbar"
+        , "dB"
+        , "status"
+        , "volumeipat"
+        , "volumestatus"
+        ]
 
 data VolumeOpts = VolumeOpts
     { onString :: String
@@ -101,12 +111,6 @@
     , Option "h" ["highs"] (ReqArg (\x o -> o { highString = x }) "") ""
     ]
 
-parseOpts :: [String] -> IO VolumeOpts
-parseOpts argv =
-    case getOpt Permute options argv of
-        (o, _, []) -> return $ foldr id defaultOpts o
-        (_, _, errs) -> ioError . userError $ concat errs
-
 percent :: Integer -> Integer -> Integer -> Float
 percent v' lo' hi' = (v - lo) / (hi - lo)
   where v = fromIntegral v'
@@ -141,7 +145,7 @@
     ++ maybe "" (const "</fc>") (cHelp opts)
 
 formatSwitch :: VolumeOpts -> Bool -> VolumeStatus -> Monitor String
-formatSwitch opts True vs = switchHelper opts onColor onString vs
+formatSwitch opts True  vs = switchHelper opts onColor  onString  vs
 formatSwitch opts False _  = switchHelper opts offColor offString VolOff
 
 -- | Convert the current volume status into user defined strings
@@ -173,7 +177,7 @@
 
 runVolume :: String -> String -> [String] -> Monitor String
 runVolume mixerName controlName argv = do
-    opts <- io $ parseOpts argv
+    opts <- io $ parseOptsWith options defaultOpts argv
     runVolumeWith opts mixerName controlName
 
 runVolumeWith :: VolumeOpts -> String -> String -> Monitor String
@@ -189,8 +193,14 @@
                          (liftA3 percent val lo hi) -- current volume in %
     s <- getFormatSwitch opts sw volStat
     ipat <- liftMonitor $ liftM3 (formatVolDStr $ volumeIconPattern opts) lo hi val
-    parseTemplate [p, b, v, d, s, ipat]
 
+    -- Volume and status in one.
+    let vs = if isVolOff sw
+            then offString opts -- User defined off string
+            else s ++ p         -- Status string, current volume in %
+
+    parseTemplate [p, b, v, d, s, ipat, vs]
+
   where
 
     readMixer =
@@ -244,5 +254,9 @@
     getFormatSwitch _ Nothing _ = unavailable
     getFormatSwitch _ _ Nothing = unavailable
     getFormatSwitch opts' (Just sw) (Just vs) = formatSwitch opts' sw vs
+
+    -- | Determine whether the volume is off based on the value of 'sw' from
+    -- 'runVolumeWith'.
+    isVolOff = not . fromMaybe False
 
     unavailable = getConfigValue naString
diff --git a/src/Xmobar/Plugins/Monitors/Weather.hs b/src/Xmobar/Plugins/Monitors/Weather.hs
--- a/src/Xmobar/Plugins/Monitors/Weather.hs
+++ b/src/Xmobar/Plugins/Monitors/Weather.hs
@@ -19,14 +19,37 @@
 
 import qualified Control.Exception as CE
 
+import qualified Data.ByteString.Lazy.Char8 as B
+import Data.Char (toLower)
+import Data.Maybe (fromMaybe)
 import Network.HTTP.Conduit
 import Network.HTTP.Types.Status
 import Network.HTTP.Types.Method
-import qualified Data.ByteString.Lazy.Char8 as B
-import Data.Char (toLower)
 
 import Text.ParserCombinators.Parsec
+import System.Console.GetOpt (ArgDescr(ReqArg), OptDescr(Option))
 
+
+-- | Options the user may specify.
+data WeatherOpts = WeatherOpts
+  { weatherString :: String
+  , useManager    :: Bool
+  }
+
+-- | Default values for options.
+defaultOpts :: WeatherOpts
+defaultOpts = WeatherOpts
+  { weatherString = ""
+  , useManager    = True
+  }
+
+-- | Apply options.
+options :: [OptDescr (WeatherOpts -> WeatherOpts)]
+options =
+  [ Option "w" ["weathers"  ] (ReqArg (\s o -> o { weatherString = s   }) "") ""
+  , Option "m" ["useManager"] (ReqArg (\b o -> o { useManager = read b }) "") ""
+  ]
+
 weatherConfig :: IO MConfig
 weatherConfig = mkMConfig
        "<station>: <tempC>C, rh <rh>% (<hour>)" -- template
@@ -45,6 +68,7 @@
        , "visibility"
        , "skyCondition"
        , "skyConditionS"
+       , "weather"
        , "tempC"
        , "tempF"
        , "dewPointC"
@@ -73,6 +97,7 @@
        , windInfo     :: WindInfo
        , visibility   :: String
        , skyCondition :: String
+       , weather      :: String
        , tempC        :: Int
        , tempF        :: Int
        , dewPointC    :: Int
@@ -169,6 +194,7 @@
        w <- pWind
        v <- getAfterString "Visibility: "
        sk <- getAfterString "Sky conditions: "
+       we <- getAfterString "Weather: "
        skipTillString "Temperature: "
        (tC,tF) <- pTemp
        skipTillString "Dew Point: "
@@ -178,7 +204,7 @@
        skipTillString "Pressure (altimeter): "
        p <- pPressure
        manyTill skipRestOfLine eof
-       return [WI st ss y m d h w v sk tC tF dC dF rh p]
+       return [WI st ss y m d h w v sk we tC tF dC dF rh p]
 
 defUrl :: String
 defUrl = "https://tgftp.nws.noaa.gov/data/observations/metar/decoded/"
@@ -186,50 +212,111 @@
 stationUrl :: String -> String
 stationUrl station = defUrl ++ station ++ ".TXT"
 
-getData :: String -> IO String
-getData station = CE.catch (do
-    manager <- newManager tlsManagerSettings
-    request <- parseUrlThrow $ stationUrl station
-    res <- httpLbs request manager
-    return $  B.unpack $ responseBody res
-    ) errHandler
-    where errHandler :: CE.SomeException -> IO String
-          errHandler _ = return "<Could not retrieve data>"
+-- | Get the decoded weather data from the given station.
+getData :: Maybe Manager -> String -> IO String
+getData weMan station = CE.catch
+    (do man <- flip fromMaybe weMan <$> mkManager
+        -- Create a new manager if none was present or the user does not want to
+        -- use one.
+        request <- parseUrlThrow $ stationUrl station
+        res <- httpLbs request man
+        return $ B.unpack $ responseBody res)
+    errHandler
+  where
+    errHandler :: CE.SomeException -> IO String
+    errHandler _ = return "<Could not retrieve data>"
 
 formatSk :: Eq p => [(p, p)] -> p -> p
 formatSk ((a,b):sks) sk = if a == sk then b else formatSk sks sk
 formatSk [] sk = sk
 
-formatWeather :: [(String,String)] -> [WeatherInfo] -> Monitor String
-formatWeather sks [WI st ss y m d h (WindInfo wc wa wm wk wkh wms) v sk tC tF dC dF r p] =
+formatWeather
+    :: WeatherOpts        -- ^ Formatting options from the cfg file
+    -> [(String,String)]  -- ^ 'SkyConditionS' for 'WeatherX'
+    -> [WeatherInfo]      -- ^ The actual weather info
+    -> Monitor String
+formatWeather opts sks [WI st ss y m d h (WindInfo wc wa wm wk wkh wms) v sk we tC tF dC dF r p] =
     do cel <- showWithColors show tC
        far <- showWithColors show tF
        let sk' = formatSk sks (map toLower sk)
+           we' = showWeather (weatherString opts) we
        parseTemplate [st, ss, y, m, d, h, wc, wa, wm, wk, wkh
-                     , wms, v, sk, sk', cel, far
+                     , wms, v, sk, sk', we', cel, far
                      , show dC, show dF, show r , show p ]
-formatWeather _ _ = getConfigValue naString
+formatWeather _ _ _ = getConfigValue naString
 
-runWeather :: [String] -> Monitor String
-runWeather = runWeather' []
+-- | Show the 'weather' field with a default string in case it was empty.
+showWeather :: String -> String -> String
+showWeather "" d = d
+showWeather s  _ = s
 
-runWeather' :: [(String, String)] -> [String] -> Monitor String
-runWeather' sks args =
-    do d <- io $ getData $ head args
-       i <- io $ runP parseData d
-       formatWeather sks i
+-- | Start a weather monitor, create a new 'Maybe Manager', should the user have
+-- chosen to use one.
+startWeather'
+    :: [(String, String)]  -- ^ 'SkyConditionS' replacement strings
+    -> String              -- ^ Weather station
+    -> [String]            -- ^ User supplied arguments
+    -> Int                 -- ^ Update rate
+    -> (String -> IO ())
+    -> IO ()
+startWeather' sks station args rate cb = do
+    opts  <- parseOptsWith options defaultOpts (getArgvs args)
+    weRef <- tryMakeManager opts
+    runMD
+        (station : args)
+        weatherConfig
+        (runWeather sks weRef opts)
+        rate
+        weatherReady
+        cb
 
+-- | Same as 'startWeather'', only for 'Weather' instead of 'WeatherX', meaning
+-- no 'SkyConditionS'.
+startWeather :: String -> [String] -> Int -> (String -> IO ()) -> IO ()
+startWeather = startWeather' []
+
+-- | Run a weather monitor.
+runWeather
+    :: [(String, String)]  -- ^ 'SkyConditionS' replacement strings
+    -> Maybe Manager       -- ^ Whether to use a 'Manager'
+    -> WeatherOpts         -- ^ Weather specific options
+    -> [String]            -- ^ User supplied arguments
+    -> Monitor String
+runWeather sks weMan opts args = do
+    d <- io $ getData weMan (head args)
+    i <- io $ runP parseData d
+    formatWeather opts sks i
+
 weatherReady :: [String] -> Monitor Bool
-weatherReady str = do
+weatherReady str = io $ do
     initRequest <- parseUrlThrow $ stationUrl $ head str
-    let request = initRequest{method = methodHead}
-    io $ CE.catch ( do
-        manager <- newManager tlsManagerSettings
-        res     <- httpLbs request manager
-        return $ checkResult $responseStatus res ) errHandler
-    where errHandler :: CE.SomeException -> IO Bool
-          errHandler _ = return False
-          checkResult status
-            | statusIsServerError status = False
-            | statusIsClientError status = False
-            | otherwise = True
+    let request = initRequest { method = methodHead }
+
+    CE.catch
+        (do man <- mkManager
+            res  <- httpLbs request man
+            return $ checkResult $ responseStatus res)
+        errHandler
+  where
+    -- | If any exception occurs, indicate that the monitor is not ready.
+    errHandler :: CE.SomeException -> IO Bool
+    errHandler _ = return False
+
+    -- | Check for and indicate any errors in the http response.
+    checkResult :: Status -> Bool
+    checkResult status
+        | statusIsServerError status = False
+        | statusIsClientError status = False
+        | otherwise = True
+
+-- | Possibly create a new 'Manager', based upon the users preference.  If one
+-- is created, this 'Manager' will be used throughout the monitor.
+tryMakeManager :: WeatherOpts -> IO (Maybe Manager)
+tryMakeManager opts =
+    if useManager opts
+        then Just <$> mkManager
+        else pure Nothing
+
+-- | Create a new 'Manager' for managing network connections.
+mkManager :: IO Manager
+mkManager = newManager $ tlsManagerSettings { managerConnCount = 1 }
diff --git a/src/Xmobar/Plugins/Monitors/Wireless.hs b/src/Xmobar/Plugins/Monitors/Wireless.hs
--- a/src/Xmobar/Plugins/Monitors/Wireless.hs
+++ b/src/Xmobar/Plugins/Monitors/Wireless.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE TypeApplications, CPP #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Plugins.Monitors.Wireless
@@ -8,17 +9,69 @@
 -- Stability   :  unstable
 -- Portability :  unportable
 --
--- A monitor reporting ESSID and link quality for wireless interfaces
+-- A monitor reporting SSID and signal level for wireless interfaces
 --
 -----------------------------------------------------------------------------
 
 module Xmobar.Plugins.Monitors.Wireless (wirelessConfig, runWireless)  where
 
 import System.Console.GetOpt
+import Data.Maybe (fromMaybe)
 
 import Xmobar.Plugins.Monitors.Common
+
+#ifdef IWLIB
 import Network.IWlib
+#elif defined USE_NL80211
+import Control.Exception (bracket)
+import qualified Data.Map as M
+import GHC.Int (Int8)
+import Data.Maybe (listToMaybe)
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
+import Data.ByteString.Char8 (unpack)
+import Data.Serialize.Put (runPut, putWord32host, putByteString)
+import Data.Serialize.Get (runGet)
 
+import System.Linux.Netlink hiding (query)
+import System.Linux.Netlink.GeNetlink.NL80211
+import System.Linux.Netlink.GeNetlink.NL80211.StaInfo
+import System.Linux.Netlink.GeNetlink.NL80211.Constants
+import System.Posix.IO (closeFd)
+
+data IwData = IwData { wiEssid :: String, wiSignal :: Maybe Int, wiQuality :: Int }
+
+getWirelessInfo :: String -> IO IwData
+getWirelessInfo ifname = do
+  bracket makeNL80211Socket (closeFd . getFd) (\s -> do
+  iflist <- getInterfaceList s
+  iwdata <- runMaybeT $ do
+    ifidx <- MaybeT . return $ foldr (\(n, i) z ->
+                                       if (ifname == "" || ifname == n) then Just i else z)
+                                     Nothing
+                                     iflist
+    scanp <- liftIO (getConnectedWifi s ifidx) >>=
+             MaybeT . return . listToMaybe
+    bssid <- MaybeT . return $ M.lookup eNL80211_ATTR_BSS (packetAttributes scanp) >>=
+                               rightToMaybe . runGet getAttributes >>=
+                               M.lookup eNL80211_BSS_BSSID
+    stap <- liftIO (query s eNL80211_CMD_GET_STATION True $ M.fromList
+                          [(eNL80211_ATTR_IFINDEX, runPut $ putWord32host ifidx),
+                           (eNL80211_ATTR_MAC, runPut $ putByteString bssid)]) >>=
+            MaybeT . return . listToMaybe
+    let ssid   = fromMaybe "" $ getWifiAttributes scanp >>= M.lookup eWLAN_EID_SSID >>=
+                                return . unpack
+        signal = staInfoFromPacket stap >>= staSignalMBM >>=
+                 return . fromIntegral @Int8 . fromIntegral
+        qlty   = fromMaybe (-1) (round @Float . (/ 0.7) . (+ 110) .
+                                                clamp (-110) (-40) . fromIntegral <$> signal)
+    MaybeT . return $ Just $ IwData ssid signal qlty
+  return $ fromMaybe (IwData "" Nothing (-1)) iwdata)
+  where
+    rightToMaybe = either (const Nothing) Just
+    clamp lb up v = if v < lb then lb else if v > up then up else v
+#endif
+
 newtype WirelessOpts = WirelessOpts
   { qualityIconPattern :: Maybe IconPattern
   }
@@ -34,37 +87,43 @@
      opts { qualityIconPattern = Just $ parseIconPattern d }) "") ""
   ]
 
-parseOpts :: [String] -> IO WirelessOpts
-parseOpts argv =
-  case getOpt Permute options argv of
-       (o, _, []) -> return $ foldr id defaultOpts o
-       (_, _, errs) -> ioError . userError $ concat errs
-
 wirelessConfig :: IO MConfig
 wirelessConfig =
-  mkMConfig "<essid> <quality>"
-            ["essid", "quality", "qualitybar", "qualityvbar", "qualityipat"]
+  mkMConfig "<ssid> <quality>"
+            ["ssid", "essid", "signal", "quality", "qualitybar", "qualityvbar", "qualityipat"]
 
 runWireless :: String -> [String] -> Monitor String
 runWireless iface args = do
-  opts <- io $ parseOpts args
+  opts <- io $ parseOptsWith options defaultOpts args
+#ifdef IWLIB
   iface' <- if "" == iface then io findInterface else return iface
+#else
+  let iface' = iface
+#endif
   wi <- io $ getWirelessInfo iface'
   na <- getConfigValue naString
   let essid = wiEssid wi
       qlty = fromIntegral $ wiQuality wi
       e = if essid == "" then na else essid
   ep <- showWithPadding e
+#ifdef USE_NL80211
+  let s = wiSignal wi
+#else
+  let s = if qlty >= 0 then Just (qlty * 0.7 - 110) else Nothing
+#endif
+  sp <- showWithPadding $ maybe "" show s
   q <- if qlty >= 0
        then showPercentWithColors (qlty / 100)
        else showWithPadding ""
   qb <- showPercentBar qlty (qlty / 100)
   qvb <- showVerticalBar qlty (qlty / 100)
   qipat <- showIconPattern (qualityIconPattern opts) (qlty / 100)
-  parseTemplate [ep, q, qb, qvb, qipat]
+  parseTemplate [ep, ep, sp, q, qb, qvb, qipat]
 
+#ifdef IWLIB
 findInterface :: IO String
 findInterface = do
   c <- readFile "/proc/net/wireless"
   let nds = lines c
   return $ if length nds > 2 then takeWhile (/= 'c') (nds!!2) else []
+#endif
diff --git a/src/Xmobar/Run/Command.hs b/src/Xmobar/Run/Command.hs
--- a/src/Xmobar/Run/Command.hs
+++ b/src/Xmobar/Run/Command.hs
@@ -41,7 +41,7 @@
     start (Com p as al r) cb =
       start (ComX p as ("Could not execute command " ++ p) al r) cb
     start (ComX prog args msg _ r) cb = if r > 0 then go else exec
-        where go = exec >> tenthSeconds r >> go
+        where go = doEveryTenthSeconds r exec
               exec = do
                 (i,o,e,p) <- runInteractiveProcess prog args Nothing Nothing
                 exit <- waitForProcess p
diff --git a/src/Xmobar/Run/Exec.hs b/src/Xmobar/Run/Exec.hs
--- a/src/Xmobar/Run/Exec.hs
+++ b/src/Xmobar/Run/Exec.hs
@@ -17,23 +17,14 @@
 --
 -----------------------------------------------------------------------------
 
-module Xmobar.Run.Exec (Exec (..), tenthSeconds) where
+module Xmobar.Run.Exec (Exec (..), tenthSeconds, doEveryTenthSeconds) where
 
 import Prelude
 import Data.Char
-import Control.Concurrent
 
+import Xmobar.App.Timer (doEveryTenthSeconds, tenthSeconds)
 import Xmobar.System.Signal
 
--- | Work around to the Int max bound: since threadDelay takes an Int, it
--- is not possible to set a thread delay grater than about 45 minutes.
--- With a little recursion we solve the problem.
-tenthSeconds :: Int -> IO ()
-tenthSeconds s | s >= x = do threadDelay (x * 100000)
-                             tenthSeconds (s - x)
-               | otherwise = threadDelay (s * 100000)
-               where x = (maxBound :: Int) `div` 100000
-
 class Show e => Exec e where
     alias   :: e -> String
     alias   e    = takeWhile (not . isSpace) $ show e
@@ -43,6 +34,6 @@
     run     _    = return ""
     start   :: e -> (String -> IO ()) -> IO ()
     start   e cb = go
-        where go = run e >>= cb >> tenthSeconds (rate e) >> go
+        where go = doEveryTenthSeconds (rate e) $ run e >>= cb
     trigger :: e -> (Maybe SignalType -> IO ()) -> IO ()
     trigger _ sh  = sh Nothing
diff --git a/src/Xmobar/X11/Draw.hs b/src/Xmobar/X11/Draw.hs
--- a/src/Xmobar/X11/Draw.hs
+++ b/src/Xmobar/X11/Draw.hs
@@ -62,6 +62,8 @@
                          (defaultDepthOfScreen (defaultScreenOfDisplay d))
 #if XFT
   when (alpha c /= 255) (liftIO $ drawBackground d p (bgColor c) (alpha c) wr)
+#else
+  _ <- return wr
 #endif
   withColors d [bgColor c, borderColor c] $ \[bgcolor, bdcolor] -> do
     gc <- liftIO $ createGC  d w
diff --git a/xmobar.cabal b/xmobar.cabal
--- a/xmobar.cabal
+++ b/xmobar.cabal
@@ -1,5 +1,5 @@
 name:               xmobar
-version:            0.32
+version:            0.33
 homepage:           http://xmobar.org
 synopsis:           A Minimalistic Text Based Status Bar
 description: 	    Xmobar is a minimalistic text based status bar.
@@ -40,9 +40,13 @@
   default: False
 
 flag with_iwlib
-  description: Wireless info support. Required for the Wireless plugin, needs iwlib installed.
+  description: Wireless info support via Wext ioctls (deprecated). Required for the Wireless plugin, needs iwlib installed.
   default: False
 
+flag with_nl80211
+  description: Wireless info support via nl80211. Required for the Wireless plugin on systems running Linux, the kernel.
+  default: False
+
 flag with_mpd
   description: MPD support. Needs libmpd installed.
   default: False
@@ -72,7 +76,7 @@
   default: False
 
 flag with_threaded
-  description: Use threaded runtime.
+  description: Use threaded runtime. Required for timer coalescing (less power usage).
   default: False
 
 flag with_rtsopts
@@ -105,6 +109,7 @@
                    Xmobar.App.Main,
                    Xmobar.App.Opts,
                    Xmobar.App.Compile,
+                   Xmobar.App.Timer,
                    Xmobar.System.Utils,
                    Xmobar.System.StatFS,
                    Xmobar.System.Environment,
@@ -208,12 +213,19 @@
        exposed-modules: Xmobar.Plugins.Mail, Xmobar.Plugins.MBox
        cpp-options: -DINOTIFY
 
-    if flag(with_iwlib) || flag(all_extensions)
+    if flag(with_iwlib) || flag(with_nl80211) || flag(all_extensions)
+       exposed-modules: Xmobar.Plugins.Monitors.Wireless
+
+    if flag(with_iwlib)
        extra-libraries: iw
        build-depends: iwlib >= 0.1.0 && < 0.2
-       exposed-modules: Xmobar.Plugins.Monitors.Wireless
        cpp-options: -DIWLIB
 
+    if !flag(with_iwlib) && (flag(with_nl80211) || flag(all_extensions))
+       build-depends: netlink >= 1.1.1.0,
+                      cereal >= 0.5.8.1
+       cpp-options: -DUSE_NL80211
+
     if flag(with_mpd) || flag(all_extensions)
        build-depends: libmpd >= 0.9.0.10
        exposed-modules: Xmobar.Plugins.Monitors.MPD
@@ -257,6 +269,11 @@
        build-depends: http-conduit, http-types
        cpp-options: -DUVMETER
 
+    if os(freebsd)
+       -- enables freebsd specific code
+       build-depends: bsd-sysctl
+       cpp-options: -DFREEBSD
+
 executable xmobar
     hs-source-dirs:     app
     main-is:            Main.hs
@@ -315,6 +332,7 @@
                  Xmobar.Plugins.Monitors.Common.Output
                  Xmobar.Plugins.Monitors.Common.Files
                  Xmobar.Run.Exec
+                 Xmobar.App.Timer
                  Xmobar.System.Signal
 
   if flag(with_alsa) || flag(all_extensions)
