@@ -359,10 +359,10 @@ async def wait_until(
359359 state_trig_timeout = False
360360 time_next = None
361361 startup_time = None
362+ now = dt_now ()
363+ if startup_time is None :
364+ startup_time = now
362365 if time_trigger is not None :
363- now = dt_now ()
364- if startup_time is None :
365- startup_time = now
366366 time_next = cls .timer_trigger_next (time_trigger , now , startup_time )
367367 _LOGGER .debug (
368368 "trigger %s wait_until time_next = %s, now = %s" , ast_ctx .name , time_next , now ,
@@ -377,11 +377,13 @@ async def wait_until(
377377 if this_timeout is None or this_timeout > time_left :
378378 ret = {"trigger_type" : "timeout" }
379379 this_timeout = time_left
380+ time_next = now + dt .timedelta (seconds = this_timeout )
380381 if state_trig_waiting :
381382 time_left = last_state_trig_time + state_hold - time .monotonic ()
382383 if this_timeout is None or time_left < this_timeout :
383384 this_timeout = time_left
384385 state_trig_timeout = True
386+ time_next = now + dt .timedelta (seconds = this_timeout )
385387 if this_timeout is None :
386388 if state_trigger is None and event_trigger is None and mqtt_trigger is None :
387389 _LOGGER .debug (
@@ -392,18 +394,33 @@ async def wait_until(
392394 _LOGGER .debug ("trigger %s wait_until no timeout" , ast_ctx .name )
393395 notify_type , notify_info = await notify_q .get ()
394396 else :
395- try :
396- this_timeout = max (0 , this_timeout )
397- _LOGGER .debug ("trigger %s wait_until %.6g secs" , ast_ctx .name , this_timeout )
398- notify_type , notify_info = await asyncio .wait_for (notify_q .get (), timeout = this_timeout )
399- state_trig_timeout = False
400- except asyncio .TimeoutError :
401- if not state_trig_timeout :
402- if not ret :
403- ret = {"trigger_type" : "time" }
404- if time_next is not None :
405- ret ["trigger_time" ] = time_next
406- break
397+ timeout_occured = False
398+ while True :
399+ try :
400+ this_timeout = max (0 , this_timeout )
401+ _LOGGER .debug ("trigger %s wait_until %.6g secs" , ast_ctx .name , this_timeout )
402+ notify_type , notify_info = await asyncio .wait_for (
403+ notify_q .get (), timeout = this_timeout
404+ )
405+ state_trig_timeout = False
406+ except asyncio .TimeoutError :
407+ actual_now = dt_now ()
408+ if actual_now < time_next :
409+ this_timeout = (time_next - actual_now ).total_seconds ()
410+ # tests/tests_function's simple now() requires us to ignore
411+ # timeouts that are up to 1us too early; otherwise wait for
412+ # longer until we are sure we are at or past time_next
413+ if this_timeout > 1e-6 :
414+ continue
415+ if not state_trig_timeout :
416+ if not ret :
417+ ret = {"trigger_type" : "time" }
418+ if time_next is not None :
419+ ret ["trigger_time" ] = time_next
420+ timeout_occured = True
421+ break
422+ if timeout_occured :
423+ break
407424 if state_trig_timeout :
408425 ret = state_trig_notify_info [1 ]
409426 state_trig_waiting = False
@@ -964,24 +981,31 @@ async def trigger_watch(self):
964981 time_left = last_state_trig_time + self .state_hold - time .monotonic ()
965982 if timeout is None or time_left < timeout :
966983 timeout = time_left
984+ time_next = now + dt .timedelta (seconds = timeout )
967985 state_trig_timeout = True
968986 if timeout is not None :
969- try :
970- timeout = max (0 , timeout )
971- _LOGGER .debug ("trigger %s waiting for %.6g secs" , self .name , timeout )
972- notify_type , notify_info = await asyncio .wait_for (
973- self .notify_q .get (), timeout = timeout
974- )
975- state_trig_timeout = False
976- now = dt_now ()
977- except asyncio .TimeoutError :
978- now += dt .timedelta (seconds = timeout )
979- if not state_trig_timeout :
980- notify_type = "time"
981- notify_info = {
982- "trigger_type" : "time" ,
983- "trigger_time" : time_next ,
984- }
987+ while True :
988+ try :
989+ timeout = max (0 , timeout )
990+ _LOGGER .debug ("trigger %s waiting for %.6g secs" , self .name , timeout )
991+ notify_type , notify_info = await asyncio .wait_for (
992+ self .notify_q .get (), timeout = timeout
993+ )
994+ state_trig_timeout = False
995+ now = dt_now ()
996+ except asyncio .TimeoutError :
997+ actual_now = dt_now ()
998+ if actual_now < time_next :
999+ timeout = (time_next - actual_now ).total_seconds ()
1000+ continue
1001+ now = time_next
1002+ if not state_trig_timeout :
1003+ notify_type = "time"
1004+ notify_info = {
1005+ "trigger_type" : "time" ,
1006+ "trigger_time" : time_next ,
1007+ }
1008+ break
9851009 elif self .have_trigger :
9861010 _LOGGER .debug ("trigger %s waiting for state change or event" , self .name )
9871011 notify_type , notify_info = await self .notify_q .get ()
0 commit comments