packages feed

HueAPI 0 → 0.1

raw patch · 2 files changed

+51/−31 lines, 2 filesdep +utf8-stringdep −bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: utf8-string

Dependencies removed: bytestring

API changes (from Hackage documentation)

- HueAPI: runHueMonad :: String -> String -> HueMonad a -> IO a
- HueAPI: type HueMonad = StateT HueData (ReaderT String IO)
+ HueAPI: getState :: Hue HueData
+ HueAPI: instance FromJSON HueError
+ HueAPI: instance FromJSON HueResult
+ HueAPI: instance Show HueError
+ HueAPI: instance Show HueResult
+ HueAPI: runHue :: String -> String -> Hue a -> IO a
+ HueAPI: type Hue = StateT HueData (ReaderT String IO)
- HueAPI: getLightState :: Name -> HueMonad LightState
+ HueAPI: getLightState :: Name -> Hue LightState
- HueAPI: initLight :: Name -> LightState -> HueMonad ()
+ HueAPI: initLight :: Name -> LightState -> Hue ()
- HueAPI: updateLight :: Name -> LightState -> HueMonad ()
+ HueAPI: updateLight :: Name -> LightState -> Hue ()

Files

HueAPI.cabal view
@@ -1,5 +1,5 @@ name:                HueAPI-version:             0+version:             0.1 synopsis:            API for controlling Philips Hue lights description: homepage:            https://github.com/sjoerdvisscher/HueAPI@@ -15,7 +15,7 @@   exposed-modules:     HueAPI   build-depends:       base ==4.6.*,                        aeson ==0.6.*,-                       bytestring ==0.10.*,+                       utf8-string ==0.3.*,                        http-conduit ==1.8.*,                        network ==2.4.*,                        containers ==0.5.*,
HueAPI.hs view
@@ -7,9 +7,10 @@   , Group(..)   , Name   -  , HueMonad-  , runHueMonad+  , Hue+  , runHue   +  , getState   , getLightState   , updateLight   , initLight@@ -18,8 +19,7 @@  import GHC.Generics import Data.Aeson-import qualified Data.ByteString as B (breakSubstring, null)-import qualified Data.ByteString.Lazy as B hiding (null)+import Data.ByteString.Lazy.UTF8 (fromString) import Network.HTTP.Conduit import Network import Data.Map.Strict (Map, toList, (!), adjust)@@ -30,6 +30,7 @@ import Control.Monad.Reader (ReaderT(..), ask) import Control.Concurrent + type Name = String  data HueData = Hue@@ -63,28 +64,43 @@ instance FromJSON Group where   parseJSON (Object v) = Group <$> v .: "action" <*> v .: "name" <*> v .: "lights" +data HueResult = HueResult HueError deriving Show+instance FromJSON HueResult where+  parseJSON (Object v) = HueResult <$> v .: "error"+data HueError = HueError Int String deriving Show+instance FromJSON HueError where+  parseJSON (Object v) = HueError <$> v .: "type" <*> v .: "description" -type HueMonad = StateT HueData (ReaderT String IO) -runHueMonad :: String -> String -> HueMonad a -> IO a-runHueMonad host key api =-  let url = "http://" ++ host ++ "/api/" ++ key ++ "/" in-  withSocketsDo $ do-    connect key host-    request' <- parseUrl url-    let request = request' { responseTimeout = Nothing }-    resp <- withManager $ httpLbs request-    d <- either fail return (eitherDecode (responseBody resp))-    runReaderT (fst <$> runStateT api d) url+type Hue = StateT HueData (ReaderT String IO) +runHue :: String -> String -> Hue a -> IO a+runHue host key hm = do+    hueData <- withSocketsDo go+    runReaderT (fst <$> runStateT hm hueData) url+  where+    url = "http://" ++ host ++ "/api/" ++ key ++ "/"+    go = do+      request' <- parseUrl url+      let request = request' { responseTimeout = Nothing }+      resp <- withManager $ httpLbs request+      either doConnect return (eitherDecode (responseBody resp))+    doConnect _ = do+      putStrLn "Press the link button on the base station"+      connect key host+      go+       -getLightState :: Name -> HueMonad LightState+getState :: Hue HueData+getState = get++getLightState :: Name -> Hue LightState getLightState name = do   d <- get   return $ state $ lights d ! name  -updateLight :: Name -> LightState -> HueMonad ()+updateLight :: Name -> LightState -> Hue () updateLight name l = do   l' <- getLightState name   when (on l /= on l') $@@ -100,7 +116,7 @@   put $ d { lights = adjust (\light -> light { state = if on l then l else l' { on = False } }) name (lights d) }  -initLight :: Name -> LightState -> HueMonad ()+initLight :: Name -> LightState -> Hue () initLight name l = do   updateLightProp name "on" "true"   updateLightProp name "bri" (show $ bri l)@@ -111,33 +127,37 @@   put $ d { lights = adjust (\light -> light { state = l }) name (lights d) }      -updateLightProp :: Name -> String -> String -> HueMonad ()+updateLightProp :: Name -> String -> String -> Hue () updateLightProp name prop value = do   url <- ask   resp <- liftIO $ withSocketsDo $ do     initReq <- parseUrl $ url ++ "lights/" ++ name ++ "/state"     let request = initReq {-        requestBody = RequestBodyLBS (B.pack $ map(toEnum.fromEnum) $ "{\"" ++ prop ++ "\":" ++ value ++ "}")+        requestBody = RequestBodyLBS (fromString $ "{\"" ++ prop ++ "\":" ++ value ++ "}")       , method = "PUT"       , responseTimeout = Nothing       }     withManager (httpLbs request)-  if B.null . snd . B.breakSubstring "Internal error, 503" . B.toStrict $ responseBody resp then return () else do-    liftIO $ print resp-    liftIO $ threadDelay 100000-    updateLightProp name prop value+  case eitherDecode $ responseBody resp of+    Right [HueResult (HueError 901 _)] -> do+      liftIO $ threadDelay 100000+      updateLightProp name prop value+    Right [HueResult (HueError i m)] -> fail $ "Error " ++ show i ++ ": " ++ m+    _ -> return ()  connect :: String -> String -> IO () connect key host = do   initReq <- parseUrl $ "http://" ++ host ++ "/api/"   let request = initReq {-      requestBody = RequestBodyLBS (B.pack $ map(toEnum.fromEnum) $+      requestBody = RequestBodyLBS (fromString $         "{\"username\":\"" ++ key ++ "\",\"devicetype\":\"Unknown\"}")     , method = "POST"     , responseTimeout = Nothing     }   resp <- withManager (httpLbs request)-  if B.null . snd . B.breakSubstring "error" . B.toStrict $ responseBody resp then return () else do-    liftIO $ print resp-    liftIO $ threadDelay 100000-    connect key host+  case eitherDecode $ responseBody resp of+    Right [HueResult (HueError 101 _)] -> do+      liftIO $ threadDelay 100000+      connect key host+    Right [HueResult (HueError i m)] -> fail $ "Error " ++ show i ++ ": " ++ m+    _ -> return ()