From 7e07428a811ea22a3d6dd53f8eb5b8aa9a3e6be6 Mon Sep 17 00:00:00 2001 From: Jos Rothman Date: Wed, 4 Nov 2026 22:44:42 +0100 Subject: [PATCH] Do not delay a browser restart without a screen; clearer reload error Without a connected monitor every Chromium start waited 30 s for one, so DevTools was unreachable for ~35 s after a restart. Only wait right after boot. The reload error now says Chromium may still be starting. Co-Authored-By: Claude Opus 5.5 --- src/voxip_kiosk/browser.py | 14 ++++++++++---- src/voxip_kiosk/web/views.py | 7 ++++++- tests/test_browser.py | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/voxip_kiosk/browser.py b/src/voxip_kiosk/browser.py index 1790c0b..6a7d680 100644 --- a/src/voxip_kiosk/browser.py +++ b/src/voxip_kiosk/browser.py @@ -19,7 +19,7 @@ from pathlib import Path from typing import Any from urllib.parse import urlencode -from . import logutil +from . import logutil, sysinfo from .config import load_config from .paths import Paths, load_paths from .validation import parse_resolution @@ -153,15 +153,21 @@ def wait_for_x(timeout: float = 60) -> bool: return False +BOOT_GRACE_SECONDS = 180 + + def prepare_display(paths: Paths, config: dict[str, Any]) -> tuple[int, int] | None: outputs: list[dict[str, Any]] = [] - # A monitor that powers up after the Pi may need a moment to report itself. - for _ in range(15): + # A monitor that powers up together with the Pi may need a moment to report + # itself. Later starts (a browser restart without a screen) should not wait. + attempts = 15 if (sysinfo.uptime_seconds() or 0) < BOOT_GRACE_SECONDS else 1 + for attempt in range(attempts): + if attempt: + time.sleep(2) proc = _run(["xrandr", "--query"]) outputs = parse_xrandr(proc.stdout) if proc and proc.returncode == 0 else [] if outputs: break - time.sleep(2) size = None if outputs: output = outputs[0] diff --git a/src/voxip_kiosk/web/views.py b/src/voxip_kiosk/web/views.py index 3d66449..5bf67c5 100644 --- a/src/voxip_kiosk/web/views.py +++ b/src/voxip_kiosk/web/views.py @@ -357,7 +357,12 @@ def kiosk_reload(): audit("page_reload") return _action_response(True, "De pagina wordt herladen.", request.referrer or url_for("ui.dashboard")) except CDPError as exc: - return _action_response(False, f"Herladen mislukt: {exc}", request.referrer or url_for("ui.dashboard")) + current_app.logger.warning("Herladen mislukt: %s", exc) + return _action_response( + False, + "Herladen mislukt: Chromium reageert niet. Als hij net (her)start, probeer het over een halve minuut opnieuw.", + request.referrer or url_for("ui.dashboard"), + ) def _helper_action(event: str, verb: str, *args: str, back: str): diff --git a/tests/test_browser.py b/tests/test_browser.py index fb302ec..93b27e3 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -78,3 +78,17 @@ def test_local_pages_exist(): www = Path(__file__).resolve().parents[1] / "www" for page in ("start.html", "offline.html", "setup.html", "kiosk.css", "logo.svg"): assert (www / page).exists() + + +def test_prepare_display_waits_for_a_screen_only_after_boot(paths, monkeypatch): + calls = [] + monkeypatch.setattr(b, "_run", lambda args: calls.append(args) or None) + monkeypatch.setattr(b.time, "sleep", lambda s: None) + monkeypatch.setattr(b.sysinfo, "uptime_seconds", lambda: 3600.0) + assert b.prepare_display(paths, dict(DEFAULTS)) is None + assert sum(c == ["xrandr", "--query"] for c in calls) == 1 + + calls.clear() + monkeypatch.setattr(b.sysinfo, "uptime_seconds", lambda: 40.0) + b.prepare_display(paths, dict(DEFAULTS)) + assert sum(c == ["xrandr", "--query"] for c in calls) == 15