AUTO-FIX pauses through the hours (also when pressed outside them); MIDI links without a state judged by the Mac

- AUTO-FIX: tap = pause / resume; a pause lasts through the current or the next auto-fix hours; key says ON TIL,
  OFF / ON <start>, PAUSED TIL; forcing auto-fix on outside the hours is gone
- MIDI: macOS 26 reports every live link's state, so no state there = NO ANSWER; on macOS 14/15 a link put back after
  a drop counts only after 20 s (macOS drops an unanswered one at ~12 s): no more OK/LOST flicker
- WS and SC PROP ignore the rc4 module's harmless warnings, as NA

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 15:38:56 -04:00
co-authored by Claude Opus 5.5
parent bf9afcbc84
commit 99a25e5da2
11 changed files with 156 additions and 37 deletions
+1 -1
View File
@@ -608,7 +608,7 @@ class TestAllAutoFix(unittest.TestCase):
def test_the_key_names_the_paused_locations_and_sits_where_auto_fix_does(self):
from settings import campuses as cv
st = lambda x: json.dumps({"autofix": {"x": x}})
states = {"na": st("AUTO-FIX\nON\nTIL 9PM"), "sc": st("AUTO-FIX\nOFF\nTIL SUN 5AM"), "ws": st("AUTO-FIX\nOFF"), "ab": None}
states = {"na": st("AUTO-FIX\nON\nTIL 9PM"), "sc": st("AUTO-FIX\nPAUSED\nTIL SUN 2PM"), "ws": st("AUTO-FIX\nOFF\nON SUN 5AM"), "ab": None}
self.assertEqual(cv.all_autofix_summary(states, 100.0), {"t": 100, "paused": 1, "x": "SC\nPAUSED"}) # WS: outside its hours
self.assertEqual(cv.all_autofix_summary({"na": st("AUTO-FIX\nON\nTIL 9PM")}, 100.0)["x"], "ALL ON")
na = json.load(open(os.path.join(ROOT, "config.json")))
+47 -7
View File
@@ -2493,16 +2493,25 @@ class TestSwitches(unittest.TestCase):
self.engine.auto_fix(self.snap, mock_comp(), SUNDAY + 30)
time.sleep(0.2)
self.assertEqual(ran, ["ableton"]) # auto-fix carried on
self.engine.autofix.set(False, SUNDAY + 40) # AUTO-FIX pressed: paused
self.engine.autofix.pause(SUNDAY + 40) # AUTO-FIX pressed in the hours: paused
tiles = self.engine.step(self.snap, SUNDAY + 41)
self.assertEqual(tiles["autofix"]["x"], "AUTO-FIX\nOFF\nTIL WED 4PM") # back on with Wednesday's hours
self.assertEqual(tiles["autofix"]["x"], "AUTO-FIX\nPAUSED\nTIL 1:30PM") # through Sunday's hours
self.assertEqual(level(tiles["autofix"]), "off")
self.assertEqual(self.engine.auto_fix(self.snap, mock_comp(), SUNDAY + 200), [])
self.assertFalse(self.new_engine().autofix.on(SUNDAY + 300)) # survives a restart
self.assertTrue(self.new_engine().autofix.paused) # survives a restart
self.assertTrue(self.engine.autofix.on(datetime(2026, 10, 14, 16, 30).timestamp())) # Wednesday's hours: back by itself
weekday = SUNDAY - 3 * 86400 # Thursday: pressed = runs anyway
self.engine.autofix.set(True, weekday)
self.assertTrue(self.engine.autofix.on(weekday + 60))
self.assertFalse(self.engine.autofix.paused)
# Pressed outside the hours (Thursday): the key said OFF / ON SUN 6:30AM; the pause holds through Sunday's hours.
thursday = SUNDAY - 3 * 86400
tiles = self.engine.step(self.snap, thursday)
self.assertEqual(tiles["autofix"]["x"], "AUTO-FIX\nOFF\nON SUN 6:30AM")
self.engine.autofix.pause(thursday)
self.assertEqual(self.engine.step(self.snap, thursday + 1)["autofix"]["x"], "AUTO-FIX\nPAUSED\nTIL SUN 1:30PM")
self.assertFalse(self.engine.autofix.on(SUNDAY)) # Sunday 8:00, inside the hours: still paused
self.assertTrue(self.engine.autofix.on(datetime(2026, 10, 14, 16, 30).timestamp())) # and over after them
self.engine.autofix.pause(thursday)
self.engine.autofix.resume(thursday + 60) # pressed again: back to the hours
self.assertTrue(self.engine.autofix.on(SUNDAY))
page = make_page.build(self.cfg)["page"]["controls"]
notes = {(ctl.get("options") or {}).get("notes", "") for row in page.values() for ctl in row.values() if ctl}
self.assertTrue(any("Auto-fix is the AUTO-FIX key" in n for n in notes))
@@ -2510,8 +2519,9 @@ class TestSwitches(unittest.TestCase):
def test_a_hold_from_north_attleboro_flips_a_switch(self):
self.engine.run_fixes("autofix", self.snap, mock_comp(), SUNDAY)
self.assertFalse(self.engine.autofix.on(SUNDAY + 1))
self.assertTrue(self.engine.autofix.paused)
self.engine.run_fixes("autofix", self.snap, mock_comp(), SUNDAY + 2)
self.assertFalse(self.engine.autofix.paused)
self.assertTrue(self.engine.autofix.on(SUNDAY + 3))
def test_the_operator_key_takes_the_midi_out_and_puts_it_back(self):
@@ -2622,3 +2632,33 @@ class TestCompanionKeeper(unittest.TestCase):
open(os.path.join(self.data, "companion-upgrading"), "w").write("x")
self.assertIsNone(self.keeper().check(self.now + 30)) # an upgrade is going on
self.assertEqual(self.opened, [])
class TestMidiNoState(unittest.TestCase):
"""macOS 26 gives each live link a state, macOS 14/15 none: a link with no state is judged by which (2026-10-21)."""
def snap(self, peers, now):
s = healthy_snapshot(now)
s.remote_midi = {"tracks": {"T": {"port": 5004, "peers": peers}}}
s.remote_midi_time = {"tracks": now}
return s
def check(self, s, now):
chk = {"type": "midi_peer", "host": "tracks", "session": "T", "peer": "Lighting", "address": "10.5.1.168:5004"}
s.remote_midi_time = {"tracks": now} # a fresh reading every 2 s
return sp.check_midi_peer(chk, s, {"now": now})[0][1]
def test_on_a_mac_that_reports_states_no_state_means_no_answer(self):
s = self.snap([{"name": "FOH", "address": "x", "state": "running"}, {"name": "Lighting", "address": "y", "state": ""}], 100.0)
self.assertEqual(self.check(s, 100.0).level, sp.ERR)
self.assertEqual(self.check(s, 100.0).msg, "MIDI NO ANSWER") # there at the first reading: long-standing
def test_on_an_older_mac_a_link_put_back_counts_only_once_it_stays(self):
s = self.snap([{"name": "Lighting", "address": "y"}], 100.0)
self.assertEqual(self.check(s, 100.0).level, sp.OK) # there from the start: configured, fine
s.remote_midi = {"tracks": {"T": {"port": 5004, "peers": []}}}
self.assertEqual(self.check(s, 101.0).msg, "MIDI LOST") # dropped
s.remote_midi = {"tracks": {"T": {"port": 5004, "peers": [{"name": "Lighting", "address": "y"}]}}}
self.assertEqual(self.check(s, 110.0).msg, "MIDI CONNECTING") # put back: not trusted yet
self.assertEqual(self.check(s, 125.0).msg, "MIDI CONNECTING")
self.assertEqual(self.check(s, 131.0).level, sp.OK) # stayed 20 s: it answered