Repository navigation
fix(myuplink): retry a failed read soon and keep a rotated token (1.2.4) - #193
Merged
Merged
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
One request that hits the host's 15 s HTTP timeout left 75 s without a reading (30 s wait, 15 s timeout, 30 s wait), past Core's 60 s watchdog, so the heat pump was marked offline on every slow request. Retry a failed read or sign-in once after 2 s; a second failure in a row waits the full 30 s, so a real outage still shows as offline. A good read asks for 30 s, the wait Core already applied. A rotated refresh_token that the host could not store (SQLITE_BUSY) was held only in memory until the next rotation. Retry the store on every poll until it succeeds, but not in driver_cleanup, where a reconnect may just have stored a newer token. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frahlg
force-pushed
the
fix/myuplink-stale-retry
branch
from
October 25, 2026 08:25
e8acd27 to
59d9ce5
Compare
Once Core waits out a busy database (srcfl/ftw#1826), one failed store can take 5 s. Stored before the read, that pushed a poll with one timed-out read to 67.9 s between readings, past the 60 s watchdog. The token is now stored after the read on every return path, and a rotation during sign-in is only marked unsaved; the same case gives 57.9 s. The harness now also checks that, after rotations while the host refuses the store, the newest token is the one stored. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 25, 2026
frahlg
added a commit
to srcfl/ftw
that referenced
this pull request
Oct 25, 2026
Move the driver pin from 219c462e to device-drivers main 57984984: easee_cloud 1.4.0 reads Easee's SignalR stream with REST as fallback (srcfl/device-drivers#192; Core grants its declared ws_hosts since #1822), and myuplink 1.2.4 retries a slow read after 2 s and keeps a rotated token (srcfl/device-drivers#193). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
7 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Fredrik's home box (Core v0.155.1-beta.1, myuplink 1.2.3, the same file as
main) Core marks the MyUplink heat pump offline many times a day:From Core start on 22 October 15:35 to 25 October 09:00 the journal has 143 failed reads in 124 runs, and exactly 124 stale events: one per run of failures, every time. In 109 of the 124 runs, one read failed and the next one, 30 s later, worked. It still happens: three times between 09:03 and 09:14 on 25 October.
Cause
Timing, not rate limits.
What Core does when it marks the driver offline
It logs a WARN, flags the driver offline (the heating page shows it offline), publishes
DriverLost, and sends the autonomous default. For this read-only driverdriver_default_modedoes nothing, so the pump is not touched; FTW never controls it. But sending the default cancels a poll that is in flight. On 22 October that cancelled the very read that would have brought the driver back (<string>:168: context canceledinapi_get). One slow request should not cause any of this.Change (myuplink 1.2.4)
Rotated refresh token
MyUplink: could not persist rotated refresh_token: database is locked (5) (SQLITE_BUSY)appeared once, on 25 October at 06:45.Could it lose the token? Yes. The driver kept the new token in memory and never tried to store it again. It could only be stored after the next rotation, about an hour later. A driver reload or Core restart or update in that hour started from the token stored before, which myUplink had just replaced. If myUplink no longer accepts a replaced token, the driver cannot sign in until the owner connects again in Settings. Reloads are common on this box: six in five days, one per Core update. This time no reload fell in the window, and the journal has no failed token refresh.
Now the driver stores the token after each poll's reading and retries a failed store on every poll until it succeeds, so the window is one poll (30 s). The store comes after the reading on every return path: once Core waits out a busy database (#1826), one failed store can take 5 s. Stored before the read, a stuck lock plus one timed-out read gave 67.9 s between readings in review; after the read it gives 57.9 s. It warns once per rotation, as before, and logs when the store succeeds. It does not store the token in
driver_cleanup: Core lets a stored token override config at start, and a reconnect has just stored a fresh token at that point. An older token, perhaps from replaced app credentials, must not overwrite it.The busy error itself comes from Core: a database transaction that reads before it writes does not wait for another writer. That fix is srcfl/ftw#1826, a separate Core PR. This driver change does not depend on it, and either can merge first.
Tests
drivers/tests/lua_harness/test_myuplink_poll_timing.lua(new): return waits, one quick retry per outage, failed sign-in, a model of Core's run loop and watchdog (one timeout under 60 s, two over), store retried until it succeeds, one warning, no store indriver_cleanup. On the old driver 16 checks fail, including a 75.3 s gap, the same as on the box.make test-driver ID=myuplinkandmake check: pass.go test ./internal/drivers -run 'MyUplink|Persist'passes with this file asdrivers/myuplink.lua. A throwaway test against a fake myUplink whose first read stalls 20 s gave the box's exact error after 15.003 s, a 2 s wait, then a good read, and the store of the rotated token after one SQLITE_BUSY.Notes
myuplink.luaand also claims 1.2.4. It has right of way. Its changes are in the metric and name code; this PR changes the poll timing and the token store. Whichever merges second rebases and takes 1.2.5.drivers/BUNDLED_SOURCE.json.🤖 Generated with Claude Code