feat(train): the Loop's firmware updates, directly or through the instance (ADR 0030, D10, D40)

Train's server relays Polar's firmware service for the people signed in
(`firmware`: offered, check, download), a fetched file kept in memory a
day per hardware and version, off with LOOP_FIRMWARE_RELAY=off. The app
asks on the day's first connection and on the band's screen, from Polar
or the instance as the person chose, never Polar in the instance's
place; an update runs only after asking, reads and keeps the band
first, and sets it up again with its figures and clock after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-28 20:52:53 +01:00
commit 388b3b1ae6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -3,8 +3,8 @@
- **Status:** decided 27–28 Oct 2026, by the developer (D1–D40 below). The shell (D12)
is built; recording (D13–D28) is built on Android, iOS waiting for a Mac; Health
Connect (D29–D31) is built; the Loop's setup and keeping (7a, D34–D36) and its
daily data (7b, D37, D38) and its sessions (7c, D39) are built; its firmware (D10,
D40) is being built.
daily data (7b, D37, D38), its sessions (7c, D39) and its firmware (D10, D40) are
built.
- **Numbering:** 0025 is held by the federation draft (stashed), 0026–0029 by the .NET
rewrite on the local branch `rewrite-dotnet`, abandoned on 27 Oct; Train takes the next
free number so that neither is ever read as this one.
@ -747,6 +747,27 @@ Strava. What was found, 27 Oct:
its clock (over asking first), the band getting a new user identifier as with every
setup (D35).
Built so (28 Oct), with choices of mine for the developer to look at: Train's server
has a `firmware` endpoint (`offered`, `check`, `download`, for the people signed in;
`LoopFirmwareRefused` where it is off, a figure isn't plain, a file isn't known or
Polar can't be reached), which names an update only where Polar gives an `https`
file, and keeps a fetched file under its hardware and version until a day after it
was fetched. On the phone, the band's date is the phone's today; a band just set up
is asked as a connection is; where the person chose the instance, Polar is never
asked directly in its place, even when the relay is off or out of reach (the
check fails then, and the screen says so), and the switch shows where the instance
relays or was chosen. An update through the instance asks the relay again, writes
the file into the person's folder (`loop_firmware.zip`), hands it to the SDK as a
`file://` address and deletes it afterwards. A read failing before the update
leaves the band untouched; after the writing, Train connects again, waits up to five
minutes for the band, sets it up with the figures it had (the profile's as they are
where none are known) and sets its clock, and takes the version offered as the
band's until it tells its own. The screen shows each stage the SDK tells while it
writes. Not yet tried with a Loop: whether the version in `/DEVICE.BPB`
(major.minor.patch) is what Polar's service expects, whether the band connects again
by itself after its restart, and whether the SDK takes the relayed file as it takes
the one it fetches.
## Consequences
- Train's sensors are one pattern: open profiles read by the app itself, on the phone;