mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Ensure we always fire time pattern changes after microsecond 0 (#39302)
This commit is contained in:
parent
7c191388a9
commit
f8704a2dfc
@ -34,6 +34,8 @@ from homeassistant.loader import bind_hass
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.async_ import run_callback_threadsafe
|
||||
|
||||
MAX_TIME_TRACKING_ERROR = 0.001
|
||||
|
||||
TRACK_STATE_CHANGE_CALLBACKS = "track_state_change_callbacks"
|
||||
TRACK_STATE_CHANGE_LISTENER = "track_state_change_listener"
|
||||
|
||||
@ -996,19 +998,40 @@ def async_track_utc_time_change(
|
||||
|
||||
calculate_next(now + timedelta(seconds=1))
|
||||
|
||||
# We always get time.time() first to avoid time.time()
|
||||
# ticking forward after fetching hass.loop.time()
|
||||
# and callback being scheduled a few microseconds early
|
||||
cancel_callback = hass.loop.call_at(
|
||||
-time.time() + hass.loop.time() + next_time.timestamp(),
|
||||
-time.time()
|
||||
+ hass.loop.time()
|
||||
+ next_time.timestamp()
|
||||
+ MAX_TIME_TRACKING_ERROR,
|
||||
pattern_time_change_listener,
|
||||
)
|
||||
|
||||
# We always get time.time() first to avoid time.time()
|
||||
# ticking forward after fetching hass.loop.time()
|
||||
# and callback being scheduled a few microseconds early
|
||||
# and callback being scheduled a few microseconds early.
|
||||
#
|
||||
# Since we loose additional time calling `hass.loop.time()`
|
||||
# we add MAX_TIME_TRACKING_ERROR to ensure
|
||||
# we always schedule the call within the time window between
|
||||
# second and the next second.
|
||||
#
|
||||
# For example:
|
||||
# If the clock ticks forward 30 microseconds when fectching
|
||||
# `hass.loop.time()` and we want the event to fire at exactly
|
||||
# 03:00:00.000000, the event would actually fire around
|
||||
# 02:59:59.999970. To ensure we always fire sometime between
|
||||
# 03:00:00.000000 and 03:00:00.999999 we add
|
||||
# MAX_TIME_TRACKING_ERROR to make up for the time
|
||||
# lost fetching the time. This ensures we do not fire the
|
||||
# event before the next time pattern match which would result
|
||||
# in the event being fired again since we would otherwise
|
||||
# potentially fire early.
|
||||
#
|
||||
cancel_callback = hass.loop.call_at(
|
||||
-time.time() + hass.loop.time() + next_time.timestamp(),
|
||||
-time.time()
|
||||
+ hass.loop.time()
|
||||
+ next_time.timestamp()
|
||||
+ MAX_TIME_TRACKING_ERROR,
|
||||
pattern_time_change_listener,
|
||||
)
|
||||
|
||||
|
@ -219,6 +219,10 @@ def test_find_next_time_expression_time_basic():
|
||||
datetime(2018, 10, 7, 10, 30, 0), 5, 0, 0
|
||||
)
|
||||
|
||||
assert find(datetime(2018, 10, 7, 10, 30, 0, 999999), "*", "/30", 0) == datetime(
|
||||
2018, 10, 7, 10, 30, 0
|
||||
)
|
||||
|
||||
|
||||
def test_find_next_time_expression_time_dst():
|
||||
"""Test daylight saving time for find_next_time_expression_time."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user