Devices: fixes from review round 4

- A switch clears every headset-specific list and its buttons at once.
- SSH goes to the IPv4 address that answered the probe, not the name again.
- A rejected headset edit changes nothing.
- The SteamOS/Lepton builds recorded in reports are read again per headset.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-28 22:00:09 +10:00
1 parent 318bc3b84f
commit d383a26746
5 files changed
+22 -7

No files matched your search

+3
View File
@@ -284,6 +284,9 @@ class Registry(Base):
self.assertEqual(self.reg.update_device(b["id"], name="Desk", user="deck", port="2222")["port"], 2222)
with self.assertRaises(fd.DeviceError):
self.reg.update_device(b["id"], user="bad user")
with self.assertRaises(fd.DeviceError):
self.reg.update_device(b["id"], user="steam", port="bad")
self.assertEqual(self.reg.get(b["id"])["user"], "deck") # a rejected edit changes nothing
self.reg.remove_device(b["id"])
self.assertEqual(self.reg.active(), a["id"])
with self.assertRaises(fd.DeviceError):
+5 -6
View File
@@ -519,12 +519,11 @@ class Registry:
"""-> the device after the change. The caller mirrors user and port into ~/.ssh/config."""
with self.lock:
d = self._find(device_id)
if name is not None:
d["name"] = check_text(name, "name") or d["alias"]
if user is not None:
d["user"] = check_user(user)
if port is not None:
d["port"] = check_port(port)
# Check everything first: a rejected edit changes nothing.
name = None if name is None else (check_text(name, "name") or d["alias"])
user = None if user is None else check_user(user)
port = None if port is None else check_port(port)
d.update({k: v for k, v in (("name", name), ("user", user), ("port", port)) if v is not None})
self.save()
return copy.deepcopy(d)
+5 -1
View File
@@ -647,7 +647,11 @@ class Link:
def handshake(self, device, a, found, user):
"""SSH to one address, following ssh -v through stages 3-5.
-> "ok", "next" (try another address) or "stop"."""
opts = self.host_opts(device, a["host"])
# An IPv4 address that answered is used as is, so ssh doesn't look the name up
# again and try an address that didn't answer (a dead IPv6 route, say). IPv6
# answers keep the name: a link-local one needs its zone, which ssh adds itself.
ip = found.get("ip") or ""
opts = self.host_opts(device, ip if re.fullmatch(r"\d{1,3}(\.\d{1,3}){3}", ip) else a["host"])
alias = device["alias"]
with self.route_lock:
if self.attempt_gen != self.gen:
+7
View File
@@ -2539,6 +2539,13 @@ function onConnection(s) {
$("canvas").hidden = true; $("viewerEmpty").hidden = false; $("zoombar").hidden = true;
["stamp", "srcBadge", "asleep"].forEach(id => $(id).hidden = true);
$("saveBtn").disabled = true;
// Lists and their buttons (Remove, Launch…) were the other headset's: clear them
// before anyone clicks one, until the new headset's arrive.
["games", "titleList", "andApps", "flatpaks", "shotGrid", "gmGrid"].forEach(id => {
if ($(id)) $(id).innerHTML = `<div class="wait">Waiting for ${esc(s.device.name)}</div>`;
});
disp.list = []; disp.port = null;
$("dispSel").innerHTML = "<option>Loading…</option>"; $("dispSel").disabled = true; $("dispCtl").hidden = true;
link.reload = true; // everything on the page was the other headset's
$("battChip").hidden = true;
}
+2
View File
@@ -87,6 +87,8 @@ def route(alias, host_opts):
them follows; frame_titles reads frame_android.SSH_OPTS at call time."""
global FRAME, HOST_OPTS
with _route_lock:
if alias != FRAME:
frame_catalog._env.clear() # the SteamOS and Lepton builds reports record are per headset
FRAME = frame_android.FRAME = alias
HOST_OPTS = list(host_opts)
MUX[:] = [*MUX_BASE, *HOST_OPTS]