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:
@@ -570,6 +570,19 @@ def keep_from(new, old):
|
||||
cfg = copy.deepcopy(new)
|
||||
have = referenced(cfg)
|
||||
kept = []
|
||||
# Switch keys made for this campus by hand (Southcoast's LIGHTS) and an AUTO-FIX key: auto-setup makes neither.
|
||||
for t in old.get("tiles", []):
|
||||
if t.get("kind") not in ("bypass", "autofix") or t["id"] in {x["id"] for x in cfg["tiles"]}:
|
||||
continue
|
||||
if t["kind"] == "autofix" and any(x.get("kind") == "autofix" for x in cfg["tiles"]):
|
||||
continue
|
||||
used = {tuple(x["pos"]) for x in cfg["tiles"] if x.get("pos")} | (
|
||||
{tuple(cfg["restart_page"]["jump_pos"])} if cfg.get("restart_page") else set())
|
||||
if t.get("pos") and tuple(t["pos"]) not in used:
|
||||
cfg["tiles"].append(copy.deepcopy(t))
|
||||
else:
|
||||
place_new(cfg, {k: v for k, v in copy.deepcopy(t).items() if k != "pos"})
|
||||
kept.append(t["label"])
|
||||
# Computers in the old setup that did not answer this time, and the keys that check them.
|
||||
gone = {a: h for a, h in (old.get("hosts") or {}).items() if not h.get("self") and a not in cfg["hosts"]}
|
||||
for alias, h in gone.items():
|
||||
|
||||
@@ -163,6 +163,14 @@ def key_guide(cfg):
|
||||
parts.append("<p>Shows the worst of: %s. Hold: %s.</p>" % (", ".join(e(tiles[m]["label"]) for m in t["members"]), hold_words(t, cfg)))
|
||||
if t.get("kind") == "service":
|
||||
parts.append("<p>%s</p>" % e(ph["alarms"]))
|
||||
if t.get("kind") == "autofix":
|
||||
parts.append("<p>Press: pause auto-fix, or run it outside its hours (auto-fix %s). It goes back to its hours by itself "
|
||||
"when they next start or end. Holds always fix.</p>" % e(ph["auto_fix"]))
|
||||
if t.get("kind") == "bypass":
|
||||
b = t.get("bypass") or {}
|
||||
parts.append("<p>Hold: hand it to an operator - the MIDI links to %s are taken out%s, and the panel stops putting them "
|
||||
"back. Hold again: everything is put back as it was.</p>" % (
|
||||
e(", ".join(b.get("peers") or [])), (" and Companion's %s is switched off" % e(", ".join(b["conns"]))) if b.get("conns") else ""))
|
||||
if t.get("kind") == "network":
|
||||
n = cfg.get("network", {})
|
||||
parts.append("<p>Watches this Mac's network port%s, and calls it a blip when %d connections from %d groups drop within %d s "
|
||||
|
||||
+2
-1
@@ -276,6 +276,7 @@ class Site:
|
||||
"live": live, "running": running, "service_on": snap.get("service_on") if fresh else None,
|
||||
"alarms_override": snap.get("alarms_override") if fresh else None,
|
||||
"auto_fix": {"phrase": schema.phrases(cfg)["auto_fix"], "state": snap.get("auto_fix")},
|
||||
"bypass": (snap.get("bypass") or {}) if fresh else {},
|
||||
"problems": sorted(problems, key=lambda p: (p["level"] != "err", p["since"] or 0)), "needs": needs,
|
||||
"computers": self.computer_lines(snap), "pages": pages, "snapshot_age": (now - snap["time"]) if snap.get("time") else None,
|
||||
"last_change": self.last_change(), "phrases": schema.phrases(cfg), "version": started.get("version"),
|
||||
@@ -1030,7 +1031,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
if path == "/api/command":
|
||||
text = str(body.get("text", "")).strip()
|
||||
verb = text.split(" ")[0].lower() if text else ""
|
||||
if verb not in ("test", "resurvey", "service"):
|
||||
if verb not in ("test", "resurvey", "service", "autofix"):
|
||||
return self.send(400, {"error": "not a command the page may send"})
|
||||
if verb == "test":
|
||||
live, reason = site.applier.live()
|
||||
|
||||
+7
-2
@@ -272,6 +272,7 @@
|
||||
const live = isLive ? `<div class="notice">Live now: ${esc(o.live.reason)}. Changes wait.</div>` : "";
|
||||
const comps = o.computers.map((c) => `<tr class="click" onclick="location.hash='#computers/${esc(c.alias)}'"><td>${wordOf(c.word)}</td><td>${esc(c.name)}</td><td class="muted small">${esc(c.reason)}</td></tr>`).join("");
|
||||
const w = o.watchdog;
|
||||
const af = (o.auto_fix && o.auto_fix.state) || {};
|
||||
const dis = editing() ? "" : "disabled";
|
||||
const chip = (ok, text, hintText) => `<span class="chip ${ok === true ? "ok" : ok === false ? "bad" : ""}" data-hint="${esc(hintText)}">${text}</span>`;
|
||||
main.innerHTML = `
|
||||
@@ -281,7 +282,8 @@
|
||||
${chip(w.running, w.running ? "Watchdog ✓" : "Watchdog down", (w.running ? "Running since " + when(w.since) : "Not running") + (w.version ? " · code " + w.version : "") + (o.heartbeat_age != null ? " · heartbeat " + ago(o.heartbeat_age) : ""))}
|
||||
${chip(o.companion_ok, o.companion_ok ? "Companion ✓" : "Companion down", o.pages && o.pages.status ? (o.pages.status.ok ? "Pages OK" : "Page " + o.pages.status.page + " is called " + (o.pages.status.live || "?")) : "")}
|
||||
${chip(o.service_on == null ? null : o.service_on, o.service_on == null ? "Alarms ?" : o.service_on ? "Alarms on" : "Alarms silenced", o.phrases.alarms)}
|
||||
${chip(null, "Auto-fix " + (o.auto_fix.state && o.auto_fix.state.on ? "active" : "waiting"), o.auto_fix.phrase + (o.auto_fix.state && o.auto_fix.state.paused ? " (waiting out a network blip)" : ""))}
|
||||
${chip(null, "Auto-fix " + (af.override === false ? "paused" : af.on ? "active" : "waiting"), o.auto_fix.phrase + (af.override === false ? " (paused with the AUTO-FIX key)" : af.paused ? " (waiting out a network blip)" : ""))}
|
||||
${Object.keys(o.bypass || {}).length ? chip(null, Object.keys(o.bypass).map((k) => ((cfg().tiles.find((t) => t.id === k) || {}).label || k)).join(", ") + ": operator", "Handed to an operator with its key: its MIDI is out until the key is held again") : ""}
|
||||
${Object.keys(o.running || {}).length ? chip(null, "Fix running", Object.values(o.running).map((r) => r.what + " on " + r.key).join(", ")) : ""}
|
||||
${o.last_change ? `<a class="chip" href="#history" data-hint="${esc(o.last_change.when + " · " + (o.last_change.by || "") + " · " + (o.last_change.note || ""))}">Last change ${esc(o.last_change.when || "")}</a>` : ""}
|
||||
</div>
|
||||
@@ -289,7 +291,9 @@
|
||||
<div>${deckHtml(o.deck, cfg(), "status")}
|
||||
<div class="row" style="margin-top:10px">
|
||||
<button id="restartwd" ${dis} data-hint="${w.running ? "Restart the watchdog: the deck goes purple for about 5 s; a running fix finishes first" : "Start the watchdog"}">${w.running ? "Restart watchdog" : "Start watchdog"}</button>
|
||||
<button id="alarms" ${dis} data-hint="${o.service_on === false ? "Alarms and auto-fix back on now" : "Same as the ALARMS key: no alarms and no auto-fix until midnight"}">${o.service_on === false ? "Alarms + auto-fix on" : "Silence + stop auto-fix"}</button>
|
||||
${af.split ? `<button id="alarms" ${dis} data-hint="${o.service_on === false ? "Alarms back on now" : "Same as the ALARMS key: no alarms until midnight (auto-fix carries on)"}">${o.service_on === false ? "Alarms on" : "Silence alarms"}</button>
|
||||
<button id="autofixbtn" ${dis} data-hint="${af.switch ? "Same as the AUTO-FIX key: nothing is fixed by itself until the hours next start or end (holds still fix)" : "Same as the AUTO-FIX key: gentle fixes run by themselves until the hours next start or end"}">${af.switch ? "Pause auto-fix" : "Auto-fix on"}</button>`
|
||||
: `<button id="alarms" ${dis} data-hint="${o.service_on === false ? "Alarms and auto-fix back on now" : "Same as the ALARMS key: no alarms and no auto-fix until midnight"}">${o.service_on === false ? "Alarms + auto-fix on" : "Silence + stop auto-fix"}</button>`}
|
||||
${w.running ? `<button id="stopwd" ${dis} data-hint="Stop the watchdog: every key purple, no alarms until it is started again">Stop</button>` : ""}
|
||||
</div></div>
|
||||
<div><div class="tablewrap"><table>${comps}</table></div></div>
|
||||
@@ -297,6 +301,7 @@
|
||||
main.querySelector("#restartwd").onclick = () => confirmBox(w.running ? "Restart the watchdog? The deck goes purple for about 5 seconds; a fix that is running finishes first." : "Start the watchdog?", w.running ? "Restart" : "Start", async () => { try { const r = await post(w.running ? "/api/watchdog/restart" : "/api/watchdog/start"); alertBox(r.ok ? "Done: " + r.detail : "Not done: " + r.detail); } catch (e) { alertBox(e.message); } });
|
||||
const stop = main.querySelector("#stopwd"); if (stop) stop.onclick = () => confirmBox("Stop the watchdog? The deck goes purple and every alarm is off until it is started again.", "Stop", async () => { try { const r = await post("/api/watchdog/stop", { word: "STOP" }); alertBox(r.ok ? "Stopped." : "Not stopped: " + r.detail); } catch (e) { alertBox(e.message); } }, "STOP");
|
||||
main.querySelector("#alarms").onclick = async () => { try { await post("/api/command", { text: o.service_on === false ? "service on" : "service off" }); setTimeout(() => overview(_, true), 1500); } catch (e) { alertBox(e.message); } };
|
||||
const afb = main.querySelector("#autofixbtn"); if (afb) afb.onclick = async () => { try { await post("/api/command", { text: af.switch ? "autofix off" : "autofix on" }); setTimeout(() => overview(_, true), 1500); } catch (e) { alertBox(e.message); } };
|
||||
for (const b of main.querySelectorAll("[data-run]")) b.onclick = () => runNow(...b.dataset.run.split("|"));
|
||||
if (!quiet) window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user