roku-api (empty) → 0.1.0.0
raw patch · 7 files changed
+290/−0 lines, 7 filesdep +basedep +bytestringdep +http-clientsetup-changed
Dependencies added: base, bytestring, http-client, network, text, xml, xml-extractors
Files
- LICENSE +30/−0
- Network/Roku.hs +10/−0
- Network/Roku/App.hs +62/−0
- Network/Roku/Keys.hs +58/−0
- Network/Roku/Request.hs +99/−0
- Setup.hs +2/−0
- roku-api.cabal +29/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, Brian McKenna++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Brian McKenna nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Network/Roku.hs view
@@ -0,0 +1,10 @@+module Network.Roku+ ( module Network.Roku.App+ , module Network.Roku.Keys+ , module Network.Roku.Request+ )+where++import Network.Roku.App+import Network.Roku.Keys+import Network.Roku.Request
+ Network/Roku/App.hs view
@@ -0,0 +1,62 @@+module Network.Roku.App where++import Control.Applicative+import Data.Text+import Text.XML.Light.Extractors++newtype AppId+ = AppId Text+ deriving Show++data AppType+ = Appl+ | Ssvr+ deriving Show++newtype AppVersion+ = AppVersion Text+ deriving Show++newtype AppName+ = AppName Text+ deriving Show++data App+ = App AppId AppType AppVersion AppName+ deriving Show++data AppOrHomescreen+ = NormalApp App+ | Homescreen+ deriving Show++data Screensaver+ = Screensaver App+ deriving Show++data ActiveAppResponse+ = ActiveAppResponse AppOrHomescreen (Maybe Screensaver)+ deriving Show++appBodyParser :: ElementExtractor App+appBodyParser = do+ let p = Right . pack+ i <- attribAs "id" p+ let t' "appl" = Right Appl+ t' "ssvr" = Right Ssvr+ t' t = Left t+ t <- attribAs "type" t'+ v <- attribAs "version" p+ n <- contents $ textAs p+ return $ App (AppId i) t (AppVersion v) (AppName n)++appsParser :: ContentsExtractor [App]+appsParser =+ element "apps" . children . many $ element "app" appBodyParser++activeAppParser :: ContentsExtractor ActiveAppResponse+activeAppParser =+ element "active-app" . children $ do+ app <- optional $ element "app" appBodyParser+ ssvr <- optional $ element "screensaver" appBodyParser+ return $ ActiveAppResponse (maybe Homescreen NormalApp app) $ Screensaver <$> ssvr
+ Network/Roku/Keys.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE OverloadedStrings #-}+module Network.Roku.Keys where++import Prelude hiding (Either(..))+import Data.ByteString.Char8++data Key+ = Home+ | Rev+ | Fwd+ | Play+ | Select+ | Left+ | Right+ | Down+ | Up+ | Back+ | InstantReplay+ | Info+ | Backspace+ | Search+ | Enter+ | Lit Char+ deriving Show++keyValue :: Key -> ByteString+keyValue Home =+ "Home"+keyValue Rev =+ "Rev"+keyValue Fwd =+ "Fwd"+keyValue Play =+ "Play"+keyValue Select =+ "Select"+keyValue Left =+ "Left"+keyValue Right =+ "Right"+keyValue Down =+ "Down"+keyValue Up =+ "Up"+keyValue Back =+ "Back"+keyValue InstantReplay =+ "InstantReplay"+keyValue Info =+ "Info"+keyValue Backspace =+ "Backspace"+keyValue Search =+ "Search"+keyValue Enter =+ "Enter"+keyValue (Lit c) =+ snoc "Lit_" c
+ Network/Roku/Request.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE OverloadedStrings #-}+module Network.Roku.Request+ ( Query(..)+ , Method(..)+ , Request(..)+ , Resource(..)+ , request+ , resource+ , QueryError(..)+ , queryActiveApp+ , queryApps+ )+where++import Data.ByteString+import Data.Semigroup+import Data.Text.Encoding+import Data.Bifunctor+import Network.Roku.App+import Text.XML.Light.Extractors+import Text.XML.Light.Input+import qualified Data.ByteString.Lazy as LBS+import qualified Network.HTTP.Client as HTTP+import qualified Network.Roku.Keys as Keys++data Query+ = Apps+ | ActiveApp+ | Icon AppId+ deriving Show++data Method+ = Get+ | Post+ deriving Show++data Request+ = Query Query+ | Keyup Keys.Key+ | Keydown Keys.Key+ | Keypress Keys.Key+ | Launch AppId+ | Install AppId+ deriving Show++data Resource+ = Resource Method ByteString+ deriving Show++resource :: Request -> Resource+resource (Query query) =+ Resource Get $ "query/" <> path+ where path = case query of+ Apps -> "apps"+ ActiveApp -> "active-app"+ Icon (AppId appId) -> "icon/" <> encodeUtf8 appId+resource (Keyup key) =+ Resource Post $ "keyup/" <> Keys.keyValue key+resource (Keydown key) =+ Resource Post $ "keydown/" <> Keys.keyValue key+resource (Keypress key) =+ Resource Post $ "keypress/" <> Keys.keyValue key+resource (Launch (AppId appId)) =+ Resource Post $ "launch/" <> encodeUtf8 appId+resource (Install (AppId appId)) =+ Resource Post $ "install/" <> encodeUtf8 appId++data QueryError+ = QueryInvalidXML LBS.ByteString+ | QueryExtractionErr ExtractionErr+ deriving Show++request :: Request -> ByteString -> HTTP.Request+request r host =+ HTTP.defaultRequest { HTTP.host = host+ , HTTP.method = method'+ , HTTP.path = path+ , HTTP.port = 8060+ }+ where Resource method path = resource r+ method' = case method of+ Post -> "POST"+ Get -> "GET"++runQuery :: Query -> ContentsExtractor a -> ByteString -> HTTP.Manager -> IO (Either QueryError a)+runQuery q p host manager = do+ r <- HTTP.httpLbs (request (Query q) host) manager+ let body = HTTP.responseBody r+ return $ do+ doc <- maybe (Left $ QueryInvalidXML body) Right $ parseXMLDoc body+ first QueryExtractionErr $ extractDocContents p doc++queryActiveApp :: ByteString -> HTTP.Manager -> IO (Either QueryError ActiveAppResponse)+queryActiveApp = do+ runQuery ActiveApp activeAppParser++queryApps :: ByteString -> HTTP.Manager -> IO (Either QueryError [App])+queryApps = do+ runQuery Apps appsParser
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ roku-api.cabal view
@@ -0,0 +1,29 @@+name: roku-api+version: 0.1.0.0+synopsis: Bindings to Roku's External Control API+description: Bindings to Roku's External Control API.+license: BSD3+license-file: LICENSE+author: Brian McKenna+maintainer: brian@brianmckenna.org+category: Network+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules: Network.Roku+ , Network.Roku.App+ , Network.Roku.Keys+ , Network.Roku.Request+ build-depends: base >=4.9 && <4.10+ , bytestring+ , http-client+ , network+ , text+ , xml+ , xml-extractors+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/puffnfresh/roku-api