Pair the pod that works
Both pods, same bench, minutes apart, no writes: − pod buttons stop at ~51 s, every session; flag 0 -> 1; 2 key offers + pod 133 s, 78 paddle edges, flag never flipped, no key offer at all So the `−` pod is not the controller. It relays more when it works — all ten buttons, its twin's included — but "when it works" is under a minute without a Zwift blessing in the last day, and the `+` pod alone is a complete shifter: its paddle shifts up, `Y` shifts down, both already mapped. Shifting is what a ride cannot do without. `take_plus_pod` becomes `take_minus_pod` — the same rule with the pods exchanged — and the housekeeping, the connect guard and the no-pod-named default follow it. Opening both is still what stops the `−` pod reporting its own paddle, so it is still one link, just the other one. The UI stops telling riders to press the pod that dies: the panel asks for the `+` pod, the tile prefers it, and the `−` pod's row says what it actually offers — all ten buttons, for about fifty seconds. This settles A-2 from the other end too. The keep-alive that "removes the daily unlock, but only for the right controller" removes nothing. The right controller never needed it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,11 +35,12 @@
|
||||
* pod stays missing — and the fix belongs next to the symptom. */
|
||||
const scanning = $derived(app.devices.scanning);
|
||||
const anyConnected = $derived(pods.some((p) => p.state === 'connected'));
|
||||
/** The pod that speaks for the pair. Connected, this is the whole controller. */
|
||||
const minusLive = $derived(controller?.minus.state === 'connected');
|
||||
/** Running on the fallback: the `+` pod alone, with no `−` paddle to shift
|
||||
* down with beyond its `Y` button. Worth saying out loud. */
|
||||
const plusOnly = $derived(!minusLive && controller?.plus.state === 'connected');
|
||||
/** The controller. Measured 2026-08-27: the `+` pod keeps reporting where the
|
||||
* `−` pod goes mute about fifty seconds in (§2.3.3). */
|
||||
const plusLive = $derived(controller?.plus.state === 'connected');
|
||||
/** Running on the fallback: the `−` pod, which relays everything but stops
|
||||
* reporting about fifty seconds in unless Zwift has blessed it today. */
|
||||
const minusOnly = $derived(!plusLive && controller?.minus.state === 'connected');
|
||||
const busy = $derived(pods.some((p) => p.state === 'searching'));
|
||||
/** A pod reporting the other's paddle: the pair may be filed the wrong way
|
||||
* round, and the rider is the only one who can say. */
|
||||
@@ -63,8 +64,8 @@
|
||||
|
||||
/** What each pod is for, so a rider who has lost one knows what they lost. */
|
||||
const PURPOSE: Record<Pod, string> = {
|
||||
minus: 'All ten buttons',
|
||||
plus: 'Fallback · shift up, A B Y Z',
|
||||
plus: 'Shift up · A B Y Z',
|
||||
minus: 'All ten buttons, for ~50 s',
|
||||
};
|
||||
|
||||
function connect(pod: Pod) {
|
||||
@@ -84,11 +85,11 @@
|
||||
<!-- Nothing can be picked up automatically while the scan is off, so
|
||||
the way to fix that sits here rather than only in the header. -->
|
||||
<button class="btn" onclick={() => app.run(() => api.startScan())}>Start scan</button>
|
||||
{:else if !minusLive}
|
||||
<!-- The − pod, not both: it is the one that carries the whole
|
||||
controller. The + pod has its own button on its own row. -->
|
||||
{:else if !plusLive}
|
||||
<!-- The + pod, not both: joining the pair is what stops the − pod
|
||||
reporting its own paddle, and the + pod is the one that lasts. -->
|
||||
<button class="btn" disabled={busy} onclick={() => app.run(() => api.connectController())}>
|
||||
{busy ? 'Searching…' : 'Find − pod'}
|
||||
{busy ? 'Searching…' : 'Find + pod'}
|
||||
</button>
|
||||
{/if}
|
||||
{#if anyConnected}
|
||||
@@ -103,21 +104,23 @@
|
||||
the badge above has already said it. -->
|
||||
{#if !scanning}
|
||||
<p class="lede"><strong>The scan is off</strong> — pods will not be picked up.</p>
|
||||
{:else if plusOnly}
|
||||
{:else if minusOnly}
|
||||
<p class="lede">
|
||||
Running on the <strong>+ pod alone</strong>: shift down with <span class="kbd">Y</span>.
|
||||
Press a button on the − pod for the D-pad.
|
||||
Running on the <strong>− pod</strong>, which relays all ten buttons but stops reporting
|
||||
after about a minute unless Zwift has blessed it today. Press a button on the
|
||||
<strong>+ pod</strong> for shifting that lasts.
|
||||
</p>
|
||||
{:else if !minusLive}
|
||||
{:else if !plusLive}
|
||||
<p class="lede">
|
||||
<strong>Press any button on the − pod.</strong> It only advertises while awake; the running
|
||||
scan connects it as soon as it does.
|
||||
<strong>Press any button on the + pod.</strong> It only advertises while awake; the running
|
||||
scan connects it as soon as it does. Its paddle shifts up and
|
||||
<span class="kbd">Y</span> shifts down.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
<div class="pods">
|
||||
{#each pods as pod (pod.pod)}
|
||||
{@const dormant = pod.pod === 'plus' && minusLive && pod.state !== 'connected'}
|
||||
{@const dormant = pod.pod === 'minus' && plusLive && pod.state !== 'connected'}
|
||||
<article class="pod" class:live={pod.state === 'connected'}>
|
||||
<!-- The paddle glyph is the pod's identity — big enough to match
|
||||
against the one printed on the hardware at arm's length. -->
|
||||
@@ -128,8 +131,8 @@
|
||||
<span class="purpose">
|
||||
{#if dormant}
|
||||
<!-- Not a fault, and the panel must not let it read as one: this
|
||||
pod is idle because the − pod is already sending its buttons. -->
|
||||
Relayed by the − pod
|
||||
pod is idle because joining the pair is what breaks it. -->
|
||||
Held back — the + pod is the controller
|
||||
{:else if pod.confirmed}
|
||||
Confirmed — sent its own {pod.symbol} paddle
|
||||
{:else if pod.state === 'connected'}
|
||||
@@ -193,7 +196,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !minusLive}
|
||||
{#if !plusLive}
|
||||
<!--
|
||||
FR-1.8. "Not connected" on its own reads as a broken app, and the real
|
||||
cause — a pod that is simply asleep — is something only the rider can fix.
|
||||
@@ -204,13 +207,13 @@
|
||||
<details class="help">
|
||||
<summary>Still not connecting?</summary>
|
||||
<ul>
|
||||
<li><strong>Press a button.</strong> A pod that is asleep does not advertise at all.</li>
|
||||
<li><strong>Press a button on the + pod.</strong> An asleep pod does not advertise.</li>
|
||||
<li><strong>Keep the scan on.</strong> Auto-connect runs off it.</li>
|
||||
<li><strong>Close Zwift.</strong> One app at a time holds a pod.</li>
|
||||
<li><strong>Charge it.</strong> A flat pod stops advertising, and the app stops chasing.</li>
|
||||
<li>
|
||||
<strong>Or use the + pod</strong> — you lose the D-pad, <span class="kbd">Y</span> still
|
||||
shifts down.
|
||||
<strong>Or use the − pod</strong> — it relays all ten buttons, but goes quiet after
|
||||
about a minute unless Zwift has blessed it in the last day.
|
||||
</li>
|
||||
<li>
|
||||
The keyboard mirrors every Click action; <span class="kbd">?</span> lists them. A ride
|
||||
|
||||
@@ -166,7 +166,8 @@
|
||||
|
||||
const minus = app.controller?.minus;
|
||||
const plus = app.controller?.plus;
|
||||
const live = minus?.state === 'connected' ? minus : plus?.state === 'connected' ? plus : null;
|
||||
// The `+` pod first: it is the one that keeps reporting (§2.3.3).
|
||||
const live = plus?.state === 'connected' ? plus : minus?.state === 'connected' ? minus : null;
|
||||
const searching = minus?.state === 'searching' || plus?.state === 'searching';
|
||||
out.push({
|
||||
key: 'click',
|
||||
@@ -177,13 +178,13 @@
|
||||
primary: live ? `Zwift Click · ${live.symbol} pod` : searching ? 'Searching…' : 'Not connected',
|
||||
detail: live
|
||||
? [
|
||||
live.pod === 'minus' ? 'All ten buttons' : 'Fallback — no D-pad',
|
||||
live.pod === 'plus' ? 'Shift up · Y shifts down' : 'All ten buttons, for ~50 s',
|
||||
live.batteryPercent != null ? `${live.batteryPercent}%` : null,
|
||||
live.buttonsSeen > 0 ? `${live.buttonsSeen} presses` : 'no presses yet',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
: 'Press a button to wake it — the keyboard works meanwhile',
|
||||
: 'Press a button on the + pod — the keyboard works meanwhile',
|
||||
tone: live ? 'tone-ok' : 'tone-idle',
|
||||
// Connected, the useful action is *rebuild the link*. A Click can wedge
|
||||
// while looking perfectly healthy — battery arriving every five seconds,
|
||||
@@ -229,7 +230,7 @@
|
||||
|
||||
/** The Click panel is a repair manual, so it appears when there is something
|
||||
* to repair. Connected, the tile above has already said everything it knows. */
|
||||
const clickNeedsHelp = $derived(app.controller?.minus.state !== 'connected');
|
||||
const clickNeedsHelp = $derived(app.controller?.plus.state !== 'connected');
|
||||
|
||||
/**
|
||||
* What the row has to say, as badges. Nothing is emitted for a device that is
|
||||
|
||||
Reference in New Issue
Block a user