{- |
@
-- these should be enabled by default in a future version of GHC
-- (they aren't entirely necessary here anyway - they just make the example even simpler)
\{\-\# LANGUAGE BlockArguments \#\-\}
\{\-\# LANGUAGE NamedFieldPuns \#\-\}

import Control.Monad.IO.Class (liftIO)
import Data.Foldable (for_)

-- | Find all devices on the network, print their addresses, and set their brightness to 50%.
main :: IO ()
main = runLifx do
    devs <- discoverDevices Nothing
    liftIO $ print devs
    for_ devs \\d -> do
        LightState{hsbk} <- sendMessage d GetColor
        sendMessage d $ SetColor hsbk{brightness = maxBound \`div\` 2} 3
@
-}
module Lifx.Lan (
    Device,
    deviceAddress,
    sendMessage,
    broadcastMessage,
    discoverDevices,
    Message (..),
    HSBK (..),
    Lifx,
    runLifx,
    LifxT (LifxT),
    runLifxT,
    LifxError (..),
    MonadLifx (..),

    -- * Responses
    StateService (..),
    Service (..),
    StatePower (..),
    LightState (..),

    -- * Low-level
    deviceFromAddress,
    encodeMessage,
    Header (..),
    unLifxT,
) where

import Control.Monad
import Control.Monad.Except
import Control.Monad.Extra
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Trans.Maybe
import Data.Either.Extra
import Data.Fixed
import Data.Foldable
import Data.Function
import Data.Functor
import Data.List
import Data.Maybe
import Data.Tuple.Extra
import Data.Word
import System.IO.Error

import Data.Binary (Binary)
import Data.Binary qualified as Binary
import Data.Binary.Get (
    ByteOffset,
    Get,
    getByteString,
    getWord16le,
    getWord32le,
    getWord64be,
    getWord8,
    runGetOrFail,
    skip,
 )
import Data.Binary.Put (
    Put,
    putWord16le,
    putWord32le,
    putWord64be,
    putWord8,
    runPut,
 )
import Data.Bits (Bits (..))
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as BL
import Data.List.NonEmpty (NonEmpty)
import Data.Map (Map)
import Data.Map.Strict qualified as Map
import Data.Time (
    NominalDiffTime,
    diffUTCTime,
    getCurrentTime,
    nominalDiffTimeToSeconds,
 )
import GHC.Generics (Generic)
import Network.Socket (
    Family (AF_INET),
    HostAddress,
    PortNumber,
    SockAddr (SockAddrInet),
    Socket,
    SocketOption (Broadcast),
    SocketType (Datagram),
    bind,
    defaultPort,
    defaultProtocol,
    hostAddressToTuple,
    setSocketOption,
    socket,
    tupleToHostAddress,
 )
import Network.Socket.ByteString (recvFrom, sendTo)
import System.Random (randomIO)
import System.Timeout (timeout)

{- Device -}

-- | A LIFX device, such as a bulb.
newtype Device = Device {Device -> HostAddress
unDevice :: HostAddress}
    deriving newtype (Device -> Device -> Bool
(Device -> Device -> Bool)
-> (Device -> Device -> Bool) -> Eq Device
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Device -> Device -> Bool
$c/= :: Device -> Device -> Bool
== :: Device -> Device -> Bool
$c== :: Device -> Device -> Bool
Eq, Eq Device
Eq Device
-> (Device -> Device -> Ordering)
-> (Device -> Device -> Bool)
-> (Device -> Device -> Bool)
-> (Device -> Device -> Bool)
-> (Device -> Device -> Bool)
-> (Device -> Device -> Device)
-> (Device -> Device -> Device)
-> Ord Device
Device -> Device -> Bool
Device -> Device -> Ordering
Device -> Device -> Device
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Device -> Device -> Device
$cmin :: Device -> Device -> Device
max :: Device -> Device -> Device
$cmax :: Device -> Device -> Device
>= :: Device -> Device -> Bool
$c>= :: Device -> Device -> Bool
> :: Device -> Device -> Bool
$c> :: Device -> Device -> Bool
<= :: Device -> Device -> Bool
$c<= :: Device -> Device -> Bool
< :: Device -> Device -> Bool
$c< :: Device -> Device -> Bool
compare :: Device -> Device -> Ordering
$ccompare :: Device -> Device -> Ordering
$cp1Ord :: Eq Device
Ord)

instance Show Device where
    show :: Device -> String
show (Device HostAddress
ha) = let (Word8
a, Word8
b, Word8
c, Word8
d) = HostAddress -> (Word8, Word8, Word8, Word8)
hostAddressToTuple HostAddress
ha in String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"." ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (Word8 -> String) -> [Word8] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Word8 -> String
forall a. Show a => a -> String
show [Word8
a, Word8
b, Word8
c, Word8
d]

{- |
>>> deviceFromAddress (192, 168, 0, 1)
192.168.0.1

'Device's are really just 'HostAddress's, but you don't need to know that to use this library.
Prefer to get devices from 'discoverDevices' where possible, rather than hardcoding addresses.
-}
deviceFromAddress :: (Word8, Word8, Word8, Word8) -> Device
deviceFromAddress :: (Word8, Word8, Word8, Word8) -> Device
deviceFromAddress = HostAddress -> Device
Device (HostAddress -> Device)
-> ((Word8, Word8, Word8, Word8) -> HostAddress)
-> (Word8, Word8, Word8, Word8)
-> Device
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8, Word8, Word8, Word8) -> HostAddress
tupleToHostAddress

deviceAddress :: Device -> HostAddress
deviceAddress :: Device -> HostAddress
deviceAddress = Device -> HostAddress
unDevice

{- Core -}

lifxPort :: PortNumber
lifxPort :: PortNumber
lifxPort = PortNumber
56700

-- | Send a message and wait for a response.
sendMessage :: MonadLifx m => Device -> Message r -> m r
sendMessage :: Device -> Message r -> m r
sendMessage Device
receiver Message r
msg = do
    m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    Bool -> HostAddress -> Message r -> m ()
forall (m :: * -> *) r.
MonadLifx m =>
Bool -> HostAddress -> Message r -> m ()
sendMessage' Bool
True (Device -> HostAddress
unDevice Device
receiver) Message r
msg
    Dict (MessageResult r)
Dict <- Dict (MessageResult r) -> m (Dict (MessageResult r))
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Dict (MessageResult r) -> m (Dict (MessageResult r)))
-> Dict (MessageResult r) -> m (Dict (MessageResult r))
forall a b. (a -> b) -> a -> b
$ Message r -> Dict (MessageResult r)
forall r. Message r -> Dict (MessageResult r)
msgResWitness Message r
msg
    Device -> m r
forall a (m :: * -> *).
(MessageResult a, MonadLifx m) =>
Device -> m a
getSendResult Device
receiver

-- | Broadcast a message and wait for responses.
broadcastMessage :: MonadLifx m => Message r -> m [(Device, r)]
broadcastMessage :: Message r -> m [(Device, r)]
broadcastMessage Message r
msg =
    Message r -> Dict (MessageResult r)
forall r. Message r -> Dict (MessageResult r)
msgResWitness Message r
msg Dict (MessageResult r)
-> (Dict (MessageResult r) -> m [(Device, r)]) -> m [(Device, r)]
forall a b. a -> (a -> b) -> b
& \Dict (MessageResult r)
Dict ->
        ((Device, NonEmpty r) -> [(Device, r)])
-> [(Device, NonEmpty r)] -> [(Device, r)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(Device
a, NonEmpty r
xs) -> (r -> (Device, r)) -> [r] -> [(Device, r)]
forall a b. (a -> b) -> [a] -> [b]
map (Device
a,) ([r] -> [(Device, r)]) -> [r] -> [(Device, r)]
forall a b. (a -> b) -> a -> b
$ NonEmpty r -> [r]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty r
xs) ([(Device, NonEmpty r)] -> [(Device, r)])
-> (Map Device (NonEmpty r) -> [(Device, NonEmpty r)])
-> Map Device (NonEmpty r)
-> [(Device, r)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map Device (NonEmpty r) -> [(Device, NonEmpty r)]
forall k a. Map k a -> [(k, a)]
Map.toList
            (Map Device (NonEmpty r) -> [(Device, r)])
-> m (Map Device (NonEmpty r)) -> m [(Device, r)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (HostAddress -> r -> m (Maybe r))
-> Maybe (Map HostAddress (NonEmpty r) -> Bool)
-> Message r
-> m (Map Device (NonEmpty r))
forall a (m :: * -> *) b r.
(MessageResult a, MonadLifx m) =>
(HostAddress -> a -> m (Maybe b))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
broadcastAndGetResult ((r -> m (Maybe r)) -> HostAddress -> r -> m (Maybe r)
forall a b. a -> b -> a
const ((r -> m (Maybe r)) -> HostAddress -> r -> m (Maybe r))
-> (r -> m (Maybe r)) -> HostAddress -> r -> m (Maybe r)
forall a b. (a -> b) -> a -> b
$ Maybe r -> m (Maybe r)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe r -> m (Maybe r)) -> (r -> Maybe r) -> r -> m (Maybe r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. r -> Maybe r
forall (f :: * -> *) a. Applicative f => a -> f a
pure) Maybe (Map HostAddress (NonEmpty r) -> Bool)
forall a. Maybe a
Nothing Message r
msg

{- |
Search for devices on the local network.
If an integer argument is given, wait until we have found that number of devices -
otherwise just keep waiting until timeout.
-}
discoverDevices :: MonadLifx m => Maybe Int -> m [Device]
discoverDevices :: Maybe Int -> m [Device]
discoverDevices Maybe Int
nDevices = Map Device (NonEmpty ()) -> [Device]
forall k a. Map k a -> [k]
Map.keys (Map Device (NonEmpty ()) -> [Device])
-> m (Map Device (NonEmpty ())) -> m [Device]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (HostAddress -> StateService -> m (Maybe ()))
-> Maybe (Map HostAddress (NonEmpty ()) -> Bool)
-> Message StateService
-> m (Map Device (NonEmpty ()))
forall a (m :: * -> *) b r.
(MessageResult a, MonadLifx m) =>
(HostAddress -> a -> m (Maybe b))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
broadcastAndGetResult HostAddress -> StateService -> m (Maybe ())
forall (m :: * -> *) (f :: * -> *) p.
(MonadLifx m, Alternative f) =>
p -> StateService -> m (f ())
f Maybe (Map HostAddress (NonEmpty ()) -> Bool)
p Message StateService
GetService
  where
    f :: p -> StateService -> m (f ())
f p
_addr StateService{PortNumber
Service
$sel:port:StateService :: StateService -> PortNumber
$sel:service:StateService :: StateService -> Service
port :: PortNumber
service :: Service
..} = do
        PortNumber -> m ()
forall (f :: * -> *). MonadLifx f => PortNumber -> f ()
checkPort PortNumber
port
        f () -> m (f ())
forall (f :: * -> *) a. Applicative f => a -> f a
pure (f () -> m (f ())) -> (Bool -> f ()) -> Bool -> m (f ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> f ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> m (f ())) -> Bool -> m (f ())
forall a b. (a -> b) -> a -> b
$ Service
service Service -> Service -> Bool
forall a. Eq a => a -> a -> Bool
== Service
ServiceUDP
    p :: Maybe (Map HostAddress (NonEmpty ()) -> Bool)
p = Maybe Int
nDevices Maybe Int
-> (Int -> Map HostAddress (NonEmpty ()) -> Bool)
-> Maybe (Map HostAddress (NonEmpty ()) -> Bool)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \Int
n -> (Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
n) (Int -> Bool)
-> (Map HostAddress (NonEmpty ()) -> Int)
-> Map HostAddress (NonEmpty ())
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map HostAddress (NonEmpty ()) -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length

-- | A colour. See https://lan.developer.lifx.com/docs/representing-color-with-hsbk.
data HSBK = HSBK
    { HSBK -> Word16
hue :: Word16
    , HSBK -> Word16
saturation :: Word16
    , HSBK -> Word16
brightness :: Word16
    , -- | takes values in the range 1500 to 9000
      HSBK -> Word16
kelvin :: Word16
    }
    deriving (HSBK -> HSBK -> Bool
(HSBK -> HSBK -> Bool) -> (HSBK -> HSBK -> Bool) -> Eq HSBK
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: HSBK -> HSBK -> Bool
$c/= :: HSBK -> HSBK -> Bool
== :: HSBK -> HSBK -> Bool
$c== :: HSBK -> HSBK -> Bool
Eq, Eq HSBK
Eq HSBK
-> (HSBK -> HSBK -> Ordering)
-> (HSBK -> HSBK -> Bool)
-> (HSBK -> HSBK -> Bool)
-> (HSBK -> HSBK -> Bool)
-> (HSBK -> HSBK -> Bool)
-> (HSBK -> HSBK -> HSBK)
-> (HSBK -> HSBK -> HSBK)
-> Ord HSBK
HSBK -> HSBK -> Bool
HSBK -> HSBK -> Ordering
HSBK -> HSBK -> HSBK
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: HSBK -> HSBK -> HSBK
$cmin :: HSBK -> HSBK -> HSBK
max :: HSBK -> HSBK -> HSBK
$cmax :: HSBK -> HSBK -> HSBK
>= :: HSBK -> HSBK -> Bool
$c>= :: HSBK -> HSBK -> Bool
> :: HSBK -> HSBK -> Bool
$c> :: HSBK -> HSBK -> Bool
<= :: HSBK -> HSBK -> Bool
$c<= :: HSBK -> HSBK -> Bool
< :: HSBK -> HSBK -> Bool
$c< :: HSBK -> HSBK -> Bool
compare :: HSBK -> HSBK -> Ordering
$ccompare :: HSBK -> HSBK -> Ordering
$cp1Ord :: Eq HSBK
Ord, Int -> HSBK -> ShowS
[HSBK] -> ShowS
HSBK -> String
(Int -> HSBK -> ShowS)
-> (HSBK -> String) -> ([HSBK] -> ShowS) -> Show HSBK
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [HSBK] -> ShowS
$cshowList :: [HSBK] -> ShowS
show :: HSBK -> String
$cshow :: HSBK -> String
showsPrec :: Int -> HSBK -> ShowS
$cshowsPrec :: Int -> HSBK -> ShowS
Show, (forall x. HSBK -> Rep HSBK x)
-> (forall x. Rep HSBK x -> HSBK) -> Generic HSBK
forall x. Rep HSBK x -> HSBK
forall x. HSBK -> Rep HSBK x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep HSBK x -> HSBK
$cfrom :: forall x. HSBK -> Rep HSBK x
Generic)

-- | A message we can send to a 'Device'. 'r' is the type of the expected response.
data Message r where
    -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getservice---packet-2
    -- (you shouldn't need this - use 'discoverDevices')
    GetService :: Message StateService
    -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getpower---packet-20
    GetPower :: Message StatePower
    -- | https://lan.developer.lifx.com/docs/changing-a-device#setpower---packet-21
    SetPower :: Bool -> Message ()
    -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getcolor---packet-101
    GetColor :: Message LightState
    -- | https://lan.developer.lifx.com/docs/changing-a-device#setcolor---packet-102
    SetColor :: HSBK -> NominalDiffTime -> Message ()
    -- | https://lan.developer.lifx.com/docs/changing-a-device#setlightpower---packet-117
    SetLightPower :: Bool -> NominalDiffTime -> Message ()

deriving instance (Eq (Message r))
deriving instance (Ord (Message r))
deriving instance (Show (Message r))

-- | https://lan.developer.lifx.com/docs/field-types#services
data Service
    = ServiceUDP
    | ServiceReserved1
    | ServiceReserved2
    | ServiceReserved3
    | ServiceReserved4
    deriving (Service -> Service -> Bool
(Service -> Service -> Bool)
-> (Service -> Service -> Bool) -> Eq Service
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Service -> Service -> Bool
$c/= :: Service -> Service -> Bool
== :: Service -> Service -> Bool
$c== :: Service -> Service -> Bool
Eq, Eq Service
Eq Service
-> (Service -> Service -> Ordering)
-> (Service -> Service -> Bool)
-> (Service -> Service -> Bool)
-> (Service -> Service -> Bool)
-> (Service -> Service -> Bool)
-> (Service -> Service -> Service)
-> (Service -> Service -> Service)
-> Ord Service
Service -> Service -> Bool
Service -> Service -> Ordering
Service -> Service -> Service
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Service -> Service -> Service
$cmin :: Service -> Service -> Service
max :: Service -> Service -> Service
$cmax :: Service -> Service -> Service
>= :: Service -> Service -> Bool
$c>= :: Service -> Service -> Bool
> :: Service -> Service -> Bool
$c> :: Service -> Service -> Bool
<= :: Service -> Service -> Bool
$c<= :: Service -> Service -> Bool
< :: Service -> Service -> Bool
$c< :: Service -> Service -> Bool
compare :: Service -> Service -> Ordering
$ccompare :: Service -> Service -> Ordering
$cp1Ord :: Eq Service
Ord, Int -> Service -> ShowS
[Service] -> ShowS
Service -> String
(Int -> Service -> ShowS)
-> (Service -> String) -> ([Service] -> ShowS) -> Show Service
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Service] -> ShowS
$cshowList :: [Service] -> ShowS
show :: Service -> String
$cshow :: Service -> String
showsPrec :: Int -> Service -> ShowS
$cshowsPrec :: Int -> Service -> ShowS
Show, (forall x. Service -> Rep Service x)
-> (forall x. Rep Service x -> Service) -> Generic Service
forall x. Rep Service x -> Service
forall x. Service -> Rep Service x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Service x -> Service
$cfrom :: forall x. Service -> Rep Service x
Generic)

-- | https://lan.developer.lifx.com/docs/information-messages#stateservice---packet-3
data StateService = StateService
    { StateService -> Service
service :: Service
    , StateService -> PortNumber
port :: PortNumber
    }
    deriving (StateService -> StateService -> Bool
(StateService -> StateService -> Bool)
-> (StateService -> StateService -> Bool) -> Eq StateService
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: StateService -> StateService -> Bool
$c/= :: StateService -> StateService -> Bool
== :: StateService -> StateService -> Bool
$c== :: StateService -> StateService -> Bool
Eq, Eq StateService
Eq StateService
-> (StateService -> StateService -> Ordering)
-> (StateService -> StateService -> Bool)
-> (StateService -> StateService -> Bool)
-> (StateService -> StateService -> Bool)
-> (StateService -> StateService -> Bool)
-> (StateService -> StateService -> StateService)
-> (StateService -> StateService -> StateService)
-> Ord StateService
StateService -> StateService -> Bool
StateService -> StateService -> Ordering
StateService -> StateService -> StateService
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: StateService -> StateService -> StateService
$cmin :: StateService -> StateService -> StateService
max :: StateService -> StateService -> StateService
$cmax :: StateService -> StateService -> StateService
>= :: StateService -> StateService -> Bool
$c>= :: StateService -> StateService -> Bool
> :: StateService -> StateService -> Bool
$c> :: StateService -> StateService -> Bool
<= :: StateService -> StateService -> Bool
$c<= :: StateService -> StateService -> Bool
< :: StateService -> StateService -> Bool
$c< :: StateService -> StateService -> Bool
compare :: StateService -> StateService -> Ordering
$ccompare :: StateService -> StateService -> Ordering
$cp1Ord :: Eq StateService
Ord, Int -> StateService -> ShowS
[StateService] -> ShowS
StateService -> String
(Int -> StateService -> ShowS)
-> (StateService -> String)
-> ([StateService] -> ShowS)
-> Show StateService
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StateService] -> ShowS
$cshowList :: [StateService] -> ShowS
show :: StateService -> String
$cshow :: StateService -> String
showsPrec :: Int -> StateService -> ShowS
$cshowsPrec :: Int -> StateService -> ShowS
Show, (forall x. StateService -> Rep StateService x)
-> (forall x. Rep StateService x -> StateService)
-> Generic StateService
forall x. Rep StateService x -> StateService
forall x. StateService -> Rep StateService x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep StateService x -> StateService
$cfrom :: forall x. StateService -> Rep StateService x
Generic)

-- | https://lan.developer.lifx.com/docs/information-messages#statepower---packet-22
newtype StatePower = StatePower
    { StatePower -> Word16
power :: Word16
    }
    deriving (StatePower -> StatePower -> Bool
(StatePower -> StatePower -> Bool)
-> (StatePower -> StatePower -> Bool) -> Eq StatePower
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: StatePower -> StatePower -> Bool
$c/= :: StatePower -> StatePower -> Bool
== :: StatePower -> StatePower -> Bool
$c== :: StatePower -> StatePower -> Bool
Eq, Eq StatePower
Eq StatePower
-> (StatePower -> StatePower -> Ordering)
-> (StatePower -> StatePower -> Bool)
-> (StatePower -> StatePower -> Bool)
-> (StatePower -> StatePower -> Bool)
-> (StatePower -> StatePower -> Bool)
-> (StatePower -> StatePower -> StatePower)
-> (StatePower -> StatePower -> StatePower)
-> Ord StatePower
StatePower -> StatePower -> Bool
StatePower -> StatePower -> Ordering
StatePower -> StatePower -> StatePower
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: StatePower -> StatePower -> StatePower
$cmin :: StatePower -> StatePower -> StatePower
max :: StatePower -> StatePower -> StatePower
$cmax :: StatePower -> StatePower -> StatePower
>= :: StatePower -> StatePower -> Bool
$c>= :: StatePower -> StatePower -> Bool
> :: StatePower -> StatePower -> Bool
$c> :: StatePower -> StatePower -> Bool
<= :: StatePower -> StatePower -> Bool
$c<= :: StatePower -> StatePower -> Bool
< :: StatePower -> StatePower -> Bool
$c< :: StatePower -> StatePower -> Bool
compare :: StatePower -> StatePower -> Ordering
$ccompare :: StatePower -> StatePower -> Ordering
$cp1Ord :: Eq StatePower
Ord, Int -> StatePower -> ShowS
[StatePower] -> ShowS
StatePower -> String
(Int -> StatePower -> ShowS)
-> (StatePower -> String)
-> ([StatePower] -> ShowS)
-> Show StatePower
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StatePower] -> ShowS
$cshowList :: [StatePower] -> ShowS
show :: StatePower -> String
$cshow :: StatePower -> String
showsPrec :: Int -> StatePower -> ShowS
$cshowsPrec :: Int -> StatePower -> ShowS
Show, (forall x. StatePower -> Rep StatePower x)
-> (forall x. Rep StatePower x -> StatePower) -> Generic StatePower
forall x. Rep StatePower x -> StatePower
forall x. StatePower -> Rep StatePower x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep StatePower x -> StatePower
$cfrom :: forall x. StatePower -> Rep StatePower x
Generic)

-- | https://lan.developer.lifx.com/docs/information-messages#lightstate---packet-107
data LightState = LightState
    { LightState -> HSBK
hsbk :: HSBK
    , LightState -> Word16
power :: Word16
    , LightState -> ByteString
label :: BS.ByteString
    }
    deriving (LightState -> LightState -> Bool
(LightState -> LightState -> Bool)
-> (LightState -> LightState -> Bool) -> Eq LightState
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LightState -> LightState -> Bool
$c/= :: LightState -> LightState -> Bool
== :: LightState -> LightState -> Bool
$c== :: LightState -> LightState -> Bool
Eq, Eq LightState
Eq LightState
-> (LightState -> LightState -> Ordering)
-> (LightState -> LightState -> Bool)
-> (LightState -> LightState -> Bool)
-> (LightState -> LightState -> Bool)
-> (LightState -> LightState -> Bool)
-> (LightState -> LightState -> LightState)
-> (LightState -> LightState -> LightState)
-> Ord LightState
LightState -> LightState -> Bool
LightState -> LightState -> Ordering
LightState -> LightState -> LightState
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: LightState -> LightState -> LightState
$cmin :: LightState -> LightState -> LightState
max :: LightState -> LightState -> LightState
$cmax :: LightState -> LightState -> LightState
>= :: LightState -> LightState -> Bool
$c>= :: LightState -> LightState -> Bool
> :: LightState -> LightState -> Bool
$c> :: LightState -> LightState -> Bool
<= :: LightState -> LightState -> Bool
$c<= :: LightState -> LightState -> Bool
< :: LightState -> LightState -> Bool
$c< :: LightState -> LightState -> Bool
compare :: LightState -> LightState -> Ordering
$ccompare :: LightState -> LightState -> Ordering
$cp1Ord :: Eq LightState
Ord, Int -> LightState -> ShowS
[LightState] -> ShowS
LightState -> String
(Int -> LightState -> ShowS)
-> (LightState -> String)
-> ([LightState] -> ShowS)
-> Show LightState
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LightState] -> ShowS
$cshowList :: [LightState] -> ShowS
show :: LightState -> String
$cshow :: LightState -> String
showsPrec :: Int -> LightState -> ShowS
$cshowsPrec :: Int -> LightState -> ShowS
Show, (forall x. LightState -> Rep LightState x)
-> (forall x. Rep LightState x -> LightState) -> Generic LightState
forall x. Rep LightState x -> LightState
forall x. LightState -> Rep LightState x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep LightState x -> LightState
$cfrom :: forall x. LightState -> Rep LightState x
Generic)

data LifxError
    = DecodeFailure BS.ByteString ByteOffset String
    | RecvTimeout
    | BroadcastTimeout [HostAddress] -- contains the addresses which we have received valid responses from
    | WrongPacketType Word16 Word16 -- expected, then actual
    | WrongSender Device HostAddress -- expected, then actual
    | UnexpectedSockAddrType SockAddr
    | UnexpectedPort PortNumber
    deriving (LifxError -> LifxError -> Bool
(LifxError -> LifxError -> Bool)
-> (LifxError -> LifxError -> Bool) -> Eq LifxError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LifxError -> LifxError -> Bool
$c/= :: LifxError -> LifxError -> Bool
== :: LifxError -> LifxError -> Bool
$c== :: LifxError -> LifxError -> Bool
Eq, Eq LifxError
Eq LifxError
-> (LifxError -> LifxError -> Ordering)
-> (LifxError -> LifxError -> Bool)
-> (LifxError -> LifxError -> Bool)
-> (LifxError -> LifxError -> Bool)
-> (LifxError -> LifxError -> Bool)
-> (LifxError -> LifxError -> LifxError)
-> (LifxError -> LifxError -> LifxError)
-> Ord LifxError
LifxError -> LifxError -> Bool
LifxError -> LifxError -> Ordering
LifxError -> LifxError -> LifxError
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: LifxError -> LifxError -> LifxError
$cmin :: LifxError -> LifxError -> LifxError
max :: LifxError -> LifxError -> LifxError
$cmax :: LifxError -> LifxError -> LifxError
>= :: LifxError -> LifxError -> Bool
$c>= :: LifxError -> LifxError -> Bool
> :: LifxError -> LifxError -> Bool
$c> :: LifxError -> LifxError -> Bool
<= :: LifxError -> LifxError -> Bool
$c<= :: LifxError -> LifxError -> Bool
< :: LifxError -> LifxError -> Bool
$c< :: LifxError -> LifxError -> Bool
compare :: LifxError -> LifxError -> Ordering
$ccompare :: LifxError -> LifxError -> Ordering
$cp1Ord :: Eq LifxError
Ord, Int -> LifxError -> ShowS
[LifxError] -> ShowS
LifxError -> String
(Int -> LifxError -> ShowS)
-> (LifxError -> String)
-> ([LifxError] -> ShowS)
-> Show LifxError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LifxError] -> ShowS
$cshowList :: [LifxError] -> ShowS
show :: LifxError -> String
$cshow :: LifxError -> String
showsPrec :: Int -> LifxError -> ShowS
$cshowsPrec :: Int -> LifxError -> ShowS
Show, (forall x. LifxError -> Rep LifxError x)
-> (forall x. Rep LifxError x -> LifxError) -> Generic LifxError
forall x. Rep LifxError x -> LifxError
forall x. LifxError -> Rep LifxError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep LifxError x -> LifxError
$cfrom :: forall x. LifxError -> Rep LifxError x
Generic)

{- Message responses -}

class MessageResult a where
    getSendResult :: MonadLifx m => Device -> m a
    default getSendResult :: (MonadLifx m, Response a) => Device -> m a
    getSendResult Device
receiver = m (Maybe a) -> m a
forall (m :: * -> *) a. Monad m => m (Maybe a) -> m a
untilJustM do
        Int
timeoutDuration <- m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
        (ByteString
bs, SockAddr
sender0) <- m (Either LifxError (ByteString, SockAddr))
-> m (ByteString, SockAddr)
forall (m :: * -> *) b.
MonadLifx m =>
m (Either LifxError b) -> m b
throwEither (m (Either LifxError (ByteString, SockAddr))
 -> m (ByteString, SockAddr))
-> m (Either LifxError (ByteString, SockAddr))
-> m (ByteString, SockAddr)
forall a b. (a -> b) -> a -> b
$ LifxError
-> Maybe (ByteString, SockAddr)
-> Either LifxError (ByteString, SockAddr)
forall a b. a -> Maybe b -> Either a b
maybeToEither LifxError
RecvTimeout (Maybe (ByteString, SockAddr)
 -> Either LifxError (ByteString, SockAddr))
-> m (Maybe (ByteString, SockAddr))
-> m (Either LifxError (ByteString, SockAddr))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Int -> m (Maybe (ByteString, SockAddr))
forall (m :: * -> *).
MonadLifx m =>
Int -> Int -> m (Maybe (ByteString, SockAddr))
receiveMessage Int
timeoutDuration (Response a => Int
forall a. Response a => Int
messageSize @a)
        HostAddress
sender <- SockAddr -> m HostAddress
forall (m :: * -> *). MonadLifx m => SockAddr -> m HostAddress
hostAddressFromSock SockAddr
sender0
        Maybe a
res <- ByteString -> m (Maybe a)
forall b (m :: * -> *).
(Response b, MonadLifx m) =>
ByteString -> m (Maybe b)
decodeMessage @a ByteString
bs
        Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe a -> Bool
forall a. Maybe a -> Bool
isJust Maybe a
res Bool -> Bool -> Bool
&& HostAddress
sender HostAddress -> HostAddress -> Bool
forall a. Eq a => a -> a -> Bool
/= Device -> HostAddress
deviceAddress Device
receiver) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ LifxError -> m ()
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> m ()) -> LifxError -> m ()
forall a b. (a -> b) -> a -> b
$ Device -> HostAddress -> LifxError
WrongSender Device
receiver HostAddress
sender
        Maybe a -> m (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
res
      where
        throwEither :: m (Either LifxError b) -> m b
throwEither m (Either LifxError b)
x =
            m (Either LifxError b)
x m (Either LifxError b) -> (Either LifxError b -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                Left LifxError
e -> LifxError -> m b
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow LifxError
e
                Right b
r -> b -> m b
forall (f :: * -> *) a. Applicative f => a -> f a
pure b
r

    broadcastAndGetResult ::
        MonadLifx m =>
        -- | Transform output and discard messages which return 'Nothing'.
        (HostAddress -> a -> m (Maybe b)) ->
        -- | Return once this predicate over received messages passes. Otherwise just keep waiting until timeout.
        Maybe (Map HostAddress (NonEmpty b) -> Bool) ->
        Message r ->
        m (Map Device (NonEmpty b))
    default broadcastAndGetResult ::
        (MonadLifx m, Response a) =>
        (HostAddress -> a -> m (Maybe b)) ->
        Maybe (Map HostAddress (NonEmpty b) -> Bool) ->
        Message r ->
        m (Map Device (NonEmpty b))
    broadcastAndGetResult HostAddress -> a -> m (Maybe b)
filter' Maybe (Map HostAddress (NonEmpty b) -> Bool)
maybeFinished Message r
msg = do
        Int
timeoutDuration <- m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
        Message r -> m ()
forall (m :: * -> *) r. MonadLifx m => Message r -> m ()
broadcast Message r
msg
        UTCTime
t0 <- IO UTCTime -> m UTCTime
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UTCTime
getCurrentTime
        (Map HostAddress (NonEmpty b) -> Map Device (NonEmpty b))
-> m (Map HostAddress (NonEmpty b)) -> m (Map Device (NonEmpty b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((HostAddress -> Device)
-> Map HostAddress (NonEmpty b) -> Map Device (NonEmpty b)
forall k1 k2 a. (k1 -> k2) -> Map k1 a -> Map k2 a
Map.mapKeysMonotonic HostAddress -> Device
Device) (m (Map HostAddress (NonEmpty b)) -> m (Map Device (NonEmpty b)))
-> (StateT (Map HostAddress (NonEmpty b)) m ()
    -> m (Map HostAddress (NonEmpty b)))
-> StateT (Map HostAddress (NonEmpty b)) m ()
-> m (Map Device (NonEmpty b))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (StateT (Map HostAddress (NonEmpty b)) m ()
 -> Map HostAddress (NonEmpty b)
 -> m (Map HostAddress (NonEmpty b)))
-> Map HostAddress (NonEmpty b)
-> StateT (Map HostAddress (NonEmpty b)) m ()
-> m (Map HostAddress (NonEmpty b))
forall a b c. (a -> b -> c) -> b -> a -> c
flip StateT (Map HostAddress (NonEmpty b)) m ()
-> Map HostAddress (NonEmpty b) -> m (Map HostAddress (NonEmpty b))
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m s
execStateT Map HostAddress (NonEmpty b)
forall k a. Map k a
Map.empty (StateT (Map HostAddress (NonEmpty b)) m ()
 -> m (Map Device (NonEmpty b)))
-> StateT (Map HostAddress (NonEmpty b)) m ()
-> m (Map Device (NonEmpty b))
forall a b. (a -> b) -> a -> b
$ StateT (Map HostAddress (NonEmpty b)) m Bool
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall (m :: * -> *). Monad m => m Bool -> m ()
untilM do
            UTCTime
t <- IO UTCTime -> StateT (Map HostAddress (NonEmpty b)) m UTCTime
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UTCTime
getCurrentTime
            let timeLeft :: Int
timeLeft = Int
timeoutDuration Int -> Int -> Int
forall a. Num a => a -> a -> a
- NominalDiffTime -> Int
forall f a r.
(HasResolution r, f ~ Fixed r, Integral a) =>
NominalDiffTime -> a
nominalDiffTimeToInt @Micro (UTCTime -> UTCTime -> NominalDiffTime
diffUTCTime UTCTime
t UTCTime
t0)
            if Int
timeLeft Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0
                then Bool -> StateT (Map HostAddress (NonEmpty b)) m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False
                else
                    Int
-> Int
-> StateT
     (Map HostAddress (NonEmpty b)) m (Maybe (ByteString, SockAddr))
forall (m :: * -> *).
MonadLifx m =>
Int -> Int -> m (Maybe (ByteString, SockAddr))
receiveMessage Int
timeLeft (Response a => Int
forall a. Response a => Int
messageSize @a) StateT
  (Map HostAddress (NonEmpty b)) m (Maybe (ByteString, SockAddr))
-> (Maybe (ByteString, SockAddr)
    -> StateT (Map HostAddress (NonEmpty b)) m Bool)
-> StateT (Map HostAddress (NonEmpty b)) m Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                        Just (ByteString
bs, SockAddr
addr) -> do
                            ByteString -> StateT (Map HostAddress (NonEmpty b)) m (Maybe a)
forall b (m :: * -> *).
(Response b, MonadLifx m) =>
ByteString -> m (Maybe b)
decodeMessage @a ByteString
bs StateT (Map HostAddress (NonEmpty b)) m (Maybe a)
-> (Maybe a -> StateT (Map HostAddress (NonEmpty b)) m ())
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                                Just a
x -> do
                                    HostAddress
hostAddr <- SockAddr -> StateT (Map HostAddress (NonEmpty b)) m HostAddress
forall (m :: * -> *). MonadLifx m => SockAddr -> m HostAddress
hostAddressFromSock SockAddr
addr
                                    m (Maybe b) -> StateT (Map HostAddress (NonEmpty b)) m (Maybe b)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (HostAddress -> a -> m (Maybe b)
filter' HostAddress
hostAddr a
x) StateT (Map HostAddress (NonEmpty b)) m (Maybe b)
-> (Maybe b -> StateT (Map HostAddress (NonEmpty b)) m ())
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                                        Just b
x' -> (Map HostAddress (NonEmpty b) -> Map HostAddress (NonEmpty b))
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map HostAddress (NonEmpty b) -> Map HostAddress (NonEmpty b))
 -> StateT (Map HostAddress (NonEmpty b)) m ())
-> (Map HostAddress (NonEmpty b) -> Map HostAddress (NonEmpty b))
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall a b. (a -> b) -> a -> b
$ (NonEmpty b -> NonEmpty b -> NonEmpty b)
-> HostAddress
-> NonEmpty b
-> Map HostAddress (NonEmpty b)
-> Map HostAddress (NonEmpty b)
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith NonEmpty b -> NonEmpty b -> NonEmpty b
forall a. Semigroup a => a -> a -> a
(<>) HostAddress
hostAddr (b -> NonEmpty b
forall (f :: * -> *) a. Applicative f => a -> f a
pure b
x')
                                        Maybe b
Nothing -> () -> StateT (Map HostAddress (NonEmpty b)) m ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
                                Maybe a
Nothing -> () -> StateT (Map HostAddress (NonEmpty b)) m ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
                            StateT (Map HostAddress (NonEmpty b)) m Bool
-> ((Map HostAddress (NonEmpty b) -> Bool)
    -> StateT (Map HostAddress (NonEmpty b)) m Bool)
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> StateT (Map HostAddress (NonEmpty b)) m Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Bool -> StateT (Map HostAddress (NonEmpty b)) m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
False) (Map HostAddress (NonEmpty b) -> Bool)
-> StateT (Map HostAddress (NonEmpty b)) m Bool
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets Maybe (Map HostAddress (NonEmpty b) -> Bool)
maybeFinished
                        Maybe (ByteString, SockAddr)
Nothing -> do
                            -- if we were waiting for a predicate to pass, then we've timed out
                            Bool
-> StateT (Map HostAddress (NonEmpty b)) m ()
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe (Map HostAddress (NonEmpty b) -> Bool) -> Bool
forall a. Maybe a -> Bool
isJust Maybe (Map HostAddress (NonEmpty b) -> Bool)
maybeFinished) (StateT (Map HostAddress (NonEmpty b)) m ()
 -> StateT (Map HostAddress (NonEmpty b)) m ())
-> StateT (Map HostAddress (NonEmpty b)) m ()
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall a b. (a -> b) -> a -> b
$ LifxError -> StateT (Map HostAddress (NonEmpty b)) m ()
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> StateT (Map HostAddress (NonEmpty b)) m ())
-> ([HostAddress] -> LifxError)
-> [HostAddress]
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [HostAddress] -> LifxError
BroadcastTimeout ([HostAddress] -> StateT (Map HostAddress (NonEmpty b)) m ())
-> StateT (Map HostAddress (NonEmpty b)) m [HostAddress]
-> StateT (Map HostAddress (NonEmpty b)) m ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (Map HostAddress (NonEmpty b) -> [HostAddress])
-> StateT (Map HostAddress (NonEmpty b)) m [HostAddress]
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets Map HostAddress (NonEmpty b) -> [HostAddress]
forall k a. Map k a -> [k]
Map.keys
                            Bool -> StateT (Map HostAddress (NonEmpty b)) m Bool
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True

class Response a where
    expectedPacketType :: Word16
    messageSize :: Int
    getBody :: Get a

instance MessageResult () where
    getSendResult :: Device -> m ()
getSendResult = m () -> Device -> m ()
forall a b. a -> b -> a
const (m () -> Device -> m ()) -> m () -> Device -> m ()
forall a b. (a -> b) -> a -> b
$ () -> m ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    broadcastAndGetResult :: (HostAddress -> () -> m (Maybe b))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
broadcastAndGetResult = (Maybe (Map HostAddress (NonEmpty b) -> Bool)
 -> Message r -> m (Map Device (NonEmpty b)))
-> (HostAddress -> () -> m (Maybe b))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
forall a b. a -> b -> a
const ((Maybe (Map HostAddress (NonEmpty b) -> Bool)
  -> Message r -> m (Map Device (NonEmpty b)))
 -> (HostAddress -> () -> m (Maybe b))
 -> Maybe (Map HostAddress (NonEmpty b) -> Bool)
 -> Message r
 -> m (Map Device (NonEmpty b)))
-> (Maybe (Map HostAddress (NonEmpty b) -> Bool)
    -> Message r -> m (Map Device (NonEmpty b)))
-> (HostAddress -> () -> m (Maybe b))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
forall a b. (a -> b) -> a -> b
$ (Message r -> m (Map Device (NonEmpty b)))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
forall a b. a -> b -> a
const ((Message r -> m (Map Device (NonEmpty b)))
 -> Maybe (Map HostAddress (NonEmpty b) -> Bool)
 -> Message r
 -> m (Map Device (NonEmpty b)))
-> (Message r -> m (Map Device (NonEmpty b)))
-> Maybe (Map HostAddress (NonEmpty b) -> Bool)
-> Message r
-> m (Map Device (NonEmpty b))
forall a b. (a -> b) -> a -> b
$ (Map Device (NonEmpty b)
forall k a. Map k a
Map.empty Map Device (NonEmpty b) -> m () -> m (Map Device (NonEmpty b))
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$) (m () -> m (Map Device (NonEmpty b)))
-> (Message r -> m ()) -> Message r -> m (Map Device (NonEmpty b))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Message r -> m ()
forall (m :: * -> *) r. MonadLifx m => Message r -> m ()
broadcast
instance Response StateService where
    expectedPacketType :: Word16
expectedPacketType = Word16
3
    messageSize :: Int
messageSize = Int
5
    getBody :: Get StateService
getBody = do
        Service
service <-
            Get Word8
getWord8 Get Word8 -> (Word8 -> Get Service) -> Get Service
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
                Word8
1 -> Service -> Get Service
forall (f :: * -> *) a. Applicative f => a -> f a
pure Service
ServiceUDP
                Word8
2 -> Service -> Get Service
forall (f :: * -> *) a. Applicative f => a -> f a
pure Service
ServiceReserved1
                Word8
3 -> Service -> Get Service
forall (f :: * -> *) a. Applicative f => a -> f a
pure Service
ServiceReserved2
                Word8
4 -> Service -> Get Service
forall (f :: * -> *) a. Applicative f => a -> f a
pure Service
ServiceReserved3
                Word8
5 -> Service -> Get Service
forall (f :: * -> *) a. Applicative f => a -> f a
pure Service
ServiceReserved4
                Word8
n -> String -> Get Service
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Get Service) -> String -> Get Service
forall a b. (a -> b) -> a -> b
$ String
"unknown service: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word8 -> String
forall a. Show a => a -> String
show Word8
n
        PortNumber
port <- do
            HostAddress
x <- Get HostAddress
getWord32le
            -- `network` lib uses `Word16` for ports, but LIFX StateService uses `Word32`
            Get PortNumber
-> (PortNumber -> Get PortNumber)
-> Maybe PortNumber
-> Get PortNumber
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Get PortNumber
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Get PortNumber) -> String -> Get PortNumber
forall a b. (a -> b) -> a -> b
$ String
"port out of range: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> HostAddress -> String
forall a. Show a => a -> String
show HostAddress
x) PortNumber -> Get PortNumber
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe PortNumber -> Get PortNumber)
-> Maybe PortNumber -> Get PortNumber
forall a b. (a -> b) -> a -> b
$ HostAddress -> Maybe PortNumber
forall a b. (Integral a, Integral b, Bounded b) => a -> Maybe b
fromIntegralSafe HostAddress
x
        StateService -> Get StateService
forall (f :: * -> *) a. Applicative f => a -> f a
pure StateService :: Service -> PortNumber -> StateService
StateService{PortNumber
Service
port :: PortNumber
service :: Service
$sel:port:StateService :: PortNumber
$sel:service:StateService :: Service
..}
instance MessageResult StateService
instance Response LightState where
    expectedPacketType :: Word16
expectedPacketType = Word16
107
    messageSize :: Int
messageSize = Int
52
    getBody :: Get LightState
getBody = do
        HSBK
hsbk <- Word16 -> Word16 -> Word16 -> Word16 -> HSBK
HSBK (Word16 -> Word16 -> Word16 -> Word16 -> HSBK)
-> Get Word16 -> Get (Word16 -> Word16 -> Word16 -> HSBK)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
getWord16le Get (Word16 -> Word16 -> Word16 -> HSBK)
-> Get Word16 -> Get (Word16 -> Word16 -> HSBK)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
getWord16le Get (Word16 -> Word16 -> HSBK)
-> Get Word16 -> Get (Word16 -> HSBK)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
getWord16le Get (Word16 -> HSBK) -> Get Word16 -> Get HSBK
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
getWord16le
        Int -> Get ()
skip Int
2
        Word16
power <- Get Word16
getWord16le
        ByteString
label <- (Word8 -> Bool) -> ByteString -> ByteString
BS.takeWhile (Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
0) (ByteString -> ByteString) -> Get ByteString -> Get ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> Get ByteString
getByteString Int
32
        Int -> Get ()
skip Int
8
        LightState -> Get LightState
forall (f :: * -> *) a. Applicative f => a -> f a
pure LightState :: HSBK -> Word16 -> ByteString -> LightState
LightState{Word16
ByteString
HSBK
label :: ByteString
power :: Word16
hsbk :: HSBK
$sel:label:LightState :: ByteString
$sel:power:LightState :: Word16
$sel:hsbk:LightState :: HSBK
..}
instance MessageResult LightState
instance Response StatePower where
    expectedPacketType :: Word16
expectedPacketType = Word16
22
    messageSize :: Int
messageSize = Int
2
    getBody :: Get StatePower
getBody = Word16 -> StatePower
StatePower (Word16 -> StatePower) -> Get Word16 -> Get StatePower
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
getWord16le
instance MessageResult StatePower

-- all `Message` response types are instances of `MessageResult`
--TODO ImpredicativeTypes:
-- msgResWitness :: Message r -> (forall a. MessageResult a => x) -> x
msgResWitness :: Message r -> Dict (MessageResult r)
msgResWitness :: Message r -> Dict (MessageResult r)
msgResWitness = \case
    GetService{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
    GetPower{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
    SetPower{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
    GetColor{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
    SetColor{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
    SetLightPower{} -> Dict (MessageResult r)
forall (c :: Constraint). c => Dict c
Dict
data Dict c where
    Dict :: c => Dict c

{- Monad -}

-- | A simple implementation of 'MonadLifx'.
type Lifx = LifxT IO

newtype LifxT m a = LifxT
    { LifxT m a
-> StateT
     Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
unLifxT ::
        StateT
            Word8
            ( ReaderT
                (Socket, Word32, Int)
                ( ExceptT
                    LifxError
                    m
                )
            )
            a
    }
    deriving newtype
        ( a -> LifxT m b -> LifxT m a
(a -> b) -> LifxT m a -> LifxT m b
(forall a b. (a -> b) -> LifxT m a -> LifxT m b)
-> (forall a b. a -> LifxT m b -> LifxT m a) -> Functor (LifxT m)
forall a b. a -> LifxT m b -> LifxT m a
forall a b. (a -> b) -> LifxT m a -> LifxT m b
forall (m :: * -> *) a b. Functor m => a -> LifxT m b -> LifxT m a
forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> LifxT m a -> LifxT m b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> LifxT m b -> LifxT m a
$c<$ :: forall (m :: * -> *) a b. Functor m => a -> LifxT m b -> LifxT m a
fmap :: (a -> b) -> LifxT m a -> LifxT m b
$cfmap :: forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> LifxT m a -> LifxT m b
Functor
        , Functor (LifxT m)
a -> LifxT m a
Functor (LifxT m)
-> (forall a. a -> LifxT m a)
-> (forall a b. LifxT m (a -> b) -> LifxT m a -> LifxT m b)
-> (forall a b c.
    (a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c)
-> (forall a b. LifxT m a -> LifxT m b -> LifxT m b)
-> (forall a b. LifxT m a -> LifxT m b -> LifxT m a)
-> Applicative (LifxT m)
LifxT m a -> LifxT m b -> LifxT m b
LifxT m a -> LifxT m b -> LifxT m a
LifxT m (a -> b) -> LifxT m a -> LifxT m b
(a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c
forall a. a -> LifxT m a
forall a b. LifxT m a -> LifxT m b -> LifxT m a
forall a b. LifxT m a -> LifxT m b -> LifxT m b
forall a b. LifxT m (a -> b) -> LifxT m a -> LifxT m b
forall a b c. (a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c
forall (m :: * -> *). Monad m => Functor (LifxT m)
forall (m :: * -> *) a. Monad m => a -> LifxT m a
forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m a
forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m b
forall (m :: * -> *) a b.
Monad m =>
LifxT m (a -> b) -> LifxT m a -> LifxT m b
forall (m :: * -> *) a b c.
Monad m =>
(a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: LifxT m a -> LifxT m b -> LifxT m a
$c<* :: forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m a
*> :: LifxT m a -> LifxT m b -> LifxT m b
$c*> :: forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m b
liftA2 :: (a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c
$cliftA2 :: forall (m :: * -> *) a b c.
Monad m =>
(a -> b -> c) -> LifxT m a -> LifxT m b -> LifxT m c
<*> :: LifxT m (a -> b) -> LifxT m a -> LifxT m b
$c<*> :: forall (m :: * -> *) a b.
Monad m =>
LifxT m (a -> b) -> LifxT m a -> LifxT m b
pure :: a -> LifxT m a
$cpure :: forall (m :: * -> *) a. Monad m => a -> LifxT m a
$cp1Applicative :: forall (m :: * -> *). Monad m => Functor (LifxT m)
Applicative
        , Applicative (LifxT m)
a -> LifxT m a
Applicative (LifxT m)
-> (forall a b. LifxT m a -> (a -> LifxT m b) -> LifxT m b)
-> (forall a b. LifxT m a -> LifxT m b -> LifxT m b)
-> (forall a. a -> LifxT m a)
-> Monad (LifxT m)
LifxT m a -> (a -> LifxT m b) -> LifxT m b
LifxT m a -> LifxT m b -> LifxT m b
forall a. a -> LifxT m a
forall a b. LifxT m a -> LifxT m b -> LifxT m b
forall a b. LifxT m a -> (a -> LifxT m b) -> LifxT m b
forall (m :: * -> *). Monad m => Applicative (LifxT m)
forall (m :: * -> *) a. Monad m => a -> LifxT m a
forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m b
forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> (a -> LifxT m b) -> LifxT m b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: a -> LifxT m a
$creturn :: forall (m :: * -> *) a. Monad m => a -> LifxT m a
>> :: LifxT m a -> LifxT m b -> LifxT m b
$c>> :: forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> LifxT m b -> LifxT m b
>>= :: LifxT m a -> (a -> LifxT m b) -> LifxT m b
$c>>= :: forall (m :: * -> *) a b.
Monad m =>
LifxT m a -> (a -> LifxT m b) -> LifxT m b
$cp1Monad :: forall (m :: * -> *). Monad m => Applicative (LifxT m)
Monad
        , Monad (LifxT m)
Monad (LifxT m)
-> (forall a. IO a -> LifxT m a) -> MonadIO (LifxT m)
IO a -> LifxT m a
forall a. IO a -> LifxT m a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
forall (m :: * -> *). MonadIO m => Monad (LifxT m)
forall (m :: * -> *) a. MonadIO m => IO a -> LifxT m a
liftIO :: IO a -> LifxT m a
$cliftIO :: forall (m :: * -> *) a. MonadIO m => IO a -> LifxT m a
$cp1MonadIO :: forall (m :: * -> *). MonadIO m => Monad (LifxT m)
MonadIO
        )

{- | Note that this throws 'LifxError's as 'IOException's, and sets timeout to 5 seconds.
Use 'runLifxT' for more control.
-}
runLifx :: Lifx a -> IO a
runLifx :: Lifx a -> IO a
runLifx Lifx a
m =
    Int -> Lifx a -> IO (Either LifxError a)
forall (m :: * -> *) a.
MonadIO m =>
Int -> LifxT m a -> m (Either LifxError a)
runLifxT Int
5_000_000 Lifx a
m IO (Either LifxError a) -> (Either LifxError a -> IO a) -> IO a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Left LifxError
e -> IOError -> IO a
forall a. IOError -> IO a
ioError (IOError -> IO a) -> IOError -> IO a
forall a b. (a -> b) -> a -> b
$ IOErrorType -> String -> Maybe Handle -> Maybe String -> IOError
mkIOError IOErrorType
userErrorType (LifxError -> String
forall a. Show a => a -> String
show LifxError
e) Maybe Handle
forall a. Maybe a
Nothing Maybe String
forall a. Maybe a
Nothing
        Right a
x -> a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x

runLifxT ::
    MonadIO m =>
    -- | Timeout for waiting for message responses, in microseconds.
    Int ->
    LifxT m a ->
    m (Either LifxError a)
runLifxT :: Int -> LifxT m a -> m (Either LifxError a)
runLifxT Int
timeoutDuration (LifxT StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
x) = do
    Socket
sock <- IO Socket -> m Socket
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Socket -> m Socket) -> IO Socket -> m Socket
forall a b. (a -> b) -> a -> b
$ Family -> SocketType -> ProtocolNumber -> IO Socket
socket Family
AF_INET SocketType
Datagram ProtocolNumber
defaultProtocol
    IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Socket -> SocketOption -> Int -> IO ()
setSocketOption Socket
sock SocketOption
Broadcast Int
1
    IO () -> m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> (SockAddr -> IO ()) -> SockAddr -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Socket -> SockAddr -> IO ()
bind Socket
sock (SockAddr -> m ()) -> SockAddr -> m ()
forall a b. (a -> b) -> a -> b
$ PortNumber -> HostAddress -> SockAddr
SockAddrInet PortNumber
defaultPort HostAddress
0
    HostAddress
source <- m HostAddress
forall a (m :: * -> *). (Random a, MonadIO m) => m a
randomIO
    ExceptT LifxError m a -> m (Either LifxError a)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT LifxError m a -> m (Either LifxError a))
-> ExceptT LifxError m a -> m (Either LifxError a)
forall a b. (a -> b) -> a -> b
$ ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m) a
-> (Socket, HostAddress, Int) -> ExceptT LifxError m a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> Word8
-> ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m) a
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
x Word8
0) (Socket
sock, HostAddress
source, Int
timeoutDuration)

-- | A monad for sending and receiving LIFX messages.
class MonadIO m => MonadLifx m where
    getSocket :: m Socket
    getSource :: m Word32
    getTimeout :: m Int
    incrementCounter :: m ()
    getCounter :: m Word8
    lifxThrow :: LifxError -> m a
    handleOldMessage ::
        -- | expected counter value
        Word8 ->
        -- | actual counter value
        Word8 ->
        -- | packet type
        Word16 ->
        -- | payload
        BL.ByteString ->
        m ()
    handleOldMessage Word8
_ Word8
_ Word16
_ ByteString
_ = () -> m ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

instance MonadIO m => MonadLifx (LifxT m) where
    getSocket :: LifxT m Socket
getSocket = StateT
  Word8
  (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
  Socket
-> LifxT m Socket
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8
   (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
   Socket
 -> LifxT m Socket)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Socket
-> LifxT m Socket
forall a b. (a -> b) -> a -> b
$ ((Socket, HostAddress, Int) -> Socket)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Socket
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (Socket, HostAddress, Int) -> Socket
forall a b c. (a, b, c) -> a
fst3
    getSource :: LifxT m HostAddress
getSource = StateT
  Word8
  (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
  HostAddress
-> LifxT m HostAddress
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8
   (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
   HostAddress
 -> LifxT m HostAddress)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     HostAddress
-> LifxT m HostAddress
forall a b. (a -> b) -> a -> b
$ ((Socket, HostAddress, Int) -> HostAddress)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     HostAddress
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (Socket, HostAddress, Int) -> HostAddress
forall a b c. (a, b, c) -> b
snd3
    getTimeout :: LifxT m Int
getTimeout = StateT
  Word8
  (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
  Int
-> LifxT m Int
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8
   (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
   Int
 -> LifxT m Int)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Int
-> LifxT m Int
forall a b. (a -> b) -> a -> b
$ ((Socket, HostAddress, Int) -> Int)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Int
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (Socket, HostAddress, Int) -> Int
forall a b c. (a, b, c) -> c
thd3
    incrementCounter :: LifxT m ()
incrementCounter = StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) ()
-> LifxT m ()
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) ()
 -> LifxT m ())
-> StateT
     Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) ()
-> LifxT m ()
forall a b. (a -> b) -> a -> b
$ (Word8 -> Word8)
-> StateT
     Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify Word8 -> Word8
forall a. (Eq a, Bounded a, Enum a) => a -> a
succ'
    getCounter :: LifxT m Word8
getCounter = StateT
  Word8
  (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
  Word8
-> LifxT m Word8
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8
   (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
   Word8
 -> LifxT m Word8)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Word8
-> LifxT m Word8
forall a b. (a -> b) -> a -> b
$ (Word8 -> Word8)
-> StateT
     Word8
     (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m))
     Word8
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets Word8 -> Word8
forall a. a -> a
id
    lifxThrow :: LifxError -> LifxT m a
lifxThrow = StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
forall (m :: * -> *) a.
StateT
  Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
-> LifxT m a
LifxT (StateT
   Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
 -> LifxT m a)
-> (LifxError
    -> StateT
         Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a)
-> LifxError
-> LifxT m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError
-> StateT
     Word8 (ReaderT (Socket, HostAddress, Int) (ExceptT LifxError m)) a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError
instance MonadLifx m => MonadLifx (MaybeT m) where
    getSocket :: MaybeT m Socket
getSocket = m Socket -> MaybeT m Socket
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    getSource :: MaybeT m HostAddress
getSource = m HostAddress -> MaybeT m HostAddress
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m HostAddress
forall (m :: * -> *). MonadLifx m => m HostAddress
getSource
    getTimeout :: MaybeT m Int
getTimeout = m Int -> MaybeT m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
    incrementCounter :: MaybeT m ()
incrementCounter = m () -> MaybeT m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    getCounter :: MaybeT m Word8
getCounter = m Word8 -> MaybeT m Word8
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    lifxThrow :: LifxError -> MaybeT m a
lifxThrow = m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> MaybeT m a)
-> (LifxError -> m a) -> LifxError -> MaybeT m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> m a
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow
instance MonadLifx m => MonadLifx (ExceptT e m) where
    getSocket :: ExceptT e m Socket
getSocket = m Socket -> ExceptT e m Socket
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    getSource :: ExceptT e m HostAddress
getSource = m HostAddress -> ExceptT e m HostAddress
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m HostAddress
forall (m :: * -> *). MonadLifx m => m HostAddress
getSource
    getTimeout :: ExceptT e m Int
getTimeout = m Int -> ExceptT e m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
    incrementCounter :: ExceptT e m ()
incrementCounter = m () -> ExceptT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    getCounter :: ExceptT e m Word8
getCounter = m Word8 -> ExceptT e m Word8
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    lifxThrow :: LifxError -> ExceptT e m a
lifxThrow = m a -> ExceptT e m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> ExceptT e m a)
-> (LifxError -> m a) -> LifxError -> ExceptT e m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> m a
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow
instance MonadLifx m => MonadLifx (StateT s m) where
    getSocket :: StateT s m Socket
getSocket = m Socket -> StateT s m Socket
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    getSource :: StateT s m HostAddress
getSource = m HostAddress -> StateT s m HostAddress
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m HostAddress
forall (m :: * -> *). MonadLifx m => m HostAddress
getSource
    getTimeout :: StateT s m Int
getTimeout = m Int -> StateT s m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
    incrementCounter :: StateT s m ()
incrementCounter = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    getCounter :: StateT s m Word8
getCounter = m Word8 -> StateT s m Word8
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    lifxThrow :: LifxError -> StateT s m a
lifxThrow = m a -> StateT s m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> StateT s m a)
-> (LifxError -> m a) -> LifxError -> StateT s m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> m a
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow
instance MonadLifx m => MonadLifx (ReaderT e m) where
    getSocket :: ReaderT e m Socket
getSocket = m Socket -> ReaderT e m Socket
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    getSource :: ReaderT e m HostAddress
getSource = m HostAddress -> ReaderT e m HostAddress
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m HostAddress
forall (m :: * -> *). MonadLifx m => m HostAddress
getSource
    getTimeout :: ReaderT e m Int
getTimeout = m Int -> ReaderT e m Int
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Int
forall (m :: * -> *). MonadLifx m => m Int
getTimeout
    incrementCounter :: ReaderT e m ()
incrementCounter = m () -> ReaderT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    getCounter :: ReaderT e m Word8
getCounter = m Word8 -> ReaderT e m Word8
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    lifxThrow :: LifxError -> ReaderT e m a
lifxThrow = m a -> ReaderT e m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> ReaderT e m a)
-> (LifxError -> m a) -> LifxError -> ReaderT e m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> m a
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow

{- Low level -}

encodeMessage :: Bool -> Bool -> Word8 -> Word32 -> Message r -> BL.ByteString
encodeMessage :: Bool -> Bool -> Word8 -> HostAddress -> Message r -> ByteString
encodeMessage Bool
tagged Bool
ackRequired Word8
sequenceCounter HostAddress
source Message r
msg =
    Put -> ByteString
runPut (Put -> ByteString) -> Put -> ByteString
forall a b. (a -> b) -> a -> b
$ Header -> Put
forall t. Binary t => t -> Put
Binary.put (Bool -> Bool -> Word8 -> HostAddress -> Message r -> Header
forall r.
Bool -> Bool -> Word8 -> HostAddress -> Message r -> Header
messageHeader Bool
tagged Bool
ackRequired Word8
sequenceCounter HostAddress
source Message r
msg) Put -> Put -> Put
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Message r -> Put
forall r. Message r -> Put
putMessagePayload Message r
msg

-- | https://lan.developer.lifx.com/docs/encoding-a-packet
data Header = Header
    { Header -> Word16
size :: Word16
    , Header -> Word16
protocol :: Word16
    , Header -> Bool
addressable :: Bool
    , Header -> Bool
tagged :: Bool
    , Header -> Word8
origin :: Word8
    , Header -> HostAddress
source :: Word32
    , Header -> Word64
target :: Word64
    , Header -> Bool
resRequired :: Bool
    , Header -> Bool
ackRequired :: Bool
    , Header -> Word8
sequenceCounter :: Word8
    , Header -> Word16
packetType :: Word16
    }
    deriving (Header -> Header -> Bool
(Header -> Header -> Bool)
-> (Header -> Header -> Bool) -> Eq Header
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Header -> Header -> Bool
$c/= :: Header -> Header -> Bool
== :: Header -> Header -> Bool
$c== :: Header -> Header -> Bool
Eq, Eq Header
Eq Header
-> (Header -> Header -> Ordering)
-> (Header -> Header -> Bool)
-> (Header -> Header -> Bool)
-> (Header -> Header -> Bool)
-> (Header -> Header -> Bool)
-> (Header -> Header -> Header)
-> (Header -> Header -> Header)
-> Ord Header
Header -> Header -> Bool
Header -> Header -> Ordering
Header -> Header -> Header
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Header -> Header -> Header
$cmin :: Header -> Header -> Header
max :: Header -> Header -> Header
$cmax :: Header -> Header -> Header
>= :: Header -> Header -> Bool
$c>= :: Header -> Header -> Bool
> :: Header -> Header -> Bool
$c> :: Header -> Header -> Bool
<= :: Header -> Header -> Bool
$c<= :: Header -> Header -> Bool
< :: Header -> Header -> Bool
$c< :: Header -> Header -> Bool
compare :: Header -> Header -> Ordering
$ccompare :: Header -> Header -> Ordering
$cp1Ord :: Eq Header
Ord, Int -> Header -> ShowS
[Header] -> ShowS
Header -> String
(Int -> Header -> ShowS)
-> (Header -> String) -> ([Header] -> ShowS) -> Show Header
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Header] -> ShowS
$cshowList :: [Header] -> ShowS
show :: Header -> String
$cshow :: Header -> String
showsPrec :: Int -> Header -> ShowS
$cshowsPrec :: Int -> Header -> ShowS
Show, (forall x. Header -> Rep Header x)
-> (forall x. Rep Header x -> Header) -> Generic Header
forall x. Rep Header x -> Header
forall x. Header -> Rep Header x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Header x -> Header
$cfrom :: forall x. Header -> Rep Header x
Generic)

instance Binary Header where
    get :: Get Header
get = do
        Word16
size <- Get Word16
getWord16le
        Word16
protBytes <- Get Word16
getWord16le
        let protocol :: Word16
protocol = (Word16 -> Int -> Word16) -> Int -> Word16 -> Word16
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
clearBit Int
12 (Word16 -> Word16) -> (Word16 -> Word16) -> Word16 -> Word16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word16 -> Int -> Word16) -> Int -> Word16 -> Word16
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
clearBit Int
13 (Word16 -> Word16) -> (Word16 -> Word16) -> Word16 -> Word16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word16 -> Int -> Word16) -> Int -> Word16 -> Word16
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
clearBit Int
14 (Word16 -> Word16) -> (Word16 -> Word16) -> Word16 -> Word16
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word16 -> Int -> Word16) -> Int -> Word16 -> Word16
forall a b c. (a -> b -> c) -> b -> a -> c
flip Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
clearBit Int
15 (Word16 -> Word16) -> Word16 -> Word16
forall a b. (a -> b) -> a -> b
$ Word16
protBytes
            addressable :: Bool
addressable = Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word16
protBytes Int
12
            tagged :: Bool
tagged = Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word16
protBytes Int
13
            origin :: Word8
origin = (if Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word16
protBytes Int
14 then Word8
0 else Word8
1) Word8 -> Word8 -> Word8
forall a. Num a => a -> a -> a
+ (if Word16 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word16
protBytes Int
15 then Word8
0 else Word8
2)
        HostAddress
source <- Get HostAddress
getWord32le
        Word64
target <- Get Word64
getWord64be
        Int -> Get ()
skip Int
6
        Word8
resAckByte <- Get Word8
getWord8
        let resRequired :: Bool
resRequired = Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
resAckByte Int
0
            ackRequired :: Bool
ackRequired = Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
resAckByte Int
1
        Word8
sequenceCounter <- Get Word8
getWord8
        Int -> Get ()
skip Int
8
        Word16
packetType <- Get Word16
getWord16le
        Int -> Get ()
skip Int
2
        Header -> Get Header
forall (f :: * -> *) a. Applicative f => a -> f a
pure Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header{Bool
Word8
Word16
HostAddress
Word64
packetType :: Word16
sequenceCounter :: Word8
ackRequired :: Bool
resRequired :: Bool
target :: Word64
source :: HostAddress
origin :: Word8
tagged :: Bool
addressable :: Bool
protocol :: Word16
size :: Word16
$sel:packetType:Header :: Word16
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
$sel:size:Header :: Word16
..}

    put :: Header -> Put
put Header{Bool
Word8
Word16
HostAddress
Word64
packetType :: Word16
sequenceCounter :: Word8
ackRequired :: Bool
resRequired :: Bool
target :: Word64
source :: HostAddress
origin :: Word8
tagged :: Bool
addressable :: Bool
protocol :: Word16
size :: Word16
$sel:packetType:Header :: Header -> Word16
$sel:sequenceCounter:Header :: Header -> Word8
$sel:ackRequired:Header :: Header -> Bool
$sel:resRequired:Header :: Header -> Bool
$sel:target:Header :: Header -> Word64
$sel:source:Header :: Header -> HostAddress
$sel:origin:Header :: Header -> Word8
$sel:tagged:Header :: Header -> Bool
$sel:addressable:Header :: Header -> Bool
$sel:protocol:Header :: Header -> Word16
$sel:size:Header :: Header -> Word16
..} = do
        Word16 -> Put
putWord16le Word16
size
        Word16 -> Put
putWord16le (Word16 -> Put) -> Word16 -> Put
forall a b. (a -> b) -> a -> b
$
            Word16
protocol
                Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word16
forall p. Bits p => Bool -> Int -> p
bitIf Bool
addressable Int
12
                Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word16
forall p. Bits p => Bool -> Int -> p
bitIf Bool
tagged Int
13
                Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word16
forall p. Bits p => Bool -> Int -> p
bitIf (Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
origin Int
0) Int
14
                Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word16
forall p. Bits p => Bool -> Int -> p
bitIf (Word8 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word8
origin Int
1) Int
15
        HostAddress -> Put
putWord32le HostAddress
source
        Word64 -> Put
putWord64be Word64
target
        Int -> Put -> Put
forall (m :: * -> *) a. Applicative m => Int -> m a -> m ()
replicateM_ Int
6 (Put -> Put) -> Put -> Put
forall a b. (a -> b) -> a -> b
$ Word8 -> Put
putWord8 Word8
0
        Word8 -> Put
putWord8 (Word8 -> Put) -> Word8 -> Put
forall a b. (a -> b) -> a -> b
$
            Word8
forall a. Bits a => a
zeroBits
                Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word8
forall p. Bits p => Bool -> Int -> p
bitIf Bool
resRequired Int
0
                Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.|. Bool -> Int -> Word8
forall p. Bits p => Bool -> Int -> p
bitIf Bool
ackRequired Int
1
        Word8 -> Put
putWord8 Word8
sequenceCounter
        Int -> Put -> Put
forall (m :: * -> *) a. Applicative m => Int -> m a -> m ()
replicateM_ Int
8 (Put -> Put) -> Put -> Put
forall a b. (a -> b) -> a -> b
$ Word8 -> Put
putWord8 Word8
0
        Word16 -> Put
putWord16le Word16
packetType
        Int -> Put -> Put
forall (m :: * -> *) a. Applicative m => Int -> m a -> m ()
replicateM_ Int
2 (Put -> Put) -> Put -> Put
forall a b. (a -> b) -> a -> b
$ Word8 -> Put
putWord8 Word8
0
      where
        bitIf :: Bool -> Int -> p
bitIf Bool
b Int
n = if Bool
b then Int -> p
forall a. Bits a => Int -> a
bit Int
n else p
forall a. Bits a => a
zeroBits

messageHeader :: Bool -> Bool -> Word8 -> Word32 -> Message r -> Header
messageHeader :: Bool -> Bool -> Word8 -> HostAddress -> Message r -> Header
messageHeader Bool
tagged Bool
ackRequired Word8
sequenceCounter HostAddress
source = \case
    GetService{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize
            , $sel:packetType:Header :: Word16
packetType = Word16
2
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
    GetPower{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize
            , $sel:packetType:Header :: Word16
packetType = Word16
20
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
    SetPower{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
+ Word16
2
            , $sel:packetType:Header :: Word16
packetType = Word16
21
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
    GetColor{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize
            , $sel:packetType:Header :: Word16
packetType = Word16
101
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
    SetColor{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
+ Word16
13
            , $sel:packetType:Header :: Word16
packetType = Word16
102
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
    SetLightPower{} ->
        Header :: Word16
-> Word16
-> Bool
-> Bool
-> Word8
-> HostAddress
-> Word64
-> Bool
-> Bool
-> Word8
-> Word16
-> Header
Header
            { $sel:size:Header :: Word16
size = Word16
forall a. Num a => a
headerSize Word16 -> Word16 -> Word16
forall a. Num a => a -> a -> a
+ Word16
6
            , $sel:packetType:Header :: Word16
packetType = Word16
117
            , Bool
Word8
Word16
HostAddress
Word64
resRequired :: Bool
origin :: Word8
addressable :: Bool
protocol :: Word16
target :: Word64
source :: HostAddress
sequenceCounter :: Word8
ackRequired :: Bool
tagged :: Bool
$sel:sequenceCounter:Header :: Word8
$sel:ackRequired:Header :: Bool
$sel:resRequired:Header :: Bool
$sel:target:Header :: Word64
$sel:source:Header :: HostAddress
$sel:origin:Header :: Word8
$sel:tagged:Header :: Bool
$sel:addressable:Header :: Bool
$sel:protocol:Header :: Word16
..
            }
  where
    target :: Word64
target = Word64
0 :: Word64
    protocol :: Word16
protocol = Word16
1024 :: Word16
    addressable :: Bool
addressable = Bool
True
    origin :: Word8
origin = Word8
0 :: Word8
    resRequired :: Bool
resRequired = Bool
False

putMessagePayload :: Message r -> Put
putMessagePayload :: Message r -> Put
putMessagePayload = \case
    Message r
GetService -> Put
forall a. Monoid a => a
mempty
    Message r
GetPower -> Put
forall a. Monoid a => a
mempty
    SetPower Bool
b ->
        Word16 -> Put
putWord16le if Bool
b then Word16
forall a. Bounded a => a
maxBound else Word16
forall a. Bounded a => a
minBound
    Message r
GetColor -> Put
forall a. Monoid a => a
mempty
    SetColor HSBK{Word16
kelvin :: Word16
brightness :: Word16
saturation :: Word16
hue :: Word16
$sel:kelvin:HSBK :: HSBK -> Word16
$sel:brightness:HSBK :: HSBK -> Word16
$sel:saturation:HSBK :: HSBK -> Word16
$sel:hue:HSBK :: HSBK -> Word16
..} NominalDiffTime
d -> do
        Word8 -> Put
putWord8 Word8
0
        Word16 -> Put
putWord16le Word16
hue
        Word16 -> Put
putWord16le Word16
saturation
        Word16 -> Put
putWord16le Word16
brightness
        Word16 -> Put
putWord16le Word16
kelvin
        HostAddress -> Put
putWord32le (HostAddress -> Put) -> HostAddress -> Put
forall a b. (a -> b) -> a -> b
$ NominalDiffTime -> HostAddress
forall f a r.
(HasResolution r, f ~ Fixed r, Integral a) =>
NominalDiffTime -> a
nominalDiffTimeToInt @Milli NominalDiffTime
d
    SetLightPower Bool
b NominalDiffTime
d -> do
        Word16 -> Put
putWord16le if Bool
b then Word16
forall a. Bounded a => a
maxBound else Word16
forall a. Bounded a => a
minBound
        HostAddress -> Put
putWord32le (HostAddress -> Put) -> HostAddress -> Put
forall a b. (a -> b) -> a -> b
$ NominalDiffTime -> HostAddress
forall f a r.
(HasResolution r, f ~ Fixed r, Integral a) =>
NominalDiffTime -> a
nominalDiffTimeToInt @Milli NominalDiffTime
d

{- Util -}

-- | Safe, wraparound variant of 'succ'.
succ' :: (Eq a, Bounded a, Enum a) => a -> a
succ' :: a -> a
succ' a
e
    | a
e a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
forall a. Bounded a => a
maxBound = a
forall a. Bounded a => a
minBound
    | Bool
otherwise = a -> a
forall a. Enum a => a -> a
succ a
e

fromIntegralSafe :: forall a b. (Integral a, Integral b, Bounded b) => a -> Maybe b
fromIntegralSafe :: a -> Maybe b
fromIntegralSafe a
x =
    Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard
        ( a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= b -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Bounded b => b
forall a. Bounded a => a
maxBound @b)
            Bool -> Bool -> Bool
&& a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
>= b -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Bounded b => b
forall a. Bounded a => a
minBound @b)
        )
        Maybe () -> b -> Maybe b
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> a -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
x

headerSize :: Num a => a
headerSize :: a
headerSize = a
36

nominalDiffTimeToInt :: forall f a r. (HasResolution r, f ~ Fixed r, Integral a) => NominalDiffTime -> a
nominalDiffTimeToInt :: NominalDiffTime -> a
nominalDiffTimeToInt NominalDiffTime
t = Integer -> a
forall a. Num a => Integer -> a
fromInteger Integer
n
  where
    MkFixed Integer
n = (Real Pico, Fractional f) => Pico -> f
forall a b. (Real a, Fractional b) => a -> b
realToFrac @Pico @f (Pico -> f) -> Pico -> f
forall a b. (a -> b) -> a -> b
$ NominalDiffTime -> Pico
nominalDiffTimeToSeconds NominalDiffTime
t

-- | Inverted 'whileM'.
untilM :: Monad m => m Bool -> m ()
untilM :: m Bool -> m ()
untilM = m Bool -> m ()
forall (m :: * -> *). Monad m => m Bool -> m ()
whileM (m Bool -> m ()) -> (m Bool -> m Bool) -> m Bool -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Bool -> Bool) -> m Bool -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Bool -> Bool
not

checkPort :: MonadLifx f => PortNumber -> f ()
checkPort :: PortNumber -> f ()
checkPort PortNumber
port = Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (PortNumber
port PortNumber -> PortNumber -> Bool
forall a. Eq a => a -> a -> Bool
/= PortNumber
lifxPort) (f () -> f ()) -> (LifxError -> f ()) -> LifxError -> f ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> f ()
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> f ()) -> LifxError -> f ()
forall a b. (a -> b) -> a -> b
$ PortNumber -> LifxError
UnexpectedPort PortNumber
port

-- these helpers are all used by 'sendMessage' and 'broadcastMessage'
decodeMessage :: forall b m. (Response b, MonadLifx m) => BS.ByteString -> m (Maybe b) -- Nothing means counter mismatch
decodeMessage :: ByteString -> m (Maybe b)
decodeMessage ByteString
bs = do
    Word8
counter <- m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    case Get Header
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, Header)
forall a.
Get a
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
runGetOrFail Get Header
forall t. Binary t => Get t
Binary.get (ByteString
 -> Either
      (ByteString, ByteOffset, String) (ByteString, ByteOffset, Header))
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, Header)
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BL.fromStrict ByteString
bs of
        Left (ByteString, ByteOffset, String)
e -> (ByteString, ByteOffset, String) -> m (Maybe b)
forall (m :: * -> *) a.
MonadLifx m =>
(ByteString, ByteOffset, String) -> m a
throwDecodeFailure (ByteString, ByteOffset, String)
e
        Right (ByteString
bs', ByteOffset
_, Header{Word16
packetType :: Word16
$sel:packetType:Header :: Header -> Word16
packetType, Word8
sequenceCounter :: Word8
$sel:sequenceCounter:Header :: Header -> Word8
sequenceCounter}) ->
            if Word8
sequenceCounter Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
counter
                then Word8 -> Word8 -> Word16 -> ByteString -> m ()
forall (m :: * -> *).
MonadLifx m =>
Word8 -> Word8 -> Word16 -> ByteString -> m ()
handleOldMessage Word8
counter Word8
sequenceCounter Word16
packetType ByteString
bs' m () -> m (Maybe b) -> m (Maybe b)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe b -> m (Maybe b)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe b
forall a. Maybe a
Nothing
                else do
                    Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word16
packetType Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
/= Response b => Word16
forall a. Response a => Word16
expectedPacketType @b) (m () -> m ()) -> (LifxError -> m ()) -> LifxError -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LifxError -> m ()
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> m ()) -> LifxError -> m ()
forall a b. (a -> b) -> a -> b
$
                        Word16 -> Word16 -> LifxError
WrongPacketType (Response b => Word16
forall a. Response a => Word16
expectedPacketType @b) Word16
packetType
                    case Get b
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, b)
forall a.
Get a
-> ByteString
-> Either
     (ByteString, ByteOffset, String) (ByteString, ByteOffset, a)
runGetOrFail Get b
forall a. Response a => Get a
getBody ByteString
bs' of
                        Left (ByteString, ByteOffset, String)
e -> (ByteString, ByteOffset, String) -> m (Maybe b)
forall (m :: * -> *) a.
MonadLifx m =>
(ByteString, ByteOffset, String) -> m a
throwDecodeFailure (ByteString, ByteOffset, String)
e
                        Right (ByteString
_, ByteOffset
_, b
res) -> Maybe b -> m (Maybe b)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe b -> m (Maybe b)) -> Maybe b -> m (Maybe b)
forall a b. (a -> b) -> a -> b
$ b -> Maybe b
forall a. a -> Maybe a
Just b
res
  where
    throwDecodeFailure :: (ByteString, ByteOffset, String) -> m a
throwDecodeFailure (ByteString
bs', ByteOffset
bo, String
e) = LifxError -> m a
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> m a) -> LifxError -> m a
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteOffset -> String -> LifxError
DecodeFailure (ByteString -> ByteString
BL.toStrict ByteString
bs') ByteOffset
bo String
e
sendMessage' :: MonadLifx m => Bool -> HostAddress -> Message r -> m ()
sendMessage' :: Bool -> HostAddress -> Message r -> m ()
sendMessage' Bool
tagged HostAddress
receiver Message r
msg = do
    Socket
sock <- m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    Word8
counter <- m Word8
forall (m :: * -> *). MonadLifx m => m Word8
getCounter
    HostAddress
source <- m HostAddress
forall (m :: * -> *). MonadLifx m => m HostAddress
getSource
    m Int -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Int -> m ()) -> (IO Int -> m Int) -> IO Int -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Int -> m Int
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Int -> m ()) -> IO Int -> m ()
forall a b. (a -> b) -> a -> b
$
        Socket -> ByteString -> SockAddr -> IO Int
sendTo
            Socket
sock
            (ByteString -> ByteString
BL.toStrict (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Bool -> Bool -> Word8 -> HostAddress -> Message r -> ByteString
forall r.
Bool -> Bool -> Word8 -> HostAddress -> Message r -> ByteString
encodeMessage Bool
tagged Bool
False Word8
counter HostAddress
source Message r
msg)
            (PortNumber -> HostAddress -> SockAddr
SockAddrInet PortNumber
lifxPort HostAddress
receiver)
hostAddressFromSock :: MonadLifx m => SockAddr -> m HostAddress
hostAddressFromSock :: SockAddr -> m HostAddress
hostAddressFromSock = \case
    SockAddrInet PortNumber
port HostAddress
ha -> PortNumber -> m ()
forall (f :: * -> *). MonadLifx f => PortNumber -> f ()
checkPort PortNumber
port m () -> m HostAddress -> m HostAddress
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> HostAddress -> m HostAddress
forall (f :: * -> *) a. Applicative f => a -> f a
pure HostAddress
ha
    SockAddr
addr -> LifxError -> m HostAddress
forall (m :: * -> *) a. MonadLifx m => LifxError -> m a
lifxThrow (LifxError -> m HostAddress) -> LifxError -> m HostAddress
forall a b. (a -> b) -> a -> b
$ SockAddr -> LifxError
UnexpectedSockAddrType SockAddr
addr
receiveMessage :: MonadLifx m => Int -> Int -> m (Maybe (BS.ByteString, SockAddr))
receiveMessage :: Int -> Int -> m (Maybe (ByteString, SockAddr))
receiveMessage Int
t Int
size = do
    Socket
sock <- m Socket
forall (m :: * -> *). MonadLifx m => m Socket
getSocket
    IO (Maybe (ByteString, SockAddr))
-> m (Maybe (ByteString, SockAddr))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
        (IO (Maybe (ByteString, SockAddr))
 -> m (Maybe (ByteString, SockAddr)))
-> (Int -> IO (Maybe (ByteString, SockAddr)))
-> Int
-> m (Maybe (ByteString, SockAddr))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int
-> IO (ByteString, SockAddr) -> IO (Maybe (ByteString, SockAddr))
forall a. Int -> IO a -> IO (Maybe a)
timeout Int
t
        (IO (ByteString, SockAddr) -> IO (Maybe (ByteString, SockAddr)))
-> (Int -> IO (ByteString, SockAddr))
-> Int
-> IO (Maybe (ByteString, SockAddr))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Socket -> Int -> IO (ByteString, SockAddr)
recvFrom Socket
sock
        (Int -> m (Maybe (ByteString, SockAddr)))
-> Int -> m (Maybe (ByteString, SockAddr))
forall a b. (a -> b) -> a -> b
$ Int
forall a. Num a => a
headerSize Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
size

broadcast :: MonadLifx m => Message r -> m ()
broadcast :: Message r -> m ()
broadcast Message r
msg = do
    m ()
forall (m :: * -> *). MonadLifx m => m ()
incrementCounter
    Bool -> HostAddress -> Message r -> m ()
forall (m :: * -> *) r.
MonadLifx m =>
Bool -> HostAddress -> Message r -> m ()
sendMessage' Bool
False ((Word8, Word8, Word8, Word8) -> HostAddress
tupleToHostAddress (Word8
255, Word8
255, Word8
255, Word8
255)) Message r
msg