feat(zones): profile-scoped groups, zone modes, sequence brightness

- Optional profile_id on groups; UI and API for shared vs profile-only groups\n- Zone content_kind (presets vs sequences); edit modal shows matching sections; devices via groups only\n- Server sequence playback folds zone brightness into preset wire b (per MAC where needed)\n- Related preset/sequence/audio/beat-route and client updates

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-13 01:58:00 +12:00
parent c1c3e5d71b
commit 6c9e06f33b
21 changed files with 1034 additions and 604 deletions

View File

@@ -54,9 +54,19 @@ class Sequence(Model):
if "group_ids" not in doc or not isinstance(doc.get("group_ids"), list):
doc["group_ids"] = []
changed = True
if doc.get("advance_mode") not in ("time", "beats"):
doc["advance_mode"] = "time"
if doc.get("advance_mode") != "beats":
doc["advance_mode"] = "beats"
changed = True
if "simulated_bpm" not in doc:
doc["simulated_bpm"] = 120
changed = True
else:
try:
sb = int(float(doc["simulated_bpm"]))
doc["simulated_bpm"] = max(30, min(300, sb))
except (TypeError, ValueError):
doc["simulated_bpm"] = 120
changed = True
if "sequence_transition" not in doc:
doc["sequence_transition"] = 500
changed = True
@@ -102,9 +112,10 @@ class Sequence(Model):
"group_ids": [],
"lanes": [[]],
"lanes_group_ids": [[]],
"advance_mode": "time",
"advance_mode": "beats",
"steps": [],
"step_duration_ms": 3000,
"simulated_bpm": 120,
"sequence_transition": 500,
"loop": True,
}