From 4f6ee83a1b30501265298f83ec308b3dffdc9698 Mon Sep 17 00:00:00 2001 From: acidburns Date: Fri, 13 Feb 2026 02:50:44 +0100 Subject: [PATCH] Reduce status polling load and add 1Hz ready LED blink --- src/main.py | 29 +++++++++++++++++++++++++++++ src/webapp.py | 10 +++++++++- templates/index.html | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index 0da9310..6daae85 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ import logging +import os import signal import threading import time @@ -14,6 +15,33 @@ from webapp import WebPortal LOG = logging.getLogger("serial-bridge") +def _configure_ready_led_blink() -> None: + led_bases = [ + "/sys/class/leds/PWR", + "/sys/class/leds/led1", + "/sys/class/leds/ACT", + "/sys/class/leds/led0", + ] + for base in led_bases: + trigger_path = os.path.join(base, "trigger") + delay_on_path = os.path.join(base, "delay_on") + delay_off_path = os.path.join(base, "delay_off") + if not (os.path.exists(trigger_path) and os.path.exists(delay_on_path) and os.path.exists(delay_off_path)): + continue + try: + with open(trigger_path, "w", encoding="utf-8") as f: + f.write("timer\n") + with open(delay_on_path, "w", encoding="utf-8") as f: + f.write("500\n") + with open(delay_off_path, "w", encoding="utf-8") as f: + f.write("500\n") + LOG.info("Ready LED blink enabled on %s (1 Hz)", base) + return + except Exception as exc: + LOG.warning("Failed to configure LED blink on %s: %s", base, exc) + LOG.warning("No controllable LED found for ready blink") + + class Supervisor(threading.Thread): def __init__(self, state: AppState, nm: NetworkManager, rtc: RTCAndNTPManager) -> None: super().__init__(daemon=True) @@ -133,6 +161,7 @@ def main() -> None: name="web-portal", ) web_thread.start() + _configure_ready_led_blink() while not stop_event.wait(1): if not web_thread.is_alive(): diff --git a/src/webapp.py b/src/webapp.py index 15488b1..366d91b 100644 --- a/src/webapp.py +++ b/src/webapp.py @@ -2,6 +2,7 @@ import json import queue import subprocess import threading +import time from typing import Any, Dict from flask import Flask, Response, jsonify, render_template, request, stream_with_context @@ -24,6 +25,9 @@ class WebPortal: self.state = state self.network_manager = network_manager self.broadcaster = broadcaster + self._status_refresh_interval_s = 15.0 + self._last_status_refresh_mono = 0.0 + self._status_lock = threading.Lock() self.app = Flask(__name__, template_folder=template_folder, static_folder=static_folder) self._register_routes() @@ -39,7 +43,11 @@ class WebPortal: @self.app.route("/api/status", methods=["GET"]) def status() -> Response: try: - self.network_manager.refresh_state() + now = time.monotonic() + with self._status_lock: + if now - self._last_status_refresh_mono >= self._status_refresh_interval_s: + self.network_manager.refresh_state() + self._last_status_refresh_mono = now return jsonify(self.state.snapshot()) except Exception as exc: self.state.update_status("Status update failed", str(exc)) diff --git a/templates/index.html b/templates/index.html index f665bbd..73eab09 100644 --- a/templates/index.html +++ b/templates/index.html @@ -146,7 +146,7 @@ document.getElementById('shutdownBtn').addEventListener('click', () => triggerSystemAction('shutdown')); refreshStatus(); - setInterval(refreshStatus, 5000); + setInterval(refreshStatus, 15000);