Reconnect to remembered hardware instead of pairing every launch
`remembered` was a HashSet inside DeviceRegistry, so it lasted exactly as long as the process. Every launch started from nothing: find the trainer, press Connect, find the strap, press Connect, and only then ride. FR-1.5 has been a Should since the beginning and was never actually true. It is a file now — devices.json in the app data directory, written when a link actually comes up rather than when Connect is pressed. A Connect the hardware then refuses is not a pairing, and writing one down would mean a trainer the rider gave up on getting chased on every launch afterwards. Forgetting is recorded too, in its own list: absent means never seen, forgotten means the rider looked at this device and said no, and auto-connect has to keep honouring that on the next launch as well. The file is advisory — a corrupt one costs auto-connect, never a ride. Auto-connect is driven by the scan rather than fired once at startup. The hardware is asleep at startup — a trainer wakes when the cranks turn, a strap when it is put on (A-4) — so a remembered device is reconnected the moment it advertises, through the same path the rider's own click takes, scan suspension included. Bounded by AUTO_ATTEMPTS on an AUTO_RETRY cooldown and cleared when the link comes up or the rider connects by hand: an app that never stops trying can never honestly say it has stopped (FR-1.11). A device disconnected by hand is left alone for the rest of the session, since a disconnect that undoes itself two ticks later is not a disconnect. Pods now prefer the pod we know. Every Click advertises the same name and the same type byte, so before this a rider whose partner was warming up in the next room got whichever pod woke first. With nothing of that kind remembered anything still goes, or there could never be a first pairing. And the pair is one pod, not two. Confirmed on this hardware 2026-08-21: pairing the − pod alone delivers all ten buttons, its twin's included — which §2.3.1 had established for the frames but not for the pairing. So take_plus_pod holds the + pod back while a known − pod may merely be asleep, and connect_controller with no pod named means the − pod rather than both. The wait is bounded by PLUS_GRACE, because a flat − pod should cost the rider a D-pad and not a controller, and Buttons is untouched: it is what makes the handover between the two configurations invisible. Not yet tested against real hardware — nothing was advertising here. The store, the retry budget and the pod-preference rules have unit tests, and a seeded devices.json was confirmed to load and seed the − pod at launch, but the connect path itself waits for a ride. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,7 +92,17 @@
|
||||
{@const bars = rssiBars(device.rssi)}
|
||||
<article class="row" class:live={isConnected(device)}>
|
||||
<div class="identity">
|
||||
<span class="name">{device.name}</span>
|
||||
<span class="name">
|
||||
{device.name}
|
||||
<!-- FR-1.5. Paired before, so it comes back on its own the next
|
||||
time it advertises — this launch or any other. Worth a badge:
|
||||
it is the difference between a device the rider has to fetch
|
||||
and one that simply turns up, and Forget below is how they
|
||||
take it back. -->
|
||||
{#if device.remembered}
|
||||
<span class="badge" title="Paired before — reconnects on its own">Remembered</span>
|
||||
{/if}
|
||||
</span>
|
||||
<span class="meta">{KIND_LABEL[device.kind]} · {device.address}</span>
|
||||
{#if device.error}
|
||||
<span class="error">{device.error}</span>
|
||||
@@ -324,6 +334,10 @@
|
||||
}
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
font-size: 1.12rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
@@ -332,6 +346,18 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.badge {
|
||||
flex: none;
|
||||
padding: 0.1rem 0.4rem;
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: 0.3rem;
|
||||
color: var(--ink-dim);
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.meta {
|
||||
font-size: 0.8rem;
|
||||
color: var(--ink-dim);
|
||||
|
||||
Reference in New Issue
Block a user