Coverage for apps/garage_door.py: 100%
32 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-12 12:23 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-12 12:23 +0000
1# The MIT License (MIT)
2#
3# Copyright © 2023 Xavier Berger
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6# and associated documentation files (the “Software”), to deal in the Software without restriction,
7# including without limitation the rights to use, copy, modify, merge, publish, distribute,
8# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9# furnished to do so, subject to the following conditions:
10#
11# The above copyright notice and this permission notice shall be included in all copies or
12# substantial portions of the Software.
13#
14# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19import appdaemon.plugins.hass.hassapi as hass
21#
22# Garage door App
23#
24# Args:
25# sun: sun.sun
26# notification_delay: 600
27# door_state: binary_sensor.porte_garage_opening
29#
30# Send a notification when sun is below horizon and garage door is style open
31# based on following conditions:
32# - When door is open when sun is passing below horizon
33# - When door is open during the night and not closed after 10 minutes
34#
37class GarageDoor(hass.Hass):
38 def initialize(self):
39 """
40 Initialize the GarageDoor application.
42 This method sets up the necessary listeners and handles for the GarageDoor application.
43 It registers callbacks for starting and stopping automation based on sun position.
45 Returns:
46 None
47 """
48 self.log("Starting GarageDoor")
50 # Handles to register / unregister callbacks
51 self.state_handles = []
52 self.delayed_notification_handle = None
54 # Activate/deactivate automation based on sun
55 self.listen_state(
56 self.callback_start_automation,
57 self.args["sun"],
58 new="below_horizon",
59 immediate=True,
60 )
61 self.listen_state(
62 self.callback_stop_automation,
63 self.args["sun"],
64 new="above_horizon",
65 immediate=True,
66 )
68 def callback_start_automation(self, entity, attribute, old, new, kwargs):
69 """
70 Callback for starting the garage door automation.
72 This method is called when conditions are met to start the garage door automation.
73 It sets up listeners for garage door state changes.
75 Args:
76 Arguments as define into Appdaemon callback documentation.
78 Returns:
79 None
80 """
81 # Start automation
82 self.log("Garage door Automation is started")
83 # Start listening for garage states
84 self.state_handles.append(
85 self.listen_state(
86 self.callback_garage_door_open,
87 self.args["door_state"],
88 new="on",
89 immediate=True,
90 )
91 )
92 self.state_handles.append(
93 self.listen_state(
94 self.callback_garage_door_close,
95 self.args["door_state"],
96 new="off",
97 immediate=True,
98 )
99 )
101 def callback_stop_automation(self, entity, attribute, old, new, kwargs):
102 """
103 Callback for stopping the garage door automation.
105 This method is called when conditions are met to stop the garage door automation.
106 It deregisters all the garage door state change callbacks.
108 Args:
109 Arguments as define into Appdaemon callback documentation.
111 Returns:
112 None
113 """
114 # Deregister garage door state change callbacks
115 while len(self.state_handles) >= 1:
116 handle = self.state_handles.pop()
117 self.cancel_listen_state(handle)
118 self.log("Garage door Automation is stopped")
120 def callback_garage_door_open(self, entity, attribute, old, new, kwargs):
121 """
122 Callback for handling the garage door opening.
124 This method is called when the garage door opens. It determines whether to send an immediate
125 notification or schedule a delayed notification based on the sun's position.
127 Args:
128 Arguments as define into Appdaemon callback documentation.
130 Returns:
131 None
132 """
133 # Automation is triggered only when sun is below horizon and door is open (new = on)
134 if old in ["on", None]:
135 # Door is open during sunset
136 self.log("Door is open during sunset => send notification")
137 self.send_notification(None)
138 else:
139 # Door was closed and is opening.
140 self.log("Door is open while sun is below horizon => trigger delayed notification")
141 self.delayed_notification_handle = self.run_in(self.send_notification, self.args["notification_delay"])
143 def callback_garage_door_close(self, entity, attribute, old, new, kwargs):
144 """
145 Callback for handling the garage door closing.
147 This method is called when the garage door closes. If there is a pending delayed notification,
148 it cancels the notification timer.
150 Args:
151 Arguments as define into Appdaemon callback documentation.
153 Returns:
154 None
155 """
156 # Automation is triggered only when sun is below horizon and door is closed (new = off)
157 if self.delayed_notification_handle is not None:
158 self.log("Cancel delayed notification")
159 self.cancel_timer(self.delayed_notification_handle)
160 self.delayed_notification_handle = None
162 def send_notification(self, kwargs):
163 """
164 Send a notification about the open garage door.
166 This method sends a notification saying tha the garage door is open during nighttime.
168 Args:
169 kwargs: Additional keyword arguments (not used in this method).
171 Returns:
172 None
173 """
174 # Send notification
175 self.log("Send notification")
176 self.fire_event(
177 "NOTIFIER",
178 action="send_to_present",
179 title=self.args["notification_title"],
180 message=self.args["notification_message"],
181 icon="mdi-garage-open",
182 color="deep-orange",
183 tag="garage_open",
184 until=[
185 {"entity_id": self.args["door_state"], "new_state": "off"},
186 {"entity_id": self.args["sun"], "new_state": "above_horizon"},
187 ],
188 )
189 self.delayed_notification_handle = None