Sync time daily once ESPHome device has requested time once

If the ESPHome device requests time once we know it has Home Assistant
time enabled. Since the clock drifts over time, we will send time again
daily to ensure it keeps in sync.

fixes https://github.com/esphome/issues/issues/4424
This commit is contained in:
J. Nick Koston 2023-12-21 21:34:38 -10:00
parent e2365545f3
commit 1e71d3f4ca
No known key found for this signature in database
2 changed files with 50 additions and 1 deletions

View File

@ -109,6 +109,7 @@ cdef class APIConnection:
cdef bint _debug_enabled
cdef public str received_name
cdef public str connected_address
cdef object _time_sync_timer
cpdef void send_message(self, object msg)
@ -154,3 +155,11 @@ cdef class APIConnection:
cdef void _register_internal_message_handlers(self)
cdef void _increase_recv_buffer_size(self)
cdef void _cancel_next_time_sync(self)
cdef void _send_time_response(self)
cdef void _schedule_next_time_sync(self)
cpdef void _send_time_response_schedule_next(self)

View File

@ -98,6 +98,12 @@ CONNECT_REQUEST_TIMEOUT = 30.0
# to reboot and connect to the network/WiFi.
TCP_CONNECT_TIMEOUT = 60.0
# How often to sync the time with the ESPHome device
# to ensure the ESPHome device has the correct time
# after the initial sync.
TIME_SYNC_INTERVAL_SECONDS = 86400.0
WRITE_EXCEPTIONS = (RuntimeError, ConnectionResetError, OSError)
@ -209,6 +215,7 @@ class APIConnection:
"_debug_enabled",
"received_name",
"connected_address",
"_time_sync_timer",
)
def __init__(
@ -253,6 +260,7 @@ class APIConnection:
self._debug_enabled = debug_enabled
self.received_name: str = ""
self.connected_address: str | None = None
self._time_sync_timer: asyncio.TimerHandle | None = None
def set_log_name(self, name: str) -> None:
"""Set the friendly log name for this connection."""
@ -313,6 +321,8 @@ class APIConnection:
self._ping_timer.cancel()
self._ping_timer = None
self._cancel_next_time_sync()
if (on_stop := self.on_stop) is not None and was_connected:
self.on_stop = None
on_stop(self._expected_disconnect)
@ -962,7 +972,37 @@ class APIConnection:
def _handle_get_time_request_internal( # pylint: disable=unused-argument
self, _msg: GetTimeRequest
) -> None:
"""Handle a GetTimeRequest."""
"""Handle a GetTimeRequest.
Once the ESPHome device has asked for time, we will
send a time response and schedule the next periodic sync
to ensure the ESPHome device has the correct time as the
ESPHome device clock is not very accurate and will drift
over time.
"""
self._send_time_response_schedule_next()
def _send_time_response_schedule_next(self) -> None:
"""Send a time response and schedule the next periodic sync."""
self._send_time_response()
self._schedule_next_time_sync()
def _schedule_next_time_sync(self) -> None:
"""Schedule the next time sync."""
self._cancel_next_time_sync()
self._time_sync_timer = self._loop.call_at(
self._loop.time() + TIME_SYNC_INTERVAL_SECONDS,
self._send_time_response_schedule_next,
)
def _cancel_next_time_sync(self) -> None:
"""Cancel the next time sync."""
if self._time_sync_timer is not None:
self._time_sync_timer.cancel()
self._time_sync_timer = None
def _send_time_response(self) -> None:
"""Send a time response."""
resp = GetTimeResponse()
resp.epoch_seconds = int(time.time())
self.send_messages((resp,))