Add time-based screen scheduling (e.g. Journey every morning 6-8)
New "Time-based schedule" section on the Rotation settings page: up to
4 rules, each a screen + a daily HH:MM-HH:MM window (crossing midnight
is fine, e.g. 22:00-02:00). While a rule's window is active, that
screen overrides rotation and any manual pin -- without touching
either's underlying state, so things go back to exactly how they were
once the window ends. Navidrome-playing still wins on top of a
schedule rule, same as it already does over everything else.
config.py: new schedules list (get_schedules/set_schedules), each rule
just {screen_id, start, end} -- being in the list *is* being enabled,
no separate flag (a settings row is "disabled" by leaving its screen
dropdown on "-- none --").
scheduler.py: _time_in_window() handles same-day and midnight-wrapping
windows, treats a zero-width window as never-matching (more likely a
blank settings row than an intentional all-day rule).
_active_scheduled_screen() slots into _select_screen() between
Navidrome-priority and the pin/rotation fallback.
Verified: unit tests for _time_in_window's edge cases (wraparound,
zero-width, malformed input) and the priority override, plus an
end-to-end run against the real app -- pinned a screen, added a rule
active right now, confirmed the status page switched to the scheduled
screen within one tick.
This commit is contained in:
1 parent
33b964f1f6
commit
9aacd4936a
5 files changed
+103
-3
No files matched your search
+13
@@ -12,6 +12,7 @@ from screens.picture import UPLOAD_DIR
|
||||
ALLOWED_IMAGE_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "bmp", "webp"}
|
||||
PIL_FORMAT_TO_EXT = {"JPEG": "jpg", "PNG": "png", "GIF": "gif", "BMP": "bmp", "WEBP": "webp"}
|
||||
PICTURE_URL_TIMEOUT = 15
|
||||
MAX_SCHEDULE_RULES = 4
|
||||
PICTURE_URL_HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
|
||||
}
|
||||
@@ -289,12 +290,24 @@ def create_app(config, screens: dict, scheduler) -> Flask:
|
||||
enabled = "rotation_enabled" in request.form
|
||||
config.set_rotation(selected, interval_minutes, enabled)
|
||||
config.set_navidrome_priority("navidrome_priority" in request.form)
|
||||
|
||||
schedule_rules = []
|
||||
for i in range(1, MAX_SCHEDULE_RULES + 1):
|
||||
rule_screen = request.form.get(f"schedule_screen_{i}", "").strip()
|
||||
start = request.form.get(f"schedule_start_{i}", "").strip()
|
||||
end = request.form.get(f"schedule_end_{i}", "").strip()
|
||||
if rule_screen in screens and start and end:
|
||||
schedule_rules.append({"screen_id": rule_screen, "start": start, "end": end})
|
||||
config.set_schedules(schedule_rules)
|
||||
|
||||
return redirect(url_for("settings_rotation", saved=1))
|
||||
return render_template(
|
||||
"settings_rotation.html",
|
||||
screens=screens,
|
||||
rotation=config.get_rotation(),
|
||||
navidrome_priority=config.get_navidrome_priority(),
|
||||
schedules=config.get_schedules(),
|
||||
max_schedule_rules=MAX_SCHEDULE_RULES,
|
||||
)
|
||||
|
||||
return app
|
||||
Reference in new issue
Block a user