mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-24 14:47:23 +00:00
feat(telnet,web): relax timeouts and hint at transient probe failures
Two halves of the same flakiness fix:
- pkg/telnet defaults: dial 2s→4s, read 5s→7s, write 2s→3s,
idleWindow 400ms→600ms. The diagnostic shell on FW 27.0.6
occasionally takes >2s to accept a fresh TCP connection (likely
while servicing other work), and the previous tight budget
produced flaky preflight results on healthy speakers that
consistently recovered on a second attempt.
- state card: when the probe error wraps an i/o timeout / "timed out"
/ "connection reset", the panel now appends a hint pointing the
user at the ↻ refresh button next to the device dropdown — instead
of leaving the user to assume telnet is permanently unreachable.
looksTransient() keeps the substring match conservative so genuine
"connection refused" / "host unreachable" errors keep the original
framing.
The 4s dial budget adds at most ~2s to summary loads on devices
where telnet is genuinely down; that's an acceptable trade-off for
removing the false-negative reports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c5362be11b
commit
d48aa63b9a
@@ -2670,6 +2670,16 @@ async function applySuggestedPlan() {
|
||||
if (status) status.innerText = "";
|
||||
}
|
||||
|
||||
// looksTransient classifies a probe-error message as likely-flaky-but-
|
||||
// retriable. Conservative substring match — only marks errors that
|
||||
// pattern-match a TCP/I/O timeout, which is the exact failure shape
|
||||
// observed on healthy FW 27.0.6 devices that recover on retry.
|
||||
function looksTransient(msg) {
|
||||
if (!msg) return false;
|
||||
const m = msg.toLowerCase();
|
||||
return m.includes("timeout") || m.includes("timed out") || m.includes("connection reset");
|
||||
}
|
||||
|
||||
// renderMigrationState fills the three-axis state card at the top of
|
||||
// the migration summary: transports, migration-state axes (URL config,
|
||||
// DNS interception, CA/TLS), and preconditions (remote_services,
|
||||
@@ -2686,7 +2696,23 @@ function renderMigrationState(summary) {
|
||||
const errorEl = document.getElementById("state-telnet-error");
|
||||
if (errorEl) {
|
||||
if (summary.telnet_probe_error && !summary.telnet_reachable) {
|
||||
errorEl.innerText = "Probe error: " + summary.telnet_probe_error;
|
||||
errorEl.replaceChildren();
|
||||
const line = document.createElement("div");
|
||||
line.innerText = "Probe error: " + summary.telnet_probe_error;
|
||||
errorEl.appendChild(line);
|
||||
|
||||
// The diagnostic shell on FW 27.0.6 occasionally drops the
|
||||
// first connection attempt under load. When the error wraps
|
||||
// an i/o timeout, the next probe almost always succeeds —
|
||||
// so nudge the user toward the ↻ refresh button rather than
|
||||
// letting them assume telnet is permanently unreachable.
|
||||
if (looksTransient(summary.telnet_probe_error)) {
|
||||
const hint = document.createElement("div");
|
||||
hint.style.cssText = "margin-top: 4px; font-size: 0.85em; color: #5d4037";
|
||||
hint.innerText = "💡 Telnet probes are occasionally flaky on this firmware. Click the ↻ refresh button next to the device dropdown to retry.";
|
||||
errorEl.appendChild(hint);
|
||||
}
|
||||
|
||||
errorEl.style.display = "block";
|
||||
} else {
|
||||
errorEl.style.display = "none";
|
||||
|
||||
+12
-4
@@ -18,14 +18,22 @@ import (
|
||||
)
|
||||
|
||||
// Default values for a fresh Client.
|
||||
//
|
||||
// The dial and read budgets were originally tighter (2s / 5s); both were
|
||||
// relaxed after observing transient i/o-timeout failures on healthy
|
||||
// speakers that reliably resolved on a second attempt. The diagnostic
|
||||
// shell on FW 27.0.6 occasionally takes >2s to accept a fresh TCP
|
||||
// connection — likely while the device is servicing other work — so a
|
||||
// short dial budget produces flaky preflight results without indicating
|
||||
// a real reachability problem.
|
||||
const (
|
||||
DefaultPort = 17000
|
||||
DefaultDialTimeout = 2 * time.Second
|
||||
DefaultReadTimeout = 5 * time.Second
|
||||
DefaultWriteTimeout = 2 * time.Second
|
||||
DefaultDialTimeout = 4 * time.Second
|
||||
DefaultReadTimeout = 7 * time.Second
|
||||
DefaultWriteTimeout = 3 * time.Second
|
||||
// idleWindow is how long we wait for further bytes after the first
|
||||
// byte of a response before treating the response as complete.
|
||||
idleWindow = 400 * time.Millisecond
|
||||
idleWindow = 600 * time.Millisecond
|
||||
)
|
||||
|
||||
// Client is a connected (or about-to-be-connected) session to a SoundTouch
|
||||
|
||||
Reference in New Issue
Block a user