ALARMS and AUTO-FIX as two keys; LIGHTS at Southcoast hands the lights to an operator

- kind autofix: its own switch on the auto-fix hours; ALARMS then only silences (NA keeps the old
  combined key until it has room for one)
- kind bypass: hold takes every network MIDI link to a peer out (here and on listed Macs), checks stop
  calling it lost, re-taken every 10 s; hold again puts back exactly what was taken plus every watched link
- a MIDI peer with no address is never called (it connected SC Tracks to itself)
- holds from NA's CAMPUS page flip switch keys; a rebuild keeps them; new campuses get AUTO-FIX
- key kind changes restart the watchdog; settings page has separate alarm / auto-fix buttons
- WS and SC configs: AUTO-FIX at 3,1; SC LIGHTS at 1,3; NA config synced from live

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 13:33:48 -04:00
co-authored by Claude Opus 5.5
parent 2f8e3d47a3
commit d0c755824b
15 changed files with 605 additions and 44 deletions
+24 -5
View File
@@ -258,8 +258,12 @@ field("tiles[].label", "str", None, "The first line on the key.", "Keys", requir
field("tiles[].pos", "list", None, "[row, column]; absent = no key of its own (shown inside a group).", "Keys",
apply="pages")
field("tiles[].kind", "enum", None, "A special key: master (ALL), service (ALARMS), network (NET), ready (READY: "
"opens the READY page and sums up its checklist).", "Keys", advanced=True,
options=["master", "service", "network", "ready"], apply="pages")
"opens the READY page and sums up its checklist), autofix (AUTO-FIX: its own switch, so ALARMS only "
"silences), bypass (hold: hand a device to an operator - its MIDI links out, its Companion connections off).",
"Keys", advanced=True, options=["master", "service", "network", "ready", "autofix", "bypass"])
field("tiles[].bypass", "dict", None, "A bypass key: peers (MIDI peer or session names to take out, e.g. Lighting), "
"hosts (the Macs whose sessions to look in; \"\" is this Mac) and conns (Companion connections to switch off).",
"Keys", advanced=True)
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 "
@@ -491,7 +495,10 @@ GLOSSARY = {
"HOLD", "REC", "PLAYING", "OK", "OFF", "ON", "ALL OK", "WARN", "TRS", "GB", "NOT", "REACHABLE", "RECORDING",
"LEFT ALONE", "TO FIX", "TO CLEAR", "NO AUTO", "FIX", "AUTO", "IN", "COMPANION", "POWER", "CYCLE",
"STILL", "OFF IN", "DISABLED IN", "CONNECTING", "CHECK", "NETWORK", "NO", "PRESSING", "NO BUTTON THERE",
"ON FOH BACKUP",
"ON FOH BACKUP", "BUSY", "TAKING MIDI OUT", "PUTTING MIDI BACK", "TAKING", "PUTTING", "MIDI OUT", "MIDI BACK",
],
"a switch key (AUTO-FIX, or one like LIGHTS that hands a device to an operator)": [
"OPERATOR", "MIDI OFF", "OFF IN SETTINGS", "SETTINGS", "TIL",
],
}
@@ -583,7 +590,7 @@ def validate(cfg, live=None):
if errors:
return errors, warnings
ids, seen_pos = set(), {}
kinds = {"master": 0, "service": 0, "network": 0}
kinds = {"master": 0, "service": 0, "network": 0, "autofix": 0}
fixes, hosts = cfg.get("fixes", {}), cfg.get("hosts", {})
for t in cfg["tiles"]:
tid = t.get("id", "")
@@ -659,6 +666,16 @@ def validate(cfg, live=None):
errors.append("there must be exactly one ALARMS key (kind service); found %d" % kinds["service"])
if kinds["network"] > 1:
errors.append("at most one NET key (kind network); found %d" % kinds["network"])
if kinds["autofix"] > 1:
errors.append("at most one AUTO-FIX key (kind autofix); found %d" % kinds["autofix"])
for t in cfg["tiles"]:
if t.get("kind") == "bypass":
b = t.get("bypass") if isinstance(t.get("bypass"), dict) else {}
if not b.get("peers"):
errors.append("key %s: a bypass key needs bypass.peers (the MIDI peers it takes out)" % t.get("id"))
for h in b.get("hosts") or []:
if h and h not in cfg.get("hosts", {}):
errors.append("key %s: bypass host %s is not a computer in the settings" % (t.get("id"), h))
for fid, fix in fixes.items():
where = "fix %s" % fid
kind = fix.get("kind", "ssh")
@@ -917,7 +934,9 @@ def phrases(cfg):
"failover": "switches by itself %s" % sp.windows_phrase(fo_windows) if fo_windows else "never switches by itself",
"live": ("changes wait while a HyperDeck is recording" + (" or %s" % sp.windows_phrase(site["live_services"])
if site.get("live_services") else "")),
"alarms": "Alarms are on every day. The ALARMS key silences them and stops every auto-fix until midnight, then both come back by themselves.",
"alarms": ("Alarms are on every day. The ALARMS key silences them until midnight; AUTO-FIX pauses or resumes "
"auto-fix on its own. Both come back by themselves." if any(t.get("kind") == "autofix" for t in cfg.get("tiles", []))
else "Alarms are on every day. The ALARMS key silences them and stops every auto-fix until midnight, then both come back by themselves."),
"hold": "HOLD %gs" % (cfg.get("page_hold_ms", 2000) / 1000.0),
"ingest": site.get("ingest_name", "NA Ingest"),
}