Sits between the classrooms, Companion and ProPresenter, and owns the truth about a parent call: validation, ids, the queue, expiry, duplicates, mode, approval, delivery, and every status a classroom is told. Companion is an operator surface and holds no state. Built after querying the church's own ProPresenter rather than working from the assumed contract, which turned out to be wrong in two ways: the message is called Parent Caller, not Parent Call, and its tokens are Name and Room rather than child and room. The finding that shaped the design is recorded in docs/propresenter-api.md. A trigger carrying a token name the message does not have still returns 204, and still puts the message on screen showing the previous call's values. So a 204 is not evidence on its own: the adapter reads the live message and refuses to trigger unless the configured tokens exist in it. Without that, a typo in .env would call the wrong child's parents and report success. Nothing claims more than it knows. A successful API call is reported as sent to sanctuary, never as displayed. The state machine is pure and synchronous, with time passed in and effects returned, so the awkward cases are testable properly: two rooms calling at once, an approval for the wrong id, an approval arriving after the request went stale, and a clear timer firing after the next call has gone up. The queue is in memory and dies with the process, because a parent call surfacing ten minutes late would be worse than one that was lost. 81 tests, then the whole thing end to end on Control: a request through the real broker and the real coordinator to the real ProPresenter, with the correct child and room on the sanctuary screen, the coordinator's own clear timer taking it down again, mode off sending nothing, and a duplicate id not sending twice.
90 lines
3.2 KiB
Markdown
90 lines
3.2 KiB
Markdown
# ProPresenter API, as actually observed
|
|
|
|
Recorded against the church's own machine on 2026-09-06, not from
|
|
documentation. Re-run `parent-call-probe` to refresh this.
|
|
|
|
```
|
|
Host 10.30.0.2:1025 (the MacBook, "MacBook-Pro")
|
|
ProPresenter 21.3
|
|
Platform mac, macOS 26.6.2
|
|
api_version v1
|
|
```
|
|
|
|
## Endpoints that exist
|
|
|
|
| Request | Result |
|
|
| --- | --- |
|
|
| `GET /version` | `200` + JSON. Note: **not** `/v1/version`, which 404s |
|
|
| `GET /v1/messages` | `200` + array of messages |
|
|
| `GET /v1/message/{id}` | `200` + one message |
|
|
| `POST /v1/message/{id}/trigger` | **`204`**, ~34ms |
|
|
| `GET /v1/message/{id}/clear` | **`204`**, ~7ms |
|
|
| `POST /v1/message/{unknown-uuid}/trigger` | `404` |
|
|
|
|
`{id}` accepts either the uuid or the URL-encoded name — both returned
|
|
`204`. The coordinator uses the uuid, resolved from the name, so that
|
|
renaming the message in ProPresenter is noticed rather than silently
|
|
changing which message is triggered.
|
|
|
|
## The message that already exists
|
|
|
|
The spec assumed a message called `Parent Call` with tokens `child` and
|
|
`room`. The real one is different, which is why this was checked:
|
|
|
|
```
|
|
name Parent Caller
|
|
uuid 7D044452-B5A1-49F5-AC9C-1FD53A9A7353
|
|
index 2
|
|
text {Name}'s parent please go to the {Room}
|
|
tokens Name, Room <- capitalised
|
|
visible_on_network: true
|
|
```
|
|
|
|
Nothing was created or renamed. The coordinator is configured to match
|
|
what is there.
|
|
|
|
Because the template reads "...please go to the {Room}", the room token
|
|
wants a human room name — "Nursery", not the `nursery` slug the topic
|
|
uses. The coordinator maps slug to display name, configurably.
|
|
|
|
## The finding that shaped the design
|
|
|
|
**An unknown token name is silently accepted.**
|
|
|
|
```
|
|
POST /v1/message/{uuid}/trigger
|
|
[{"name":"child","text":{"text":"SHOULD NOT APPEAR"}}]
|
|
|
|
-> HTTP 204
|
|
-> message goes on screen showing the PREVIOUS token values
|
|
```
|
|
|
|
Verified deliberately, with a known baseline set and cleared first, and
|
|
a 2 second settle before reading state.
|
|
|
|
Two things follow, and both are in the adapter:
|
|
|
|
1. **A 204 does not mean the right thing was displayed.** If the token
|
|
names were misconfigured, every parent call would return 204 — so the
|
|
teacher would be told "Sent to sanctuary" — while the sanctuary
|
|
screen showed *the previous child's name and room*. In a church that
|
|
is the worst failure this system could have: the wrong family walks
|
|
out. So the coordinator reads the live message definition and refuses
|
|
to trigger unless the configured token names actually exist in it. A
|
|
mismatch is reported as an error and never as success.
|
|
|
|
2. **Token values persist between triggers.** Every trigger therefore
|
|
sends both tokens, always, so a call can never inherit half of the
|
|
one before it.
|
|
|
|
## Timing
|
|
|
|
The API returns before its own state settles: reading `/v1/messages`
|
|
immediately after a `204` can still show the previous values, and the
|
|
new ones appear within about a second. Nothing in the coordinator reads
|
|
back state to confirm a trigger, so this only matters to anyone
|
|
debugging by hand — do not believe a read taken in the same second.
|
|
|
|
`is_active` goes `true` on trigger and `false` on clear, and token
|
|
values survive a clear.
|