{-# Language OverloadedStrings, DataKinds, ScopedTypeVariables, GADTs, DeriveDataTypeable #-} {-| Module: MQTT Copyright: Lukas Braun 2014-2016 License: GPL-3 Maintainer: koomi+mqtt@hackerspace-bamberg.de An MQTT client library. -} module Network.MQTT ( -- * Setup run , Terminated(..) , disconnect , Config , defaultConfig , Commands , mkCommands -- ** Config accessors , cHost , cPort , cClean , cWill , cUsername , cPassword , cKeepAlive , cClientID , cLogDebug , cPublished , cCommands , cInputBufferSize -- * Subscribing and publishing , subscribe , unsubscribe , publish -- * Reexports , module Network.MQTT.Types ) where import Control.Applicative ((<$>)) import Control.Concurrent import Control.Concurrent.STM import Control.Exception (finally) import Control.Monad (void) import Data.ByteString (ByteString) import Data.Maybe (fromJust) import Data.Unique import Network import System.IO (hSetBinaryMode) import Network.MQTT.Internal import Network.MQTT.Types -- | Defaults for 'Config', connects to a server running on -- localhost. defaultConfig :: Commands -> TChan (Message 'PUBLISH) -> Config defaultConfig commands published = Config { cHost = "localhost" , cPort = 1883 , cClean = True , cWill = Nothing , cUsername = Nothing , cPassword = Nothing , cKeepAlive = Nothing , cClientID = "mqtt-haskell" , cResendTimeout = 20 , cLogDebug = const $ return () , cCommands = commands , cPublished = published , cInputBufferSize = 0x1000 } -- | Connect to the configured broker, write received 'Publish' messages to the -- 'cPublished' channel and handle commands from the 'cCommands' channel. -- -- Exceptions are propagated. run :: Config -> IO Terminated run conf = do h <- connectTo (cHost conf) (PortNumber $ cPort conf) hSetBinaryMode h True terminatedVar <- newEmptyTMVarIO sendSignal <- newEmptyMVar mainLoop conf h (readTMVar terminatedVar) sendSignal `finally` atomically (putTMVar terminatedVar ()) -- | Close the connection after sending a 'Disconnect' message. -- -- See also: 'Will' disconnect :: Config -> IO () disconnect mqtt = writeCmd mqtt CmdDisconnect -- | Subscribe to the 'Topic's with the corresponding 'QoS'. -- Returns the 'QoS' that were granted (lower or equal to the ones requested) -- in the same order. -- -- The 'Topic's may contain -- . subscribe :: Config -> [(Topic, QoS)] -> IO [QoS] subscribe mqtt topics = do msgID <- fromIntegral . hashUnique <$> newUnique msg <- sendAwait mqtt (Message (Header False Confirm False) (Subscribe msgID topics)) SSUBACK return $ granted $ body $ msg -- | Unsubscribe from the given 'Topic's. unsubscribe :: Config -> [Topic] -> IO () unsubscribe mqtt topics = do msgID <- fromIntegral . hashUnique <$> newUnique void $ sendAwait mqtt (Message (Header False Confirm False) (Unsubscribe msgID topics)) SUNSUBACK -- | Publish a message to the given 'Topic' at the requested 'QoS' level. -- The payload can be any sequence of bytes, including none at all. -- 'True' means the server should retain the message for future subscribers to -- the topic. -- -- The 'Topic' must not contain -- . publish :: Config -> QoS -> Bool -> Topic -> ByteString -> IO () publish mqtt qos retain topic body = do msgID <- if qos > NoConfirm then Just . fromIntegral . hashUnique <$> newUnique else return Nothing let pub = Message (Header False qos retain) (Publish topic msgID body) case qos of NoConfirm -> send mqtt pub Confirm -> void $ sendAwait mqtt pub SPUBACK Handshake -> do void $ sendAwait mqtt pub SPUBREC void $ sendAwait mqtt (Message (Header False Confirm False) (PubRel (fromJust msgID))) SPUBCOMP