A failed login left no session, so the next 1s poll signed in again, and the next, for ever. Resi answers that with 429s, and since the retries never let up the account never got out of them: at North Attleboro the decoder picker stayed empty with "Resi login failed (429)". The picker made it worse by building a fresh client -- and signing in -- on every admin page load. - After a failed login, further attempts wait 15s, doubling to 5min (or as long as Resi's Retry-After asks); calls in between fail at once with the reason and how long until the next try. - The status says what happened: rate-limited, password rejected, or Resi unreachable. - The decoder picker reuses the running module's client when the login matches, otherwise one client per login, so it shares the hold-off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
92 lines
3.0 KiB
JSON
92 lines
3.0 KiB
JSON
{
|
|
"id": "resi-cues",
|
|
"name": "Resi Cues",
|
|
"version": "1.0.2",
|
|
"proddash": ">=1.4.0",
|
|
"description": "Time until the next Resi cue point, on the board and as a timer",
|
|
"provides": ["timers"],
|
|
"client": "client.js",
|
|
"server": "server.js",
|
|
"style": "style.css",
|
|
"minSize": { "w": 2, "h": 1 },
|
|
"defaultSize": { "w": 4, "h": 3 },
|
|
"configGroups": {
|
|
"Resi account": {
|
|
"help": "The Resi Studio login the board reads with. Cue points and the decoder's playhead both come from Resi's cloud, so nothing is installed at the decoder. Worth using a dedicated low-privilege Resi user rather than a personal one."
|
|
},
|
|
"Decoder": {
|
|
"help": "Which decoder to follow. The list is read from the account once the login above is saved."
|
|
},
|
|
"Timing": {
|
|
"collapsed": true,
|
|
"help": "Resi refreshes a decoder's position only every few seconds, on an irregular cadence, and each reading is already slightly stale. The board estimates between readings rather than repeating them, so these only change how often it asks — not how accurate it is."
|
|
}
|
|
},
|
|
"configSchema": {
|
|
"username": {
|
|
"type": "string",
|
|
"label": "Resi login",
|
|
"group": "Resi account",
|
|
"default": "",
|
|
"help": "The email address used to sign in to studio.resi.io."
|
|
},
|
|
"password": {
|
|
"type": "password",
|
|
"label": "Password",
|
|
"group": "Resi account",
|
|
"default": ""
|
|
},
|
|
"player": {
|
|
"type": "select",
|
|
"label": "Decoder",
|
|
"group": "Decoder",
|
|
"default": "",
|
|
"optionsRoute": "/decoders",
|
|
"help": "Leave unset to follow the only decoder on the account."
|
|
},
|
|
"pollSeconds": {
|
|
"type": "number",
|
|
"label": "Position poll (s)",
|
|
"group": "Timing",
|
|
"default": 1,
|
|
"help": "How often Resi is asked where the playhead is. A faster poll notices a fired cue sooner; it does not make the countdown smoother, which is handled by estimating between readings."
|
|
},
|
|
"cueRefreshSeconds": {
|
|
"type": "number",
|
|
"label": "Cue list refresh (s)",
|
|
"group": "Timing",
|
|
"default": 5,
|
|
"help": "How often the cue list is re-read. It is also re-read immediately whenever the playhead jumps, so a cue fired at the host shows up without waiting for this."
|
|
},
|
|
"positionTrimMs": {
|
|
"type": "number",
|
|
"label": "Position trim (ms)",
|
|
"group": "Timing",
|
|
"default": 0,
|
|
"help": "Leave at zero. Only needed if the whole chain reads consistently behind; a positive value pushes the estimate forward."
|
|
}
|
|
},
|
|
"instanceSchema": {
|
|
"showCueName": {
|
|
"type": "switch",
|
|
"label": "Cue name",
|
|
"default": true
|
|
},
|
|
"showPrevious": {
|
|
"type": "switch",
|
|
"label": "Previous cue",
|
|
"default": true
|
|
},
|
|
"showPosition": {
|
|
"type": "switch",
|
|
"label": "Playhead",
|
|
"default": true
|
|
},
|
|
"showCueList": {
|
|
"type": "switch",
|
|
"label": "Cue list",
|
|
"default": false
|
|
}
|
|
}
|
|
}
|