Step 0 of the settings page: registry, safe restart, remembered state, test verb, page markers

- schema.py: every setting with type, default, help, owner screen and the rules a config must
  pass; tests/test_schema.py keeps it in step with the code (every key the code reads is declared,
  every default matches, check types and fix kinds agree, the glossary covers every word a key can
  show, today's config still builds today's pages apart from the key notes)
- status_panel.py: VERSION + started.json; SIGTERM lets a running fix finish and starts nothing new
  (RESTARTING SOON); fixes-running.json and live-now.json for the settings page; state.json keeps
  the ALARMS override, acknowledgements, auto-fix tries and fix times across restarts, keyed by
  what a check is about rather than its position; 'test' and 'resurvey' commands; queued commands
  dropped at start; overnight windows; a HyperDeck switched off is always switched back on;
  the spotify fix kind (playlist set once, in config 'spotify'); Windows remote checks batched for
  cmd.exe with conditional echoes; every fix's kind and host validated; never_auto; wording and
  numbers from config (site block, timing, page_hold_ms, auto-fix keep_months); hyperdeck_probe
  removed; --check and --exit-timeout
- make_page.py: numbers from config, markers on every key, nav templates per Companion version,
  --out; push_page.py: a page is overwritten only when every key carries a marker (foreign keys
  named by position), secret-free fetch, host from the site; nav_keys.py: host from the site;
  netgear.py: switches and the shared-port limit from config
- install.sh: validates with the new code first, keeps prev/ for --rollback, --keep-config,
  derived ExitTimeOut, wider deploy list, waits for the start stamp
- config.json: site, timing, spotify sections; Windows hosts marked; deployed to NA Ingest and
  both pages re-imported once (--force) so every key carries its marker

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 16:00:41 -04:00
co-authored by Claude Fable 5.1
parent a3934b7c62
commit 5d66805687
13 changed files with 1822 additions and 307 deletions
+9 -4
View File
@@ -139,15 +139,17 @@ A problem has to last 3 seconds before it shows, so one bad reading doesn't trig
This folder is the source of truth. Edit `config.json` here, then copy the folder to NA Ingest:
```bash
scp status_panel.py coremidi.py netgear.py config.json install.sh checklist-ingest:status-panel-install/
scp status_panel.py coremidi.py netgear.py make_page.py push_page.py nav_keys.py nav_templates.json config.json install.sh atem_watch.js atem_state.js atem-reference.xml checklist-ingest:status-panel-install/
```
Then run the installer there. It backs up the old config and restarts the watchdog:
Then run the installer there. It checks the config with the new code first (a bad config never gets as far as the watchdog), keeps the code that was running in `prev/`, backs up the old config, writes the LaunchAgent with the exit timeout this config needs (the longest fix plus its verify time, so a restart never cuts a fix in half) and restarts the watchdog:
```bash
ssh checklist-ingest 'zsh ~/status-panel-install/install.sh'
```
`install.sh --keep-config` updates the code and leaves the live config alone; `install.sh --rollback` puts the previous code back.
If you add or move tiles, rebuild the page:
```bash
@@ -172,6 +174,8 @@ zsh install.sh --remove # stop the watchdog
The log is `~/Library/Logs/status-panel.log` on NA Ingest. Every fault and clear is written there with a timestamp, which is handy for a post-service review.
What the watchdog keeps in its data folder (`~/Library/Application Support/status-panel/`): `state.json` (the ALARMS override, acknowledged faults, auto-fix tries and when each fix last ran, so a restart forgets nothing), `started.json` (when it started, with which config), `live-now.json` (whether a HyperDeck is recording: changes to the panel wait while it is), `fixes-running.json` (the fixes running right now). A restart (`launchctl kickstart -k`, or the installer) lets a running fix finish first and starts nothing new meanwhile: keys say RESTARTING SOON. Windows in `config.json` may run past midnight (`"end"` earlier than `"start"`), and a remote check on a Windows PC (`"os": "windows"` on the host) is batched for cmd.exe.
## Optional: see what the tracks send to the lights
The TRACKS tile can show when the tracks rig last sent MIDI ("TRK MIDI 3s"). For that, NA Ingest has to *listen* to the "NA Tracks" session. Use a **separate** session so nothing Companion sends can reach Ableton:
@@ -204,7 +208,8 @@ To go back, pick 1.3.0 on the connection again.
| `status_panel.py` | the watchdog |
| `netgear.py` | reads the Netgear Dante switches (MAC tables, PoE) and power-cycles one PoE port |
| `coremidi.py` | reads network MIDI sessions and listens to MIDI sources (ctypes, no extra installs) |
| `config.json` | tiles, positions, checks, service windows |
| `config.json` | tiles, positions, checks, fixes, hosts, windows: every setting (the `site` block names the campus; `spotify` holds the playlist; `timing` and `page_hold_ms` the numbers behind HOLD 2s and WAIT 1 MIN) |
| `schema.py` | the registry of every setting: type, default, help, which screen of the settings page owns it, and the rules a config must pass (`tests/test_schema.py` keeps it in step with the code) |
| `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 |
@@ -212,4 +217,4 @@ To go back, pick 1.3.0 on the connection again.
| `nav_templates.json` | Companion's own nav key designs, coloured by `make_page.py` |
| `remote-access/` | one-time setup scripts for SSH to the Macs and Windows PCs |
| `tascam-quiet/` | builds the quiet Tascam module |
| `tests/` | offline tests, including a fake Companion and a MIDI loopback |
| `tests/` | offline tests, including a fake Companion and a MIDI loopback; `test_schema.py` checks that every key the code reads is declared in `schema.py` with the same default |