Diagrams
This commit is contained in:
1 parent
022342af32
commit
617bd255ff
10 files changed
+124
-71
No files matched your search
@@ -60,6 +60,7 @@ library
|
|||||||
|
|
||||||
-- Modules exported by the library.
|
-- Modules exported by the library.
|
||||||
exposed-modules: AFRP
|
exposed-modules: AFRP
|
||||||
|
, AFRP.Diagram
|
||||||
, HomeAssistant.Controller
|
, HomeAssistant.Controller
|
||||||
, HomeAssistant.Controller.Bedroom
|
, HomeAssistant.Controller.Bedroom
|
||||||
, HomeAssistant.Controller.Children
|
, HomeAssistant.Controller.Children
|
||||||
|
|||||||
+24
-22
@@ -5,7 +5,7 @@ module AFRP
|
|||||||
, Auto(..)
|
, Auto(..)
|
||||||
, DecodedAuto(..)
|
, DecodedAuto(..)
|
||||||
, eff
|
, eff
|
||||||
, withEntities
|
, named
|
||||||
, Event(..)
|
, Event(..)
|
||||||
, hold
|
, hold
|
||||||
, holdKnown
|
, holdKnown
|
||||||
@@ -67,6 +67,8 @@ import qualified Data.Conduit.Cereal as CC
|
|||||||
import qualified Conduit as C
|
import qualified Conduit as C
|
||||||
import Control.Monad.Catch (MonadCatch, handle)
|
import Control.Monad.Catch (MonadCatch, handle)
|
||||||
import Control.Exception.Annotated (throw)
|
import Control.Exception.Annotated (throw)
|
||||||
|
import AFRP.Diagram
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
|
||||||
data Codec s = Codec { getter :: !(Get s), putter :: !(Putter s) }
|
data Codec s = Codec { getter :: !(Get s), putter :: !(Putter s) }
|
||||||
@@ -257,54 +259,54 @@ load path a = handle defaultOnMissingFile (flip deserialize a <$> liftIO (B.read
|
|||||||
| isDoesNotExistError e = pure $ FailDecode "State doesn't exist yet" a
|
| isDoesNotExistError e = pure $ FailDecode "State doesn't exist yet" a
|
||||||
| otherwise = throw e
|
| otherwise = throw e
|
||||||
|
|
||||||
|
|
||||||
-- | The set of entity ids an arrow subscribes to. Static: it does not
|
-- | The set of entity ids an arrow subscribes to. Static: it does not
|
||||||
-- change as the machine steps, so the runtime can read it once to build
|
-- change as the machine steps, so the runtime can read it once to build
|
||||||
-- trigger subscriptions.
|
-- trigger subscriptions.
|
||||||
data Mealy eff a b = Mealy
|
data Mealy eff a b = Mealy
|
||||||
{ entities :: S.Set T.Text
|
{ entities :: S.Set T.Text
|
||||||
|
, runDiagramC :: forall x. (DiagramF x -> x) -> x
|
||||||
, runMealy :: forall m. Monad m => (forall x. eff x -> m x) -> Auto m a b
|
, runMealy :: forall m. Monad m => (forall x. eff x -> m x) -> Auto m a b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Provide a name
|
||||||
|
named :: Text -> Mealy eff a b -> Mealy eff a b
|
||||||
|
named n f = f{runDiagramC = \run -> run (Step n (emptyDiagram run))}
|
||||||
|
|
||||||
instance Semigroup b => Semigroup (Mealy eff a b) where
|
instance Semigroup b => Semigroup (Mealy eff a b) where
|
||||||
Mealy ast af <> Mealy bst bf = Mealy (ast <> bst) $ \nt -> af nt <> bf nt
|
Mealy ast ad af <> Mealy bst bd bf = Mealy (ast <> bst) (combineDiagram Merge ad bd) $ \nt -> af nt <> bf nt
|
||||||
|
|
||||||
|
|
||||||
instance Monoid b => Monoid (Mealy eff a b) where
|
instance Monoid b => Monoid (Mealy eff a b) where
|
||||||
mempty = Mealy mempty $ \_nt -> mempty
|
mempty = Mealy mempty emptyDiagram $ \_nt -> mempty
|
||||||
|
|
||||||
eff :: (Request -> a -> eff b) -> Mealy eff a b
|
eff :: (Request -> a -> eff b) -> Mealy eff a b
|
||||||
eff f = Mealy mempty $ \nt -> do
|
eff f = Mealy mempty emptyDiagram $ \nt -> do
|
||||||
Stateful (Codec get put) (State () False) $ \s req a -> do
|
Stateful (Codec get put) (State () False) $ \s req a -> do
|
||||||
b <- nt (f req a)
|
b <- nt (f req a)
|
||||||
pure (b, s)
|
pure (b, s)
|
||||||
|
|
||||||
-- | Override the static entity set of an arrow. Use when a combinator
|
|
||||||
-- (e.g. 'switch') hides continuation entities from the runtime's
|
|
||||||
-- startup subscription scan.
|
|
||||||
withEntities :: S.Set T.Text -> Mealy eff a b -> Mealy eff a b
|
|
||||||
withEntities es (Mealy _ f) = Mealy es f
|
|
||||||
|
|
||||||
instance Category (Mealy eff) where
|
instance Category (Mealy eff) where
|
||||||
{- HLINT ignore "Use const" -}
|
{- HLINT ignore "Use const" -}
|
||||||
id = Mealy mempty $ \_nt -> id
|
id = Mealy mempty emptyDiagram $ \_nt -> id
|
||||||
(Mealy ast f) . (Mealy bst g) = Mealy (ast <> bst) $ \nt -> do
|
(Mealy ast ad f) . (Mealy bst bd g) = Mealy (ast <> bst) (combineDiagram Then ad bd) $ \nt -> do
|
||||||
f nt . g nt
|
f nt . g nt
|
||||||
|
|
||||||
instance Arrow (Mealy eff) where
|
instance Arrow (Mealy eff) where
|
||||||
arr f = Mealy mempty $ \_nt -> arr f
|
arr f = Mealy mempty emptyDiagram $ \_nt -> arr f
|
||||||
first (Mealy st f) = Mealy st $ \nt -> first (f nt)
|
first (Mealy st d f) = Mealy st d $ \nt -> first (f nt)
|
||||||
|
|
||||||
instance ArrowChoice (Mealy eff) where
|
instance ArrowChoice (Mealy eff) where
|
||||||
left (Mealy st f) = Mealy st $ \nt -> left (f nt)
|
left (Mealy st d f) = Mealy st d $ \nt -> left (f nt)
|
||||||
|
|
||||||
|
|
||||||
instance Functor (Mealy eff a) where
|
instance Functor (Mealy eff a) where
|
||||||
fmap f (Mealy st g) = Mealy st $ \nt -> fmap f (g nt)
|
fmap f (Mealy st d g) = Mealy st d $ \nt -> fmap f (g nt)
|
||||||
|
|
||||||
instance Applicative (Mealy eff a) where
|
instance Applicative (Mealy eff a) where
|
||||||
pure b = Mealy mempty $ \_ -> pure b
|
pure b = Mealy mempty emptyDiagram $ \_ -> pure b
|
||||||
Mealy ast f <*> Mealy bst x = Mealy (ast <> bst) $ \nt ->
|
Mealy ast ad f <*> Mealy bst bd x = Mealy (ast <> bst) (combineDiagram Compose ad bd) $ \nt ->
|
||||||
f nt <*> x nt
|
f nt <*> x nt
|
||||||
|
|
||||||
data Event a
|
data Event a
|
||||||
@@ -369,10 +371,10 @@ preMapAccum' f x extract = Stateful (Codec get put) (pure x) (\s _req a -> pure
|
|||||||
|
|
||||||
|
|
||||||
mapAccum :: (Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
mapAccum :: (Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
||||||
mapAccum step x extract = Mealy mempty $ \_nt -> mapAccum' step x extract
|
mapAccum step x extract = Mealy mempty emptyDiagram $ \_nt -> mapAccum' step x extract
|
||||||
|
|
||||||
preMapAccum :: (Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
preMapAccum :: (Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
||||||
preMapAccum step x extract = Mealy mempty $ \_nt -> preMapAccum' step x extract
|
preMapAccum step x extract = Mealy mempty emptyDiagram $ \_nt -> preMapAccum' step x extract
|
||||||
|
|
||||||
mapAccum' :: forall m x a b. (Monad m, Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
mapAccum' :: forall m x a b. (Monad m, Eq x, Serialize x) => (x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
||||||
mapAccum' f x extract = Stateful (Codec get put) (pure x) (\s _req a -> pure $ step s a)
|
mapAccum' f x extract = Stateful (Codec get put) (pure x) (\s _req a -> pure $ step s a)
|
||||||
@@ -381,7 +383,7 @@ mapAccum' f x extract = Stateful (Codec get put) (pure x) (\s _req a -> pure $ s
|
|||||||
step s a = let s' = f (state s) a in (extract s', State s' (dirty s || state s /= s'))
|
step s a = let s' = f (state s) a in (extract s', State s' (dirty s || state s /= s'))
|
||||||
|
|
||||||
preMapAccumRequest :: (Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
preMapAccumRequest :: (Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
||||||
preMapAccumRequest step x extract = Mealy mempty $ \_ -> preMapAccumRequest' step x extract
|
preMapAccumRequest step x extract = Mealy mempty emptyDiagram $ \_ -> preMapAccumRequest' step x extract
|
||||||
|
|
||||||
preMapAccumRequest' :: forall m x a b. (Serialize x, Eq x, Monad m) => (Request -> x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
preMapAccumRequest' :: forall m x a b. (Serialize x, Eq x, Monad m) => (Request -> x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
||||||
preMapAccumRequest' f x extract = Stateful (Codec get put) (State x False) (\s req a -> pure $ step s req a)
|
preMapAccumRequest' f x extract = Stateful (Codec get put) (State x False) (\s req a -> pure $ step s req a)
|
||||||
@@ -390,7 +392,7 @@ preMapAccumRequest' f x extract = Stateful (Codec get put) (State x False) (\s r
|
|||||||
step s req a = let s' = f req (state s) a in (extract (state s), State s' (dirty s || state s /= s'))
|
step s req a = let s' = f req (state s) a in (extract (state s), State s' (dirty s || state s /= s'))
|
||||||
|
|
||||||
mapAccumRequest :: (Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
mapAccumRequest :: (Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Mealy eff a b
|
||||||
mapAccumRequest step x extract = Mealy mempty $ \_ -> mapAccumRequest' step x extract
|
mapAccumRequest step x extract = Mealy mempty emptyDiagram $ \_ -> mapAccumRequest' step x extract
|
||||||
|
|
||||||
mapAccumRequest' :: forall m x a b. (Monad m, Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
mapAccumRequest' :: forall m x a b. (Monad m, Serialize x, Eq x) => (Request -> x -> a -> x) -> x -> (x -> b) -> Auto m a b
|
||||||
mapAccumRequest' f x extract = Stateful (Codec get put) (State x False) (\s req a -> pure $ step s req a)
|
mapAccumRequest' f x extract = Stateful (Codec get put) (State x False) (\s req a -> pure $ step s req a)
|
||||||
@@ -606,7 +608,7 @@ requestLocalTime :: Request -> LocalTime
|
|||||||
requestLocalTime Request{requestTime, requestTimeZone} = utcToLocalTime requestTimeZone requestTime
|
requestLocalTime Request{requestTime, requestTimeZone} = utcToLocalTime requestTimeZone requestTime
|
||||||
|
|
||||||
currentTime :: Mealy eff a LocalTime
|
currentTime :: Mealy eff a LocalTime
|
||||||
currentTime = Mealy mempty $ \_nt -> Fun $ \req _ ->
|
currentTime = Mealy mempty emptyDiagram $ \_nt -> Fun $ \req _ ->
|
||||||
requestLocalTime req
|
requestLocalTime req
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
module AFRP.Diagram where
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Text.Printf as Text
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
-- For rendering the arrows
|
||||||
|
data DiagramF f
|
||||||
|
= Empty
|
||||||
|
| Step Text f -- Named step
|
||||||
|
| Then f f -- >>>
|
||||||
|
| Both f f -- &&&
|
||||||
|
| Branch f f -- |||
|
||||||
|
| Merge f f -- <>
|
||||||
|
| Compose f f -- <*>
|
||||||
|
|
||||||
|
type DiagramAlgebra a = DiagramF a -> a
|
||||||
|
type DiagramC= forall a. DiagramAlgebra a -> a
|
||||||
|
|
||||||
|
combineDiagram :: (forall a. a -> a -> DiagramF a) -> DiagramC -> DiagramC -> DiagramC
|
||||||
|
combineDiagram constructor a b r = r (constructor (a r) (b r))
|
||||||
|
|
||||||
|
emptyDiagram :: DiagramC
|
||||||
|
emptyDiagram r = r Empty
|
||||||
|
|
||||||
|
|
||||||
|
renderF :: DiagramF (Maybe String) -> Maybe String
|
||||||
|
renderF = \case
|
||||||
|
Empty -> Nothing
|
||||||
|
Step n x -> Just $ T.unpack n <> maybe "" (Text.printf "( %s )") x
|
||||||
|
Then (Just a) (Just b) -> Just $ a <> " -> " <> b
|
||||||
|
Then (Just a) _ -> Just a
|
||||||
|
Then _ b -> b
|
||||||
|
Both (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
||||||
|
Both (Just a) _ -> Just a
|
||||||
|
Both _ b -> b
|
||||||
|
Branch (Just a) (Just b) -> Just $ Text.printf "(%s || %s)" a b
|
||||||
|
Branch (Just a) Nothing -> Just $ Text.printf "(%s || <skip>)" a
|
||||||
|
Branch _ b -> b
|
||||||
|
Merge (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
||||||
|
Merge (Just a) _ -> Just a
|
||||||
|
Merge _ b -> b
|
||||||
|
Compose (Just a) (Just b) -> Just $ Text.printf "(%s, %s)" a b
|
||||||
|
Compose (Just a) _ -> Just a
|
||||||
|
Compose _ b -> b
|
||||||
@@ -69,12 +69,12 @@ bedroomPresenceController = proc x -> do
|
|||||||
_ <- AFRP.changes >>> traceEvent -< snap
|
_ <- AFRP.changes >>> traceEvent -< snap
|
||||||
case p of
|
case p of
|
||||||
Event LightingOff -> do
|
Event LightingOff -> do
|
||||||
callService (light bedroomLightTargets Off) -< ()
|
callService "Turn off lights" (light bedroomLightTargets Off) -< ()
|
||||||
Event LightingOn -> do
|
Event LightingOn -> do
|
||||||
callServicesDyn (\(c,xs) -> concatMap (uncurry (toLight c)) xs) -< (capped, M.toList (getSnapshot snap))
|
callServicesDyn "Turn on lights (capped)" (\(c,xs) -> concatMap (uncurry (toLight c)) xs) -< (capped, M.toList (getSnapshot snap))
|
||||||
returnA -< ()
|
returnA -< ()
|
||||||
Event LightingSleep -> do
|
Event LightingSleep -> do
|
||||||
callService (activateScene "scene.makuuhuone_lepotila") -< ()
|
callService "Turn on sleep" (activateScene "scene.makuuhuone_lepotila") -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
toLight :: Bool -> T.Text -> LightObservation -> [Service]
|
toLight :: Bool -> T.Text -> LightObservation -> [Service]
|
||||||
@@ -110,21 +110,21 @@ bedroomButtonController = proc x -> do
|
|||||||
-- Any event will cause the sleep mode to be turned off
|
-- Any event will cause the sleep mode to be turned off
|
||||||
case ev of
|
case ev of
|
||||||
Event (Masse (OnButton ShortRelease)) -> do
|
Event (Masse (OnButton ShortRelease)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_masse") -< ()
|
callService "scene.makuuhuone_masse" (activateScene "scene.makuuhuone_masse") -< ()
|
||||||
Event (Masse (OnButton DoubleClick)) -> do
|
Event (Masse (OnButton DoubleClick)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_keski") -< ()
|
callService "scene.makuuhuone_keski" (activateScene "scene.makuuhuone_keski") -< ()
|
||||||
Event (Masse (OnButton LongClick)) -> do
|
Event (Masse (OnButton LongClick)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_kirkas") -< ()
|
callService "scene.makuuhuone_kirkas" (activateScene "scene.makuuhuone_kirkas") -< ()
|
||||||
Event (Masse (OffButton _)) -> do
|
Event (Masse (OffButton _)) -> do
|
||||||
callService (light [AreaId "makuuhuone"] Off) -< ()
|
callService "Lights off" (light [AreaId "makuuhuone"] Off) -< ()
|
||||||
Event (Enishen (OnButton ShortRelease)) -> do
|
Event (Enishen (OnButton ShortRelease)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_jemina") -< ()
|
callService "scene.makuuhuone_jemina" (activateScene "scene.makuuhuone_jemina") -< ()
|
||||||
Event (Enishen (OnButton DoubleClick)) -> do
|
Event (Enishen (OnButton DoubleClick)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_keski") -< ()
|
callService "scene.makuuhuone_keski" (activateScene "scene.makuuhuone_keski") -< ()
|
||||||
Event (Enishen (OnButton LongClick)) -> do
|
Event (Enishen (OnButton LongClick)) -> do
|
||||||
callService (activateScene "scene.makuuhuone_kirkas") -< ()
|
callService "scene.makuuhuone_kirkas" (activateScene "scene.makuuhuone_kirkas") -< ()
|
||||||
Event (Enishen (OffButton _)) -> do
|
Event (Enishen (OffButton _)) -> do
|
||||||
callService (light [AreaId "makuuhuone"] Off) -< ()
|
callService "Lights off" (light [AreaId "makuuhuone"] Off) -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
|
|
||||||
|
|
||||||
@@ -137,8 +137,8 @@ bedroomDrawerController :: HASS (Event Value) ()
|
|||||||
bedroomDrawerController = proc x -> do
|
bedroomDrawerController = proc x -> do
|
||||||
st <- drawer >>> traceEvent -< x
|
st <- drawer >>> traceEvent -< x
|
||||||
case st of
|
case st of
|
||||||
Event Open -> callService (switchService [entity] True) -< ()
|
Event Open -> callService "drawer light on" (switchService [entity] True) -< ()
|
||||||
Event Closed -> callService (switchService [entity] False) -< ()
|
Event Closed -> callService "drawer light off" (switchService [entity] False) -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
entity = EntityId "switch.bedroom_drawer_light_masse"
|
entity = EntityId "switch.bedroom_drawer_light_masse"
|
||||||
@@ -163,8 +163,8 @@ humidifierController = proc x -> do
|
|||||||
case st of
|
case st of
|
||||||
-- Turn the humidifier on when the door is closed
|
-- Turn the humidifier on when the door is closed
|
||||||
-- the humidifier is low-powered, no point in losing all the humidity
|
-- the humidifier is low-powered, no point in losing all the humidity
|
||||||
Event Open -> callService (humidifier False) -< ()
|
Event Open -> callService "Humidifier off" (humidifier False) -< ()
|
||||||
Event Closed -> callService (humidifier True) -< ()
|
Event Closed -> callService "Humidifier on" (humidifier True) -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
humidifier state = Service
|
humidifier state = Service
|
||||||
|
|||||||
@@ -85,11 +85,11 @@ schoolLightController = lightsOn <> lightsOff
|
|||||||
|
|
||||||
lightsOn :: HASS a ()
|
lightsOn :: HASS a ()
|
||||||
lightsOn = foldMap atTime timersOn
|
lightsOn = foldMap atTime timersOn
|
||||||
>>> AFRP.onEvent (callService (light [AreaId "lasten_makuuhuone"] (On def{lightBrightness=Just $ BrightnessPercent 100} )))
|
>>> AFRP.onEvent (callService "Lights on" (light [AreaId "lasten_makuuhuone"] (On def{lightBrightness=Just $ BrightnessPercent 100} )))
|
||||||
|
|
||||||
lightsOff :: HASS a ()
|
lightsOff :: HASS a ()
|
||||||
lightsOff = foldMap atTime timersOff
|
lightsOff = foldMap atTime timersOff
|
||||||
>>> AFRP.onEvent (callService (light [AreaId "lasten_makuuhuone"] Off))
|
>>> AFRP.onEvent (callService "Lights off" (light [AreaId "lasten_makuuhuone"] Off))
|
||||||
|
|
||||||
|
|
||||||
childrenBedroomButton :: HASS (Event Value) (Event (IkeaButton IkeaGesture))
|
childrenBedroomButton :: HASS (Event Value) (Event (IkeaButton IkeaGesture))
|
||||||
@@ -99,10 +99,10 @@ childrenBedroomButtonController :: HASS (Event Value) ()
|
|||||||
childrenBedroomButtonController = proc x -> do
|
childrenBedroomButtonController = proc x -> do
|
||||||
ev <- childrenBedroomButton >>> traceEvent -< x
|
ev <- childrenBedroomButton >>> traceEvent -< x
|
||||||
case ev of
|
case ev of
|
||||||
Event (OnButton ShortRelease) -> callService (activateScene "scene.lasten_nukkuvalmistelu") -< ()
|
Event (OnButton ShortRelease) -> callService "scene.lasten_nukkuvalmistelu" (activateScene "scene.lasten_nukkuvalmistelu") -< ()
|
||||||
Event (OnButton DoubleClick) -> callService (activateScene "scene.lasten_luku") -< ()
|
Event (OnButton DoubleClick) -> callService "scene.lasten_luku" (activateScene "scene.lasten_luku") -< ()
|
||||||
Event (OnButton LongClick) -> callService (activateScene "scene.lasten_kirkas") -< ()
|
Event (OnButton LongClick) -> callService "scene.lasten_kirkas" (activateScene "scene.lasten_kirkas") -< ()
|
||||||
Event (OffButton _) -> callService (light [AreaId "lasten_makuuhuone"] Off) -< ()
|
Event (OffButton _) -> callService "Lights off" (light [AreaId "lasten_makuuhuone"] Off) -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
|
|
||||||
newtype PM25 = PM25 Float
|
newtype PM25 = PM25 Float
|
||||||
@@ -153,8 +153,8 @@ childrenAirFilterController = proc ev -> do
|
|||||||
humidifier <- humidifierOperatingState >>> AFRP.holdKnown >>> arr join -< ev
|
humidifier <- humidifierOperatingState >>> AFRP.holdKnown >>> arr join -< ev
|
||||||
case level of
|
case level of
|
||||||
Event PM25High ->
|
Event PM25High ->
|
||||||
AFRP.whenA (== Just Idle) (callService (fanOnTo (Percentage 80))) -< humidifier
|
AFRP.whenA (== Just Idle) (callService "Fan strong" (fanOnTo (Percentage 80))) -< humidifier
|
||||||
Event PM25Low -> callService (fanOnTo (Percentage 20)) -< ()
|
Event PM25Low -> callService "Fan low" (fanOnTo (Percentage 20)) -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
fanOnTo percentage = Service
|
fanOnTo percentage = Service
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{-# LANGUAGE Arrows #-}
|
{-# LANGUAGE Arrows #-}
|
||||||
{-# LANGUAGE GADTs #-}
|
{-# LANGUAGE GADTs #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module HomeAssistant.Controller.Effect
|
module HomeAssistant.Controller.Effect
|
||||||
( HASSEff(..)
|
( HASSEff(..)
|
||||||
@@ -13,9 +14,10 @@ module HomeAssistant.Controller.Effect
|
|||||||
, traceValue
|
, traceValue
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import AFRP (Mealy, eff, Event (..), Request)
|
import AFRP (Mealy, eff, Event (..), Request, named)
|
||||||
import Control.Arrow (returnA)
|
import Control.Arrow (returnA)
|
||||||
import HomeAssistant.Controller.Service (Service)
|
import HomeAssistant.Controller.Service (Service (..))
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
data HASSEff a where
|
data HASSEff a where
|
||||||
CallService :: Request -> Service -> HASSEff ()
|
CallService :: Request -> Service -> HASSEff ()
|
||||||
@@ -25,17 +27,20 @@ data HASSEff a where
|
|||||||
|
|
||||||
type HASS a b = Mealy HASSEff a b
|
type HASS a b = Mealy HASSEff a b
|
||||||
|
|
||||||
callService :: Service -> HASS a ()
|
serviceLabel :: T.Text -> HASS a b -> HASS a b
|
||||||
callService service = eff (\req _ -> CallService req service)
|
serviceLabel lbl = named ("Service: " <> lbl)
|
||||||
|
|
||||||
callServices :: [Service] -> HASS a ()
|
callService :: T.Text -> Service -> HASS a ()
|
||||||
callServices service = eff (\req _ -> CallServices req service)
|
callService lbl service = serviceLabel lbl $ eff (\req _ -> CallService req service)
|
||||||
|
|
||||||
callServiceDyn :: (a -> Service) -> HASS a ()
|
callServices :: T.Text -> [Service] -> HASS a ()
|
||||||
callServiceDyn mkService = eff (\req a -> CallService req (mkService a))
|
callServices lbl service = serviceLabel lbl $ eff (\req _ -> CallServices req service)
|
||||||
|
|
||||||
callServicesDyn :: (a -> [Service]) -> HASS a ()
|
callServiceDyn :: T.Text -> (a -> Service) -> HASS a ()
|
||||||
callServicesDyn mkService = eff (\req a -> CallServices req (mkService a))
|
callServiceDyn lbl mkService = serviceLabel lbl $ eff (\req a -> CallService req (mkService a))
|
||||||
|
|
||||||
|
callServicesDyn :: T.Text -> (a -> [Service]) -> HASS a ()
|
||||||
|
callServicesDyn lbl mkService = serviceLabel lbl $ eff (\req a -> CallServices req (mkService a))
|
||||||
|
|
||||||
debug :: Show a => HASS a a
|
debug :: Show a => HASS a a
|
||||||
debug = proc x -> do
|
debug = proc x -> do
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ module HomeAssistant.Controller.Entity
|
|||||||
, entityBool'
|
, entityBool'
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import AFRP (Mealy (..), Event, events, filterA, (>>|), toEvent)
|
import AFRP (Mealy (..), Event, events, filterA, (>>|), toEvent, named)
|
||||||
import Control.Arrow (arr)
|
import Control.Arrow (arr)
|
||||||
import Control.Category ((>>>))
|
import Control.Category ((>>>))
|
||||||
import Data.Aeson (Value)
|
import Data.Aeson (Value)
|
||||||
@@ -19,12 +19,13 @@ import qualified Data.Text as T
|
|||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
import Control.Lens (has, only, (^?), to)
|
import Control.Lens (has, only, (^?), to)
|
||||||
import qualified Data.Text.Lens as TL
|
import qualified Data.Text.Lens as TL
|
||||||
|
import AFRP.Diagram (emptyDiagram)
|
||||||
|
|
||||||
entityChangeEvent :: T.Text -> Mealy eff (Event Value) (Event Value)
|
entityChangeEvent :: T.Text -> Mealy eff (Event Value) (Event Value)
|
||||||
entityChangeEvent entityId = entityChangeEvent' entityId >>> toEvent
|
entityChangeEvent entityId = entityChangeEvent' entityId >>> toEvent
|
||||||
|
|
||||||
entityChangeEvent' :: T.Text -> Mealy eff (Event Value) (Either () Value)
|
entityChangeEvent' :: T.Text -> Mealy eff (Event Value) (Either () Value)
|
||||||
entityChangeEvent' entityId = Mealy (S.singleton entityId) $ runMealy (events >>| filterA isEntity)
|
entityChangeEvent' entityId = named entityId $ Mealy (S.singleton entityId) emptyDiagram $ runMealy (events >>| filterA isEntity)
|
||||||
where
|
where
|
||||||
isEntity :: Value -> Bool
|
isEntity :: Value -> Bool
|
||||||
isEntity = has (key "event" . key "variables" . key "trigger" . key "entity_id" . _String . only entityId)
|
isEntity = has (key "event" . key "variables" . key "trigger" . key "entity_id" . _String . only entityId)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ hallwayLightsController = proc x -> do
|
|||||||
traceEvent -< ev
|
traceEvent -< ev
|
||||||
case ev of
|
case ev of
|
||||||
-- The lights entity is a "group of lights" in hass
|
-- The lights entity is a "group of lights" in hass
|
||||||
Event LightingOn | lightsAllowed now -> callServiceDyn (light [EntityId "light.hallway_lights"]) -< On def
|
Event LightingOn | lightsAllowed now -> callServiceDyn "Lights on" (light [EntityId "light.hallway_lights"]) -< On def
|
||||||
Event LightingOff -> callServiceDyn (light [EntityId "light.hallway_lights"]) -< Off
|
Event LightingOff -> callServiceDyn "Lights off" (light [EntityId "light.hallway_lights"]) -< Off
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
lightsAllowed (LocalTime _ tod) = not (tod > TimeOfDay 1 45 0 && tod < TimeOfDay 5 0 0)
|
lightsAllowed (LocalTime _ tod) = not (tod > TimeOfDay 1 45 0 && tod < TimeOfDay 5 0 0)
|
||||||
@@ -23,8 +23,8 @@ kitchenCeilingController = proc x -> do
|
|||||||
ev <- lightingModeEvents -< p
|
ev <- lightingModeEvents -< p
|
||||||
traceEvent -< ev
|
traceEvent -< ev
|
||||||
case ev of
|
case ev of
|
||||||
Event LightingOn | lightsAllowed now -> callServiceDyn (light [EntityId "light.kitchen_ceiling"]) -< On def
|
Event LightingOn | lightsAllowed now -> callServiceDyn "light.kitchen_ceiling on" (light [EntityId "light.kitchen_ceiling"]) -< On def
|
||||||
Event LightingOff -> callServiceDyn (light [EntityId "light.kitchen_ceiling"]) -< Off
|
Event LightingOff -> callServiceDyn "light.kitchen_ceiling off" (light [EntityId "light.kitchen_ceiling"]) -< Off
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
lightsAllowed (LocalTime _ tod) = not (tod > TimeOfDay 1 45 0 && tod < TimeOfDay 5 0 0)
|
lightsAllowed (LocalTime _ tod) = not (tod > TimeOfDay 1 45 0 && tod < TimeOfDay 5 0 0)
|
||||||
@@ -64,16 +64,16 @@ dinnerTableController = proc x -> do
|
|||||||
ev <- dinnerLightEvents -< x
|
ev <- dinnerLightEvents -< x
|
||||||
case ev of
|
case ev of
|
||||||
Event LightingOn -> do
|
Event LightingOn -> do
|
||||||
sleep -< False
|
sleep "sleep off"-< False
|
||||||
callServiceDyn (light [EntityId "light.kitchen_dining_large"]) -< On def
|
callServiceDyn "Dining light on" (light [EntityId "light.kitchen_dining_large"]) -< On def
|
||||||
Event LightingOff -> do
|
Event LightingOff -> do
|
||||||
sleep -< False
|
sleep "sleep off" -< False
|
||||||
callServiceDyn (light [EntityId "light.kitchen_dining_large"]) -< Off
|
callServiceDyn "Dining light off" (light [EntityId "light.kitchen_dining_large"]) -< Off
|
||||||
Event LightingSleep -> do
|
Event LightingSleep -> do
|
||||||
sleep -< True
|
sleep "sleep on" -< True
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
where
|
where
|
||||||
sleep = callServiceDyn (switchService [EntityId "switch.adaptive_lighting_ruokapoydan_valot_sleep_mode"])
|
sleep lbl = callServiceDyn lbl (switchService [EntityId "switch.adaptive_lighting_ruokapoydan_valot_sleep_mode"])
|
||||||
|
|
||||||
kitchenMotionController :: HASS (Event Value) ()
|
kitchenMotionController :: HASS (Event Value) ()
|
||||||
kitchenMotionController = kitchenCeilingController <> dinnerTableController
|
kitchenMotionController = kitchenCeilingController <> dinnerTableController
|
||||||
@@ -39,8 +39,8 @@ livingroomPresenceController = proc x -> do
|
|||||||
ev <- lightingModeEvents . livingroomPresence -< x
|
ev <- lightingModeEvents . livingroomPresence -< x
|
||||||
traceEvent -< ev
|
traceEvent -< ev
|
||||||
case ev of
|
case ev of
|
||||||
AFRP.Event LightingOn -> callServiceDyn (light livingroomLightTargets) -< On def
|
AFRP.Event LightingOn -> callServiceDyn "Lights on" (light livingroomLightTargets) -< On def
|
||||||
AFRP.Event LightingOff -> callServiceDyn (light livingroomLightTargets) -< Off
|
AFRP.Event LightingOff -> callServiceDyn "Lights off" (light livingroomLightTargets) -< Off
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
returnA -< ()
|
returnA -< ()
|
||||||
|
|
||||||
@@ -88,6 +88,6 @@ livingroomPlantLights = (season &&& tod)
|
|||||||
where
|
where
|
||||||
setLights = proc mode -> do
|
setLights = proc mode -> do
|
||||||
case mode of
|
case mode of
|
||||||
AFRP.Event Growth -> callService (activateScene "scene.olohuoneen_kasvivalo") -< ()
|
AFRP.Event Growth -> callService "scene.olohuoneen_kasvivalo" (activateScene "scene.olohuoneen_kasvivalo") -< ()
|
||||||
AFRP.Event Ambience -> callService (activateScene "scene.kasvivalo_iltatila") -< ()
|
AFRP.Event Ambience -> callService "scene.kasvivalo_iltatila" (activateScene "scene.kasvivalo_iltatila") -< ()
|
||||||
_ -> returnA -< ()
|
_ -> returnA -< ()
|
||||||
Reference in new issue
Block a user