{-|
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 = forall a. Monoid a => [a] -> a
mconcat [String
baseURL, String
"api/1/energy_sites/", 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 :: forall (m :: * -> *). Monad m => Energy m EnergyID
currentEnergyID = 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 { forall (m :: * -> *) a. Energy m a -> ReaderT EnergyEnv m a
runEnergyM :: ReaderT EnergyEnv m a }
  deriving (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
<* :: forall a b. 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
*> :: forall a b. 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 :: forall a b c.
(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
<*> :: forall a b. 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 :: forall a. a -> Energy m a
$cpure :: forall (m :: * -> *) a. Applicative m => a -> Energy m a
Applicative, 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
<$ :: forall a b. a -> Energy m b -> Energy m a
$c<$ :: forall (m :: * -> *) a b.
Functor m =>
a -> Energy m b -> Energy m a
fmap :: forall a b. (a -> b) -> Energy m a -> Energy m b
$cfmap :: forall (m :: * -> *) a b.
Functor m =>
(a -> b) -> Energy m a -> Energy m b
Functor, 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 :: forall a. a -> Energy m a
$creturn :: forall (m :: * -> *) a. Monad m => a -> Energy m a
>> :: forall a b. 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
>>= :: forall a 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
Monad, 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 :: forall a. IO a -> Energy m a
$cliftIO :: forall (m :: * -> *) a. MonadIO m => IO a -> Energy m a
MonadIO,
            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 :: forall e a.
Exception e =>
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
MonadCatch, 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 :: forall e a. Exception e => e -> Energy m a
$cthrowM :: forall (m :: * -> *) e a.
(MonadThrow m, Exception e) =>
e -> Energy m a
MonadThrow, 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 :: * -> *}. 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)
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
generalBracket :: forall a b c.
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 b.
((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 b.
((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
MonadMask, MonadReader EnergyEnv,
            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 :: forall a. String -> Energy m a
$cfail :: forall (m :: * -> *) a. MonadFail m => String -> Energy m a
MonadFail, 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 :: forall msg.
ToLogStr msg =>
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
$cmonadLoggerLog :: forall (m :: * -> *) msg.
(MonadLogger m, ToLogStr msg) =>
Loc -> LogSource -> LogLevel -> msg -> Energy m ()
MonadLogger)

instance MonadUnliftIO m => MonadUnliftIO (Energy m) where
  withRunInIO :: forall b. ((forall a. Energy m a -> IO a) -> IO b) -> Energy m b
withRunInIO (forall a. Energy m a -> IO a) -> IO b
inner = forall (m :: * -> *) a. ReaderT EnergyEnv m a -> Energy m a
Energy forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO 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 (forall a. ReaderT EnergyEnv m a -> IO a
run forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< 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 :: forall (m :: * -> *) a.
MonadIO m =>
IO AuthInfo -> EnergyID -> Energy m a -> m a
runEnergy IO AuthInfo
ai EnergyID
ei Energy m a
f = forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (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
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: " 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 :: forall j (m :: * -> *). (FromJSON j, MonadIO m) => Energy m j
siteData = forall (m :: * -> *). Monad m => Energy m EnergyID
currentEnergyID forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \EnergyID
e -> 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 :: forall j (m :: * -> *). (FromJSON j, MonadIO m) => Energy m j
siteConfig = forall (m :: * -> *). Monad m => Energy m EnergyID
currentEnergyID forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \EnergyID
e -> 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")
-}