From 64105256815ba6d6db500263a38e29d9a5a36373 Mon Sep 17 00:00:00 2001 From: christian Date: Thu, 8 Oct 2026 18:38:19 -0400 Subject: [PATCH] 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 --- README.md | 4 ++-- config.json | 4 ++-- make_page.py | 6 ++++-- push_page.py | 2 +- restart-page.companionconfig | Bin 3477 -> 3478 bytes status-page.companionconfig | Bin 5831 -> 5914 bytes status_panel.py | 29 ++++++++++++++++++-------- tests/test_status_panel.py | 38 ++++++++++++++++++++++++++++------- 8 files changed, 61 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 5e7aef0..c3b4732 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Row 0 holds the group buttons. Each one shows the first problem in its group, pl It checks over SSH every 10 s that ProPresenter, Ableton and Church Translate are running, using NA Ingest's key `~/.ssh/id_status_panel`. That key was authorized on the Macs with `remote-access/mac-authorize.sh`. - **ALARMS:** alarms are armed all the time (`service_windows` in `config.json`: every day, 00:00–23:59). Press ALARMS to mute the blinking, for example during maintenance. It re-arms itself at midnight. -## Restart page (page 23) +## Restart page (page 20) Tap **RESTART PAGE** on the status page (row 2). Each key restarts one thing **even when the status page says it is fine**; hold it 2 s. It shows RESTARTING, then DONE (or FAILED / what to do). The same thing will not run twice within a minute. **STATUS PAGE** (bottom left) goes back. @@ -106,7 +106,7 @@ The **page number key** on every page follows the ALL button. It's green when ev | APPS | ProPresenter API (offline or error only: its harmless yellow warning, every 15–20 min, is ignored with `"ignore_warning": true`); Ableton OSC; tracks Mac pings; Bible TV app answering on :8080 (every 5 s); Bible TV OSC. Messages name the app ("PROPRES OFFLINE", "BIBLE APP DOWN"). Hold 2 s restarts the ProPresenter, Ableton and Bible TV Companion connections. | | VISTA | Vista on the Lighting PC: its Art-Net poll (sent every 0.5 s) must be heard within 5 s, otherwise **SILENT** (Vista closed or crashed, or the Lighting PC is off the network). Hold 2 s starts Vista if it is not running; Vista then opens **Prop Takeover** from its show folder (`E:\media\Documents\Vista 3\Shows`) by itself, through its own autoload preference (`` in `E:\media\Documents\Vista 3\.preferences`, set 2026-10-08). Resolume is the ARENA button. | | RESI | decoder (RossTalk connection and ping, 10.1.10.203); **encoder** (ping and its web page, 10.1.10.202, LAOEncoder); resi-studio-cues; Resi cloud reachable. Only shows they are on the network, not that the encoder is streaming. | -| NET | NA Ingest's link; gateway 10.1.0.1 pings; **4+ connections from 3+ groups dropping within 10 s** (what happened Sunday 10/5 at 10:21) | +| NET | NA Ingest's link; gateway 10.1.0.1 pings; **4+ connections from 3+ groups dropping within 10 s** (what happened Sunday 10/5 at 10:21). A blip **flashes amber** for 5 minutes, then goes by itself; press NET to stop the flashing, hold NET 2 s to clear it now. | | INGEST | the bridge (makes ProPresenter's stage screen count down to the next Resi cue), lobby captions server and caption screen, **ProdCom** (transcription, answering on :24480), ProdDash (running and its page on :24500); free disk | | RF (group) | Shure connections; battery ≤1 bar red, 2 bars amber; RF interference (only while that transmitter is on); PSM RF muted; each receiver pinged every 3 s ("AXIENT 1 NO PING" in about 6–9 s; Companion's Shure modules take 18–27 s to notice). The address is the one set on its Companion connection, read from Companion's settings every minute, so changing it there is enough; `"host"` in `config.json` is only the fallback. | diff --git a/config.json b/config.json index 51fec2e..5387384 100644 --- a/config.json +++ b/config.json @@ -20,7 +20,7 @@ "burst_connections": 4, "burst_groups": 3, "burst_window_seconds": 10, - "remember_seconds": 180 + "remember_seconds": 300 }, "alert_webhook": "", @@ -78,7 +78,7 @@ }, "restart_page": { - "page": 23, "page_name": "23\\nRestart", "jump_pos": [2, 6], "back_pos": [3, 0], + "page": 20, "page_name": "20\\nRestart", "jump_pos": [2, 6], "back_pos": [3, 0], "keys": [ { "id": "rs_dvs_prop", "label": "DVS PROP", "pos": [0, 1], "fix": "dvs_prop" }, { "id": "rs_dvs_tracks", "label": "DVS TRACKS", "pos": [0, 2], "fix": "dvs_tracks" }, diff --git a/make_page.py b/make_page.py index 02e0772..4f3691f 100644 --- a/make_page.py +++ b/make_page.py @@ -166,8 +166,10 @@ def button(tid, tile, cfg): press = "ack " + tid fixable = "members" in tile or any((c["type"] in RESTARTABLE and c.get("conn")) or c.get("fix") for c in tile.get("checks", [])) - hold = ("restart " + tid) if fixable or tile.get("hold_always") else None - if tile.get("hold_always"): + hold = ("restart " + tid) if fixable or tile.get("hold_always") or kind == "network" else None + if kind == "network": + note = "Press: stop the flashing. Hold 2 s: clear a network blip (it also clears itself after 5 minutes)." + elif tile.get("hold_always"): note = "Press: acknowledge. Hold 2 s: %s." % cfg["fixes"][tile["hold_always"]]["label"] else: note = "Press: acknowledge." + (" Hold 2 s: fix what this button is failing on (nothing if it is green)." diff --git a/push_page.py b/push_page.py index 6df100a..e8e256b 100644 --- a/push_page.py +++ b/push_page.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Import the status page (19) or the Restart page (23) into Companion without clicking through the UI. +"""Import the status page (19) or the Restart page (20; both numbers come from config.json) into Companion without clicking through the UI. Does exactly what Import/Export -> Import -> "import into page N" does, over the same internal API the Companion web UI uses (tRPC over a WebSocket at /trpc): diff --git a/restart-page.companionconfig b/restart-page.companionconfig index 6cb2fed9f0aaa9bd4b7b99db3f23d860d514cb51..747487b8af7437295a1bcefbca0b170c0e1dc2d6 100644 GIT binary patch delta 3452 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 delta 3422 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 diff --git a/status_panel.py b/status_panel.py index adf95a8..856265c 100644 --- a/status_panel.py +++ b/status_panel.py @@ -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 diff --git a/tests/test_status_panel.py b/tests/test_status_panel.py index c5978f8..11bab45 100644 --- a/tests/test_status_panel.py +++ b/tests/test_status_panel.py @@ -85,8 +85,8 @@ def run_steps(engine, snap, start, seconds, step=1.0): def level(tile): - for name, ((b, f), _) in sp.COLORS.items(): - if (tile["b"], tile["f"]) == (b, f): + for name, ((b, f), (b2, f2)) in sp.COLORS.items(): + if (tile["b"], tile["f"], tile["b2"], tile["f2"]) == (b, f, b2, f2): return name return "?" @@ -513,9 +513,32 @@ class TestBehaviour(unittest.TestCase): self.snap.conns[label]["category"] = "good" tiles, t = run_steps(engine, self.snap, t, 1) self.assertTrue(tiles["net"]["x"].startswith("NET\nBLIP ")) - self.assertEqual(level(tiles["net"]), "warn") + self.assertEqual(level(tiles["net"]), "notice") # flashes amber + self.assertTrue(tiles["all"]["x"].startswith("1 WARN\nNET BLIP ")) # a warning, not a DOWN tiles, _ = run_steps(engine, self.snap, t + 200, 1) - self.assertEqual(tiles["net"]["x"], "NET\nOK") + self.assertTrue(tiles["net"]["x"].startswith("NET\nBLIP ")) # still up after 3+ minutes + tiles, _ = run_steps(engine, self.snap, t + 320, 1) + self.assertEqual(tiles["net"]["x"], "NET\nOK") # gone by itself after 5 + + def test_network_blip_press_and_hold(self): + engine = sp.Engine(CONFIG) + t = SUNDAY + run_steps(engine, self.snap, t, 2) + drop = ("x32", "VX1000", "UHDJr", "RESI_Decoder", "tascam") + for label in drop: + self.snap.conns[label]["category"] = "error" + _, t = run_steps(engine, self.snap, t + 3, 4) + for label in drop: + self.snap.conns[label]["category"] = "good" + tiles, t = run_steps(engine, self.snap, t, 1) + engine.ack("net") # press: stop the flashing + tiles, t = run_steps(engine, self.snap, t, 1) + self.assertEqual((tiles["net"]["x"].split("\n")[1][:5], level(tiles["net"])), ("BLIP ", "warn")) + engine.run_fixes("net", self.snap, None, t) # hold: gone now + tiles, t = run_steps(engine, self.snap, t + 4, 1) + self.assertEqual((tiles["net"]["x"], tiles["all"]["x"]), ("NET\nOK", "ALL OK")) + engine.run_fixes("net", self.snap, None, t) + self.assertEqual(engine.notes["net"]["text"], "NOTHING\nTO CLEAR") def test_racks_powering_off_is_not_a_network_event(self): engine = sp.Engine(CONFIG) @@ -1253,13 +1276,13 @@ class TestPage(unittest.TestCase): n = sum(1 for r in self.controls.values() for c in r.values() if c["type"] == "button-layered") self.assertEqual(n, len(positioned) + 2) # + the page number and RESTART keys jump = self.controls["2"]["6"]["steps"]["0"]["action_sets"]["up"][0] - self.assertEqual((jump["definitionId"], jump["options"]["page"]["value"]), ("set_page", 23)) + self.assertEqual((jump["definitionId"], jump["options"]["page"]["value"]), ("set_page", 20)) # The master is where the smallest Stream Deck can see it. self.assertEqual(CONFIG["tiles"][0]["pos"], [0, 1]) def test_restart_page(self): export = make_page.build_restart(CONFIG) - self.assertEqual((export["oldPageNumber"], export["page"]["name"]), (23, "23\\nRestart")) + self.assertEqual((export["oldPageNumber"], export["page"]["name"]), (20, "20\\nRestart")) c = export["page"]["controls"] holds = {} for r in c.values(): @@ -1310,7 +1333,8 @@ class TestPage(unittest.TestCase): self.assertIn("2000", ingest["action_sets"]) # restarts its services self.assertIn("2000", self.controls["0"]["2"]["steps"]["0"]["action_sets"]) # REC group net = self.controls["3"]["4"]["steps"]["0"] - self.assertNotIn("2000", net["action_sets"]) # nothing to fix there + self.assertEqual(net["action_sets"]["2000"][0]["options"]["value"]["value"], # hold: clear a blip + "$(custom:sp_cmd);restart net $(internal:time_unix)") ids = [a["id"] for r in self.controls.values() for c in r.values() for s in c.get("steps", {}).values() for al in s["action_sets"].values() for a in al] self.assertEqual(len(ids), len(set(ids)))