Files
home-assistant-controller/src/HomeAssistant/Controller/Bedroom.hs
T

157 lines
5.6 KiB
Haskell

{-# LANGUAGE Arrows #-}
{-# LANGUAGE OverloadedStrings #-}
module HomeAssistant.Controller.Bedroom where
import HomeAssistant.Controller
import qualified Data.Text as T
import AFRP (Event (..), (>>|), toEvent, lMerge, duration, edge)
import Data.Aeson (Value, object, (.=))
import Control.Arrow ((>>>), returnA, arr, Arrow (..))
import Control.Lens ((^?), to, traversed)
import Data.Aeson.Lens (key, _String)
import Data.Bool (bool)
import Data.Time (NominalDiffTime)
import qualified AFRP
bedroomLights :: [Target]
bedroomLights = map EntityId ["light.bedroom_masse","light.bedroom_jemina","light.bedroom_ceiling","light.bedroom_led_switch"]
createBedroomScene :: Service
createBedroomScene = Service
{ serviceDomain="scene"
, serviceName= "create"
, serviceData= Just $ object
[ "scene_id" .= ("makuuhuone_lights_snapshot" :: T.Text)
, "snapshot_entities" .= [l | EntityId l <- bedroomLights]
]
, serviceTarget= []
}
activateScene :: T.Text -> Service
activateScene sceneId = Service
{ serviceDomain="scene"
, serviceName= "turn_on"
, serviceTarget= [EntityId sceneId]
, serviceData = Nothing
}
-- Bedroom presence
bedroomPresence :: HASS (Event Value) (Event Presence)
bedroomPresence = presence "binary_sensor.presence_sensor_bedroom_occupancy"
bedroomPresenceController :: HASS (Event Value) ()
bedroomPresenceController = proc x -> do
p <- bedroomPresence -< x
case p of
Event Unoccupied -> callService createBedroomScene >>> callService (light bedroomLights False) -< ()
Event Occupied -> callService (activateScene "makuuhuone_lights_snapshot") -< ()
_ -> returnA -< ()
data IkeaGesture
= ShortClick
| ShortRelease
| DoubleClick
| LongClick
deriving Show
data IkeaButton a = OnButton !a | OffButton !a
deriving Show
toIkeaQuickButton :: T.Text -> Maybe (IkeaButton IkeaGesture)
toIkeaQuickButton = \case
-- 1 is on, 2 is off side of the button
"1_initial_press" -> Just $ OnButton ShortClick
"1_short_release" -> Just $ OnButton ShortRelease
"1_double_press" -> Just $ OnButton DoubleClick
"1_long_press" -> Just $ OnButton LongClick
"2_initial_press" -> Just $ OffButton ShortClick
"2_short_release" -> Just $ OffButton ShortRelease
"2_double_press" -> Just $ OffButton DoubleClick
"2_long_press" -> Just $ OffButton LongClick
_ -> Nothing
ikeaQuickButton :: T.Text -> HASS (Event Value) (Event (IkeaButton IkeaGesture))
ikeaQuickButton entityId =
entityChangeEvent' entityId
>>| arr (maybe (Left ()) Right . eventType)
>>> toEvent
where
eventType v = v ^? key "event" . key "variables" . key "trigger" . key "to_state" . key "attributes" . key "event_type" . _String . to toIkeaQuickButton . traversed
data BedroomControls
= Masse (IkeaButton IkeaGesture)
| Enishen (IkeaButton IkeaGesture)
deriving Show
bedroomButton :: HASS (Event Value) (Event BedroomControls)
bedroomButton = (masseButton &&& enishenButton) >>> arr (uncurry lMerge)
where
masseButton = fmap Masse <$> ikeaQuickButton "event.bedroom_quick_remote_masse_action"
enishenButton = fmap Enishen <$> ikeaQuickButton "event.bedroom_quick_jemina_action"
bedroomButtonController :: HASS (Event Value) ()
bedroomButtonController = proc x -> do
ev <- bedroomButton >>> traceEvent -< x
case ev of
Event (Masse (OnButton ShortRelease)) -> callService (activateScene "scene.makuuhuone_masse") -< ()
Event (Masse (OnButton DoubleClick)) -> callService (activateScene "scene.makuuhuone_keski") -< ()
Event (Masse (OnButton LongClick)) -> callService (activateScene "scene.makuuhuone_kirkas") -< ()
Event (Masse (OffButton _)) -> callService (light [AreaId "makuuhuone"] False) -< ()
Event (Enishen (OnButton ShortRelease)) -> callService (activateScene "scene.makuuhuone_jemina") -< ()
Event (Enishen (OnButton DoubleClick)) -> callService (activateScene "scene.makuuhuone_keski") -< ()
Event (Enishen (OnButton LongClick)) -> callService (activateScene "scene.makuuhuone_kirkas") -< ()
Event (Enishen (OffButton _)) -> callService (light [AreaId "makuuhuone"] False) -< ()
_ -> returnA -< ()
drawer :: HASS (Event Value) (Event DoorState)
drawer = entityBool "binary_sensor.bedroom_nightstand_drawer_sensor_masse_contact"
>>> arr (fmap (bool Closed Open))
bedroomDrawerController :: HASS (Event Value) ()
bedroomDrawerController = proc x -> do
st <- drawer >>> traceEvent -< x
case st of
Event Open -> callService (switch [entity] True) -< ()
Event Closed -> callService (switch [entity] False) -< ()
_ -> returnA -< ()
where
entity = EntityId "switch.bedroom_drawer_light_masse"
door :: HASS (Event Value) (Event DoorState)
door = entityBool "binary_sensor.makuuhuone_ovi_contact"
>>> arr (fmap (bool Closed Open))
waitFor :: NominalDiffTime -> HASS a (Event ())
waitFor n = duration >>> arr (> n) >>> edge
delayedDoor :: HASS (Event Value) (Event DoorState)
delayedDoor = door
>>> AFRP.debounce 15
>>> AFRP.hold Open
>>> AFRP.changes
>>> traceEvent
humidifierController :: HASS (Event Value) ()
humidifierController = proc x -> do
st <- delayedDoor -< x
case st of
-- Turn the humidifier on when the door is closed
-- the humidifier is low-powered, no point in losing all the humidity
Event Open -> callService (humidifier False) -< ()
Event Closed -> callService (humidifier True) -< ()
_ -> returnA -< ()
where
humidifier state = Service
{ serviceDomain="humidifier"
, serviceName= bool "turn_off" "turn_on" state
, serviceData= Nothing
, serviceTarget= [EntityId "humidifier.makuuhuone_ilmankostutin"]
}