@@ -30,18 +30,49 @@ class TimeActiveDecorator(TriggerHandlerDecorator, AutoKwargsDecorator):
3030
3131 name = "time_active"
3232 args_schema = vol .Schema (vol .All ([vol .Coerce (str )], vol .Length (min = 0 )))
33- kwargs_schema = vol .Schema ({vol .Optional ("hold_off" , default = 0.0 ): vol .Any (None , cv .positive_float )})
33+ kwargs_schema = vol .Schema (
34+ {
35+ vol .Optional ("hold_off" , default = 0.0 ): vol .Any (None , cv .positive_float ),
36+ vol .Optional ("hold_off_send_last" , default = False ): cv .boolean ,
37+ }
38+ )
3439
3540 hold_off : float | None
41+ hold_off_send_last : bool
3642
3743 last_trig_time : float = 0.0
44+ _hold_off_task : asyncio .Task | None = None
45+ _pending_data : DispatchData | None = None
46+
47+ async def _dispatch_after_hold_off (self ) -> None :
48+ """Dispatch the latest suppressed payload after the current hold-off window."""
49+ while self ._pending_data is not None :
50+ delay = self .last_trig_time + self .hold_off - time .monotonic ()
51+ if delay > 0.0 :
52+ await asyncio .sleep (delay )
53+
54+ data = self ._pending_data
55+ _LOGGER .debug ("%s hold_off_send_last dispatching after delay %s" , self , delay )
56+ await self .dm .dispatch (data )
57+ if self ._pending_data is data :
58+ self ._pending_data = None
3859
3960 async def handle_dispatch (self , data : DispatchData ) -> bool :
4061 """Handle dispatch."""
4162 if self .last_trig_time > 0.0 and self .hold_off is not None and self .hold_off > 0.0 :
4263 if time .monotonic () - self .last_trig_time < self .hold_off :
64+ if self .hold_off_send_last :
65+ self ._pending_data = data
66+ if self ._hold_off_task is None or self ._hold_off_task .done ():
67+ self ._hold_off_task = self .dm .hass .async_create_background_task (
68+ self ._dispatch_after_hold_off (), f"{ self } hold_off_send_last"
69+ )
4370 return False
4471
72+ if data is self ._pending_data :
73+ self .last_trig_time = time .monotonic ()
74+ return True
75+
4576 if len (self .args ) > 0 :
4677 if "trigger_time" in data .func_args and isinstance (data .func_args ["trigger_time" ], dt .datetime ):
4778 now = data .func_args ["trigger_time" ]
@@ -53,12 +84,23 @@ async def handle_dispatch(self, data: DispatchData) -> bool:
5384 _LOGGER .debug ("time_active now %s, %s" , now , self )
5485 if await trigger .TrigTime .timer_active_check (time_spec , now , self .dm .startup_time ):
5586 self .last_trig_time = time .monotonic ()
87+ if data is not self ._pending_data :
88+ self ._pending_data = None
5689 return True
5790 return False
5891
5992 self .last_trig_time = time .monotonic ()
93+ if data is not self ._pending_data :
94+ self ._pending_data = None
6095 return True
6196
97+ async def stop (self ) -> None :
98+ """Stop pending hold-off dispatch."""
99+ await super ().stop ()
100+ self ._pending_data = None
101+ if self ._hold_off_task is not None :
102+ self ._hold_off_task .cancel ()
103+
62104
63105class TimeTriggerDecorator (TriggerDecorator ):
64106 """Implementation for @time_trigger."""
0 commit comments