feat(espnow): broadcast delivery with group-filtered routing

Send presets and select on broadcast with groups; unicast only for
per-device settings. V1 select as [preset_id, step?]. Sequence steps
use beat counts; manual presets get select each beat, auto only on
step change. Bridge downlink router, Pi envelope delivery, and tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-24 01:44:28 +12:00
parent 1a69fabd98
commit b87382d2be
35 changed files with 1802 additions and 591 deletions

View File

@@ -43,7 +43,7 @@ def test_suppress_next_notify_skips_one_select(monkeypatch):
assert delivered == []
bdr.notify_beat_detected()
assert delivered == [(["desk"], "5")]
assert delivered == [("5", None)]
def test_suppress_does_not_advance_beat_counter(monkeypatch):
@@ -52,8 +52,8 @@ def test_suppress_does_not_advance_beat_counter(monkeypatch):
bdr.set_sequence_manual_lane_route(
0,
["desk"],
"42",
{"p": "radiate", "a": False, "manual_beat_n": 2},
"5",
{"p": "chase", "a": False, "manual_beat_n": 2},
)
bdr.mark_sequence_manual_lane_select_sent(0)
@@ -61,14 +61,14 @@ def test_suppress_does_not_advance_beat_counter(monkeypatch):
assert delivered == []
bdr.notify_beat_detected()
assert delivered == [(["desk"], "42")]
assert delivered == [("5", None)]
delivered.clear()
bdr.notify_beat_detected()
assert delivered == []
bdr.notify_beat_detected()
assert delivered == [(["desk"], "42")]
assert delivered == [("5", None)]
def test_duplicate_lanes_dedupe_to_one_select_per_beat(monkeypatch):
@@ -87,19 +87,57 @@ def test_duplicate_lanes_dedupe_to_one_select_per_beat(monkeypatch):
bdr._lane_manual[0] = dict(entry)
bdr.notify_beat_detected()
assert delivered == [(["desk"], "42")]
assert delivered == [("42", None)]
def test_sequence_lane_manual_delivers_per_beat_select(monkeypatch):
delivered = _patch_delivery(monkeypatch)
bdr.set_sequence_manual_lane_route(
0,
["desk"],
"42",
{"p": "radiate", "a": False, "manual_beat_n": 1},
)
bdr.notify_beat_detected()
assert delivered == [("42", None)]
def test_sequence_auto_lane_skips_per_beat_select(monkeypatch):
delivered = _patch_delivery(monkeypatch)
bdr.set_sequence_manual_lane_route(
0,
["desk"],
"3",
{"p": "colour_cycle", "a": True, "manual_beat_n": 1},
)
with bdr._route_lock:
assert 0 not in bdr._lane_manual
bdr.notify_beat_detected()
assert delivered == []
def test_sequence_lane_chase_delivers_per_beat_select(monkeypatch):
delivered = _patch_delivery(monkeypatch)
bdr.set_sequence_manual_lane_route(
0,
["desk"],
"5",
{"p": "chase", "a": False, "manual_beat_n": 1},
)
bdr.notify_beat_detected()
assert delivered == [("5", None)]
def test_standalone_overlay_skipped_when_sequence_lane_covers(monkeypatch):
delivered = _patch_delivery(monkeypatch)
body = {"p": "radiate", "a": False, "manual_beat_n": 1}
body = {"p": "chase", "a": False, "manual_beat_n": 1}
bdr.set_sequence_manual_lane_route(1, ["desk"], "42", body)
bdr._apply_manual_beat_route_standalone_overlay(["desk"], "42", body)
bdr.set_sequence_manual_lane_route(1, ["desk"], "5", body)
bdr._apply_manual_beat_route_standalone_overlay(["desk"], "5", body)
with bdr._route_lock:
assert -1 not in bdr._lane_manual
assert 1 in bdr._lane_manual
bdr.notify_beat_detected()
assert delivered == [(["desk"], "42")]
assert delivered == [("5", None)]