From 61e2c0ef615110e57bd97a24ccfa1c716016268b Mon Sep 17 00:00:00 2001 From: Chris Engel Date: Sat, 27 Aug 2022 15:22:36 -0500 Subject: [PATCH] Add support for room/bathroom doors This adds support to specify the doors to the room. As long as that door is closed the light will be blocked from turning off. Closing the door is also treated as a motion event and will cause the light to turn on. You can also override the default delay to have the light turn off quicker after the door is opened by specifying 'door_open_delay' otherwise it will turn off after the normal delay Additional config options: door: - binary_sensor.bathroom_door door_open_delay: 5 --- README.md | 7 +++++ apps/automoli/automoli.py | 56 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae49a4e..99df363 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,11 @@ bathroom: - switch.plug_68fe8b4c9fa1 motion: - binary_sensor.motion_sensor_158d033224e141 + # For the bathroom leave the light on as long as the door is closed + door: + - binary_sensor.bathroom_door + # Optional : When the door opens turn off the light after specified delay + door_open_delay: 10 ``` ## Auto-Discovery of Lights and Sensors @@ -152,6 +157,8 @@ key | optional | type | default | description `humidity_threshold` | True | integer | | If humidity is *above* this value, lights will *not switched off* `motion_state_on` | True | integer | | If using motion sensors which don't send events if already activated, like Xiaomi do, add this to your config with "on". This will listen to state changes instead `motion_state_off` | True | integer | | If using motion sensors which don't send events if already activated, like Xiaomi do, add this to your config with "off". This will listen to the state changes instead. +`door` | True | list/string | | Door close sensor, light will not turn off when door is closed +`door_open_delay` | True | integer | | Override normal delay when door is opened `debug_log` | True | bool | false | Activate debug logging (for this room) ### daytimes diff --git a/apps/automoli/automoli.py b/apps/automoli/automoli.py index c5db86f..158bf2e 100644 --- a/apps/automoli/automoli.py +++ b/apps/automoli/automoli.py @@ -245,6 +245,9 @@ async def initialize(self) -> None: # general delay self.delay = int(self.args.pop("delay", DEFAULT_DELAY)) + # Delay override upon door open + self.door_open_delay = int(self.args.pop("door_open_delay", 0)) + # directly switch to new daytime light settings self.transition_on_daytime_switch: bool = bool( self.args.pop("transition_on_daytime_switch", False) @@ -354,11 +357,24 @@ async def initialize(self) -> None: ) ) + # doors + self.doors: dict[str, Any] = {} + + # enumerate sensors for doors + self.doors[EntityType.DOOR_WINDOW.idx] = self.listr( + self.args.pop( + "door", + await self.find_sensors( + EntityType.DOOR_WINDOW.prefix, self.room_name, states + ), + ) + ) + self.room = Room( name=self.room_name, room_lights=self.lights, motion=self.sensors[EntityType.MOTION.idx], - door_window=set(), + door_window=self.doors[EntityType.DOOR_WINDOW.idx], temperature=set(), push_data=dict(), appdaemon=self.get_ad_api(), @@ -440,6 +456,15 @@ async def initialize(self) -> None: ) ) + # set up event listener for door events + for door in self.doors[EntityType.DOOR_WINDOW.idx]: + listener.add( + self.listen_state( + self.door_event, + entity_id=door + ) + ) + self.args.update( { "room": self.room_name.capitalize(), @@ -604,6 +629,24 @@ async def motion_event( if event != "state_changed_detection": await self.refresh_timer() + async def door_event( + self, entity: str, attribute: str, old: str, new: str, kwargs: dict[str, Any] + ) -> None: + + self.lg( + f"{stack()[0][3]}: received '{old}' -> '{new}' event from {entity}", + level=logging.DEBUG + ) + if new == "off": + # calling motion event handler for door close + data: dict[str, Any] = {"entity_id": entity, "new": new, "old": old} + await self.motion_event("state_changed_detection", data,kwargs) + + else: + # Handle door open events by overriding the off delay + await self.refresh_timer(self.door_open_delay) + + def has_min_ad_version(self, required_version: str) -> bool: required_version = required_version if required_version else "4.0.7" return bool( @@ -630,7 +673,7 @@ async def clear_handles(self, handles: set[str] = None) -> None: self.lg(f"{stack()[0][3]}: cancelled scheduled callbacks", level=logging.DEBUG) - async def refresh_timer(self) -> None: + async def refresh_timer(self, delay_override: int = 0) -> None: """refresh delay timer.""" fnn = f"{stack()[0][3]}:" @@ -646,6 +689,9 @@ async def refresh_timer(self) -> None: # if no delay is set or delay = 0, lights will not switched off by AutoMoLi if delay := self.active.get("delay"): + if delay_override != 0: + delay = delay_override + self.lg( f"{fnn} {self.active = } | {delay = } | {self.dim = }", level=logging.DEBUG, @@ -720,6 +766,12 @@ async def is_blocked(self) -> bool: ) return True + # The doors, if closed we are blocked + for door in self.doors[EntityType.DOOR_WINDOW.idx]: + if (await self.get_state(door) == "off"): + self.lg(f"blocked due to closed door : {door}", level=logging.DEBUG) + return True + return False async def dim_lights(self, _: Any) -> None: