From 5adab6c518ba3279184004ce6f949be7aab5529b Mon Sep 17 00:00:00 2001 From: christian Date: Thu, 15 Oct 2026 14:30:41 -0400 Subject: [PATCH] Settings page: ON/OFF switches everywhere, the Auto switch screen, a Companion button picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ON/OFF on every key, check, fix, Restart and READY key, computer, MIDI link, HyperDeck, Dante device, watched Companion connection, switcher and Spotify check, outlet fix, and one main switch each for auto-fix, the auto switch and the READY page. Switched off = kept in the settings, grey OFF on the deck, never checked or run (effective_config in the watchdog). - Settings › Auto switch: a plain summary, what counts as down (each trigger editable in place: address, program and computer, Dante device, the button it presses, timings), the hours folded away, the buttons; + Button, + What counts as down, Remove. - Pick a Companion button by clicking it on its page, drawn like Companion (read-only API: /api/companion/pages and /api/companion/page). - STREAM STB / STREAM FOH Restart keys removed (the Studio B page has its own); the alert names the switch-back button instead. - Dante screen: shows the DANTE key's device list again (it picked STB AUDIO's one device). Co-Authored-By: Claude Opus 5.5 --- README.md | 8 +- config.json | 18 -- restart-page.companionconfig | Bin 3809 -> 3574 bytes schema.py | 24 +- settings/probes.py | 2 + settings/server.py | 80 ++++++- settings/ui/app.js | 417 +++++++++++++++++++++++++++++++---- settings/ui/style.css | 15 ++ status_panel.py | 61 ++++- tests/test_schema.py | 2 + tests/test_status_panel.py | 118 +++++++++- 11 files changed, 663 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 0a9d777..1a4f65b 100644 --- a/README.md +++ b/README.md @@ -89,14 +89,14 @@ The stream's audio comes in on the Dante device STUDIO-A-Resi-AJA-SDI, channels - **When:** inside the `"failover"` windows (Sunday 06:30–13:30 today), even while ALARMS is muted. Once per fault: it re-arms once the fault has been clear for 10 s (a check that cannot ask, say an SSH hiccup, does not count as clear). - **How fast:** about 5–6 s from a crash to the press (2–3 s to notice, 1 s debounce, 2 s `after_seconds`). A vanished Dante device takes as long as Companion's Dante module needs to notice it. - **Unlike auto-fix:** it does not wait 20 s, does not stop for a network blip, and is not limited to 2 tries per 10 minutes. The one guard: when several Dante devices vanish at once (that is NA Ingest's view of Dante, not Studio B) it holds back, like auto-fix. -- **What the keys show:** AUDIO (and the hidden STB AUDIO) carry an extra line **ON FOH BACKUP** while the fault lasts and for 15 minutes after (`note_seconds`), or until STREAM STB is held. Hold AUDIO right after and it says JUST TRIED. +- **What the keys show:** AUDIO (and the hidden STB AUDIO) carry an extra line **ON FOH BACKUP** while the fault lasts and for 15 minutes after (`note_seconds`). Hold AUDIO right after and it says JUST TRIED. - **Log and alert:** every press goes to the daily auto-fix log as `FAILOVER:` lines, to the watchdog log, and to `alert_webhook` if set. -- **Switching back is yours.** It never switches back by itself: when Studio B is up again, hold **STREAM STB** on the Restart page for 2 s (the fix's `"undo"`). That presses Studio B on the Audio page and clears ON FOH BACKUP. +- **Switching back is yours.** It never switches back by itself: when Studio B is up again, press **Studio B** on Companion's Studio B page (page 7) or Audio page (page 4). The alert names that button (the fix's `"undo"`). - **Never:** a press outside the window, a second press for the same fault, or a press back to Studio B. In `config.json`: the `"failover"` block (`windows`, `after_seconds` 2, `note_seconds` 900); `"failover": true` and `"undo"` on the fix (`stream_foh_backup` undoes with `stream_studio_b`); `"interval_seconds"` on ping checks (default 3) and remote checks (default 10). The remote poll runs each computer's checks in its own thread, so a dead Studio B never delays the checks on another Mac. -**Another fallback button:** any Companion button can be a fix. On the settings page, Keys › Fixes › + Fix › "Press a Companion button" (page, row, column and what the button says), tick failover under more and pick its undo; Settings › Auto-fix › Failover calendar holds the window and `after_seconds`; Keys › + Key / + check… adds the checks that call it (a ping with interval seconds and failures, a program on a Mac, a Dante device) with fix = the press; Keys › Restart page › + Restart key adds the keys that press it by hand. The Help page "How do I add…" has an entry "A fallback button". +**Another fallback button:** any Companion button can be a fix. On the settings page, Keys › Fixes › + Fix › "Press a Companion button" (page, row, column and what the button says), tick failover under more and pick its undo; **Settings › Auto switch** has it all on one screen: which buttons switch by themselves (tick, page/row/column, the button to switch back with, Press it now), the hours (calendar), `after_seconds` and `note_seconds`, and what counts as down (how often each check looks, how many misses, how long a fault must last). Keys › + Key / + check… adds more checks that call it (a ping, a program on a Mac, a Dante device) with fix = the press. The Help page "How do I add…" has an entry "A fallback button". ## Restart page (page 20) @@ -112,8 +112,6 @@ Tap **RESTART PAGE** on the status page (row 2). Each key restarts one thing **e | PRODDASH / CAPTIONS / BRIDGE | that service on NA Ingest | | TASCAM | reconnects Companion to the recorder (see TASCAM above) | | SPOTIFY | plays the Christian EDM 2026 playlist on NA Ingest (opens Spotify first if it is closed) | -| STREAM STB | presses Studio B on Companion's Audio page: the stream audio back on Studio B (how you switch back after a failover, see Audio failover) | -| STREAM FOH | presses FOH Backup on Companion's Audio page: the stream audio from the FOH console. Flashes HOLD TO FIX while Studio B is down; once the failover has pressed it, it shows ON FOH BACKUP (green) instead. | A pressing key says PRESSING, then DONE; a press that finds no button there says NO BUTTON THERE. diff --git a/config.json b/config.json index f68c079..6615fbc 100644 --- a/config.json +++ b/config.json @@ -1049,24 +1049,6 @@ "watch": [ "conn:tascam" ] - }, - { - "id": "rs_stream_stb", - "label": "STREAM STB", - "pos": [ - 3, - 2 - ], - "fix": "stream_studio_b" - }, - { - "id": "rs_stream_foh", - "label": "STREAM FOH", - "pos": [ - 3, - 3 - ], - "fix": "stream_foh_backup" } ] }, diff --git a/restart-page.companionconfig b/restart-page.companionconfig index 7c451322ef948334ccbc940dbde787b36b4e6be1..1458889c5f6f5794d58215dc8521bcaeffa7fe67 100644 GIT binary patch delta 3545 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 Ae*gdg diff --git a/schema.py b/schema.py index 2a56dde..8cb99a5 100644 --- a/schema.py +++ b/schema.py @@ -113,6 +113,8 @@ field("auto_fix.windows[].start", "hhmm", None, "", "Schedules", required=True) field("auto_fix.windows[].end", "hhmm", None, "", "Schedules", required=True) field("auto_fix.while_muted", "list", [], "Auto-fixes that keep running inside the window even while ALARMS is " "muted (harmless ones, like playing the Spotify playlist).", "Schedules") +field("auto_fix.disabled", "bool", False, "Auto-fix switched off as a whole: nothing is fixed by itself (holds " + "still work).", "Schedules") field("auto_fix.after_seconds", "int", 20, "A fault must last this long before auto-fix acts.", "Schedules", advanced=True, lo=5, hi=600) field("auto_fix.max_tries", "int", 2, "Tries per fault, then it is yours to hold.", "Schedules", advanced=True, @@ -130,6 +132,8 @@ field("auto_fix.keep_months", "int", 1, "Daily log files are deleted after this advanced=True, lo=1, hi=12) field("failover", "dict", {}, "Automatic fallback: when a fix marked failover may act (it acts even while ALARMS is " "muted).", "Schedules") +field("failover.disabled", "bool", False, "The auto switch switched off as a whole: nothing is pressed by itself.", + "Schedules") field("failover.windows", "list", [{"days": ["sun"], "start": "06:30", "end": "13:30"}], "When a failover may switch " "by itself.", "Schedules") field("failover.windows[].days", "days", None, "", "Schedules", required=True) @@ -184,6 +188,8 @@ field("hosts.*.ssh", "str", None, "How this Mac logs in: user@address.", "Comput field("hosts.*.mac", "mac", None, "The PC's hardware address, to wake it when it is off.", "Computers") field("hosts.*.wake_broadcast", "ip", None, "The broadcast address the wake-up goes to.", "Computers", advanced=True) field("hosts.*.name", "str", None, "The computer, as people say it.", "Computers", apply="none") +field("hosts.*.disabled", "bool", False, "Switched off: kept, but nothing logs in to it - its checks and fixes are " + "skipped (a computer that is away or being rebuilt).", "Computers") field("hosts.*.os", "enum", "mac", "Mac or Windows (a PC's checks are written for cmd.exe).", "Computers", options=["mac", "windows", "other"]) field("hosts.*.role", "str", None, "What it is for.", "Computers", apply="none") @@ -229,6 +235,8 @@ field("fixes.*.mac", "mac", None, "The device expected on that port.", "Fixes") field("fixes.*.playlist", "str", None, "A playlist other than the site's (rare).", "Fixes", advanced=True) field("fixes.*.template", "str", None, "The builder this fix was made with.", "Fixes", advanced=True, apply="none") field("fixes.*.params", "dict", None, "The builder's fields.", "Fixes", advanced=True, apply="none") +field("fixes.*.disabled", "bool", False, "Switched off: kept, but never runs (a hold says TURNED OFF; auto-fix " + "and the auto switch skip it).", "Fixes") field("fixes.*.action", "enum", None, "What a READY page fix does.", "Fixes", options=list(sp.READY_ACTIONS)) field("fixes.*.page", "int", None, "The Companion page the button is on.", "Fixes", lo=1) field("fixes.*.row", "int", None, "The button's row on that page (the top row is 0).", "Fixes", lo=0) @@ -250,6 +258,10 @@ field("tiles[].kind", "enum", None, "A special key: master (ALL), service (ALARM options=["master", "service", "network", "ready"], apply="pages") field("tiles[].members", "list", None, "A group: the keys it sums up.", "Keys") field("tiles[].checks", "list", [], "What the key watches.", "Keys") +field("tiles[].disabled", "bool", False, "Switched off: the key stays on the page (grey, OFF) but checks nothing " + "and fixes nothing, like a connection switched off in Companion.", "Keys") +field("tiles[].checks[].devices_off", "list", [], "Dante devices switched off: kept in the list, not watched.", "Keys") +field("tiles[].checks[].disabled", "bool", False, "Switched off: this check is kept but not looked at.", "Keys") field("tiles[].hold_always", "str", None, "A fix a hold runs even when the key is green.", "Keys", apply="pages") field("tiles[].checks[].type", "enum", None, "What kind of check.", "Keys", required=True) field("tiles[].checks[].conn", "str", None, "The Companion connection.", "Keys") @@ -316,6 +328,8 @@ field("restart_page.keys[].id", "id", None, "", "Keys", advanced=True, required= field("restart_page.keys[].label", "str", None, "", "Keys", required=True, apply="pages") field("restart_page.keys[].pos", "list", None, "[row, column].", "Keys", required=True, apply="pages") field("restart_page.keys[].fix", "str", None, "What holding it runs.", "Keys", required=True) +field("restart_page.keys[].disabled", "bool", False, "Switched off: the key stays on the page (OFF) but a hold does " + "nothing.", "Keys", apply="pages") field("restart_page.keys[].watch", "list", [], "Keep flashing HOLD TO FIX while these fixes' checks or " "'conn: