Timers: every timer and clock in the room, from any source

The Timers module becomes the one place a production tech looks for every
timer and clock: ProPresenter's timers as before, the LTC timecode, a
time-of-day clock (the Clock module's format / seconds / date, folded in as
a sub-part — modules/clock itself is untouched), and timers other modules
offer over the module guide's provider contract: PCO Plan's service /
current item / countdown, or anything else declaring provides: ["timers"].

Server: a small `sources` abstraction. Each source turns its own world into
the guide's standard timer objects and reports health — ProPresenter (the
existing poller, wrapped, behind ppEnabled), LTC (the existing listener,
untouched, as a kind:"clock" timer whose status.text is the timecode, with
fps/df/ageMs in an `ltc` extension), Clock, and one module source per
provider found in the shell's /api/modules over loopback (shell.port):
/timers/stream when it answers as SSE, else /timers polled every 2 s;
re-discovered every 30 s; backoff 3 s → 15 s; a provider's timers survive a
10 s grace and then go. PCO Plan sits behind pcoEnabled, the rest behind
moduleTimersEnabled. The module is itself a provider: GET /timers and
GET /timers/stream serve the merged set. The header comment says how to
add a platform in a dozen lines.

Client: one tile, every timer, grouped under source headings — label, the
big time derived locally from the snapshot and a server-offset-corrected
clock (so digits tick smoothly between snapshots), a status line, a detail
line. Countdowns count down, elapsed count up, clocks show the time; the
LTC card still counts frames. Feedback colours (running / warning near the
end / overrun / idle) are per-tile colour settings applied as custom
properties on the tile root, so they win over dashboard themes; left at
their defaults they follow the theme. Gear settings: Clock, ProPresenter
timers, LTC timecode, Module timers, Source headings, Status lines, the
clock's format / seconds / date; a "Timers ▾" header menu hides any single
timer (source:id, saved with the layout; the old hiddenItems and
timer:<uuid> / ltc picker variants keep working).

Manifest: 1.3.0, proddash >=1.4.0, provides timers, configGroups with a
toggle switch per integration (ProPresenter / LTC Timecode / PCO Plan /
Other modules) and group help; switch labels are nouns.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-13 13:03:30 -04:00
co-authored by Claude Fable 5.1
parent a6dc2ddb85
commit bf1cabbc6b
4 changed files with 1849 additions and 669 deletions
+81 -9
View File
@@ -1,9 +1,10 @@
{
"id": "propresenter-timers",
"name": "Timers",
"version": "1.2.1",
"proddash": ">=1.2.0",
"description": "Live ProPresenter timers and LTC timecode",
"version": "1.3.0",
"proddash": ">=1.4.0",
"description": "Timers, Clocks, and LTC From configured Modules",
"provides": ["timers"],
"permissions": [
{ "kind": "microphone", "reason": "Decoding SMPTE LTC timecode from an audio input on this machine" },
{ "kind": "local-network", "reason": "Reaching ProPresenter on this network" }
@@ -12,8 +13,32 @@
"server": "server.js",
"style": "style.css",
"minSize": { "w": 2, "h": 1 },
"defaultSize": { "w": 4, "h": 2 },
"defaultSize": { "w": 4, "h": 3 },
"configGroups": {
"ProPresenter": {
"toggle": "ppEnabled",
"help": "The timers configured in ProPresenter, read over its network API. Enable the network in ProPresenter's Preferences › Network and enter the host and port shown there. Switch it off on a day ProPresenter isn't running and nothing is polled."
},
"LTC Timecode": {
"toggle": "ltcEnabled",
"help": "SMPTE LTC decoded from an audio input on this machine, shown as a running timecode. Pick the device and the channel the timecode arrives on; macOS has to grant ProdDash the microphone (see the launcher's Permissions)."
},
"PCO Plan": {
"toggle": "pcoEnabled",
"help": "Timers the PCO Plan module offers — service elapsed, current item, countdown to service — appear here under a PCO Plan heading when that module is installed and configured. Nothing to set up on this side."
},
"Other modules": {
"toggle": "moduleTimersEnabled",
"help": "Any other installed module that offers timers (\"provides\": [\"timers\"] in its manifest) is found automatically and listed under its own heading. Nothing to configure here."
}
},
"configSchema": {
"ppEnabled": {
"type": "switch",
"label": "ProPresenter timers",
"group": "ProPresenter",
"default": true
},
"propresenter": {
"type": "endpoint",
"label": "ProPresenter",
@@ -22,13 +47,14 @@
},
"password": {
"type": "password",
"label": "API password (only if set in ProPresenter)",
"label": "API password",
"group": "ProPresenter",
"default": ""
"default": "",
"help": "Only if a password is set in ProPresenter's network preferences."
},
"ltcEnabled": {
"type": "switch",
"label": "LTC listener — decode timecode from an audio input on this machine",
"label": "LTC timecode",
"group": "LTC Timecode",
"default": false
},
@@ -42,10 +68,23 @@
},
"ltcChannel": {
"type": "number",
"label": "Channel on that device (1-based)",
"label": "Channel",
"group": "LTC Timecode",
"showWhen": "ltcEnabled",
"default": 1
"default": 1,
"help": "The channel on that device carrying the timecode, counted from 1."
},
"pcoEnabled": {
"type": "switch",
"label": "PCO Plan timers",
"group": "PCO Plan",
"default": true
},
"moduleTimersEnabled": {
"type": "switch",
"label": "Module timers",
"group": "Other modules",
"default": true
},
"timerFont": {
"type": "select",
@@ -57,5 +96,38 @@
{ "value": "monospace", "label": "Monospace — steadier changing digits" }
]
}
},
"instanceGroups": {
"Show": { "collapsed": false, "columns": 2 },
"Clock": { "collapsed": false },
"Colours": {
"collapsed": true,
"columns": 2,
"help": "The time's colour per state. Left at the default, a colour follows the dashboard theme; one you change here wins over it."
}
},
"instanceSchema": {
"showClock": { "type": "boolean", "label": "Clock", "default": true, "group": "Show" },
"showProPresenter": { "type": "boolean", "label": "ProPresenter timers", "default": true, "group": "Show" },
"showLtc": { "type": "boolean", "label": "LTC timecode", "default": true, "group": "Show" },
"showModules": { "type": "boolean", "label": "Module timers", "default": true, "group": "Show" },
"showHeadings": { "type": "boolean", "label": "Source headings", "default": true, "group": "Show" },
"showStatus": { "type": "boolean", "label": "Status lines", "default": true, "group": "Show" },
"clockFormat": {
"type": "select",
"label": "Clock format",
"default": "24h",
"group": "Clock",
"options": [
{ "value": "24h", "label": "24-hour" },
{ "value": "12h", "label": "12-hour" }
]
},
"clockSeconds": { "type": "boolean", "label": "Seconds", "default": true, "group": "Clock" },
"clockDate": { "type": "boolean", "label": "Date", "default": false, "group": "Clock" },
"runningColor": { "type": "color", "label": "Running", "default": "#2ee59a", "group": "Colours" },
"warningColor": { "type": "color", "label": "Warning", "default": "#f0b429", "group": "Colours" },
"overrunColor": { "type": "color", "label": "Overrun", "default": "#f4433c", "group": "Colours" },
"idleColor": { "type": "color", "label": "Idle", "default": "#8b98a5", "group": "Colours" }
}
}