{-|
Module:      Tesla.Energy
Description: Tesla energy-specific APIs.

Access of energy-specific APIs.
-}

{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE DuplicateRecordFields      #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE UndecidableInstances       #-}

module Tesla.Energy (
  runEnergy, siteData, siteConfig, Energy
  ) where

import           Control.Exception       (Exception)
import           Control.Monad.Catch     (MonadCatch (..), MonadMask (..), MonadThrow (..))
import           Control.Monad.IO.Class  (MonadIO (..))
import           Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO)
import           Control.Monad.Logger    (MonadLogger)
import           Control.Monad.Reader    (MonadReader, ReaderT (..), asks, runReaderT)
import           Data.Aeson              (FromJSON (..))

import           Tesla
import           Tesla.Auth
import           Tesla.Internal.HTTP

-- | Get the URL for a named endpoint for a given vehicle.
energyURL :: EnergyID -> String -> String
energyURL :: EnergyID -> String -> String
energyURL EnergyID
v String
c = [String] -> String
forall a. Monoid a => [a] -> a
mconcat [String
baseURL, String
"api/1/energy_sites/", EnergyID -> String
forall a. Show a => a -> String
show EnergyID
v, String
"/", String
c]

data EnergyEnv = EnergyEnv {
  EnergyEnv -> IO AuthInfo
_authInfo :: IO AuthInfo,
  EnergyEnv -> EnergyID
_eid      :: EnergyID
  }

-- | Get the current energy ID from the Energy Monad.
currentEnergyID :: Monad m => Energy m EnergyID
currentEnergyID :: Energy m EnergyID
currentEnergyID = (EnergyEnv -> EnergyID) -> Energy m EnergyID
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks EnergyEnv -> EnergyID
_eid

