Regroup Dante, MIDI and apps into one button each, and fix the morning Bonjour check.

DANTE now alarms for all 19 devices by name. MIDI covers the Lighting PC and
SuperRack peers plus the Lighting PC and NA Tracks sessions; APPS covers
ProPresenter, Ableton, the tracks Mac and the Bible TV app. Five buttons
became three, and a check's tag names the device in the message.

dns-sd pads hours before 10 AM with a space, which the Bonjour parser did not
allow for, so every session read as gone each morning. Parsing is now its
own function with a test for both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 09:22:38 -04:00
co-authored by Claude Opus 5.5
parent 38b6616e3f
commit c0c83459e5
5 changed files with 100 additions and 47 deletions
+18 -10
View File
@@ -346,7 +346,7 @@ def check_ping(chk, snap, ctx):
fails = snap.ping.get(chk["host"])
if fails is not None and fails >= chk.get("failures", 2):
msg = chk.get("msg") or chk.get("name", chk["host"]) + " OFFLINE"
return [("ping", Result(ERR, msg, service_only=chk.get("service_only", False)))]
return [("ping", Result(ERR, msg, service_only=chk.get("service_only", False), named=True))]
return [("ping", Result(OK))]
@@ -370,7 +370,7 @@ def check_process(chk, snap, ctx):
def check_http(chk, snap, ctx):
if snap.http.get(chk["url"]) is False:
return [("http", Result(ERR, chk.get("msg") or chk.get("name", "WEB") + " UNREACH"))]
return [("http", Result(ERR, chk.get("msg") or chk.get("name", "WEB") + " UNREACH", named=True))]
return [("http", Result(OK))]
@@ -398,14 +398,14 @@ def check_midi_peer(chk, snap, ctx):
# clock sync, then "running". A peer that dies is removed from the list:
# at once if it says goodbye, after ~60 s if it crashes or its cable is pulled.
if p["state"] in ("running", "connected"):
return [("peer", Result(OK, info="MIDI OK"))]
return [("peer", Result(OK))]
return [("peer", Result(ERR, "MIDI " + (p["state"] or "?").upper()[:8]))]
return [("peer", Result(ERR, "MIDI LOST"))]
def check_mdns(chk, snap, ctx):
if snap.mdns_time and chk["instance"] not in snap.mdns:
return [("mdns", Result(ERR, chk.get("name", chk["instance"]) + " GONE"))]
return [("mdns", Result(ERR, chk.get("name", chk["instance"]) + " GONE", named=True))]
return [("mdns", Result(OK))]
@@ -595,6 +595,9 @@ class Engine:
log.exception("check %s/%s failed", t["id"], chk["type"])
results = [("error", Result(WARN, "CHECK BUG"))]
for sub, res in results:
# "tag" names the device when one button covers several (APPS, MIDI).
if chk.get("tag") and res.level in (WARN, ERR) and not res.named:
res.msg, res.named = "%s %s" % (chk["tag"], res.msg), True
raw["%s:%d:%s" % (t["id"], i, sub)] = (t["id"], res, chk)
for sub, res in self.network_results(snap, now):
raw["net:%s" % sub] = ("net", res, {"type": "network"})
@@ -877,6 +880,16 @@ def poll_system(snap, cfg):
snap.procs = procs
def parse_dns_sd(out):
"""Instance names still present in `dns-sd -B` output."""
names = set()
for line in out.splitlines():
m = re.match(r"^\s*\S+\s+(Add|Rmv)\s+\d+\s+\d+\s+\S+\s+_apple-midi\._udp\.\s+(.+?)\s*$", line)
if m:
(names.add if m.group(1) == "Add" else names.discard)(m.group(2))
return names
def poll_mdns(snap, seconds=3):
proc = subprocess.Popen(["/usr/bin/dns-sd", "-B", "_apple-midi._udp", "local."],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
@@ -885,12 +898,7 @@ def poll_mdns(snap, seconds=3):
except subprocess.TimeoutExpired:
proc.kill()
out, _ = proc.communicate()
names = set()
for line in out.splitlines():
m = re.match(r"^\S+\s+(Add|Rmv)\s+\d+\s+\d+\s+\S+\s+_apple-midi\._udp\.\s+(.+?)\s*$", line)
if m:
(names.add if m.group(1) == "Add" else names.discard)(m.group(2))
snap.mdns, snap.mdns_time = names, time.time()
snap.mdns, snap.mdns_time = parse_dns_sd(out), time.time()
_UNVERIFIED = ssl._create_unverified_context() # reachability only; nothing is sent