Add Runtime.Bus with channel-based effect interpreter

This commit is contained in:
2026-08-20 19:29:56 +03:00
parent aa5a91bfa3
commit 78248388ac
7 changed files with 114 additions and 16 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ data Service = Service
, serviceData :: Maybe Value
, serviceTarget :: T.Text
}
deriving Show
deriving (Show, Eq)
data HASSEff a where
CallService :: Service -> HASSEff ()
+1 -8
View File
@@ -16,6 +16,7 @@ module HomeAssistant.Runtime
import AFRP (Mealy(..), Event(..))
import HomeAssistant.Controller (HASSEff(..), lightController, Service(..))
import HomeAssistant.Runtime.Bus (CallIdGen(..), mkCallIdGen)
import Data.Aeson ((.=), Value (Null), encode, eitherDecode, object)
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
@@ -23,7 +24,6 @@ import qualified Network.WebSockets as WS
import Network.Socket (withSocketsDo)
import System.Environment (getEnv)
import Data.Time (getCurrentTime)
import Data.IORef (newIORef, atomicModifyIORef')
step :: (forall x. eff x -> IO x) -> Mealy eff a b -> a -> IO (b, Mealy eff a b)
step nt (Mealy f) a = do
@@ -102,13 +102,6 @@ wsCallService conn requestId domain service entityId =
]
]
newtype CallIdGen = CallIdGen { generateCallId :: IO Int }
mkCallIdGen :: Int -> IO CallIdGen
mkCallIdGen start = do
gen <- newIORef start
pure $ CallIdGen $ atomicModifyIORef' gen (\old -> let new = old + 1 in new `seq` (new, new))
hassEval :: CallIdGen -> WS.Connection -> HASSEff a -> IO a
hassEval gen conn = \case
CallService x -> do
+52
View File
@@ -0,0 +1,52 @@
{-# LANGUAGE LambdaCase #-}
module HomeAssistant.Runtime.Bus
( Bus(..)
, CallIdGen(..)
, mkCallIdGen
, newBus
, channelHassEval
) where
import Control.Concurrent.STM
( TChan
, TVar
, atomically
, newBroadcastTChanIO
, newTChanIO
, newTVarIO
, writeTChan
)
import Data.Aeson (Value)
import Data.IORef (atomicModifyIORef', newIORef)
import HomeAssistant.Controller (HASSEff (..), Service)
import Network.WebSockets (Connection)
-- | Shared runtime state: inbound is a broadcast channel (controllers
-- read from 'dupTChanIO' copies), outbound queues service calls for the
-- writer, conn holds the current websocket (Nothing before first connect).
data Bus = Bus
{ busInbound :: TChan Value
, busOutbound :: TChan Service
, busConn :: TVar (Maybe Connection)
, busGen :: CallIdGen
}
newBus :: Int -> IO Bus
newBus start = Bus
<$> newBroadcastTChanIO
<*> newTChanIO
<*> newTVarIO Nothing
<*> mkCallIdGen start
channelHassEval :: Bus -> HASSEff a -> IO a
channelHassEval bus = \case
CallService svc -> atomically $ writeTChan (busOutbound bus) svc
Pure a -> pure a
newtype CallIdGen = CallIdGen { generateCallId :: IO Int }
mkCallIdGen :: Int -> IO CallIdGen
mkCallIdGen start = do
gen <- newIORef start
pure $ CallIdGen $ atomicModifyIORef' gen (\old -> let new = old + 1 in new `seq` (new, new))