2022-12-26 14:45:49 -05:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
2020-04-01 22:06:00 -04:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Bluetooth plugin
|
2020-04-01 22:06:00 -04:00
|
|
|
--
|
|
|
|
-- Use the bluez interface on DBus to check status
|
2021-11-26 23:35:03 -05:00
|
|
|
--
|
|
|
|
-- org.bluez dynamically updates its DBus interfaces using the standard Object
|
|
|
|
-- Manager. The adapter is located at path "/org/bluez/hci<X>" where X is
|
|
|
|
-- usually 0, and each device is "/org/bluez/hci<X>/<MAC_ADDRESS>".
|
|
|
|
--
|
|
|
|
-- This plugin will reflect if the adapter is powered and if any device is
|
|
|
|
-- connected to it. The rough outline for this procedure:
|
|
|
|
-- 1) get the adapter from the object manager
|
|
|
|
-- 2) get all devices associated with the adapter using the object interface
|
|
|
|
-- 3) determine if the adapter is powered
|
|
|
|
-- 4) determine if any devices are connected
|
|
|
|
-- 5) format the icon; powered vs not powered controls the color and connected
|
|
|
|
-- vs not connected controls the icon (connected bluetooth symbol has two
|
|
|
|
-- dots flanking it)
|
|
|
|
--
|
|
|
|
-- Step 3 can be accomplished using the "org.bluez.Adapter1" interface and
|
|
|
|
-- querying the "Powered" property. Step 4 can be done using the
|
|
|
|
-- "org.bluez.Device1" interface and the "Connected" property for each device
|
|
|
|
-- path. Since these are properties, we can asynchronously read changes to them
|
|
|
|
-- via the "PropertiesChanged" signal.
|
|
|
|
--
|
|
|
|
-- If any devices are added/removed, steps 2-4 will need to be redone and any
|
|
|
|
-- listeners will need to be updated. (TODO not sure which signals to use in
|
|
|
|
-- determining if a device is added)
|
|
|
|
--
|
|
|
|
-- TODO also not sure if I need to care about multiple adapters and/or the
|
|
|
|
-- adapter changing.
|
2020-04-01 22:06:00 -04:00
|
|
|
|
2021-06-21 23:41:57 -04:00
|
|
|
module Xmobar.Plugins.Bluetooth
|
2022-12-30 14:58:23 -05:00
|
|
|
( Bluetooth (..)
|
2021-06-21 23:41:57 -04:00
|
|
|
, btAlias
|
2021-11-23 18:28:38 -05:00
|
|
|
, btDep
|
2022-12-30 14:58:23 -05:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
import DBus
|
|
|
|
import DBus.Client
|
|
|
|
import Data.Internal.DBus
|
2023-01-01 18:33:02 -05:00
|
|
|
import Data.Internal.XIO
|
2022-12-30 16:37:52 -05:00
|
|
|
import RIO
|
2022-12-31 19:47:02 -05:00
|
|
|
import RIO.FilePath
|
|
|
|
import RIO.List
|
|
|
|
import qualified RIO.Map as M
|
2022-12-30 14:58:23 -05:00
|
|
|
import qualified RIO.Text as T
|
|
|
|
import XMonad.Internal.DBus.Common
|
|
|
|
import Xmobar
|
|
|
|
import Xmobar.Plugins.Common
|
2020-03-21 01:18:38 -04:00
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
btAlias :: T.Text
|
2021-11-26 23:35:03 -05:00
|
|
|
btAlias = "bluetooth"
|
|
|
|
|
2022-07-09 17:08:10 -04:00
|
|
|
btDep :: DBusDependency_ SysClient
|
2022-12-30 14:58:23 -05:00
|
|
|
btDep =
|
|
|
|
Endpoint [Package Official "bluez"] btBus btOMPath omInterface $
|
|
|
|
Method_ getManagedObjects
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
data Bluetooth = Bluetooth Icons Colors deriving (Read, Show)
|
2020-03-21 01:18:38 -04:00
|
|
|
|
2021-11-26 23:35:03 -05:00
|
|
|
instance Exec Bluetooth where
|
2022-12-26 14:45:49 -05:00
|
|
|
alias (Bluetooth _ _) = T.unpack btAlias
|
2021-11-26 23:35:03 -05:00
|
|
|
start (Bluetooth icons colors) cb =
|
2023-01-01 22:29:29 -05:00
|
|
|
withDBusClientConnection cb "bluetooth" $ startAdapter icons colors cb
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2022-12-30 16:58:30 -05:00
|
|
|
startAdapter
|
2023-01-01 23:03:31 -05:00
|
|
|
:: Icons
|
2022-12-30 16:58:30 -05:00
|
|
|
-> Colors
|
|
|
|
-> Callback
|
|
|
|
-> SysClient
|
2023-01-01 23:03:31 -05:00
|
|
|
-> RIO SimpleApp ()
|
2021-11-26 23:35:03 -05:00
|
|
|
startAdapter is cs cb cl = do
|
|
|
|
state <- newMVar emptyState
|
2023-01-01 23:03:31 -05:00
|
|
|
let dpy = displayIcon cb (iconFormatter is cs)
|
2023-01-01 23:20:15 -05:00
|
|
|
mapRIO (BTEnv state dpy) $ do
|
2023-01-01 23:03:31 -05:00
|
|
|
ot <- getBtObjectTree cl
|
2023-01-01 23:20:15 -05:00
|
|
|
case findAdapter ot of
|
|
|
|
Nothing -> logError "could not find bluetooth adapter"
|
|
|
|
Just adapter -> do
|
|
|
|
-- set up adapter
|
|
|
|
initAdapter adapter cl
|
|
|
|
void $ addAdaptorListener adapter cl
|
|
|
|
-- set up devices on the adapter (and listeners for adding/removing devices)
|
|
|
|
let devices = findDevices adapter ot
|
|
|
|
addDeviceAddedListener adapter cl
|
|
|
|
addDeviceRemovedListener adapter cl
|
|
|
|
forM_ devices $ \d -> addAndInitDevice d cl
|
|
|
|
-- after setting things up, show the icon based on the initialized state
|
|
|
|
dpy
|
2021-06-21 23:41:57 -04:00
|
|
|
|
2021-11-26 23:35:03 -05:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Icon Display
|
2021-11-26 23:35:03 -05:00
|
|
|
--
|
|
|
|
-- Color corresponds to the adaptor powered state, and the icon corresponds to
|
|
|
|
-- if it is paired or not. If the adaptor state is undefined, display "N/A"
|
2021-11-08 00:27:39 -05:00
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
type IconFormatter = (Maybe Bool -> Bool -> T.Text)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
type Icons = (T.Text, T.Text)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
displayIcon :: Callback -> IconFormatter -> BTIO ()
|
2021-11-26 23:35:03 -05:00
|
|
|
displayIcon callback formatter =
|
2023-01-01 23:03:31 -05:00
|
|
|
liftIO . callback . T.unpack . uncurry formatter =<< readState
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
-- TODO maybe I want this to fail when any of the device statuses are Nothing
|
|
|
|
iconFormatter :: Icons -> Colors -> IconFormatter
|
2021-11-27 17:33:02 -05:00
|
|
|
iconFormatter (iconConn, iconDisc) cs powered connected =
|
|
|
|
maybe na (\p -> colorText cs p icon) powered
|
2021-11-26 23:35:03 -05:00
|
|
|
where
|
|
|
|
icon = if connected then iconConn else iconDisc
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Connection State
|
2021-11-26 23:35:03 -05:00
|
|
|
--
|
|
|
|
-- The signal handlers all run on separate threads, yet the icon depends on
|
|
|
|
-- the state reflected by all these signals. The best (only?) way to do this is
|
|
|
|
-- is to track the shared state of the bluetooth adaptor and its devices using
|
|
|
|
-- an MVar.
|
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
data BTEnv = BTEnv
|
2023-01-01 23:20:15 -05:00
|
|
|
{ btState :: !(MVar BtState)
|
|
|
|
, btDisplay :: !(BTIO ())
|
|
|
|
, btEnv :: !SimpleApp
|
2023-01-01 23:03:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
instance HasLogFunc BTEnv where
|
|
|
|
logFuncL = lens btEnv (\x y -> x {btEnv = y}) . logFuncL
|
|
|
|
|
|
|
|
type BTIO = RIO BTEnv
|
|
|
|
|
2021-11-26 23:35:03 -05:00
|
|
|
data BTDevice = BTDevice
|
2022-12-30 14:58:23 -05:00
|
|
|
{ btDevConnected :: Maybe Bool
|
2021-11-26 23:35:03 -05:00
|
|
|
, btDevSigHandler :: SignalHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
type ConnectedDevices = M.Map ObjectPath BTDevice
|
|
|
|
|
|
|
|
data BtState = BtState
|
|
|
|
{ btDevices :: ConnectedDevices
|
|
|
|
, btPowered :: Maybe Bool
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyState :: BtState
|
2022-12-30 14:58:23 -05:00
|
|
|
emptyState =
|
|
|
|
BtState
|
|
|
|
{ btDevices = M.empty
|
|
|
|
, btPowered = Nothing
|
|
|
|
}
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
readState :: BTIO (Maybe Bool, Bool)
|
|
|
|
readState = do
|
|
|
|
p <- readPowered
|
|
|
|
c <- readDevices
|
2021-11-26 23:35:03 -05:00
|
|
|
return (p, anyDevicesConnected c)
|
|
|
|
|
2023-01-01 23:09:23 -05:00
|
|
|
modifyState :: (BtState -> BTIO (BtState, a)) -> BTIO a
|
|
|
|
modifyState f = do
|
|
|
|
m <- asks btState
|
|
|
|
modifyMVar m f
|
|
|
|
|
2021-11-26 23:35:03 -05:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Object manager
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
findAdapter :: ObjectTree -> Maybe ObjectPath
|
|
|
|
findAdapter = find (("/org/bluez/hci" `isPrefixOf`) . formatObjectPath) . M.keys
|
|
|
|
|
|
|
|
findDevices :: ObjectPath -> ObjectTree -> [ObjectPath]
|
|
|
|
findDevices adapter = filter (adaptorHasDevice adapter) . M.keys
|
|
|
|
|
|
|
|
adaptorHasDevice :: ObjectPath -> ObjectPath -> Bool
|
2022-12-31 19:47:02 -05:00
|
|
|
adaptorHasDevice adaptor device = case splitPathNoRoot device of
|
|
|
|
[org, bluez, hciX, _] -> splitPathNoRoot adaptor == [org, bluez, hciX]
|
2022-12-30 14:58:23 -05:00
|
|
|
_ -> False
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2022-12-31 19:47:02 -05:00
|
|
|
splitPathNoRoot :: ObjectPath -> [FilePath]
|
|
|
|
splitPathNoRoot = dropWhile (== "/") . splitDirectories . formatObjectPath
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 19:58:23 -05:00
|
|
|
getBtObjectTree
|
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m)
|
|
|
|
=> SysClient
|
|
|
|
-> m ObjectTree
|
2022-07-09 17:44:14 -04:00
|
|
|
getBtObjectTree sys = callGetManagedObjects sys btBus btOMPath
|
2021-11-08 00:27:39 -05:00
|
|
|
|
2021-11-27 13:24:13 -05:00
|
|
|
btOMPath :: ObjectPath
|
|
|
|
btOMPath = objectPath_ "/"
|
|
|
|
|
2023-01-01 19:41:46 -05:00
|
|
|
addBtOMListener
|
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m)
|
|
|
|
=> SignalCallback m
|
|
|
|
-> SysClient
|
|
|
|
-> m ()
|
2022-12-30 10:56:09 -05:00
|
|
|
addBtOMListener sc = void . addInterfaceAddedListener btBus btOMPath sc
|
2021-06-21 23:41:57 -04:00
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
addDeviceAddedListener :: ObjectPath -> SysClient -> BTIO ()
|
|
|
|
addDeviceAddedListener adapter client = addBtOMListener addDevice client
|
2021-11-26 23:35:03 -05:00
|
|
|
where
|
2023-01-01 23:20:15 -05:00
|
|
|
addDevice = pathCallback adapter $ \d ->
|
|
|
|
addAndInitDevice d client
|
2020-03-21 14:30:27 -04:00
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
addDeviceRemovedListener :: ObjectPath -> SysClient -> BTIO ()
|
|
|
|
addDeviceRemovedListener adapter sys =
|
2022-07-09 17:08:10 -04:00
|
|
|
addBtOMListener remDevice sys
|
2021-11-26 23:35:03 -05:00
|
|
|
where
|
2023-01-01 23:20:15 -05:00
|
|
|
remDevice = pathCallback adapter $ \d -> do
|
2023-01-01 23:03:31 -05:00
|
|
|
old <- removeDevice d
|
2022-12-30 16:58:30 -05:00
|
|
|
forM_ old $ liftIO . removeMatch (toClient sys) . btDevSigHandler
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
pathCallback :: ObjectPath -> (ObjectPath -> BTIO ()) -> SignalCallback BTIO
|
|
|
|
pathCallback adapter f [device, _] = forM_ (fromVariant device) $ \d -> do
|
|
|
|
dpy <- asks btDisplay
|
2022-12-30 16:37:52 -05:00
|
|
|
when (adaptorHasDevice adapter d) $ f d >> dpy
|
2023-01-01 23:20:15 -05:00
|
|
|
pathCallback _ _ _ = return ()
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Adapter
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
initAdapter :: ObjectPath -> SysClient -> BTIO ()
|
|
|
|
initAdapter adapter client = do
|
2021-11-26 23:35:03 -05:00
|
|
|
reply <- callGetPowered adapter client
|
2023-01-01 22:03:17 -05:00
|
|
|
logInfo $ "initializing adapter at path " <> adapter_
|
2023-01-01 21:30:07 -05:00
|
|
|
-- TODO this could fail if the variant is something weird; the only
|
|
|
|
-- indication I will get is "NA"
|
2023-01-01 23:03:31 -05:00
|
|
|
putPowered $ fromSingletonVariant reply
|
2023-01-01 22:03:17 -05:00
|
|
|
where
|
|
|
|
adapter_ = displayWrapQuote $ displayObjectPath adapter
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2022-12-30 16:58:30 -05:00
|
|
|
matchBTProperty
|
2023-01-01 19:41:46 -05:00
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m)
|
2022-12-30 16:58:30 -05:00
|
|
|
=> SysClient
|
|
|
|
-> ObjectPath
|
|
|
|
-> m (Maybe MatchRule)
|
2022-07-09 17:44:14 -04:00
|
|
|
matchBTProperty sys p = matchPropertyFull sys btBus (Just p)
|
2021-11-27 13:24:13 -05:00
|
|
|
|
2023-01-01 21:30:07 -05:00
|
|
|
withBTPropertyRule
|
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m, IsVariant a)
|
|
|
|
=> SysClient
|
|
|
|
-> ObjectPath
|
|
|
|
-> (Maybe a -> m ())
|
|
|
|
-> InterfaceName
|
|
|
|
-> T.Text
|
|
|
|
-> m (Maybe SignalHandler)
|
|
|
|
withBTPropertyRule cl path update iface prop = do
|
|
|
|
res <- matchBTProperty cl path
|
|
|
|
case res of
|
|
|
|
Just rule -> Just <$> addMatchCallback rule (signalToUpdate . matchConnected) cl
|
|
|
|
Nothing -> do
|
|
|
|
logError $
|
|
|
|
"could not add listener for prop "
|
|
|
|
<> prop_
|
|
|
|
<> " on path "
|
|
|
|
<> path_
|
|
|
|
return Nothing
|
|
|
|
where
|
|
|
|
path_ = displayObjectPath path
|
|
|
|
prop_ = Utf8Builder $ encodeUtf8Builder prop
|
|
|
|
signalToUpdate = withSignalMatch update
|
|
|
|
matchConnected = matchPropertyChanged iface prop
|
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
addAdaptorListener :: ObjectPath -> SysClient -> BTIO (Maybe SignalHandler)
|
|
|
|
addAdaptorListener adaptor sys = do
|
|
|
|
dpy <- asks btDisplay
|
|
|
|
let procMatch b = putPowered b >> dpy
|
2023-01-01 21:30:07 -05:00
|
|
|
withBTPropertyRule sys adaptor procMatch adapterInterface adaptorPowered
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 19:52:01 -05:00
|
|
|
callGetPowered
|
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m)
|
|
|
|
=> ObjectPath
|
|
|
|
-> SysClient
|
|
|
|
-> m [Variant]
|
2022-12-30 14:58:23 -05:00
|
|
|
callGetPowered adapter =
|
|
|
|
callPropertyGet btBus adapter adapterInterface $
|
|
|
|
memberName_ $
|
|
|
|
T.unpack adaptorPowered
|
2021-11-23 18:28:38 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
putPowered :: Maybe Bool -> BTIO ()
|
2023-01-01 23:09:23 -05:00
|
|
|
putPowered ds = modifyState $ \s -> return (s {btPowered = ds}, ())
|
2021-11-24 00:21:18 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
readPowered :: BTIO (Maybe Bool)
|
|
|
|
readPowered = fmap btPowered $ readMVar =<< asks btState
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
adapterInterface :: InterfaceName
|
|
|
|
adapterInterface = interfaceName_ "org.bluez.Adapter1"
|
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
adaptorPowered :: T.Text
|
2021-11-26 23:35:03 -05:00
|
|
|
adaptorPowered = "Powered"
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Devices
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
addAndInitDevice :: ObjectPath -> SysClient -> BTIO ()
|
|
|
|
addAndInitDevice device client = do
|
|
|
|
res <- addDeviceListener device client
|
2023-01-01 21:36:16 -05:00
|
|
|
case res of
|
2023-01-01 22:03:17 -05:00
|
|
|
Just handler -> do
|
|
|
|
logInfo $ "initializing device at path " <> device_
|
2023-01-01 23:03:31 -05:00
|
|
|
initDevice handler device client
|
2023-01-01 21:36:16 -05:00
|
|
|
Nothing -> logError $ "could not initialize device at path " <> device_
|
|
|
|
where
|
|
|
|
device_ = displayWrapQuote $ displayObjectPath device
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
initDevice :: SignalHandler -> ObjectPath -> SysClient -> BTIO ()
|
|
|
|
initDevice sh device sys = do
|
2022-07-09 17:44:14 -04:00
|
|
|
reply <- callGetConnected device sys
|
2022-12-30 14:58:23 -05:00
|
|
|
void $
|
2023-01-01 23:03:31 -05:00
|
|
|
insertDevice device $
|
2022-12-30 14:58:23 -05:00
|
|
|
BTDevice
|
|
|
|
{ btDevConnected = fromVariant =<< listToMaybe reply
|
|
|
|
, btDevSigHandler = sh
|
|
|
|
}
|
|
|
|
|
2023-01-01 23:20:15 -05:00
|
|
|
addDeviceListener :: ObjectPath -> SysClient -> BTIO (Maybe SignalHandler)
|
|
|
|
addDeviceListener device sys = do
|
|
|
|
dpy <- asks btDisplay
|
|
|
|
let procMatch c = updateDevice device c >> dpy
|
2023-01-01 21:30:07 -05:00
|
|
|
withBTPropertyRule sys device procMatch devInterface devConnected
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 19:52:01 -05:00
|
|
|
callGetConnected
|
|
|
|
:: (MonadReader env m, HasLogFunc env, MonadUnliftIO m)
|
|
|
|
=> ObjectPath
|
|
|
|
-> SysClient
|
|
|
|
-> m [Variant]
|
2022-12-30 14:58:23 -05:00
|
|
|
callGetConnected p =
|
|
|
|
callPropertyGet btBus p devInterface $
|
|
|
|
memberName_ (T.unpack devConnected)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
insertDevice :: ObjectPath -> BTDevice -> BTIO Bool
|
2023-01-01 23:09:23 -05:00
|
|
|
insertDevice device dev = modifyState $ \s -> do
|
|
|
|
let new = M.insert device dev $ btDevices s
|
|
|
|
return (s {btDevices = new}, anyDevicesConnected new)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
updateDevice :: ObjectPath -> Maybe Bool -> BTIO Bool
|
2023-01-01 23:09:23 -05:00
|
|
|
updateDevice device status = modifyState $ \s -> do
|
|
|
|
let new = M.update (\d -> Just d {btDevConnected = status}) device $ btDevices s
|
|
|
|
return (s {btDevices = new}, anyDevicesConnected new)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
anyDevicesConnected :: ConnectedDevices -> Bool
|
|
|
|
anyDevicesConnected = or . mapMaybe btDevConnected . M.elems
|
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
removeDevice :: ObjectPath -> BTIO (Maybe BTDevice)
|
2023-01-01 23:09:23 -05:00
|
|
|
removeDevice device = modifyState $ \s -> do
|
|
|
|
let devs = btDevices s
|
|
|
|
return (s {btDevices = M.delete device devs}, M.lookup device devs)
|
2021-11-26 23:35:03 -05:00
|
|
|
|
2023-01-01 23:03:31 -05:00
|
|
|
readDevices :: BTIO ConnectedDevices
|
|
|
|
readDevices = fmap btDevices $ readMVar =<< asks btState
|
2021-11-26 23:35:03 -05:00
|
|
|
|
|
|
|
devInterface :: InterfaceName
|
|
|
|
devInterface = interfaceName_ "org.bluez.Device1"
|
2021-11-25 00:12:00 -05:00
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
devConnected :: T.Text
|
2021-11-26 23:35:03 -05:00
|
|
|
devConnected = "Connected"
|