-- | Energy Monad for accessing energy-specific things.
newtype Energy m a = Energy { Energy m a -> ReaderT EnergyEnv m a
runEnergyM :: ReaderT EnergyEnv m a }
  deriving (Functor (Energy m)
a -> Energy m a
Functor (Energy m)
-> (forall a. a -> Energy m a)
-> (forall a b. Energy m (a -> b) -> Energy m a -> Energy m b)
-> (forall a b c.
    (a -> b -> c) -> Energy m a -> Energy m b -> Energy m c)
-> (forall a b. Energy m a -> Energy m b -> Energy m b)
-> (forall a b. Energy m a -> Energy m b -> Energy m a)
-> Applicative (Energy m)
Energy m a -> Energy m b -> Energy m b
Energy m a -> Energy m b -> Energy m a
Energy m (a -> b) -> Energy m a -> Energy m b
(a -> b -> c) -> Energy m a -> Energy m b -> Energy m c
forall a. a -> Energy m a
forall a b. Energy m a -> Energy m b -> Energy m a
forall a b. Energy m a -> Energy m b -> Energy m b
forall a b. Energy m (a -> b) -> Energy m a -> Energy m b
forall a b c.
(a -> b -> c) -> Energy m a -> Energy m b -> Energy 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
forall (m :: * -> *). Applicative m => Functor (Energy m)
forall (m :: * -> *) a. Applicative m => a -> Energy m a
forall (m :: * -> *) a b.
Applicative m =>
Energy m a -> Energy m b -> Energy m a
forall (m :: * -> *) a b.
Applicative m =>
Energy m a -> Energy m b -> Energy m b
forall (m :: * -> *) a b.
Applicative m =>
Energy m (a -> b) -> Energy m a -> Energy m b
forall (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c) -> Energy m a -> Energy m b -> Energy m c
<* :: Energy m a -> Energy m b -> Energy m a
$c<* :: forall (m :: * -> *) a b.
Applicative m =>
Energy m a -> Energy m b -> Energy m a
*> :: Energy m a -> Energy m b -> Energy m b
$c*> :: forall (m :: * -> *) a b.
Applicative m =>
Energy m a -> Energy m b -> Energy m b
liftA2 :: (a -> b -> c) -> Energy m a -> Energy m b -> Energy m c
$cliftA2 :: forall (m :: * -> *) a b c.
Applicative m =>
(a -> b -> c) -> Energy m a -> Energy m b -> Energy m c
<*> :: Energy m (a -> b) -> Energy m a -> Energy m b
$c<*> :: forall (m :: * -> *) a b.
Applicative m =>
Energy m (a -> b) -> Energy m a -> Energy m b
pure :: a -> Energy m a
$cpure :: forall (m :: * -> *) a. Applicative m => a -> Energy m a
$cp1Applicative :: forall (m :: * -> *). Applicative m => Functor (Energy m)
Applicative, a -> Energy m b -> Energy m a
(a -> b) -> Energy m a -> Energy m b
(forall a b. (a -> b) -> Energy m a -> Energy m b)
-> (forall a b. a -> Energy m b -> Energy m a)
-> Functor (Energy m)
forall a b. a -> Energy m b -> Energy m a
forall a b. (a -> b) -> Energy m a -> Energy m b
forall (m :: * -> *) a b.
Functor m =>
a -> Energy m b -> Energy m a
forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> Energy m a -> Energy m b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Energy m b -> Energy m a
$c<$ :: forall (m :: * -> *) a b.
Functor m =>
a -> Energy m b -> Energy m a
fmap :: (a -> b) -> Energy m a -> Energy m b
$cfmap :: forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> Energy m a -> Energy m b
Functor, Applicative (Energy m)
a -> Energy m a
Applicative (Energy m)
-> (forall a b. Energy m a -> (a -> Energy m b) -> Energy m b)
-> (forall a b. Energy m a -> Energy m b -> Energy m b)
-> (forall a. a -> Energy m a)
-> Monad (Energy m)
Energy m a -> (a -> Energy m b) -> Energy m b
Energy m a -> Energy m b -> Energy m b
forall a. a -> Energy m a
forall a b. Energy m a -> Energy m b -> Energy m b
forall a b. Energy m a -> (a -> Energy m b) -> Energy m b
forall (m :: * -> *). Monad m => Applicative (Energy m)
forall (m :: * -> *) a. Monad m => a -> Energy m a
forall (m :: * -> *) a b.
Monad m =>
Energy m a -> Energy m b -> Energy m b
forall (m :: * -> *) a b.
Monad m =>
Energy m a -> (a -> Energy m b) -> Energy 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 -> Energy m a
$creturn :: forall (m :: * -> *) a. Monad m => a -> Energy m a
>> :: Energy m a -> Energy m b -> Energy m b
$c>> :: forall (m :: * -> *) a b.
Monad m =>
Energy m a -> Energy m b -> Energy m b
>>= :: Energy m a -> (a -> Energy m b) -> Energy m b
$c>>= :: forall (m :: * -> *) a b.
Monad m =>
Energy m a -> (a -> Energy m b) -> Energy m b
$cp1Monad :: forall (m :: * -> *). Monad m => Applicative (Energy m)
Monad, Monad (Energy m)
Monad (Energy m)
-> (forall a. IO a -> Energy m a) -> MonadIO (Energy m)
IO a -> Energy m a
forall a. IO a -> Energy m a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
forall (m :: * -> *). MonadIO m => Monad (Energy m)
forall (m :: * -> *) a. MonadIO m => IO a -> Energy m a
liftIO :: IO a -> Energy m a
$cliftIO :: forall (m :: * -> *) a. MonadIO m => IO a -> Energy m a
$cp1MonadIO :: forall (m :: * -> *). MonadIO m => Monad (Energy m)
MonadIO,
            MonadThrow (Energy m)
MonadThrow (Energy m)
-> (forall e a.
    Exception e =>
    Energy m a -> (e -> Energy m a) -> Energy m a)
-> MonadCatch (Energy m)
Energy m a -> (e -> Energy m a) -> Energy m a
forall e a.
Exception e =>
Energy m a -> (e -> Energy m a) -> Energy m a
forall (m :: * -> *).
MonadThrow m
-> (forall e a. Exception e => m a -> (e -> m a) -> m a)
-> MonadCatch m
forall (m :: * -> *). MonadCatch m => MonadThrow (Energy m)
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
Energy m a -> (e -> Energy m a) -> Energy m a
catch :: Energy m a -> (e -> Energy m a) -> Energy m a
$ccatch :: forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
Energy m a -> (e -> Energy m a) -> Energy m a
$cp1MonadCatch :: forall (m :: * -> *). MonadCatch m => MonadThrow (Energy m)
MonadCatch, Monad (Energy m)
e -> Energy m a
Monad (Energy m)
-> (forall e a. Exception e => e -> Energy m a)
-> MonadThrow (Energy m)
forall e a. Exception e => e -> Energy m a
forall (m :: * -> *).
Monad m -> (forall e a. Exception e => e -> m a) -> MonadThrow m
forall (m :: * -> *). MonadThrow m => Monad (Energy m)
forall (m :: * -> *) e a.
(MonadThrow m, Exception e) =>
e -> Energy m a
throwM :: e -> Energy m a
$cthrowM :: forall (m :: * -> *) e a.
(MonadThrow m, Exception e) =>
e -> Energy m a
$cp1MonadThrow :: forall (m :: * -> *). MonadThrow m => Monad (Energy m)
MonadThrow, MonadCatch (Energy m)
MonadCatch (Energy m)
-> (forall b.
    ((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b)
-> (forall b.
    ((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b)
-> (forall a b c.
    Energy m a
    -> (a -> ExitCase b -> Energy m c)
    -> (a -> Energy m b)
    -> Energy m (b, c))
-> MonadMask (Energy m)
Energy m a
-> (a -> ExitCase b -> Energy m c)
-> (a -> Energy m b)
-> Energy m (b, c)
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
forall b.
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
forall a b c.
Energy m a
-> (a -> ExitCase b -> Energy m c)
-> (a -> Energy m b)
-> Energy m (b, c)
forall (m :: * -> *).
MonadCatch m
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall a b c.
    m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c))
-> MonadMask m
forall (m :: * -> *). MonadMask m => MonadCatch (Energy m)
forall (m :: * -> *) b.
MonadMask m =>
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
forall (m :: * -> *) a b c.
MonadMask m =>
Energy m a
-> (a -> ExitCase b -> Energy m c)
-> (a -> Energy m b)
-> Energy m (b, c)
generalBracket :: Energy m a
-> (a -> ExitCase b -> Energy m c)
-> (a -> Energy m b)
-> Energy m (b, c)
$cgeneralBracket :: forall (m :: * -> *) a b c.
MonadMask m =>
Energy m a
-> (a -> ExitCase b -> Energy m c)
-> (a -> Energy m b)
-> Energy m (b, c)
uninterruptibleMask :: ((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
$cuninterruptibleMask :: forall (m :: * -> *) b.
MonadMask m =>
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
mask :: ((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
$cmask :: forall (m :: * -> *) b.
MonadMask m =>
((forall a. Energy m a -> Energy m a) -> Energy m b) -> Energy m b
$cp1MonadMask :: forall (m :: * -> *). MonadMask m => MonadCatch (Energy m)
MonadMask, MonadReader EnergyEnv,
            Monad (Energy m)
Monad (Energy m)
-> (forall a. String -> Energy m a) -> MonadFail (Energy m)
String -> Energy m a
forall a. String -> Energy m a
forall (m :: * -> *).
Monad m -> (forall a. String -> m a) -> MonadFail m
forall (m :: * -> *). MonadFail m => Monad (Energy m)
forall (m :: * -> *) a. MonadFail m => String -> Energy m a
fail :: String -> Energy m a
$cfail :: forall (m :: * -> *) a. MonadFail m => String -> Energy m a
$cp1MonadFail :: forall (m :: * -> *). MonadFail m => Monad (Energy m)
MonadFail, Monad (Energy m)
Monad (Energy m)
-> (forall msg.
    ToLogStr msg =>
    Loc -> LogSource -> LogLevel -> msg -> Energy m ())
-> MonadLogger (Energy m)
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
forall msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
forall (m :: * -> *).
Monad m
-> (forall msg.
    ToLogStr msg =>
    Loc -> LogSource -> LogLevel -> msg -> m ())
-> MonadLogger m
forall (m :: * -> *). MonadLogger m => Monad (Energy m)
forall (m :: * -> *) msg.
(MonadLogger m, ToLogStr msg) =>
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
monadLoggerLog :: Loc -> LogSource -> LogLevel -> msg -> Energy m ()
$cmonadLoggerLog :: forall (m :: * -> *) msg.
(MonadLogger m, ToLogStr msg) =>
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
$cp1MonadLogger :: forall (m :: * -> *). MonadLogger m => Monad (Energy m)
MonadLogger)

instance MonadUnliftIO m => MonadUnliftIO (Energy m) where
  withRunInIO :: ((forall a. Energy m a -> IO a) -> IO b) -> Energy m b
withRunInIO (forall a. Energy m a -> IO a) -> IO b
inner = ReaderT EnergyEnv m b -> Energy m b
forall (m :: * -> *) a. ReaderT EnergyEnv m a -> Energy m a
Energy (ReaderT EnergyEnv m b -> Energy m b)
-> ReaderT EnergyEnv m b -> Energy m b
forall a b. (a -> b) -> a -> b
$ ((forall a. ReaderT EnergyEnv m a -> IO a) -> IO b)
-> ReaderT EnergyEnv m b
forall (m :: * -> *) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO (((forall a. ReaderT EnergyEnv m a -> IO a) -> IO b)
 -> ReaderT EnergyEnv m b)
-> ((forall a. ReaderT EnergyEnv m a -> IO a) -> IO b)
-> ReaderT EnergyEnv m b
forall a b. (a -> b) -> a -> b
$ \forall a. ReaderT EnergyEnv m a -> IO a
run -> (forall a. Energy m a -> IO a) -> IO b
inner (ReaderT EnergyEnv m a -> IO a
forall a. ReaderT EnergyEnv m a -> IO a
run (ReaderT EnergyEnv m a -> IO a)
-> (Energy m a -> ReaderT EnergyEnv m a) -> Energy m a -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Energy m a -> ReaderT EnergyEnv m a
forall (m :: * -> *) a. Energy m a -> ReaderT EnergyEnv m a
runEnergyM)

instance (Monad m, MonadIO m, MonadReader EnergyEnv m) => HasTeslaAuth m where
  teslaAuth :: m AuthInfo
teslaAuth = IO AuthInfo -> m AuthInfo
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO AuthInfo -> m AuthInfo) -> m (IO AuthInfo) -> m AuthInfo
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (EnergyEnv -> IO AuthInfo) -> m (IO AuthInfo)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks EnergyEnv -> IO AuthInfo
_authInfo

-- | Run a Energy Monad with the given Vehicle ID
runEnergy :: MonadIO m => IO AuthInfo -> EnergyID -> Energy m a -> m a
runEnergy :: IO AuthInfo -> EnergyID -> Energy m a -> m a
runEnergy IO AuthInfo
ai EnergyID
ei Energy m a
f = ReaderT EnergyEnv m a -> EnergyEnv -> m a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (Energy m a -> ReaderT EnergyEnv m a
forall (m :: * -> *) a. Energy m a -> ReaderT EnergyEnv m a
runEnergyM Energy m a
f) (IO AuthInfo -> EnergyID -> EnergyEnv
EnergyEnv IO AuthInfo
ai EnergyID
ei)

newtype BadEnergyException = BadEnergy String deriving BadEnergyException -> BadEnergyException -> Bool
(BadEnergyException -> BadEnergyException -> Bool)
-> (BadEnergyException -> BadEnergyException -> Bool)
-> Eq BadEnergyException
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: BadEnergyException -> BadEnergyException -> Bool
$c/= :: BadEnergyException -> BadEnergyException -> Bool
== :: BadEnergyException -> BadEnergyException -> Bool
$c== :: BadEnergyException -> BadEnergyException -> Bool
Eq

instance Show BadEnergyException where
  show :: BadEnergyException -> String
show (BadEnergy String
s) = String
"BadEnergy: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
s

instance Exception BadEnergyException

{- 404?
siteSummary :: (FromJSON j, MonadIO m) => Energy m j
siteSummary = currentEnergyID >>= \e -> jget (energyURL e "status")
-}

-- | Fetch the "live_status" describing the current active state of an
-- energy site.
siteData :: (FromJSON j, MonadIO m) => Energy m j
siteData :: Energy m j
siteData = Energy m EnergyID
forall (m :: * -> *). Monad m => Energy m EnergyID
currentEnergyID Energy m EnergyID -> (EnergyID -> Energy m j) -> Energy m j
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \EnergyID
e -> String -> Energy m j
forall (m :: * -> *) j.
(HasTeslaAuth m, FromJSON j, MonadIO m) =>
String -> m j
jgetAuth (EnergyID -> String -> String
energyURL EnergyID
e String
"live_status")

-- | Fetch the "site_info" describing the basic configuration of an energy site.
siteConfig :: (FromJSON j, MonadIO m) => Energy m j
siteConfig :: Energy m j
siteConfig = Energy m EnergyID
forall (m :: * -> *). Monad m => Energy m EnergyID
currentEnergyID Energy m EnergyID -> (EnergyID -> Energy m j) -> Energy m j
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \EnergyID
e -> String -> Energy m j
forall (m :: * -> *) j.
(HasTeslaAuth m, FromJSON j, MonadIO m) =>
String -> m j
jgetAuth (EnergyID -> String -> String
energyURL EnergyID
e String
"site_info")

{- Errors that 'kind' is required.
siteHistory :: (FromJSON j, MonadIO m) => Energy m j
siteHistory = currentEnergyID >>= \e -> jget (energyURL e "history")
-}