Wiring view: every machine and what connects it (Companion, MIDI, Dante, watchdog, web, Art-Net), hide types or single links, scripts per computer

RESI LIVE NOT REC follows the Resi auto-record service (custom autorec_status, not during its REC hold; stale heartbeat = no verdict);
INGEST watches AUTO REC (running, heartbeat, Resi reachable), hold restarts it. Probes list each computer's scripts by file name only.
HyperDeck addresses read from bonjourHost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 13:23:02 -04:00
co-authored by Claude Opus 5.5
parent 6c78ed3221
commit 26804e4ce2
11 changed files with 655 additions and 43 deletions
+46 -6
View File
@@ -484,6 +484,25 @@ def check_launchd(chk, snap, ctx):
return [("svc", Result(OK))]
def check_heartbeat(chk, snap, ctx):
"""A service that writes the time into a Companion variable (the Resi auto-record: custom autorec_hb):
older than max_age_seconds is stuck. With status_var, a status containing warn_if is amber."""
conn = chk.get("conn", "custom")
raw = snap.var(conn, chk["var"])
if raw in (None, ""):
return [("hb", Result(OK))] # not read yet
try:
age = ctx["now"] - float(raw)
except ValueError:
age = None
if age is None or age > chk.get("max_age_seconds", 90):
return [("hb", Result(ERR, chk.get("msg", "STUCK"), named=True))]
status = (snap.var(conn, chk["status_var"]) or "") if chk.get("status_var") else ""
if chk.get("warn_if") and chk["warn_if"].upper() in status.upper():
return [("hb", Result(WARN, chk.get("warn_msg", chk["warn_if"]), named=True))]
return [("hb", Result(OK))]
def check_process(chk, snap, ctx):
running = snap.procs.get(chk["match"])
if running is False:
@@ -956,7 +975,7 @@ CHECKS = {
"psm": check_psm, "tascam": check_tascam, "ping": check_ping, "launchd": check_launchd,
"process": check_process, "http": check_http, "disk": check_disk, "midi_peer": check_midi_peer, "midi_wired": check_midi_wired, "pref": check_pref, "spotify": check_spotify,
"mdns": check_mdns, "midi_rx": check_midi_rx, "remote": check_remote, "artnet": check_artnet,
"ready": check_ready, "ready_manual": check_ready_manual, "tune_rx": check_tune_rx,
"ready": check_ready, "ready_manual": check_ready_manual, "tune_rx": check_tune_rx, "heartbeat": check_heartbeat,
}
@@ -1040,6 +1059,14 @@ def needed_variables(cfg):
rw = cfg.get("rec_watch") or {}
if rw.get("conn"):
fast.append((rw["conn"], rw.get("var", "playback_state")))
if rw.get("heartbeat"):
fast.append((rw["conn"], rw["heartbeat"]))
for tile in all_tiles(cfg):
for chk in tile.get("checks", []):
if chk["type"] == "heartbeat":
fast.append((chk.get("conn", "custom"), chk["var"]))
if chk.get("status_var"):
fast.append((chk.get("conn", "custom"), chk["status_var"]))
dedupe = lambda xs: list(dict.fromkeys(xs))
return dedupe(fast), dedupe(slow)
@@ -1957,13 +1984,22 @@ class Engine:
self.toggle_bypass(tid, comp, now)
def resi_playing(self, snap, now):
"""config "rec_watch": Resi's playback (resi-studio-cues "playback_state") has read PLAYING for
after_seconds, with the module itself connected. The decks must record then."""
"""config "rec_watch": Resi is live and the decks should record, for after_seconds. NA reads the Resi
auto-record service's own verdict (custom variable autorec_status: RECORDING / STARTING / LIVE, NOT
RECORDING; not while it holds REC until 10 minutes before a service), and nothing while its heartbeat
is stale. Without a heartbeat the connection must be up (a module's playback_state PLAYING)."""
rw = self.cfg.get("rec_watch") or {}
if not rw.get("conn"):
return False
value = (snap.var(rw["conn"], rw.get("var", "playback_state")) or "").strip().upper()
if value != rw.get("playing", "PLAYING").upper() or conn_result(snap, rw["conn"], now).level != OK:
value = (snap.var(rw["conn"], rw.get("var", "playback_state")) or "").upper()
if rw.get("heartbeat"):
try:
fresh = now - float(snap.var(rw["conn"], rw["heartbeat"]) or 0) < rw.get("stale_seconds", 90)
except ValueError:
fresh = False
else:
fresh = conn_result(snap, rw["conn"], now).level == OK
if not fresh or not any(w.upper() in value for w in rw.get("live", ["PLAYING"])):
self.resi_since = None
return False
self.resi_since = self.resi_since or now
@@ -2865,7 +2901,11 @@ def companion_conn_hosts(path):
inst = json.loads(value)
except (TypeError, ValueError):
continue
host = (inst.get("config") or {}).get("host") if isinstance(inst, dict) else None
conf = (inst.get("config") or {}) if isinstance(inst, dict) else {}
host = conf.get("host")
if not (isinstance(host, str) and host.strip()) and isinstance(conf.get("bonjourHost"), str):
host = conf["bonjourHost"].rsplit(":", 1)[0] if re.match(r"^[\d.]+:\d+$", conf["bonjourHost"].strip()) \
else conf["bonjourHost"] # HyperDecks: "10.1.10.11:9993"
if inst.get("label") and isinstance(host, str) and host.strip():
hosts[inst["label"]] = host.strip()
return hosts