Ping the Shure receivers at the address Companion has for them

A power-off test today took 18-27 s to show on RF (Companion's Shure
modules' own timeout). Each Axient, ULXD and PSM receiver is now pinged
every 3 s: "AXIENT 1 NO PING" in about 6-9 s. A ping check may name a
Companion "conn"; its address is read from Companion's settings database
(read-only, every 60 s), with "host" as the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 16:44:08 -04:00
co-authored by Claude Opus 5.5
parent 46fb55533b
commit 6d273fa3d8
4 changed files with 98 additions and 11 deletions
+55 -4
View File
@@ -135,6 +135,7 @@ class Snapshot:
self.midi = {} # session name -> {"peers": [...]}
self.remote_midi = {} # host -> the same, read on that Mac over SSH (None: unreachable)
self.peer_addr = {} # (host or "", session, peer) -> address it was last seen at
self.conn_hosts = {} # Companion connection label -> its "host" setting
self.midi_time = 0.0
self.mdns = set() # Bonjour _apple-midi._udp instance names
self.mdns_time = 0.0
@@ -357,8 +358,14 @@ def check_tascam(chk, snap, ctx):
return out
def ping_target(chk, snap):
"""A ping check names a "host", or a Companion "conn" whose own address is used (so a new IP set
in Companion needs no config change); "host" is then only the fallback."""
return (snap.conn_hosts.get(chk["conn"]) if chk.get("conn") else None) or chk.get("host")
def check_ping(chk, snap, ctx):
fails = snap.ping.get(chk["host"])
fails = snap.ping.get(ping_target(chk, snap))
if fails is not None and fails >= chk.get("failures", 2):
msg = chk.get("msg") or chk.get("name", chk["host"]) + " NO PING"
return [("ping", Result(ERR, msg, service_only=chk.get("service_only", False), named=True))]
@@ -1154,6 +1161,47 @@ def remember_peers(snap, host, sessions):
snap.peer_addr[(host, name, p["name"])] = p["address"]
def companion_db_path(cfg):
if cfg["companion"].get("db"):
return os.path.expanduser(cfg["companion"]["db"])
root = os.path.expanduser("~/Library/Application Support/companion")
vers = [d for d in os.listdir(root) if re.fullmatch(r"v\d+\.\d+", d)] if os.path.isdir(root) else []
if not vers:
return None
newest = max(vers, key=lambda d: tuple(int(x) for x in d[1:].split(".")))
return os.path.join(root, newest, "db.sqlite")
def companion_conn_hosts(path):
"""{connection label: host} from Companion's own settings database, opened read-only."""
import sqlite3
if not path or not os.path.exists(path):
return {}
db = sqlite3.connect("file:%s?mode=ro" % urllib.parse.quote(path), uri=True, timeout=2)
try:
rows = db.execute("SELECT value FROM instances").fetchall()
finally:
db.close()
hosts = {}
for (value,) in rows:
try:
inst = json.loads(value)
except (TypeError, ValueError):
continue
host = (inst.get("config") or {}).get("host") if isinstance(inst, dict) else None
if inst.get("label") and isinstance(host, str) and host.strip():
hosts[inst["label"]] = host.strip()
return hosts
def poll_conn_hosts(snap, cfg):
if any(c["type"] == "ping" and c.get("conn") for t in cfg["tiles"] for c in t.get("checks", [])):
try:
snap.conn_hosts = companion_conn_hosts(companion_db_path(cfg)) or snap.conn_hosts
except Exception as e: # Companion's schema is its own business: fall back to "host"
log.warning("could not read connection addresses from Companion: %s", e)
def poll_midi(snap, cfg):
import coremidi
snap.midi, snap.midi_time = coremidi.sessions(), time.time()
@@ -1529,8 +1577,10 @@ def load_config(path):
return cfg
def ping_hosts(cfg):
hosts = [c["host"] for t in cfg["tiles"] for c in t.get("checks", []) if c["type"] == "ping"]
def ping_hosts(cfg, snap=None):
hosts = [ping_target(c, snap) if snap else c.get("host")
for t in cfg["tiles"] for c in t.get("checks", []) if c["type"] == "ping"]
hosts = [h for h in hosts if h]
gw = cfg.get("network", {}).get("gateway")
return list(dict.fromkeys(hosts + ([gw] if gw else [])))
@@ -1557,7 +1607,8 @@ def start_pollers(cfg, snap, stop, data_dir=None):
Periodic("connections", 1.0, lambda: poll_connections(c1, snap), stop),
Periodic("variables", 1.0, lambda: poll_variables(c2, snap, fast), stop),
Periodic("slow-variables", 3.0, lambda: poll_variables(c3, snap, slow), stop),
Periodic("pings", 3.0, lambda: poll_pings(snap, ping_hosts(cfg), iface), stop),
Periodic("conn-hosts", 60.0, lambda: poll_conn_hosts(snap, cfg), stop),
Periodic("pings", 3.0, lambda: poll_pings(snap, ping_hosts(cfg, snap), iface), stop),
Periodic("midi", 2.0, lambda: poll_midi(snap, cfg), stop),
Periodic("system", 10.0, lambda: poll_system(snap, cfg), stop),
Periodic("bonjour", 15.0, lambda: poll_mdns(snap), stop),