Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync time daily once ESPHome device has requested time once #803

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions aioesphomeapi/connection.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
42 changes: 41 additions & 1 deletion aioesphomeapi/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@
# 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)


Expand Down Expand Up @@ -209,6 +215,7 @@ class APIConnection:
"_debug_enabled",
"received_name",
"connected_address",
"_time_sync_timer",
)

def __init__(
Expand Down Expand Up @@ -253,6 +260,7 @@ def __init__(
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."""
Expand Down Expand Up @@ -313,6 +321,8 @@ def _cleanup(self) -> None:
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)
Expand Down Expand Up @@ -962,7 +972,37 @@ def _handle_ping_request_internal( # pylint: disable=unused-argument
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,))
Expand Down