From 48a754a118c76cef8f683c9b27a3e5aef3ff1aff Mon Sep 17 00:00:00 2001 From: christian Date: Wed, 21 Oct 2026 14:57:45 -0400 Subject: [PATCH] NA's page keys name the other locations with a problem in a blue band; PROP OUT reads Chronicles - campuses.remote_summary -> custom variable sp_remote; band flashes until acknowledged there, steady after, "SC?" when silent - nav_locations_band (NA only); red stays for NA's own problems - PROP OUT: /Volumes/Location Assets/{date} on NA ProP (Chronicles share) Co-Authored-By: Claude Opus 5.5 --- config.json | 7 ++++--- make_page.py | 24 ++++++++++++++++++++++++ ready-page.companionconfig | Bin 3986 -> 3449 bytes restart-page.companionconfig | Bin 3692 -> 3847 bytes schema.py | 3 +++ settings/campuses.py | 27 ++++++++++++++++++++++++++- status-page.companionconfig | Bin 6354 -> 6527 bytes tests/test_campus.py | 24 ++++++++++++++++++++++++ 8 files changed, 81 insertions(+), 4 deletions(-) diff --git a/config.json b/config.json index d3a924e..300dc0e 100644 --- a/config.json +++ b/config.json @@ -935,8 +935,8 @@ "pco_module": "pco-plan", "pco_service_type": "275974", "projects": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Waters Creative/Ableton/Projects", - "prop_bundle_folder": "~/Library/CloudStorage/GoogleDrive-media@waterschurch.org/Shared drives/Location Assets/{date}", - "prop_bundle_host": "ingest", + "prop_bundle_folder": "/Volumes/Location Assets/{date}", + "prop_bundle_host": "prop", "prop_bundle_pattern": "*.probundle", "prop_host": "prop", "prop_lists": [ @@ -2298,5 +2298,6 @@ "command": "sp_cmd", "heartbeat": "sp_hb", "state": "sp_state" - } + }, + "nav_locations_band": true } diff --git a/make_page.py b/make_page.py index 5a2f01b..b50281e 100644 --- a/make_page.py +++ b/make_page.py @@ -265,6 +265,28 @@ def nav_templates(cfg): raise FileNotFoundError("no nav key templates for Companion %s" % version) +REMOTE_VAR = "sp_remote" # North Attleboro's summary of the other locations (settings/campuses.py writes it) +BAND_BLUE, BAND_DARK = 0x1E6FFF, 0x0B2A66 + + +def locations_band(flash_var): + """A blue band across the bottom of NA's page-number key naming the other locations with a problem + ("SC", "SC WS"): it flashes until that location acknowledges, then stays. Red stays NA's own + (2026-10-21, the user: "i dont want the NA page buttons to flash red, that should he reserved for on site + problems").""" + r = "$(custom:%s)" % REMOTE_VAR + on = "$(internal:time_unix) - jsonpath(%s, '$.t') < 30 && jsonpath(%s, '$.n') > 0" % (r, r) + color = "jsonpath(%s, '$.f') == 1 && $(custom:%s) == 1 ? %d : %d" % (r, flash_var, BAND_DARK, BAND_BLUE) + box = {"id": stable_id("nav", "band_box"), "name": "Locations", "usage": "auto", "type": "box", "enabled": prop(on, True), + "opacity": prop(100), "x": prop(0), "y": prop(60), "width": prop(100), "height": prop(40), "rotation": prop(0), + "color": prop(color, True), "borderWidth": prop(0), "borderColor": prop(0), "borderPosition": prop("inside")} + text = {"id": stable_id("nav", "band_text"), "name": "Locations text", "usage": "auto", "type": "text", "enabled": prop(on, True), + "opacity": prop(100), "x": prop(0), "y": prop(60), "width": prop(100), "height": prop(40), "rotation": prop(0), + "text": prop("jsonpath(%s, '$.x')" % r, True), "color": prop(0xFFFFFF), "halign": prop("center"), "valign": prop("center"), + "fontsize": prop("auto"), "fontsizeAllowShrink": prop(True), "font": prop("companion-sans"), "outlineColor": prop(4278190080)} + return [box, text] + + def nav_key(kind, names, status_page=None, hold_ms=1000, tap_page=1, cfg=None): """Companion's own UP / page number / DOWN key, coloured like the ALL button. @@ -291,6 +313,8 @@ def nav_key(kind, names, status_page=None, hold_ms=1000, tap_page=1, cfg=None): layer["borderColor"] = prop(fg, True) paint(layer.get("children", [])) paint(key["style"]["layers"]) + if kind == "pagenum" and (cfg.get("nav_locations_band") or False): + key["style"]["layers"] += locations_band(opts["flash"]) if kind == "pagenum" and status_page: step = key["steps"]["0"] tap = step["action_sets"]["down"] diff --git a/ready-page.companionconfig b/ready-page.companionconfig index 20ab251fc9bebd2a90d4db93528e4ebe823eff95..fc78fa98c6770f3cad63cf4e30f06be0305914d3 100644 GIT binary patch literal 3449 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 bWp!N@OyWIOg6q$o*7M{4cM*slq3r+w@}kci literal 3986 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 stale: + names.append(tag + "?") + flashing = True + continue + try: + all_ = json.loads(st.get("state") or "{}").get("all") or {} + except ValueError: + all_ = {} + if not all_ or str(all_.get("x") or "").startswith("ALL OK"): + continue + names.append(tag) + flashing = flashing or all_.get("b") != all_.get("b2") + return {"t": int(now), "n": len(names), "x": " ".join(names), "f": 1 if flashing else 0} + + def campus_command(keys, text, now, restart=False): """The LOCATION page's taps and holds ("tap 1 3