reactive-banana-automation 0.3.0 → 0.4.0
raw patch · 4 files changed
+122/−104 lines, 4 filesdep +transformersPVP ok
version bump matches the API change (PVP)
Dependencies added: transformers
API changes (from Hackage documentation)
- Reactive.Banana.Automation: Automation :: (sensors -> (actuators -> IO ()) -> MomentAutomation ()) -> Automation sensors actuators
- Reactive.Banana.Automation: data MomentAutomation a
- Reactive.Banana.Automation: instance Control.Monad.Fix.MonadFix Reactive.Banana.Automation.MomentAutomation
- Reactive.Banana.Automation: instance Data.Semigroup.Semigroup (Reactive.Banana.Automation.Automation sensors actuators)
- Reactive.Banana.Automation: instance GHC.Base.Applicative Reactive.Banana.Automation.MomentAutomation
- Reactive.Banana.Automation: instance GHC.Base.Functor Reactive.Banana.Automation.MomentAutomation
- Reactive.Banana.Automation: instance GHC.Base.Monad Reactive.Banana.Automation.MomentAutomation
- Reactive.Banana.Automation: instance GHC.Base.Monoid (Reactive.Banana.Automation.Automation sensors actuators)
- Reactive.Banana.Automation: instance Reactive.Banana.Types.MonadMoment Reactive.Banana.Automation.MomentAutomation
- Reactive.Banana.Automation: newtype Automation sensors actuators
- Reactive.Banana.Automation: onBehaviorChange :: Behavior a -> (a -> IO ()) -> MomentAutomation ()
- Reactive.Banana.Automation: onBehaviorChangeMaybe :: Behavior (Maybe a) -> (a -> IO ()) -> MomentAutomation ()
+ Reactive.Banana.Automation: actuateBehavior :: Behavior a -> (a -> actuators) -> Automation sensors actuators ()
+ Reactive.Banana.Automation: actuateBehaviorMaybe :: Behavior (Maybe a) -> (a -> actuators) -> Automation sensors actuators ()
+ Reactive.Banana.Automation: data Automation sensors actuators a
+ Reactive.Banana.Automation: instance Control.Monad.Fix.MonadFix (Reactive.Banana.Automation.Automation sensors actuators)
+ Reactive.Banana.Automation: instance Data.Semigroup.Semigroup (Reactive.Banana.Automation.Automation sensors actuators ())
+ Reactive.Banana.Automation: instance GHC.Base.Applicative (Reactive.Banana.Automation.Automation sensors actuators)
+ Reactive.Banana.Automation: instance GHC.Base.Functor (Reactive.Banana.Automation.Automation sensors actuators)
+ Reactive.Banana.Automation: instance GHC.Base.Monad (Reactive.Banana.Automation.Automation sensors actuators)
+ Reactive.Banana.Automation: instance GHC.Base.Monoid (Reactive.Banana.Automation.Automation sensors actuators ())
+ Reactive.Banana.Automation: instance GHC.Classes.Eq a => GHC.Classes.Eq (Reactive.Banana.Automation.Sensed a)
+ Reactive.Banana.Automation: instance GHC.Classes.Ord a => GHC.Classes.Ord (Reactive.Banana.Automation.Sensed a)
+ Reactive.Banana.Automation: instance Reactive.Banana.Types.MonadMoment (Reactive.Banana.Automation.Automation sensors actuators)
- Reactive.Banana.Automation: automationStepper :: a -> Event a -> MomentAutomation (Behavior a)
+ Reactive.Banana.Automation: automationStepper :: a -> Event a -> Automation sensors actuators (Behavior a)
- Reactive.Banana.Automation: clockSignalBehavior :: Timestamp t => EventSource (ClockSignal t) v -> MomentAutomation (Behavior (Maybe (ClockSignal t)))
+ Reactive.Banana.Automation: clockSignalBehavior :: Timestamp t => (sensors -> EventSource (ClockSignal t) v) -> Automation sensors actuators (Behavior (Maybe (ClockSignal t)))
- Reactive.Banana.Automation: elapsedTimeSince :: (Num t, Timestamp t) => (a -> Bool) -> Event (Timestamped t a) -> MomentAutomation (Event t)
+ Reactive.Banana.Automation: elapsedTimeSince :: (Num t, Timestamp t) => (a -> Bool) -> Event (Timestamped t a) -> Automation sensors actuators (Event t)
- Reactive.Banana.Automation: getEventFrom :: EventSource a v -> MomentAutomation (Event a)
+ Reactive.Banana.Automation: getEventFrom :: (sensors -> EventSource a v) -> Automation sensors actuators (Event a)
- Reactive.Banana.Automation: observeAutomation :: Automation sensors actuators -> IO sensors -> IO ((sensors -> IO ()) -> IO [actuators])
+ Reactive.Banana.Automation: observeAutomation :: Automation sensors actuators () -> IO sensors -> IO ((sensors -> IO ()) -> IO [actuators])
- Reactive.Banana.Automation: onEvent :: Event a -> (a -> IO ()) -> MomentAutomation ()
+ Reactive.Banana.Automation: onEvent :: Event a -> (a -> IO ()) -> Automation sensors actuators ()
- Reactive.Banana.Automation: runAutomation :: Automation sensors actuators -> IO sensors -> (actuators -> IO ()) -> (sensors -> IO ()) -> IO ()
+ Reactive.Banana.Automation: runAutomation :: Automation sensors actuators () -> IO sensors -> (actuators -> IO ()) -> (sensors -> IO ()) -> IO ()
- Reactive.Banana.Automation: sensedBehavior :: EventSource (Sensed a) v -> MomentAutomation (Behavior (Sensed a))
+ Reactive.Banana.Automation: sensedBehavior :: (sensors -> EventSource (Sensed a) v) -> Automation sensors actuators (Behavior (Sensed a))
- Reactive.Banana.Automation: sensedEvent :: EventSource (Sensed a) v -> MomentAutomation (Event a)
+ Reactive.Banana.Automation: sensedEvent :: (sensors -> EventSource (Sensed a) v) -> Automation sensors actuators (Event a)
- Reactive.Banana.Automation: sensedEventBehavior :: Event (Sensed a) -> MomentAutomation (Behavior (Sensed a))
+ Reactive.Banana.Automation: sensedEventBehavior :: Event (Sensed a) -> Automation sensors actuators (Behavior (Sensed a))
- Reactive.Banana.Automation.Examples: fridge :: Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: fridge :: Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: motionActivatedLight :: Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: motionActivatedLight :: Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: nightLight :: Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: nightLight :: Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: showBehaviorLCDDisplay :: (a -> String) -> (Sensors -> MomentAutomation (Behavior a)) -> Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: showBehaviorLCDDisplay :: (a -> String) -> Automation Sensors Actuators (Behavior a) -> Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: sprinklersStartingAt :: TimeOfDay -> Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: sprinklersStartingAt :: TimeOfDay -> Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: thisHouse :: Automation Sensors Actuators
+ Reactive.Banana.Automation.Examples: thisHouse :: Automation Sensors Actuators ()
- Reactive.Banana.Automation.Examples: totalRainfall :: Sensors -> MomentAutomation (Behavior Integer)
+ Reactive.Banana.Automation.Examples: totalRainfall :: Automation Sensors Actuators (Behavior Integer)
- Reactive.Banana.Automation.Examples: totalRainfallSince :: TimeOfDay -> Sensors -> MomentAutomation (Behavior (Timestamped (ClockSignal LocalTime) Integer))
+ Reactive.Banana.Automation.Examples: totalRainfallSince :: TimeOfDay -> Automation Sensors Actuators (Behavior (Timestamped (ClockSignal LocalTime) Integer))
Files
- CHANGELOG +9/−0
- Reactive/Banana/Automation.hs +81/−73
- Reactive/Banana/Automation/Examples.hs +29/−29
- reactive-banana-automation.cabal +3/−2
CHANGELOG view
@@ -1,3 +1,12 @@+reactive-banana-automation (0.4.0) upstream; urgency=medium++ * Simplified using ReaderT, so sensors and actuators do not need to be+ manually passed around. (API change)+ * Add Ord and Eq instances for Sensed.+ * Renamed onBehaviorChange to actuateBehavior.++ -- Joey Hess <id@joeyh.name> Wed, 09 May 2018 12:12:08 -0400+ reactive-banana-automation (0.3.0) upstream; urgency=medium * EventSource is now a data type with an extra value for expansion.
Reactive/Banana/Automation.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, LambdaCase, DeriveFunctor #-}+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, LambdaCase, DeriveFunctor #-} -- | Home (etc) automation using reactive-banana. --@@ -17,8 +17,7 @@ -- of using this library. module Reactive.Banana.Automation ( -- * Framework- Automation(..),- MomentAutomation,+ Automation, runAutomation, observeAutomation, -- * Events@@ -49,8 +48,8 @@ clockSignalBehavior, -- * Actuators PowerChange(..),- onBehaviorChange,- onBehaviorChangeMaybe,+ actuateBehavior,+ actuateBehaviorMaybe, -- * Ranges Range(..), belowRange,@@ -63,6 +62,8 @@ import Reactive.Banana.Frameworks import Data.Semigroup as Sem import Control.Monad.Fix+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Class import Control.Concurrent.STM import Data.Time.Clock import Data.Time.Clock.POSIX@@ -80,11 +81,11 @@ -- > data Sensors = Sensors { fridgeTemperature :: EventSource (Sensed Double) () } -- > data Actuators = FridgePower PowerChange deriving (Show) -- > --- > fridge :: Automation Sensors Actuators--- > fridge = Automation $ \sensors actuators -> do--- > btemperature <- sensedBehavior (fridgeTemperature sensors)+-- > fridge :: Automation Sensors Actuators ()+-- > fridge = do+-- > btemperature <- sensedBehavior fridgeTemperature -- > let bpowerchange = calcpowerchange <$> btemperature--- > onBehaviorChange bpowerchange (actuators . FridgePower)+-- > actuateBehavior bpowerchange FridgePower -- > where -- > calcpowerchange (Sensed temp) -- > | temp `belowRange` allowedtemp = Just PowerOff@@ -92,47 +93,41 @@ -- > | otherwise = Nothing -- > calcpowerchange SensorUnavailable = Nothing -- > allowedtemp = Range 1 4-newtype Automation sensors actuators = Automation (sensors -> (actuators -> IO ()) -> MomentAutomation ())+-- +-- Automation is a wrapper around reactive-banana's `MomentIO`,+-- but without the `MonadIO` instance, so this monad+-- is limited to using its sensors and actuators for IO. That allows+-- it to be fully tested using `observeAutomation`.+newtype Automation sensors actuators a = Automation+ { unAutomation :: ReaderT (sensors, actuators -> IO ()) MomentIO a } -instance Sem.Semigroup (Automation sensors actuators) where- Automation a <> Automation b = Automation $ \sensors actuators -> do- a sensors actuators- b sensors actuators+instance Sem.Semigroup (Automation sensors actuators ()) where+ Automation a <> Automation b = Automation (a >> b) -instance Monoid (Automation sensors actuators) where- mempty = Automation $ \_sensors _actuators -> return ()+instance Monoid (Automation sensors actuators ()) where+ mempty = Automation (return ()) mappend = (Sem.<>) --- | This is simply a wrapper around reactive-banana's `MomentIO`,--- but without the `MonadIO` instance, so an `Automation` using this monad--- is limited to using its sensors and actuators for IO. That allows--- it to be fully tested using `observeAutomation`.------ All of "Reactive.Banana.Combinators" can be used with this monad.-newtype MomentAutomation a = MomentAutomation- { unMomentAutomation :: MomentIO a }--instance Functor MomentAutomation where- fmap f = MomentAutomation . fmap f . unMomentAutomation+instance Functor (Automation sensors actuators) where+ fmap f = Automation . fmap f . unAutomation -instance Monad MomentAutomation where- return = MomentAutomation . return- m >>= g = MomentAutomation $- unMomentAutomation m >>= unMomentAutomation . g-instance Applicative MomentAutomation where- pure = MomentAutomation . pure- f <*> a = MomentAutomation $- unMomentAutomation f <*> unMomentAutomation a-instance MonadFix MomentAutomation where- mfix f = MomentAutomation $ mfix (unMomentAutomation . f)+instance Monad (Automation sensors actuators) where+ return = Automation . return+ m >>= g = Automation $ unAutomation m >>= unAutomation . g+instance Applicative (Automation sensors actuators) where+ pure = Automation . pure+ f <*> a = Automation $ unAutomation f <*> unAutomation a+instance MonadFix (Automation sensors actuators) where+ mfix f = Automation $ mfix (unAutomation . f) -instance MonadMoment MomentAutomation where- liftMoment = MomentAutomation . liftMoment+-- | All of "Reactive.Banana.Combinators" can be used with this monad.+instance MonadMoment (Automation sensors actuators) where+ liftMoment = Automation . lift . liftMoment -setupAutomation :: Automation sensors actuators -> IO sensors -> (actuators -> IO ()) -> IO sensors-setupAutomation (Automation automation) mksensors actutators = do+setupAutomation :: Automation sensors actuators () -> IO sensors -> (actuators -> IO ()) -> IO sensors+setupAutomation automation mksensors actuators = do sensors <- mksensors- network <- compile $ unMomentAutomation $ automation sensors actutators+ network <- compile $ flip runReaderT (sensors, actuators) $ unAutomation automation actuate network return sensors @@ -156,7 +151,7 @@ -- -- Note that this function does not return; the sensor feeding action is -- run in a loop.-runAutomation :: Automation sensors actuators -> IO sensors -> (actuators -> IO ()) -> (sensors -> IO ()) -> IO ()+runAutomation :: Automation sensors actuators () -> IO sensors -> (actuators -> IO ()) -> (sensors -> IO ()) -> IO () runAutomation automation mksensors actuators poller = do sensors <- setupAutomation automation mksensors actuators mainloop sensors@@ -183,7 +178,7 @@ -- > [FridgeRelay PowerOff] -- -- Note that internal state is maintained between calls to the runner.-observeAutomation :: Automation sensors actuators -> IO sensors -> IO ((sensors -> IO ()) -> IO [actuators])+observeAutomation :: Automation sensors actuators () -> IO sensors -> IO ((sensors -> IO ()) -> IO [actuators]) observeAutomation automation mksensors = do tv <- newTVarIO [] lck <- newEmptyTMVarIO@@ -223,41 +218,43 @@ gotEvent = snd . getEventSource -- | Get an Event from an EventSource.-getEventFrom :: EventSource a v -> MomentAutomation (Event a)-getEventFrom = MomentAutomation . fromAddHandler . addHandler+getEventFrom :: (sensors -> EventSource a v) -> Automation sensors actuators (Event a)+getEventFrom getsensor = Automation $ do+ sensor <- getsensor . fst <$> ask+ lift $ fromAddHandler $ addHandler sensor -- | Runs an action when an event occurs.-onEvent :: Event a -> (a -> IO ()) -> MomentAutomation ()-onEvent e a = MomentAutomation . reactimate $ fmap a e+onEvent :: Event a -> (a -> IO ()) -> Automation sensors actuators ()+onEvent e a = Automation . lift . reactimate $ fmap a e -- | A value read from a sensor. -- -- Sensors are sometimes not available, or have not provided a value -- yet. data Sensed a = SensorUnavailable | Sensed a- deriving (Show, Functor)+ deriving (Show, Functor, Ord, Eq) -- | Create an Event from sensed values. -- -- The Event only contains values when the sensor provided a reading, -- not times when it was unavailable.-sensedEvent :: EventSource (Sensed a) v -> MomentAutomation (Event a)-sensedEvent s = do- e <- getEventFrom s+sensedEvent :: (sensors -> EventSource (Sensed a) v) -> Automation sensors actuators (Event a)+sensedEvent getsensor = do+ e <- getEventFrom getsensor return $ filterJust $ flip fmap e $ \case SensorUnavailable -> Nothing Sensed a -> Just a -- | Create a Behavior from sensed values.-sensedBehavior :: EventSource (Sensed a) v -> MomentAutomation (Behavior (Sensed a))-sensedBehavior s = sensedEventBehavior =<< getEventFrom s+sensedBehavior :: (sensors -> EventSource (Sensed a) v) -> Automation sensors actuators (Behavior (Sensed a))+sensedBehavior getsensor = sensedEventBehavior =<< getEventFrom getsensor -sensedEventBehavior :: Event (Sensed a) -> MomentAutomation (Behavior (Sensed a))+sensedEventBehavior :: Event (Sensed a) -> Automation sensors actuators (Behavior (Sensed a)) sensedEventBehavior = automationStepper SensorUnavailable --- | `stepper` lifted into `MomentAutomation`-automationStepper :: a -> Event a -> MomentAutomation (Behavior a)-automationStepper a e = MomentAutomation $ stepper a e+-- | `stepper` lifted into `Automation`+automationStepper :: a -> Event a -> Automation sensors actuators (Behavior a)+automationStepper a e = Automation $ lift $ stepper a e -- | Call when a sensor has sensed a value. --@@ -335,7 +332,7 @@ :: (Num t, Timestamp t) => (a -> Bool) -> Event (Timestamped t a)- -> MomentAutomation (Event t)+ -> Automation sensors actuators (Event t) elapsedTimeSince f event = fmap (fmap reduce) $ accumE Nothing $ go <$> event where go v' (Just (t, _v))@@ -368,27 +365,38 @@ -- | Create a Behavior from a ClockSignal. It will initially be Nothing, -- and then updates with each incoming clock signal.-clockSignalBehavior :: Timestamp t => EventSource (ClockSignal t) v -> MomentAutomation (Behavior (Maybe (ClockSignal t)))-clockSignalBehavior s = MomentAutomation . stepper Nothing - =<< fmap Just <$> getEventFrom s+clockSignalBehavior+ :: Timestamp t+ => (sensors -> EventSource (ClockSignal t) v)+ -> Automation sensors actuators (Behavior (Maybe (ClockSignal t)))+clockSignalBehavior getsensor = Automation $ do+ sensor <- getsensor . fst <$> ask+ e <- fmap Just <$> lift (fromAddHandler $ addHandler sensor)+ lift $ stepper Nothing e -- | For controlling relays and other things that can have -- their power turned on and off. data PowerChange = PowerOff | PowerOn deriving (Show) --- | Runs an action when a behavior's value changes.-onBehaviorChange :: Behavior a -> (a -> IO ()) -> MomentAutomation ()-onBehaviorChange b a = MomentAutomation $ do- c <- changes b- reactimate' $ fmap a <$> c+-- | Makes a Behavior drive an actuator. This will happen when the+-- Behavior's value changes, but possibly more often as well, depending on+-- how the Behavior is constructed.+actuateBehavior :: Behavior a -> (a -> actuators) -> Automation sensors actuators ()+actuateBehavior b getactuator = Automation $ do+ actuators <- snd <$> ask+ c <- lift $ changes b+ lift $ reactimate' $+ fmap (actuators . getactuator) <$> c --- | Variant of `onBehaviorChange` that does nothing when a behavior--- changes to Nothing.-onBehaviorChangeMaybe :: Behavior (Maybe a) -> (a -> IO ()) -> MomentAutomation ()-onBehaviorChangeMaybe b a = MomentAutomation $ do- c <- changes b- reactimate' $ fmap (maybe (return ()) a) <$> c+-- | Variant of `actuateBehavior` that does nothing when a behavior+-- is Nothing.+actuateBehaviorMaybe :: Behavior (Maybe a) -> (a -> actuators) -> Automation sensors actuators ()+actuateBehaviorMaybe b getactuator = Automation $ do+ actuators <- snd <$> ask+ c <- lift $ changes b+ lift $ reactimate' $+ fmap (maybe (return ()) (actuators . getactuator)) <$> c -- | The range between two values (inclusive). --
Reactive/Banana/Automation/Examples.hs view
@@ -57,14 +57,14 @@ -- [] -- >>> runner $ \sensors -> fridgeTemperature sensors =: 0.5 -- [FridgePower PowerOff]-fridge :: Automation Sensors Actuators-fridge = Automation $ \sensors actuators -> do+fridge :: Automation Sensors Actuators ()+fridge = do -- Create a Behavior that reflects the most recently reported -- temperature of the fridge.- btemperature <- sensedBehavior (fridgeTemperature sensors)+ btemperature <- sensedBehavior fridgeTemperature -- Calculate when the fridge should turn on and off. let bpowerchange = calcpowerchange <$> btemperature- onBehaviorChangeMaybe bpowerchange (actuators . FridgePower)+ actuateBehaviorMaybe bpowerchange FridgePower where calcpowerchange (Sensed temp) | temp `belowRange` allowedtemp = Just PowerOff@@ -93,15 +93,15 @@ -- [] -- >>> runner $ \sensors -> sensedAt 400 (motionSensor sensors) False -- [LightSwitch PowerOff]-motionActivatedLight :: Automation Sensors Actuators-motionActivatedLight = Automation $ \sensors actuators -> do+motionActivatedLight :: Automation Sensors Actuators ()+motionActivatedLight = do -- Make an Event that contains the time elapsed since the last -- detected motion. timesincemotion <- elapsedTimeSince (== True)- =<< sensedEvent (motionSensor sensors)+ =<< sensedEvent motionSensor -- Make a Behavior for the light switch. lightchange <- stepper Nothing $ calcchange <$> timesincemotion- onBehaviorChangeMaybe lightchange (actuators . LightSwitch)+ actuateBehaviorMaybe lightchange LightSwitch where calcchange t | t == 0 = Just PowerOn -- motion was just detected@@ -124,12 +124,12 @@ -- [LightSwitch PowerOn] -- >>> runner $ \sensors -> clockSignalAt (LocalTime day midday) (clock sensors) -- [LightSwitch PowerOff]-nightLight :: Automation Sensors Actuators-nightLight = Automation $ \sensors actuators -> do- bclock <- clockSignalBehavior (clock sensors)+nightLight :: Automation Sensors Actuators ()+nightLight = do+ bclock <- clockSignalBehavior clock let bhour = (fmap . fmap) (todHour . localTimeOfDay) <$> bclock let lightchange = calcchange <$> bhour- onBehaviorChangeMaybe lightchange (actuators . LightSwitch)+ actuateBehaviorMaybe lightchange LightSwitch where calcchange (Just (ClockSignal t)) | t > 18 = Just PowerOn@@ -141,10 +141,10 @@ -- -- While it could be used to drive a real LCD, this is mostly useful -- for testing behaviors.-showBehaviorLCDDisplay :: (a -> String) -> (Sensors -> MomentAutomation (Behavior a)) -> Automation Sensors Actuators-showBehaviorLCDDisplay fmt mkb = Automation $ \sensors actuators -> do- b <- mkb sensors- onBehaviorChange b (actuators . LCDDisplay . fmt)+showBehaviorLCDDisplay :: (a -> String) -> Automation Sensors Actuators (Behavior a) -> Automation Sensors Actuators ()+showBehaviorLCDDisplay fmt mkb = do+ b <- mkb+ actuateBehavior b (LCDDisplay . fmt) -- | The rain gauge sensor is a tipping bucket type; the bucket collects 0.01 -- inches of rain and then tips, which triggers the `rainGaugeTipSensor`.@@ -159,9 +159,9 @@ -- [LCDDisplay "2"] -- >>> runner $ \sensors -> sensed (rainGaugeTipSensor sensors) () -- [LCDDisplay "3"]-totalRainfall :: Sensors -> MomentAutomation (Behavior Integer)-totalRainfall sensors = do- tipevents <- sensedEvent (rainGaugeTipSensor sensors)+totalRainfall :: Automation Sensors Actuators (Behavior Integer)+totalRainfall = do+ tipevents <- sensedEvent rainGaugeTipSensor accumB 0 $ const succ <$> tipevents -- | This behavior contains the total rainfall since a specified `TimeOfDay`,@@ -188,11 +188,11 @@ -- [LCDDisplay "1"] -- >>> runner $ \sensors -> sensed (rainGaugeTipSensor sensors) () -- [LCDDisplay "2"]-totalRainfallSince :: TimeOfDay -> Sensors -> MomentAutomation (Behavior (Timestamped (ClockSignal LocalTime) Integer))-totalRainfallSince tod sensors = do- clockevents <- getEventFrom (clock sensors)- bclock <- clockSignalBehavior (clock sensors)- tipevents <- sensedEvent (rainGaugeTipSensor sensors)+totalRainfallSince :: TimeOfDay -> Automation Sensors Actuators (Behavior (Timestamped (ClockSignal LocalTime) Integer))+totalRainfallSince tod = do+ clockevents <- getEventFrom clock+ bclock <- clockSignalBehavior clock+ tipevents <- sensedEvent rainGaugeTipSensor -- The tip events, with the tip signal replaced with -- the clock time when it occurred. let tiptimes = bclock <@ tipevents@@ -240,14 +240,14 @@ -- [SprinklerSwitch PowerOff] -- >>> runner $ \sensors -> clockSignalAt (LocalTime day (TimeOfDay 0 1 0)) (clock sensors) -- [SprinklerSwitch PowerOff]-sprinklersStartingAt :: TimeOfDay -> Automation Sensors Actuators-sprinklersStartingAt starttod = Automation $ \sensors actuators -> do+sprinklersStartingAt :: TimeOfDay -> Automation Sensors Actuators ()+sprinklersStartingAt starttod = do -- This contains a ClockSignal, so we know it should update -- whenever the clock does, and so we don't need to add in a -- separate behavior for the clock.- brainfall <- totalRainfallSince starttod sensors+ brainfall <- totalRainfallSince starttod let b = calcchange <$> brainfall- onBehaviorChangeMaybe b (actuators . SprinklerSwitch)+ actuateBehaviorMaybe b SprinklerSwitch where stoptod = starttod { todHour = (todHour starttod + 1) `mod` 24 } calcchange (Timestamped (ClockSignal t) rain)@@ -266,7 +266,7 @@ -- [FridgePower PowerOn] -- >>> runner $ \sensors -> sensedAt 0 (motionSensor sensors) True -- [LightSwitch PowerOn]-thisHouse :: Automation Sensors Actuators+thisHouse :: Automation Sensors Actuators () thisHouse = mconcat [ fridge , nightLight
reactive-banana-automation.cabal view
@@ -1,5 +1,5 @@ Name: reactive-banana-automation-Version: 0.3.0+Version: 0.4.0 Cabal-Version: >= 1.8 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -32,7 +32,8 @@ base (>= 4.6 && < 5.0), reactive-banana (>= 1.1 && < 1.3), time (>= 1.6 && < 1.9),- stm (>= 2.4 && < 2.5)+ stm (>= 2.4 && < 2.5),+ transformers (>= 0.5 && < 0.6) Exposed-Modules: Reactive.Banana.Automation Reactive.Banana.Automation.Examples