NET blip flashes, clears itself after 5 minutes, hold NET clears it; Restart page is now page 20

- The remembered network blip is a flashing amber warning (new "notice"
  level, not counted as DOWN): press NET to stop the flashing, hold NET 2 s
  to clear it, otherwise it goes by itself after 5 minutes (was 3, solid).
- The user moved the Restart page to page 20: config, the RESTART PAGE key
  on page 19 and both pages re-pushed. A scan of all pages finds no other
  button pointing at the old page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 18:38:19 -04:00
co-authored by Claude Opus 5.5
parent 482a0a4f82
commit 6410525681
8 changed files with 61 additions and 22 deletions
+21 -8
View File
@@ -40,13 +40,14 @@ OK, WARN, ERR, OFF = "ok", "warn", "err", "off"
# Display levels, least to most urgent. "idle" is a service-only fault while
# service mode is off (OBS PC off midweek); "quiet" is any other fault then.
ORDER = ["off", "ok", "idle", "warn", "quiet", "ack", "err"]
ORDER = ["off", "ok", "idle", "warn", "notice", "quiet", "ack", "err"]
RANK = {name: i for i, name in enumerate(ORDER)}
# (background, text) for the flash-on phase and the flash-off phase.
COLORS = {
"ok": ((0x007A2E, 0xFFFFFF), (0x007A2E, 0xFFFFFF)),
"warn": ((0xFFA000, 0x000000), (0xFFA000, 0x000000)),
"notice": ((0xFFA000, 0x000000), (0x000000, 0xFFA000)), # flashing amber: something happened (NET BLIP)
"quiet": ((0x3A0000, 0xFF8080), (0x3A0000, 0xFF8080)),
"ack": ((0x8B0000, 0xFFFFFF), (0x8B0000, 0xFFFFFF)),
"err": ((0xFF0000, 0xFFFFFF), (0x000000, 0xFF3030)),
@@ -60,14 +61,15 @@ COLORS = {
class Result:
"""One check's verdict. `named` means msg already says which device it is."""
__slots__ = ("level", "msg", "info", "service_only", "named")
__slots__ = ("level", "msg", "info", "service_only", "named", "flash")
def __init__(self, level, msg="", info="", service_only=False, named=False):
def __init__(self, level, msg="", info="", service_only=False, named=False, flash=False):
self.level = level
self.msg = msg
self.info = info
self.service_only = service_only
self.named = named
self.flash = flash # a warning that flashes until acknowledged (NET BLIP)
def __repr__(self):
return "Result(%s, %r, info=%r)" % (self.level, self.msg, self.info)
@@ -715,9 +717,10 @@ class Engine:
else:
self.burst["cleared"] = now
if self.burst["cleared"] is not None:
if now - self.burst["cleared"] < self.net.get("remember_seconds", 180):
if now - self.burst["cleared"] < self.net.get("remember_seconds", 300):
stamp = datetime.fromtimestamp(self.burst["start"]).strftime("%H:%M")
out.append(("burst", Result(WARN, "BLIP " + stamp)))
# Flashes (to be noticed) until acknowledged, then goes by itself; a hold clears it.
out.append(("burst", Result(WARN, "BLIP " + stamp, flash=True)))
else:
self.burst = None
if not any(sub == "burst" for sub, _ in out):
@@ -873,6 +876,16 @@ class Engine:
self.notes[tile_id] = {"text": text, "level": level, "until": now + seconds}
def run_fixes(self, tile_id, snap, comp, now, cooldown=60):
if self.tiles.get(tile_id, {}).get("kind") == "network":
# NET: a hold clears a remembered network blip (link and gateway clear themselves).
if self.burst is None:
self.note(tile_id, "NOTHING\nTO CLEAR", "idle", now, 4)
else:
log.info("HOLD net: network blip cleared by hand")
self.burst = None
self.conn_drops.clear()
self.note(tile_id, "CLEARED", "ok", now, 3)
return []
plan = self.fix_plan(tile_id, snap)
always = self.tiles.get(tile_id, {}).get("hold_always")
if not plan and always:
@@ -988,7 +1001,7 @@ def display_level(res, acked, service_on):
if res.level == OK:
return "ok"
if res.level == WARN:
return "warn"
return "notice" if res.flash and not acked else "warn"
if res.service_only and not service_on:
return "idle"
if not service_on:
@@ -1003,7 +1016,7 @@ def worst(items):
level = max((lv for lv, _, _ in items), key=lambda lv: RANK[lv])
if all(lv == "off" for lv, _, _ in items):
level = "off"
faults = [it for it in items if it[0] in ("idle", "warn", "quiet", "ack", "err")]
faults = [it for it in items if it[0] in ("idle", "warn", "notice", "quiet", "ack", "err")]
faults.sort(key=lambda it: (-RANK[it[0]], it[2]))
return level, faults
@@ -1075,7 +1088,7 @@ def render(engine, service_on):
text, level = "ALL OK", "ok"
else:
first = next(fault_text(t, r) for lv, r, s, t in items if (lv, r, s) == faults[0])
n_bad = sum(1 for f in faults if f[0] != "warn")
n_bad = sum(1 for f in faults if f[0] not in ("warn", "notice"))
text = ("%d DOWN" % n_bad if n_bad else "%d WARN" % len(faults)) + "\n" + first
out[tid] = colored(text, level)
continue