The settings card stops emptying when one message is late

A snapshot has two halves and they travel separately: telemetry comes
from the trigger, the settings only when the charger has something to say
about itself. A read can land with the first and not the second — most
often the first read after a reconnect — and the card was seeded from
that answer alone, so it collapsed to the one control telemetry happens
to carry. That is the state of one message, not the state of the charger.

Three things, from the outside in.

The card keeps what the charger has reported, per serial, across reads. A
value stays until another replaces it. They are its own last word either
way, and the same ones the server fills a grouped command's siblings from
when a caller leaves them out.

A charger that goes quiet is no longer written off for good. The miss
counter decides whether a read waits for the settings frame at all, and
it only ever rose: three unanswered requests early on and no later read
waited again, however freely the charger answered afterwards. The comment
said "recently enough"; the code said "ever". Answering clears it now.

And the first settings are worth the wait a settings write already gives
them. Stale settings and never-reported settings were both allowed four
seconds. Stale has something to fall back on; never-reported is the empty
card, so it gets the full wait — still bounded by the miss counter, so a
charger that truly never answers costs it three times and no more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-03 20:26:38 +02:00
co-authored by Claude Opus 5
parent c65ce0c081
commit 4d51a34c44
4 changed files with 68 additions and 6 deletions
+21 -4
View File
@@ -706,18 +706,35 @@ const mqttDraft = ref({});
const mqttBase = ref({});
const mqttBusy = ref("");
// The settings the charger has reported, kept per serial across reads.
//
// The two halves of a snapshot arrive on different messages: the telemetry comes
// from the trigger, the settings only when the charger has something to say
// about itself. A read can land with the first and not the second — most often
// the first read after a reconnect, before any settings frame has arrived — and
// seeding the controls from that answer alone emptied the card of everything the
// charger had already told us. Which is not the state of the charger; it is the
// state of one message.
//
// So a value the charger has reported stays until it reports another. The values
// are its own last word either way, and the same ones the server fills a grouped
// command's siblings from when a caller leaves them out.
const mqttSeen = ref({}); // serial → the last value each setting was reported with
function syncMqttSettings() {
const draft = {};
const sn = ctlSerial.value.trim();
const seen = { ...(mqttSeen.value[sn] || {}) };
for (const block of MQTT_SETTING_BLOCKS) {
for (const f of block.fields) {
for (const [key, at] of fieldEntries(f)) {
const v = snapshotValue(at);
if (isSet(v) && v !== "") draft[key] = v;
if (isSet(v) && v !== "") seen[key] = v;
}
}
}
mqttDraft.value = draft;
mqttBase.value = { ...draft };
mqttSeen.value = { ...mqttSeen.value, [sn]: seen };
mqttDraft.value = { ...seen };
mqttBase.value = { ...seen };
}
// Only the settings the charger has actually reported get a control. A value it