Server: stop the photo from advancing during quiet hours too
Build and push server image / build-and-push (push) Successful in 35s

Quiet hours only clamped what refresh_interval_s the *device* is told
to sleep for -- the separate elapsed-time check in
photo_queue.get_current() (used by /frame/image, /frame/photo-info,
/frame/face-labels, and /api/queue) had no quiet-hours awareness at
all, since it runs independent of the device. An open web UI tab
polling /api/queue overnight, or just loading the page during a quiet
window, would silently advance which photo is "current" on raw elapsed
time alone -- nothing reaches the panel until the device wakes after
quiet hours end, but the pointer moving mid-window still isn't what
"don't do anything overnight" implies.

get_current() now takes an in_quiet_hours flag that suppresses only the
elapsed-time trigger; an unset/invalid current photo is still picked
regardless (showing nothing is worse than showing something even at
3am). New _in_quiet_hours() helper in main.py, passed at all four call
sites.
This commit is contained in:
2026-07-21 00:05:46 -04:00
parent 475888306e
commit 759c042f92
2 changed files with 33 additions and 11 deletions
+19 -4
View File
@@ -129,6 +129,21 @@ def _effective_refresh_interval_s(cfg: config.FrameConfig) -> int:
return min(cfg.refresh_interval_s, seconds_to_boundary)
def _in_quiet_hours(cfg: config.FrameConfig) -> bool:
"""Whether quiet hours are in effect right now -- separate from
_effective_refresh_interval_s, which only shapes what the *device* is
told to sleep for. This instead gates photo_queue.get_current()'s
time-based advance, since that check runs independent of the device
(also triggered by the web UI's /api/queue, e.g. an open browser tab
polling overnight) and would otherwise happily advance the current
photo mid-quiet-hours on raw elapsed time alone."""
if not cfg.quiet_hours_enabled:
return False
now = datetime.now(_zoneinfo(cfg.timezone))
in_quiet, _ = _quiet_hours_state(now, cfg.quiet_hours_start, cfg.quiet_hours_end)
return in_quiet
def _max_expected_gap_s(cfg: config.FrameConfig) -> int:
"""Longest gap between wakes the device might legitimately have --
normally just refresh_interval_s, but quiet hours can make the real
@@ -350,7 +365,7 @@ def frame_image():
with config.locked():
cfg = config.load() # re-read: state may have changed since the unlocked read above
if photo_queue.get_current(cfg, assets):
if photo_queue.get_current(cfg, assets, in_quiet_hours=_in_quiet_hours(cfg)):
config.save(cfg)
return Response(content=_render_asset(client, cfg, cfg.current_asset_id), media_type="application/octet-stream")
@@ -593,7 +608,7 @@ def frame_photo_info():
with config.locked():
cfg = config.load() # re-read: state may have changed since the unlocked read above
if photo_queue.get_current(cfg, assets):
if photo_queue.get_current(cfg, assets, in_quiet_hours=_in_quiet_hours(cfg)):
config.save(cfg)
if not cfg.current_asset_id:
@@ -662,7 +677,7 @@ def frame_face_labels():
with config.locked():
cfg = config.load() # re-read: state may have changed since the unlocked read above
if photo_queue.get_current(cfg, assets):
if photo_queue.get_current(cfg, assets, in_quiet_hours=_in_quiet_hours(cfg)):
config.save(cfg)
if not cfg.current_asset_id:
@@ -703,7 +718,7 @@ def api_queue():
with config.locked():
cfg = config.load() # re-read: state may have changed since the unlocked read above
current_changed = photo_queue.get_current(cfg, assets)
current_changed = photo_queue.get_current(cfg, assets, in_quiet_hours=_in_quiet_hours(cfg))
queue_before = list(cfg.queue)
photo_queue.sync_queue_length(cfg, assets)
if current_changed or cfg.queue != queue_before: