@@ -224,8 +224,7 @@ def sync_time(self, server=None, tz_offset=None, tuning=None):
224224 NTP_SERVER – NTP host (default: "pool.ntp.org")
225225 NTP_TZ – timezone offset in hours (float, default: 0)
226226 NTP_DST – extra offset for daylight saving (0=no, 1=yes; default: 0)
227- NTP_INTERVAL – re-sync interval in seconds (default: 3600, not used internally,
228- but available for user loop scheduling)
227+ NTP_INTERVAL – re-sync interval in seconds (default: 3600, not used internally)
229228
230229 NTP_TIMEOUT – socket timeout per attempt (seconds, default: 5.0)
231230 NTP_CACHE_SECONDS – cache results, 0 = always fetch fresh (default: 0)
@@ -249,86 +248,100 @@ def sync_time(self, server=None, tz_offset=None, tuning=None):
249248 Returns:
250249 time.struct_time
251250 """
252- # Bring up Wi-Fi using the existing flow.
251+ # Ensure Wi-Fi up
253252 self .connect ()
254253
255- # Build a socket pool from the existing ESP interface.
254+ # Socket pool
256255 pool = acm .get_radio_socketpool (self ._wifi .esp )
257256
258- # Settings with environment fallbacks.
257+ # Settings & overrides
259258 server = server or os .getenv ("NTP_SERVER" ) or "pool.ntp.org"
260-
261- if tz_offset is None :
262- tz_env = os .getenv ("NTP_TZ" )
263- try :
264- tz_offset = float (tz_env ) if tz_env not in {None , "" } else 0.0
265- except Exception :
266- tz_offset = 0.0
267-
268- # Simple DST additive offset (no IANA time zone logic).
269- try :
270- dst = float (os .getenv ("NTP_DST" ) or 0 )
271- except Exception :
272- dst = 0.0
273- tz_offset += dst
274-
275- # Optional tuning (env can override passed defaults).
259+ tz = tz_offset if tz_offset is not None else _combined_tz_offset (0.0 )
276260 t = tuning or {}
277261
278- def _f (name , default ):
279- v = os .getenv (name )
280- try :
281- return float (v ) if v not in {None , "" } else float (default )
282- except Exception :
283- return float (default )
284-
285- def _i (name , default ):
286- v = os .getenv (name )
287- try :
288- return int (v ) if v not in {None , "" } else int (default )
289- except Exception :
290- return int (default )
291-
292- timeout = float (t .get ("timeout" , _f ("NTP_TIMEOUT" , 5.0 )))
293- cache_seconds = int (t .get ("cache_seconds" , _i ("NTP_CACHE_SECONDS" , 0 )))
294- require_year = int (t .get ("require_year" , _i ("NTP_REQUIRE_YEAR" , 2022 )))
262+ timeout = float (t .get ("timeout" , _get_float_env ("NTP_TIMEOUT" , 5.0 )))
263+ cache_seconds = int (t .get ("cache_seconds" , _get_int_env ("NTP_CACHE_SECONDS" , 0 )))
264+ require_year = int (t .get ("require_year" , _get_int_env ("NTP_REQUIRE_YEAR" , 2022 )))
265+ ntp_retries = int (t .get ("retries" , _get_int_env ("NTP_RETRIES" , 8 )))
266+ ntp_delay_s = float (t .get ("retry_delay" , _get_float_env ("NTP_DELAY_S" , 1.0 )))
295267
296- # Query NTP and set the system RTC.
268+ # NTP client
297269 ntp = adafruit_ntp .NTP (
298270 pool ,
299271 server = server ,
300- tz_offset = tz_offset ,
272+ tz_offset = tz ,
301273 socket_timeout = timeout ,
302274 cache_seconds = cache_seconds ,
303275 )
304276
305- # Multiple reply attempts on transient timeouts
306- ntp_retries = int (t .get ("retries" , _i ("NTP_RETRIES" , 8 )))
307- ntp_delay_s = float (t .get ("retry_delay" , _f ("NTP_DELAY_S" , 1.0 )))
308-
309- last_exc = None
310- for attempt in range (ntp_retries ):
311- try :
312- now = ntp .datetime # struct_time
313- break # success
314- except OSError as e :
315- last_exc = e
316- # Only retry on timeout-like errors
317- if getattr (e , "errno" , None ) == 116 or "ETIMEDOUT" in str (e ):
318- # Reassert Wi-Fi via existing policy, then wait a bit
319- self .connect ()
320- if self ._debug :
321- print ("NTP timeout, retry" , attempt + 1 , "of" , ntp_retries )
322- time .sleep (ntp_delay_s )
323- continue
324- # Non-timeout: don't spin
325- break
326-
327- if last_exc and "now" not in locals ():
328- raise last_exc
277+ # Attempt fetch (retries on timeout)
278+ now = _ntp_get_datetime (
279+ ntp ,
280+ connect_cb = self .connect ,
281+ retries = ntp_retries ,
282+ delay_s = ntp_delay_s ,
283+ debug = getattr (self , "_debug" , False ),
284+ )
329285
286+ # Sanity check & commit
330287 if now .tm_year < require_year :
331288 raise RuntimeError ("NTP returned an unexpected year; not setting RTC" )
332289
333290 rtc .RTC ().datetime = now
334291 return now
292+
293+
294+ # ---- Internal helpers to keep sync_time() small and Ruff-friendly ----
295+
296+
297+ def _get_float_env (name , default ):
298+ v = os .getenv (name )
299+ try :
300+ return float (v ) if v not in {None , "" } else float (default )
301+ except Exception :
302+ return float (default )
303+
304+
305+ def _get_int_env (name , default ):
306+ v = os .getenv (name )
307+ if v in {None , "" }:
308+ return int (default )
309+ try :
310+ return int (v )
311+ except Exception :
312+ try :
313+ return int (float (v )) # tolerate "5.0"
314+ except Exception :
315+ return int (default )
316+
317+
318+ def _combined_tz_offset (base_default ):
319+ """Return tz offset hours including DST via env (NTP_TZ + NTP_DST)."""
320+ tz = _get_float_env ("NTP_TZ" , base_default )
321+ dst = _get_float_env ("NTP_DST" , 0 )
322+ return tz + dst
323+
324+
325+ def _ntp_get_datetime (ntp , connect_cb , retries , delay_s , debug = False ):
326+ """Fetch ntp.datetime with limited retries on timeout; re-connect between tries."""
327+ last_exc = None
328+ for i in range (retries ):
329+ last_exc = None
330+ try :
331+ return ntp .datetime # struct_time
332+ except OSError as e :
333+ last_exc = e
334+ is_timeout = (getattr (e , "errno" , None ) == 116 ) or ("ETIMEDOUT" in str (e ))
335+ if not is_timeout :
336+ break
337+ if debug :
338+ print (f"NTP timeout, attempt { i + 1 } /{ retries } " )
339+ connect_cb () # re-assert Wi-Fi using existing policy
340+ time .sleep (delay_s )
341+ continue
342+ except Exception as e :
343+ last_exc = e
344+ break
345+ if last_exc :
346+ raise last_exc
347+ raise RuntimeError ("NTP sync failed" )
0 commit comments