From 3118cdc28161445d607b05e65303ba95c1f5f322 Mon Sep 17 00:00:00 2001 From: christian Date: Thu, 8 Oct 2026 09:40:45 -0400 Subject: [PATCH] Colour the page nav keys on every page to follow the ALL button. nav_templates.json holds Companion's own UP / page number / DOWN keys after 'convert to regular button'; make_page.py colours them from the ALL tile and nav_keys.py copies them onto all 96 other pages (288 keys, recorded in nav_locations.json; --revert restores the built-in keys). Also rewrites the README's top half, which an earlier edit had duplicated. Co-Authored-By: Claude Opus 5.5 --- README.md | 93 ++- make_page.py | 32 +- nav_keys.py | 116 ++++ nav_locations.json | 290 +++++++++ nav_templates.json | 1112 +++++++++++++++++++++++++++++++++++ status-page.companionconfig | Bin 4378 -> 6361 bytes tests/test_status_panel.py | 12 +- 7 files changed, 1593 insertions(+), 62 deletions(-) create mode 100644 nav_keys.py create mode 100644 nav_locations.json create mode 100644 nav_templates.json diff --git a/README.md b/README.md index ab89f68..fdcc642 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,58 @@ # Status Indicators — Companion STATUS page for NA Ingest -One Companion page (page 19, **"19 STATUS"**) that is solid green when everything is fine and **blinks red with the actual problem** ("HD2 NO DISK", "LIGHT PC MIDI LOST") when something needs attention. +One Companion page (page 19, **"19 Status"**) that is solid green when everything is fine and **blinks red with the actual problem** ("HD3 NO DISK", "LIGHT PC MIDI LOST") when something needs attention. The UP / page number / DOWN keys on every other page follow the same colour, so you see a problem from any page. There are two parts: -- **`status_panel.py`** — a watchdog that runs on NA Ingest as the LaunchAgent `org.waterschurch.status-panel`. Every second it reads Companion's connection statuses and module variables. It also checks what Companion can't see: network MIDI peers, Bonjour MIDI sessions, pings, launchd services, disk space, and whether the Resi cloud is reachable. It writes the result into one Companion custom variable. +- **`status_panel.py`** — a watchdog that runs on NA Ingest as the LaunchAgent `org.waterschurch.status-panel`. Every second it reads Companion's connection statuses and module variables. It also checks what Companion can't see: network MIDI peers, Bonjour MIDI sessions, pings, launchd services, disk space, the Bible TV app, and whether the Resi cloud is reachable. It writes the result into one Companion custom variable. - **`status-page.companionconfig`** — the page. Every button just draws what the watchdog wrote and blinks on your existing `flash_tick` trigger. ## Layout (Stream Deck XL; MK.2 and Mini show the top-left corner) -| | col 1 | col 2 | col 3 | col 4 | col 5 | col 6 | col 7 | -|--------|-------|-------|-------|-------|-------|-------|-------| -| row 0 | **ALL** | REC | AUDIO | RF | SHOW | VIDEO | STREAM | -| row 1 | HD1 | HD2 | HD3 | HD4 | HD5 | HD6 | TASCAM | -| row 2 | X32 | DANTE | MIDI | APPS | | | | -| row 3 | ATEM | OBS | RESI | NET | INGEST | LED | ARENA | +| | col 0 | col 1 | col 2 | col 3 | col 4 | col 5 | col 6 | col 7 | +|--------|-------|-------|-------|-------|-------|-------|-------|-------| +| row 0 | UP | **ALL** | REC | AUDIO | RF | SHOW | VIDEO | STREAM | +| row 1 | page | HD1 | HD2 | HD3 | HD4 | HD5 | HD6 | TASCAM | +| row 2 | DOWN | X32 | DANTE | MIDI | APPS | | | | +| row 3 | ALARMS | ATEM | OBS | RESI | NET | INGEST | LED | ARENA | -Column 0 holds page up / page number / page down, plus **SERVICE** at row 3. +Row 0 holds the group buttons. Each one shows the first problem in its group, plus "+N" if there are more: -Row 0 holds the group tiles. Each one shows the first problem in its group, plus "+N" if there are more. RF covers all six Shure units: AD4Q ×2, ULXD4, and PSM1000 ×3. +- REC: HD1–6 and the Tascam. +- AUDIO: X32 and Dante. +- RF: all six Shure units (AD4Q ×2, ULXD4, PSM1000 ×3). +- SHOW: MIDI and APPS. +- VIDEO: ATEM, LED and Arena. +- STREAM: OBS and Resi. ## Colours -| Tile | Meaning | +| Colour | Meaning | |---|---| -| Green | OK. The second line shows something useful, like `28h` of record time or `10/10` Dante devices. | -| **Blinking red** | Show-stopping, during service mode. | +| Green | OK. The second line shows something useful, like `28h` of record time or `19/19` Dante devices. | +| **Blinking red** | Something is wrong. | | Solid dark red | Acknowledged. It stays red until the problem clears, then goes green. A new failure blinks again. | | Amber | Warning: low disk, battery at 2 bars, RF interference, a device that keeps dropping out ("WING GONE x3"), a network blip in the last 3 minutes ("BLIP 10:21"). | -| Dim red, not blinking | A fault outside service mode. | -| Grey | Expected off outside service: OBS, a HyperDeck with no disk, Bible TV. | +| Dim red, not blinking | A fault while alarms are muted. | +| Grey | Not in use: an optional HyperDeck with no disk, or a disabled connection. | | **Purple** | The watchdog has stopped. The master shows `WATCHDOG DOWN` and Companion's own error count. | ## Buttons -- **Press any tile:** acknowledge its alarms (stops the blinking). Press **ALL** to acknowledge everything. -- **Hold a device tile for 2 seconds:** restart that tile's *Companion connection*. This is the fix for a connection stuck after a network blip. It only restarts Companion's side and never reboots the device. -- **SERVICE:** service mode turns on automatically **Sunday 07:00–12:30** (`service_windows` in `config.json`). Press SERVICE to switch it on or off by hand, for example for a Wednesday event. The manual setting lasts until the next scheduled start or end. +- **Press any button:** acknowledge its alarms (stops the blinking). Press **ALL** to acknowledge everything. +- **Hold a device button for 2 seconds:** restart that button's *Companion connection*. This is the fix for a connection stuck after a network blip. It only restarts Companion's side and never reboots the device. +- **ALARMS:** alarms are armed all the time (`service_windows` in `config.json`: every day, 00:00–23:59). Press ALARMS to mute the blinking, for example during maintenance. It re-arms itself at midnight. -## What each tile checks +## Page keys on every page -| Tile | Checks | +The UP / page number / DOWN keys on **every page** follow the ALL button. They're green when everything is fine, flash red when something is wrong, and turn purple if the watchdog stops. They are Companion's own nav keys converted to regular buttons (`nav_templates.json`), so presses work exactly as before. + +- `python3 nav_keys.py` applies them to every page. Run it again after adding pages. +- `python3 nav_keys.py --revert` puts Companion's built-in nav keys back. + +## What each button checks + +| Button | Checks | |---|---| | HD1–HD6 | Companion connection; **NO DISK**; record time **LOW** (<30 min, amber) / **FULL** (<5 min); **NOT REC** when another deck is recording and this one isn't | | | HD2 and HD4 are `"optional": true` in `config.json`: with no disk they sit grey with no alarm, and once a disk is in they are watched like the others. Add or remove `"optional": true` on any deck to change that. | @@ -49,44 +61,6 @@ Row 0 holds the group tiles. Each one shows the first problem in its group, plus | DANTE | All 19 Dante devices ("19/19"), named when one drops: "WING GONE", "TRACKS GONE", "PROP MAC GONE". | | MIDI | Lighting PC and SuperRack still joined to NA Ingest's network MIDI session; the Lighting PC and NA Tracks MIDI sessions still on the network. A clean disconnect shows within seconds; a crash or pulled cable takes about a minute (macOS's own timeout, measured). | | APPS | ProPresenter API; Ableton OSC; tracks Mac pings; Bible TV app answering on :8080 (every 5 s); Bible TV OSC. Messages name the app ("PROPRES OFFLINE", "BIBLE APP DOWN"). Hold 2 s restarts the ProPresenter, Ableton and Bible TV Companion connections. | -| RESI | NET | INGEST | LED | ARENA | - -Column 0 holds page up / page number / page down, plus **SERVICE** at row 3. - -Row 0 holds the group tiles. Each one shows the first problem in its group, plus "+N" if there are more. RF covers all six Shure units: AD4Q ×2, ULXD4, and PSM1000 ×3. - -## Colours - -| Tile | Meaning | -|---|---| -| Green | OK. The second line shows something useful, like `28h` of record time or `10/10` Dante devices. | -| **Blinking red** | Show-stopping, during service mode. | -| Solid dark red | Acknowledged. It stays red until the problem clears, then goes green. A new failure blinks again. | -| Amber | Warning: low disk, battery at 2 bars, RF interference, a device that keeps dropping out ("WING GONE x3"), a network blip in the last 3 minutes ("BLIP 10:21"). | -| Dim red, not blinking | A fault outside service mode. | -| Grey | Expected off outside service: OBS, a HyperDeck with no disk, Bible TV. | -| **Purple** | The watchdog has stopped. The master shows `WATCHDOG DOWN` and Companion's own error count. | - -## Buttons - -- **Press any tile:** acknowledge its alarms (stops the blinking). Press **ALL** to acknowledge everything. -- **Hold a device tile for 2 seconds:** restart that tile's *Companion connection*. This is the fix for a connection stuck after a network blip. It only restarts Companion's side and never reboots the device. -- **SERVICE:** service mode turns on automatically **Sunday 07:00–12:30** (`service_windows` in `config.json`). Press SERVICE to switch it on or off by hand, for example for a Wednesday event. The manual setting lasts until the next scheduled start or end. - -## What each tile checks - -| Tile | Checks | -|---|---| -| HD1–HD6 | Companion connection; **NO DISK**; record time **LOW** (<30 min, amber) / **FULL** (<5 min); **NOT REC** when another deck is recording and this one isn't | -| | HD2 and HD4 are `"optional": true` in `config.json`: with no disk they sit grey with no alarm, and once a disk is in they are watched like the others. Add or remove `"optional": true` on any deck to change that. | -| TASCAM | connection; the recorder's own Error / Caution | -| X32, ATEM, ARENA, LED, OBS | Companion connection | -| DANTE | All 19 Dante devices ("19/19"), named when one drops: "WING GONE", "TRACKS GONE", "PROP MAC GONE". | -| TRACKS | NA-Tracks Dante device; tracks Mac pings; "NA Tracks" MIDI session still on the network; Ableton OSC connection | -| LIGHT PC | Lighting PC still joined to NA Ingest's network MIDI session; its MIDI session still on the network | -| RACK | SuperRack-LiveBox still joined to the MIDI session; its Dante device | -| BIBLE TV | TV pings; OSC connection | -| PROP | ProPresenter API; NA-ProPresenter-Mac Dante device | | RESI | decoder (RossTalk); resi-studio-cues; Resi cloud reachable | | NET | NA Ingest's link; gateway 10.1.0.1 pings; **4+ connections from 3+ groups dropping within 10 s** (what happened Sunday 10/5 at 10:21) | | INGEST | Resi bridge and lobby captions services running; ProdDash running; free disk | @@ -162,4 +136,7 @@ The **LIGHT PC** tile can do the same in the other direction. If the lighting so | `config.json` | tiles, positions, checks, service windows | | `make_page.py` | builds `status-page.companionconfig` from `config.json` | | `install.sh` | installs or updates the LaunchAgent on NA Ingest | +| `push_page.py` | imports the status page into page 19 over Companion's API | +| `nav_keys.py` | colours (or restores) the page nav keys on every page | +| `nav_templates.json` | Companion's own nav key designs, coloured by `make_page.py` | | `tests/` | offline tests, including a fake Companion and a MIDI loopback | diff --git a/make_page.py b/make_page.py index d18809c..27c9155 100644 --- a/make_page.py +++ b/make_page.py @@ -132,8 +132,38 @@ def button(tid, tile, cfg): } +NAV_LOCATIONS = {"pageup": ("0", "0"), "pagenum": ("1", "0"), "pagedown": ("2", "0")} +NAV_MARKER = "STATUS nav key" + + +def nav_key(kind, names): + """Companion's own UP / page number / DOWN key, coloured like the ALL button.""" + templates = json.load(open(os.path.join(HERE, "nav_templates.json"))) + key = json.loads(json.dumps(templates[kind])) + stale = "$(internal:time_unix) - $(custom:%s) > %d" % (names["heartbeat"], STALE_SECONDS) + pick = lambda field: "jsonpath($(custom:%s), '$.all.%s')" % (names["state"], field) + bg = "%s ? %d : ($(custom:flash_tick) == 1 ? %s : %s)" % (stale, PURPLE, pick("b"), pick("b2")) + fg = "%s ? %d : ($(custom:flash_tick) == 1 ? %s : %s)" % (stale, 0xFFFFFF, pick("f"), pick("f2")) + + def paint(layers): + for layer in layers: + if layer["type"] == "box": + layer["color"] = prop(bg, True) + elif layer["type"] == "text": + layer["color"] = prop(fg, True) + elif layer["type"] == "line": + layer["borderColor"] = prop(fg, True) + paint(layer.get("children", [])) + paint(key["style"]["layers"]) + key["options"]["notes"] = ("%s (%s): colour follows the ALL button on the status page. " + "Undo on every page with: python3 nav_keys.py --revert" % (NAV_MARKER, kind)) + return key + + def build(cfg): - controls = {"0": {"0": {"type": "pageup"}}, "1": {"0": {"type": "pagenum"}}, "2": {"0": {"type": "pagedown"}}} + controls = {} + for kind, (r, c) in NAV_LOCATIONS.items(): + controls.setdefault(r, {})[c] = nav_key(kind, cfg["variables"]) for tile in cfg["tiles"]: if "pos" not in tile: continue diff --git a/nav_keys.py b/nav_keys.py new file mode 100644 index 0000000..cc5289b --- /dev/null +++ b/nav_keys.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Make the UP / page number / DOWN keys on every page follow the status page's ALL button. + +The status page (page 19) carries coloured copies of Companion's own nav keys +(see make_page.py and nav_templates.json). This copies them over the built-in +nav keys on every other page, wherever those sit, using the same "copy button" +call as the Companion UI. Each key keeps its normal press action. + + python3 nav_keys.py --dry-run list what would change + python3 nav_keys.py apply (also refreshes keys already converted) + python3 nav_keys.py --revert put Companion's built-in nav keys back everywhere + +Only nav keys are touched. Every location changed is recorded in nav_locations.json. +""" +import argparse +import gzip +import json +import os +import urllib.error +import urllib.request + +from make_page import NAV_LOCATIONS, NAV_MARKER +from push_page import Trpc, WebSocket + +HERE = os.path.dirname(os.path.abspath(__file__)) +RECORD = os.path.join(HERE, "nav_locations.json") +NAV_TYPES = ("pageup", "pagenum", "pagedown") + + +def page_export(host, port, page): + url = "http://%s:%d/int/export/page/%d?includeSecrets=false" % (host, port, page) + try: + with urllib.request.urlopen(url, timeout=10) as r: + raw = r.read() + except urllib.error.HTTPError: + return None + try: + raw = gzip.decompress(raw) + except OSError: + pass + return json.loads(raw) + + +def nav_kind(control): + if control.get("type") in NAV_TYPES: + return control["type"] + notes = control.get("options", {}).get("notes", "") or "" + if control.get("type") == "button-layered" and notes.startswith(NAV_MARKER): + for kind in NAV_TYPES: + if "(%s)" % kind in notes: + return kind + return None + + +def find_nav_keys(host, port, status_page): + """[(page, row, column, kind)] for every nav key outside the status page.""" + found, page = [], 1 + while True: + export = page_export(host, port, page) + if export is None: + break + if page != status_page: + for r, cols in export["page"]["controls"].items(): + for c, control in cols.items(): + kind = nav_kind(control or {}) + if kind: + found.append((page, int(r), int(c), kind)) + page += 1 + return found + + +def main(): + cfg = json.load(open(os.path.join(HERE, "config.json"))) + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--host", default="na-ingest.local") + ap.add_argument("--port", type=int, default=8000) + ap.add_argument("--dry-run", action="store_true") + ap.add_argument("--revert", action="store_true") + args = ap.parse_args() + status_page = cfg["page"] + + record = json.load(open(RECORD)) if os.path.exists(RECORD) else {} + if args.revert: + targets = [(int(k.split("/")[0]), int(k.split("/")[1]), int(k.split("/")[2]), v) for k, v in record.items()] + else: + targets = find_nav_keys(args.host, args.port, status_page) + print("%d nav keys on %d pages" % (len(targets), len({t[0] for t in targets}))) + if args.dry_run: + for t in targets: + print(" page %d row %d col %d: %s" % t) + return + + ws = WebSocket(args.host, args.port, "/trpc") + done = 0 + try: + api = Trpc(ws) + for page, row, col, kind in targets: + location = {"pageNumber": page, "row": row, "column": col} + if args.revert: + api.mutation("controls.resetControl", {"location": location, "newType": kind}) + record.pop("%d/%d/%d" % (page, row, col), None) + else: + r, c = NAV_LOCATIONS[kind] + source = {"pageNumber": status_page, "row": int(r), "column": int(c)} + if not api.mutation("controls.copyControl", {"fromLocation": source, "toLocation": location}): + raise RuntimeError("copy to page %d row %d col %d failed" % (page, row, col)) + record["%d/%d/%d" % (page, row, col)] = kind + done += 1 + finally: + ws.close() + json.dump(record, open(RECORD, "w"), indent=1, sort_keys=True) + print("%s %d nav keys" % ("restored" if args.revert else "coloured", done)) + + +if __name__ == "__main__": + main() diff --git a/nav_locations.json b/nav_locations.json new file mode 100644 index 0000000..6aed03b --- /dev/null +++ b/nav_locations.json @@ -0,0 +1,290 @@ +{ + "1/0/0": "pageup", + "1/1/0": "pagenum", + "1/2/0": "pagedown", + "10/0/0": "pageup", + "10/1/0": "pagenum", + "10/2/0": "pagedown", + "11/0/0": "pageup", + "11/1/0": "pagenum", + "11/2/0": "pagedown", + "12/0/0": "pageup", + "12/1/0": "pagenum", + "12/2/0": "pagedown", + "13/0/0": "pageup", + "13/1/0": "pagenum", + "13/2/0": "pagedown", + "14/0/0": "pageup", + "14/1/0": "pagenum", + "14/2/0": "pagedown", + "15/0/0": "pageup", + "15/0/1": "pagenum", + "15/1/0": "pagenum", + "15/2/0": "pagedown", + "16/0/0": "pageup", + "16/1/0": "pagenum", + "16/2/0": "pagedown", + "17/0/0": "pageup", + "17/1/0": "pagenum", + "17/2/0": "pagedown", + "18/0/0": "pageup", + "18/1/0": "pagenum", + "18/2/0": "pagedown", + "2/0/0": "pageup", + "2/1/0": "pagenum", + "2/2/0": "pagedown", + "20/0/0": "pageup", + "20/1/0": "pagenum", + "20/2/0": "pagedown", + "21/0/0": "pageup", + "21/1/0": "pagenum", + "21/2/0": "pagedown", + "22/0/0": "pageup", + "22/1/0": "pagenum", + "22/2/0": "pagedown", + "23/0/0": "pageup", + "23/1/0": "pagenum", + "23/2/0": "pagedown", + "24/0/0": "pageup", + "24/1/0": "pagenum", + "24/2/0": "pagedown", + "25/0/0": "pageup", + "25/1/0": "pagenum", + "25/2/0": "pagedown", + "26/0/0": "pageup", + "26/1/0": "pagenum", + "26/2/0": "pagedown", + "27/0/0": "pageup", + "27/1/0": "pagenum", + "27/2/0": "pagedown", + "28/0/0": "pageup", + "28/1/0": "pagenum", + "28/2/0": "pagedown", + "29/0/0": "pageup", + "29/1/0": "pagenum", + "29/2/0": "pagedown", + "3/0/0": "pageup", + "3/1/0": "pagenum", + "3/2/0": "pagedown", + "30/0/0": "pageup", + "30/1/0": "pagenum", + "30/2/0": "pagedown", + "31/0/0": "pageup", + "31/1/0": "pagenum", + "31/2/0": "pagedown", + "32/0/0": "pageup", + "32/1/0": "pagenum", + "32/2/0": "pagedown", + "33/0/0": "pageup", + "33/1/0": "pagenum", + "33/2/0": "pagedown", + "34/0/0": "pageup", + "34/1/0": "pagenum", + "34/2/0": "pagedown", + "35/0/0": "pageup", + "35/1/0": "pagenum", + "35/2/0": "pagedown", + "36/0/0": "pageup", + "36/1/0": "pagenum", + "36/2/0": "pagedown", + "37/0/0": "pageup", + "37/1/0": "pagenum", + "37/2/0": "pagedown", + "38/0/0": "pageup", + "38/1/0": "pagenum", + "38/2/0": "pagedown", + "39/0/0": "pageup", + "39/1/0": "pagenum", + "39/2/0": "pagedown", + "4/0/0": "pageup", + "4/1/0": "pagenum", + "4/2/0": "pagedown", + "40/0/0": "pageup", + "40/1/0": "pagenum", + "40/2/0": "pagedown", + "41/0/0": "pageup", + "41/1/0": "pagenum", + "41/2/0": "pagedown", + "42/0/0": "pageup", + "42/1/0": "pagenum", + "42/2/0": "pagedown", + "43/0/0": "pageup", + "43/1/0": "pagenum", + "43/2/0": "pagedown", + "44/0/0": "pageup", + "44/1/0": "pagenum", + "44/2/0": "pagedown", + "45/0/0": "pageup", + "45/1/0": "pagenum", + "45/2/0": "pagedown", + "46/0/0": "pageup", + "46/1/0": "pagenum", + "46/2/0": "pagedown", + "47/0/0": "pageup", + "47/1/0": "pagenum", + "47/2/0": "pagedown", + "48/0/0": "pageup", + "48/1/0": "pagenum", + "48/2/0": "pagedown", + "49/0/0": "pageup", + "49/1/0": "pagenum", + "49/2/0": "pagedown", + "5/0/0": "pageup", + "5/1/0": "pagenum", + "5/2/0": "pagedown", + "50/0/0": "pageup", + "50/1/0": "pagenum", + "50/2/0": "pagedown", + "51/0/0": "pageup", + "51/1/0": "pagenum", + "51/2/0": "pagedown", + "52/0/0": "pageup", + "52/1/0": "pagenum", + "52/2/0": "pagedown", + "53/0/0": "pageup", + "53/1/0": "pagenum", + "53/2/0": "pagedown", + "54/0/0": "pageup", + "54/1/0": "pagenum", + "54/2/0": "pagedown", + "55/0/0": "pageup", + "55/1/0": "pagenum", + "55/2/0": "pagedown", + "56/0/0": "pageup", + "56/1/0": "pagenum", + "56/2/0": "pagedown", + "57/0/0": "pageup", + "57/1/0": "pagenum", + "57/2/0": "pagedown", + "58/0/0": "pageup", + "58/1/0": "pagenum", + "58/2/0": "pagedown", + "59/0/0": "pageup", + "59/1/0": "pagenum", + "59/2/0": "pagedown", + "6/0/0": "pageup", + "6/1/0": "pagenum", + "6/2/0": "pagedown", + "60/0/0": "pageup", + "60/1/0": "pagenum", + "60/2/0": "pagedown", + "61/0/0": "pageup", + "61/1/0": "pagenum", + "61/2/0": "pagedown", + "62/0/0": "pageup", + "62/1/0": "pagenum", + "62/2/0": "pagedown", + "63/0/0": "pageup", + "63/1/0": "pagenum", + "63/2/0": "pagedown", + "64/0/0": "pageup", + "64/1/0": "pagenum", + "64/2/0": "pagedown", + "65/0/0": "pageup", + "65/1/0": "pagenum", + "65/2/0": "pagedown", + "66/0/0": "pageup", + "66/1/0": "pagenum", + "66/2/0": "pagedown", + "67/0/0": "pageup", + "67/1/0": "pagenum", + "67/2/0": "pagedown", + "68/0/0": "pageup", + "68/1/0": "pagenum", + "68/2/0": "pagedown", + "69/0/0": "pageup", + "69/1/0": "pagenum", + "69/2/0": "pagedown", + "7/0/0": "pageup", + "7/1/0": "pagenum", + "7/2/0": "pagedown", + "70/0/0": "pageup", + "70/1/0": "pagenum", + "70/2/0": "pagedown", + "71/0/0": "pageup", + "71/1/0": "pagenum", + "71/2/0": "pagedown", + "72/0/0": "pageup", + "72/1/0": "pagenum", + "72/2/0": "pagedown", + "73/0/0": "pageup", + "73/1/0": "pagenum", + "73/2/0": "pagedown", + "74/0/0": "pageup", + "74/1/0": "pagenum", + "74/2/0": "pagedown", + "75/0/0": "pageup", + "75/1/0": "pagenum", + "75/2/0": "pagedown", + "76/0/0": "pageup", + "76/1/0": "pagenum", + "76/2/0": "pagedown", + "77/0/0": "pageup", + "77/1/0": "pagenum", + "77/2/0": "pagedown", + "78/0/0": "pageup", + "78/1/0": "pagenum", + "78/2/0": "pagedown", + "79/0/0": "pageup", + "79/1/0": "pagenum", + "79/2/0": "pagedown", + "8/0/0": "pageup", + "8/1/0": "pagenum", + "8/2/0": "pagedown", + "80/0/0": "pageup", + "80/1/0": "pagenum", + "80/2/0": "pagedown", + "81/0/0": "pageup", + "81/1/0": "pagenum", + "81/2/0": "pagedown", + "82/0/0": "pageup", + "82/1/0": "pagenum", + "82/2/0": "pagedown", + "83/0/0": "pageup", + "83/1/0": "pagenum", + "83/2/0": "pagedown", + "84/0/0": "pageup", + "84/1/0": "pagenum", + "84/2/0": "pagedown", + "85/0/0": "pageup", + "85/1/0": "pagenum", + "85/2/0": "pagedown", + "86/0/0": "pageup", + "86/1/0": "pagenum", + "86/2/0": "pagedown", + "87/0/0": "pageup", + "87/1/0": "pagenum", + "87/2/0": "pagedown", + "88/0/0": "pageup", + "88/1/0": "pagenum", + "88/2/0": "pagedown", + "89/0/0": "pageup", + "89/1/0": "pagenum", + "89/2/0": "pagedown", + "9/0/0": "pageup", + "9/1/0": "pagenum", + "9/2/0": "pagedown", + "90/0/0": "pageup", + "90/1/0": "pagenum", + "90/2/0": "pagedown", + "91/0/0": "pageup", + "91/1/0": "pagenum", + "91/2/0": "pagedown", + "92/0/0": "pageup", + "92/1/0": "pagenum", + "92/2/0": "pagedown", + "93/0/0": "pageup", + "93/1/0": "pagenum", + "93/2/0": "pagedown", + "94/0/0": "pageup", + "94/1/0": "pagenum", + "94/2/0": "pagedown", + "95/0/0": "pageup", + "95/1/0": "pagenum", + "95/2/0": "pagedown", + "96/0/0": "pageup", + "96/2/0": "pagedown", + "97/0/0": "pageup", + "97/1/0": "pagenum", + "97/2/0": "pagedown" +} \ No newline at end of file diff --git a/nav_templates.json b/nav_templates.json new file mode 100644 index 0000000..70d9ddb --- /dev/null +++ b/nav_templates.json @@ -0,0 +1,1112 @@ +{ + "_comment": "Companion 5.0.7's own UP / page number / DOWN keys after 'convert to regular button' (captured from page 19). make_page.py colours them to follow the ALL button.", + "pageup": { + "type": "button-layered", + "style": { + "layers": [ + { + "type": "canvas", + "id": "canvas", + "name": "Canvas", + "decoration": { + "value": "none", + "isExpression": false + }, + "showStatusIcons": { + "value": "none", + "isExpression": false + }, + "usage": "auto" + }, + { + "id": "izGK69mOF_3K_vast-75-", + "name": "Box", + "usage": "auto", + "type": "box", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "color": { + "value": 986895, + "isExpression": false + }, + "borderWidth": { + "value": 0, + "isExpression": false + }, + "borderColor": { + "value": 0, + "isExpression": false + }, + "borderPosition": { + "value": "inside", + "isExpression": false + } + }, + { + "id": "S2zfksBcHbLGhAkGStpCD", + "name": "Group", + "usage": "auto", + "type": "group", + "enabled": { + "value": "!$(internal:_graphics_page_plusminus)", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "squareCoords": { + "value": true, + "isExpression": false + }, + "children": [ + { + "id": "0gsiGY7PIeOguUcCpGq3-", + "name": "Line", + "usage": "auto", + "type": "line", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "fromX": { + "value": 64, + "isExpression": false + }, + "fromY": { + "value": 42, + "isExpression": false + }, + "toX": { + "value": 50, + "isExpression": false + }, + "toY": { + "value": 28, + "isExpression": false + }, + "borderWidth": { + "value": 2.5, + "isExpression": false + }, + "borderColor": { + "value": 16777215, + "isExpression": false + }, + "borderPosition": { + "value": "center", + "isExpression": false + } + }, + { + "id": "Y7I_lCA16E5VyG_dkN__4", + "name": "Line", + "usage": "auto", + "type": "line", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "fromX": { + "value": 36, + "isExpression": false + }, + "fromY": { + "value": 42, + "isExpression": false + }, + "toX": { + "value": 50, + "isExpression": false + }, + "toY": { + "value": 28, + "isExpression": false + }, + "borderWidth": { + "value": 2.5, + "isExpression": false + }, + "borderColor": { + "value": 16777215, + "isExpression": false + }, + "borderPosition": { + "value": "center", + "isExpression": false + } + } + ] + }, + { + "id": "25BTnu27N9XsTWY1yWL-2", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": "$(internal:_graphics_page_plusminus)", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 50, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "$(internal:_graphics_page_direction_flipped) ? \"\u2013\" : \"+\"", + "isExpression": true + }, + "color": { + "value": 16777215, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "bottom", + "isExpression": false + }, + "fontsize": { + "value": 60, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + }, + { + "id": "LErrXOlybEb79QDcL10JX", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 53, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 47, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "UP", + "isExpression": false + }, + "color": { + "value": 16762368, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "top", + "isExpression": false + }, + "fontsize": { + "value": 35, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + } + ] + }, + "options": { + "stepProgression": "auto", + "stepExpression": "", + "rotaryActions": false, + "canModifyStyleInApis": false, + "notes": "" + }, + "feedbacks": [], + "steps": { + "0": { + "action_sets": { + "down": [ + { + "type": "action", + "id": "HL1ak-HDycKYHqHq2k6dJ", + "definitionId": "inc_page", + "connectionId": "internal", + "options": { + "surfaceId": { + "value": "self", + "isExpression": false + } + }, + "children": {} + } + ], + "up": [] + }, + "options": { + "runWhileHeld": [] + } + } + }, + "localVariables": [] + }, + "pagenum": { + "type": "button-layered", + "style": { + "layers": [ + { + "type": "canvas", + "id": "canvas", + "name": "Canvas", + "decoration": { + "value": "none", + "isExpression": false + }, + "showStatusIcons": { + "value": "none", + "isExpression": false + }, + "usage": "auto" + }, + { + "id": "UIOLp5jwlucdF28LC6JAG", + "name": "Box", + "usage": "auto", + "type": "box", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "color": { + "value": 986895, + "isExpression": false + }, + "borderWidth": { + "value": 0, + "isExpression": false + }, + "borderColor": { + "value": 0, + "isExpression": false + }, + "borderPosition": { + "value": "inside", + "isExpression": false + } + }, + { + "id": "cwDjasrSdQFzuQ-P-CsyY", + "name": "Group", + "usage": "auto", + "type": "group", + "enabled": { + "value": "!!$(this:page_name) && toLowerCase($(this:page_name)) != \"page\" && $(this:page_name) != \"$NA\"", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "squareCoords": { + "value": false, + "isExpression": false + }, + "children": [ + { + "id": "ZjhWugmmxc99lkPHQhpeS", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "$(this:page_name)", + "isExpression": false + }, + "color": { + "value": 16777215, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "center", + "isExpression": false + }, + "fontsize": { + "value": 30, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + } + ] + }, + { + "id": "mL485oO4IXLUfwfslwWA6", + "name": "Group", + "usage": "auto", + "type": "group", + "enabled": { + "value": "!$(this:page_name) || toLowerCase($(this:page_name)) == \"page\" || $(this:page_name) == \"$NA\"", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "squareCoords": { + "value": false, + "isExpression": false + }, + "children": [ + { + "id": "2RN8veJZ2JCIL0aJB-NKg", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 40, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "PAGE", + "isExpression": false + }, + "color": { + "value": 16762368, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "bottom", + "isExpression": false + }, + "fontsize": { + "value": 41.25, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + }, + { + "id": "tS6Hw2JiYkzwwOmSgQqUD", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 45, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 55, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "getVariable(\"this:page\") || \"x\"", + "isExpression": true + }, + "color": { + "value": 16777215, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "top", + "isExpression": false + }, + "fontsize": { + "value": 54.5, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": true, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + } + ] + } + ] + }, + "options": { + "stepProgression": "auto", + "stepExpression": "", + "rotaryActions": false, + "canModifyStyleInApis": false, + "notes": "" + }, + "feedbacks": [], + "steps": { + "0": { + "action_sets": { + "down": [ + { + "type": "action", + "id": "vWdrmcd4HrTyNEcTOVTXG", + "definitionId": "set_page", + "connectionId": "internal", + "options": { + "surfaceId": { + "value": "self", + "isExpression": false + }, + "page": { + "value": "startup", + "isExpression": false + } + }, + "children": {} + } + ], + "up": [] + }, + "options": { + "runWhileHeld": [] + } + } + }, + "localVariables": [] + }, + "pagedown": { + "type": "button-layered", + "style": { + "layers": [ + { + "type": "canvas", + "id": "canvas", + "name": "Canvas", + "decoration": { + "value": "none", + "isExpression": false + }, + "showStatusIcons": { + "value": "none", + "isExpression": false + }, + "usage": "auto" + }, + { + "id": "FzNzbZUl1CdElE55Mrgkm", + "name": "Box", + "usage": "auto", + "type": "box", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "color": { + "value": 986895, + "isExpression": false + }, + "borderWidth": { + "value": 0, + "isExpression": false + }, + "borderColor": { + "value": 0, + "isExpression": false + }, + "borderPosition": { + "value": "inside", + "isExpression": false + } + }, + { + "id": "5knFW43hkb1Nd0jsVA_2Y", + "name": "Group", + "usage": "auto", + "type": "group", + "enabled": { + "value": "!$(internal:_graphics_page_plusminus)", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 100, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "squareCoords": { + "value": true, + "isExpression": false + }, + "children": [ + { + "id": "zfBarA9McgqDraSpEOTGj", + "name": "Line", + "usage": "auto", + "type": "line", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "fromX": { + "value": 64, + "isExpression": false + }, + "fromY": { + "value": 55, + "isExpression": false + }, + "toX": { + "value": 50, + "isExpression": false + }, + "toY": { + "value": 69, + "isExpression": false + }, + "borderWidth": { + "value": 2.5, + "isExpression": false + }, + "borderColor": { + "value": 16777215, + "isExpression": false + }, + "borderPosition": { + "value": "center", + "isExpression": false + } + }, + { + "id": "BAlGM2Bv7HO2WgEjaxACZ", + "name": "Line", + "usage": "auto", + "type": "line", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "fromX": { + "value": 36, + "isExpression": false + }, + "fromY": { + "value": 55, + "isExpression": false + }, + "toX": { + "value": 50, + "isExpression": false + }, + "toY": { + "value": 69, + "isExpression": false + }, + "borderWidth": { + "value": 2.5, + "isExpression": false + }, + "borderColor": { + "value": 16777215, + "isExpression": false + }, + "borderPosition": { + "value": "center", + "isExpression": false + } + } + ] + }, + { + "id": "39vkw7GhE4wyf0H1L-tWb", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": "$(internal:_graphics_page_plusminus)", + "isExpression": true + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 50, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 50, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "$(internal:_graphics_page_direction_flipped) ? \"+\" : \"\u2013\"", + "isExpression": true + }, + "color": { + "value": 16777215, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "top", + "isExpression": false + }, + "fontsize": { + "value": 60, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + }, + { + "id": "jNHbkussdvJZZ_5eaFoE-", + "name": "Text", + "usage": "auto", + "type": "text", + "enabled": { + "value": true, + "isExpression": false + }, + "opacity": { + "value": 100, + "isExpression": false + }, + "x": { + "value": 0, + "isExpression": false + }, + "y": { + "value": 0, + "isExpression": false + }, + "width": { + "value": 100, + "isExpression": false + }, + "height": { + "value": 47, + "isExpression": false + }, + "rotation": { + "value": 0, + "isExpression": false + }, + "text": { + "value": "DOWN", + "isExpression": false + }, + "color": { + "value": 16762368, + "isExpression": false + }, + "halign": { + "value": "center", + "isExpression": false + }, + "valign": { + "value": "bottom", + "isExpression": false + }, + "fontsize": { + "value": 35, + "isExpression": false + }, + "fontsizeAllowShrink": { + "value": false, + "isExpression": false + }, + "font": { + "value": "companion-sans", + "isExpression": false + }, + "outlineColor": { + "value": 4278190080, + "isExpression": false + } + } + ] + }, + "options": { + "stepProgression": "auto", + "stepExpression": "", + "rotaryActions": false, + "canModifyStyleInApis": false, + "notes": "" + }, + "feedbacks": [], + "steps": { + "0": { + "action_sets": { + "down": [ + { + "type": "action", + "id": "aWZ4y8okI7hY_70P47nbl", + "definitionId": "dec_page", + "connectionId": "internal", + "options": { + "surfaceId": { + "value": "self", + "isExpression": false + } + }, + "children": {} + } + ], + "up": [] + }, + "options": { + "runWhileHeld": [] + } + } + }, + "localVariables": [] + } +} \ No newline at end of file diff --git a/status-page.companionconfig b/status-page.companionconfig index b9ea3e97df3935a5868c82bbfac29f6f8f71474e..a395e4c0004c9f46b118b9489cbaf715f68192af 100644 GIT binary patch literal 6361 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 BKxzO0 literal 4378 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 diff --git a/tests/test_status_panel.py b/tests/test_status_panel.py index 99ae8d1..c7fcd27 100644 --- a/tests/test_status_panel.py +++ b/tests/test_status_panel.py @@ -555,11 +555,17 @@ class TestPage(unittest.TestCase): def test_layout(self): self.assertEqual(self.export["type"], "page") self.assertEqual(self.export["oldPageNumber"], 19) - self.assertEqual(self.controls["0"]["0"], {"type": "pageup"}) - self.assertEqual(self.controls["2"]["0"], {"type": "pagedown"}) + for (r, kind, action) in (("0", "pageup", "inc_page"), ("1", "pagenum", "set_page"), ("2", "pagedown", "dec_page")): + key = self.controls[r]["0"] + self.assertEqual(key["type"], "button-layered") + self.assertTrue(key["options"]["notes"].startswith("STATUS nav key (%s)" % kind)) + self.assertEqual(key["steps"]["0"]["action_sets"]["down"][0]["definitionId"], action) + box = [l for l in key["style"]["layers"] if l["type"] == "box"][0] + self.assertTrue(box["color"]["isExpression"]) + self.assertIn("'$.all.b'", box["color"]["value"]) # follows the ALL button positioned = [t for t in CONFIG["tiles"] if "pos" in t] n = sum(1 for r in self.controls.values() for c in r.values() if c["type"] == "button-layered") - self.assertEqual(n, len(positioned)) + self.assertEqual(n, len(positioned) + 3) # + the three nav keys # The master is where the smallest Stream Deck can see it. self.assertEqual(CONFIG["tiles"][0]["pos"], [0, 1])