mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 02:37:08 +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 import dt as dt_util
|
||||||
from homeassistant.util.async_ import run_callback_threadsafe
|
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_CALLBACKS = "track_state_change_callbacks"
|
||||||
TRACK_STATE_CHANGE_LISTENER = "track_state_change_listener"
|
TRACK_STATE_CHANGE_LISTENER = "track_state_change_listener"
|
||||||
|
|
||||||
@ -996,19 +998,40 @@ def async_track_utc_time_change(
|
|||||||
|
|
||||||
calculate_next(now + timedelta(seconds=1))
|
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(
|
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,
|
pattern_time_change_listener,
|
||||||
)
|
)
|
||||||
|
|
||||||
# We always get time.time() first to avoid time.time()
|
# We always get time.time() first to avoid time.time()
|
||||||
# ticking forward after fetching hass.loop.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(
|
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,
|
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
|
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():
|
def test_find_next_time_expression_time_dst():
|
||||||
"""Test daylight saving time for find_next_time_expression_time."""
|
"""Test daylight saving time for find_next_time_expression_time."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user