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
+33
View File
@@ -266,6 +266,39 @@ if (mine.length && (stuck || (REPOINT && elsewhere))) {
"""
# Disconnect, for a Mac without python: what disconnect_peer does. Prints SPMIDI"asked" / "not connected".
JXA_DISCONNECT = r"""
ObjC.import('CoreMIDI'); ObjC.import('Foundation');
var SESSION = __SESSION__, PEER = __PEER__;
function str(o, k) { var r = Ref(); if ($.MIDIObjectGetStringProperty(o, k, r) !== 0) return null; return ObjC.unwrap(ObjC.castRefToObject(r[0])); }
function dict(o, k) { var r = Ref(); if ($.MIDIObjectGetDictionaryProperty(o, k, r) !== 0) return null; return ObjC.deepUnwrap(ObjC.castRefToObject(r[0])); }
var found = null, n = $.MIDIGetNumberOfDevices();
for (var i = 0; i < n && !found; i++) {
var dev = $.MIDIGetDevice(i), drv = str(dev, $('driver')) || '';
if (drv.indexOf('Network') < 0 && drv.toLowerCase().indexOf('rtp') < 0) continue;
for (var j = 0; j < $.MIDIDeviceGetNumberOfEntities(dev); j++) {
var ent = $.MIDIDeviceGetEntity(dev, j), s = dict(ent, $('apple.midirtp.session'));
if (s && (s['dns-sd-name'] || str(ent, $('name'))) === SESSION) { found = [ent, s]; break; }
}
}
if (!found) throw new Error('no network MIDI session named ' + SESSION + ' on this Mac');
var ent = found[0], s = found[1], peers = s.peers || [], result = 'not connected';
var left = peers.filter(function (p) { return p.name !== PEER; });
if (left.length !== peers.length) {
s.peers = left;
var e = $.MIDIObjectSetDictionaryProperty(ent, $('apple.midirtp.session'), $(s));
if (e !== 0) throw new Error('MIDIObjectSetDictionaryProperty failed: ' + e);
result = 'asked';
}
'SPMIDI' + JSON.stringify(result);
"""
def jxa_disconnect(session_name, peer_name):
import json as _json
return JXA_DISCONNECT.replace("__SESSION__", _json.dumps(session_name)).replace("__PEER__", _json.dumps(peer_name))
def jxa_connect(session_name, peer_name, address, repoint=False):
"""The JXA_CONNECT script for one peer (values go in as JSON, never as code)."""
import json as _json