Wiring: layouts (Hub, Columns, Groups, Free), All/None, Dante in its own box, gentle trackpad zoom; fix the Keys pages (a second wireLayout) and Computers > Setup (a second setupHtml)
RossTalk and OSC: the watchdog reads Companion's RossTalk port (ROSSTALK OFF, PROP ROSSTALK LOST at NA/WS/SC), Bible TV and Ableton must be listening for Companion's OSC (BIBLE OSC OFF, ABLETON OSC OFF), and both are their own connection types on the Wiring screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+19
-1
@@ -109,8 +109,12 @@ class TestServer(unittest.TestCase):
|
||||
json.dump(live, open(os.path.join(self.data, "machines-live.json"), "w"))
|
||||
snap = json.load(open(os.path.join(self.data, "snapshot.json")))
|
||||
snap.update({"conns": {"ATEM": {"category": "good", "enabled": True}, "x32": {"category": "error", "enabled": True},
|
||||
"BibleTV": {"category": "good", "enabled": True}, "RESI_Decoder": {"category": "good", "enabled": True},
|
||||
"HyperDeck_1": {"category": "good", "enabled": True}, "tcp-HyperDeck-1": {"category": "good", "enabled": True}},
|
||||
"conn_hosts": {"ATEM": "10.1.10.2", "x32": "10.1.1.112", "HyperDeck_1": "10.1.10.11", "tcp-HyperDeck-1": "10.1.10.11"},
|
||||
"conn_hosts": {"ATEM": "10.1.10.2", "x32": "10.1.1.112", "HyperDeck_1": "10.1.10.11", "tcp-HyperDeck-1": "10.1.10.11",
|
||||
"BibleTV": "10.6.7.13", "RESI_Decoder": "10.1.10.203"},
|
||||
"conn_info": {"BibleTV": {"module": "generic-osc", "port": 3333}, "RESI_Decoder": {"module": "resi-decoders", "port": None}},
|
||||
"rosstalk": {"listen": True, "clients": [{"ip": "10.1.14.225", "cmd": ""}], "targets": ["10.1.10.203"], "time": time.time()},
|
||||
"remote_midi": {"tracks": {"NA Tracks": {"peers": [{"address": "10.1.13.140:5004", "name": "SuperRack-LiveBox", "state": "running"}]}}},
|
||||
"dante_map": {"NA-Tracks": {"switch": "10.66.1.100", "port": "0/6", "seen": time.time()},
|
||||
"FOH-SuperRack-LiveBox": {"switch": "10.66.1.102", "port": "0/9", "seen": time.time()},
|
||||
@@ -134,6 +138,9 @@ class TestServer(unittest.TestCase):
|
||||
self.assertIn(("dante", "h:tracks", "sw:10.66.1.100", "ok"), edges) # NA-Tracks is the Tracks Mac
|
||||
self.assertIn("FOH-SuperRack-LiveBox", nodes["d:10.1.13.140"]["names"]) # same box as its MIDI peer
|
||||
self.assertIn("d:studiobmon", nodes) # a monitor, not the Studio B Mac
|
||||
self.assertIn(("osc", "h:ingest", "h:bibletv", "ok"), edges) # OSC to the Bible TV PC
|
||||
self.assertIn(("rosstalk", "h:ingest", "d:10.1.10.203", "ok"), edges) # RossTalk to the Resi decoder
|
||||
self.assertIn(("rosstalk", "h:prop", "h:ingest", "ok"), edges) # ProPresenter's cues into Companion
|
||||
self.assertEqual({t["id"] for t in w["types"]} >= {"companion", "midi", "watch", "dante"}, True)
|
||||
|
||||
def test_gear_and_keys_and_pages(self):
|
||||
@@ -416,6 +423,17 @@ class TestBindingAndProbes(unittest.TestCase):
|
||||
tasks={"\\StatusPanel\\Start Vista": "Running", "\\StatusPanel\\Start rtpMIDI": "Ready"})
|
||||
self.assertEqual([(x["name"], x["running"]) for x in win], [("Start Vista", True), ("start-vista.ps1", True), ("Start rtpMIDI", False)])
|
||||
|
||||
def test_the_page_script_has_no_two_functions_with_one_name(self):
|
||||
# 2026-10-27: a second wireLayout (the Wiring screen's) replaced the Keys screen's and broke it ("null errors"),
|
||||
# and a second setupHtml had broken Computers > Setup the same way.
|
||||
import collections
|
||||
import re
|
||||
js = open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "settings", "ui", "app.js")).read()
|
||||
# the page's own names: two spaces in, inside its one (function () { ... })
|
||||
names = collections.Counter(re.findall(r"^ (?:async )?function\s+([A-Za-z0-9_$]+)\s*\(", js, re.M))
|
||||
names.update(re.findall(r"^ (?:const|let)\s+([A-Za-z0-9_$]+)\s*=", js, re.M))
|
||||
self.assertEqual([n for n, c in names.items() if c > 1], [])
|
||||
|
||||
def test_probe_commands_never_carry_names_from_the_settings(self):
|
||||
cfg = json.loads(json.dumps(CONFIG))
|
||||
cfg["hosts"]["tracks"]["runs"][1]["process"] = "x; rm -rf ~" # a hostile name in the settings
|
||||
|
||||
@@ -677,6 +677,38 @@ class TestHyperDeckRecord(unittest.TestCase):
|
||||
self.assertFalse(sp.hyperdeck_record("127.0.0.1", "x", port=1, timeout=1))
|
||||
|
||||
|
||||
class TestRossTalk(unittest.TestCase):
|
||||
"""Companion's RossTalk port (2026-10-27): ProPresenter's cues come in on 7788, Companion talks to the Resi decoder."""
|
||||
NA = ("COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME\n"
|
||||
"node 925 Studio 37u IPv6 0x1 0t0 TCP *:7788 (LISTEN)\n"
|
||||
"node 925 Studio 155u IPv6 0x2 0t0 TCP 10.1.1.1:7788->10.1.14.225:61156 (ESTABLISHED)\n"
|
||||
"node 1058 Studio 22u IPv4 0x3 0t0 TCP 10.1.1.1:49697->10.1.10.203:7788 (ESTABLISHED)\n")
|
||||
WS = ("COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME\n"
|
||||
"node 973 wsprop 38u IPv6 0x1 0t0 TCP *:7788 (LISTEN)\n"
|
||||
"node 973 wsprop 43u IPv6 0x2 0t0 TCP 127.0.0.1:7788->127.0.0.1:53750 (ESTABLISHED)\n"
|
||||
"node 17116 wsprop 22u IPv4 0x3 0t0 TCP 10.2.2.210:57966->10.2.1.166:7788 (ESTABLISHED)\n"
|
||||
"ProPresen 53124 wsprop 77u IPv4 0x4 0t0 TCP 127.0.0.1:53750->127.0.0.1:7788 (ESTABLISHED)\n")
|
||||
|
||||
def check(self, chk, rt, now=1000.0):
|
||||
snap = sp.Snapshot()
|
||||
snap.rosstalk = dict(rt, time=now) if rt is not None else None
|
||||
return dict(sp.check_rosstalk(chk, snap, {"now": now}))["rosstalk"]
|
||||
|
||||
def test_parsing(self):
|
||||
self.assertEqual(sp.parse_rosstalk(self.NA), {"listen": True, "clients": [{"ip": "10.1.14.225", "cmd": ""}], "targets": ["10.1.10.203"]})
|
||||
self.assertEqual(sp.parse_rosstalk(self.WS), {"listen": True, "clients": [{"ip": "127.0.0.1", "cmd": "ProPresen"}], "targets": ["10.2.1.166"]})
|
||||
|
||||
def test_prop_link(self):
|
||||
na, ws = sp.parse_rosstalk(self.NA), sp.parse_rosstalk(self.WS)
|
||||
self.assertEqual(self.check({"address": "10.1.14.225", "name": "PROP"}, na).level, sp.OK)
|
||||
self.assertEqual(self.check({"address": "local", "process": "ProPresenter", "name": "PROP"}, ws).level, sp.OK)
|
||||
gone = sp.parse_rosstalk("\n".join(l for l in self.NA.splitlines() if "61156" not in l))
|
||||
self.assertEqual(self.check({"address": "10.1.14.225", "name": "PROP"}, gone).msg, "PROP ROSSTALK LOST")
|
||||
off = sp.parse_rosstalk("COMMAND\n")
|
||||
self.assertEqual(self.check({"address": "10.1.14.225", "name": "PROP"}, off).msg, "ROSSTALK OFF")
|
||||
self.assertEqual(self.check({"address": "10.1.14.225"}, None).level, sp.OK) # not looked yet
|
||||
|
||||
|
||||
class TestTextSize(unittest.TestCase):
|
||||
def test_no_word_is_split(self):
|
||||
for text in ("TASCAM\nOK", "SERVICE\nON\nmanual", "OBS\nOFFLINE", "INGEST\n97GB free",
|
||||
|
||||
Reference in New Issue
Block a user