From 65a5c67afc0a06687f9bea2afc11e552337d96d6 Mon Sep 17 00:00:00 2001 From: tajniak81 <13187254+tajniak81@users.noreply.github.com> Date: Wed, 2 Sep 2026 10:00:54 +0200 Subject: [PATCH] The settings card sets things MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card added last commit showed the charger's settings and did nothing with them, which for a page whose whole point is acting on the charger is half a card. It also took the settings block out of the readings card to do it, so reading the charger top to bottom now had a hole in it. The readings card is whole again — phases, live data, settings, device, alarms, exactly as before. What the settings card holds is the same values with controls on them. Which values get a control is the register map's decision, not a design one. Six holding registers are writable, and four of them are settings: the current ceiling, boost, the timeout and the phase count. Charging mode, the two balancing flags and the LED brightness sit in the measurement block, which the charger reports over FC04 and does not accept writes on — they are set in the Anker app. So the card is in two halves and says which is which, rather than offering a control that would quietly do nothing. The limits come from the same places the server's own checks do: the slider floors at 6 A because below that the charger pauses instead of charging slowly and ModbusSetMaxCurrent refuses it, its ceiling is the charger's reported rating, and the timeout floors just above the spec's "more than five seconds". Phase and boost write on the change itself, having one value each; current and timeout are typed, so they wait for Apply. Every write goes through the existing control action, so it is gated, rate limited and audited like the buttons in the control card, and the card reseeds from the snapshot afterwards — the charger clamps what it is given, and the form should show what it took rather than what was asked. Co-Authored-By: Claude Opus 5 --- Web App/web/src/i18n/da.json | 8 ++ Web App/web/src/i18n/en.json | 8 ++ Web App/web/src/i18n/pl.json | 8 ++ Web App/web/src/views/Charging.vue | 165 +++++++++++++++++++++++++++-- 4 files changed, 182 insertions(+), 7 deletions(-) diff --git a/Web App/web/src/i18n/da.json b/Web App/web/src/i18n/da.json index b157d82..53b7750 100644 --- a/Web App/web/src/i18n/da.json +++ b/Web App/web/src/i18n/da.json @@ -117,6 +117,14 @@ "live": "Live data", "settings": "Indstillinger", "settingsTitle": "Laderindstillinger", + "apply": "Anvend", + "turnOn": "Slå til", + "turnOff": "Slå fra", + "limitFloorHint": "{amps} A er bunden — derunder holder laderen pause i stedet for at lade langsomt.", + "boostHint": "Kun den aktuelle session; laderen rydder det, når sessionen slutter.", + "timeoutHint": "Mindst {n} sekunder. Laderen falder tilbage til sin egen strategi, hvis intet skriver inden for tiden.", + "settingsReported": "Rapporteret, kan ikke indstilles", + "settingsReportedHint": "Laderen rapporterer disse; Modbus-kortet har intet register til at skrive dem. Ret dem i Anker-appen.", "device": "Enhed", "alarms": "Alarmer", "phase": "Fase", diff --git a/Web App/web/src/i18n/en.json b/Web App/web/src/i18n/en.json index cf77f6b..dde8d20 100644 --- a/Web App/web/src/i18n/en.json +++ b/Web App/web/src/i18n/en.json @@ -103,6 +103,14 @@ "live": "Live data", "settings": "Settings", "settingsTitle": "Charger settings", + "apply": "Apply", + "turnOn": "Turn on", + "turnOff": "Turn off", + "limitFloorHint": "{amps} A is the floor — below it the charger pauses rather than charging slowly.", + "boostHint": "The current session only; the charger clears it when the session ends.", + "timeoutHint": "At least {n} seconds. The charger falls back to its own strategy if nothing writes within it.", + "settingsReported": "Reported, not settable", + "settingsReportedHint": "The charger reports these; the Modbus map has no register to write them. Change them in the Anker app.", "device": "Device", "alarms": "Alarms", "phase": "Phase", diff --git a/Web App/web/src/i18n/pl.json b/Web App/web/src/i18n/pl.json index 431dc03..04712b9 100644 --- a/Web App/web/src/i18n/pl.json +++ b/Web App/web/src/i18n/pl.json @@ -119,6 +119,14 @@ "live": "Dane na żywo", "settings": "Ustawienia", "settingsTitle": "Ustawienia ładowarki", + "apply": "Zastosuj", + "turnOn": "Włącz", + "turnOff": "Wyłącz", + "limitFloorHint": "{amps} A to dolna granica — poniżej ładowarka wstrzymuje ładowanie, zamiast ładować wolniej.", + "boostHint": "Tylko bieżąca sesja; ładowarka kasuje to po jej zakończeniu.", + "timeoutHint": "Co najmniej {n} sekund. Bez zapisu w tym czasie ładowarka wraca do własnej strategii.", + "settingsReported": "Raportowane, nieustawialne", + "settingsReportedHint": "Ładowarka je raportuje; mapa Modbus nie ma rejestru do ich zapisu. Zmień je w aplikacji Anker.", "device": "Urządzenie", "alarms": "Alarmy", "phase": "Faza", diff --git a/Web App/web/src/views/Charging.vue b/Web App/web/src/views/Charging.vue index 78bab9d..cd3782c 100644 --- a/Web App/web/src/views/Charging.vue +++ b/Web App/web/src/views/Charging.vue @@ -379,6 +379,55 @@ const modbusSettings = computed(() => { ]); }); +// --- What can actually be set ------------------------------------------------- +// +// Only the control block is writable: the current ceiling, boost, the timeout +// and the phase count (21001-21005), plus the start/stop command the control +// card sends. Everything else in the settings readback — charging mode, the two +// balancing flags, the LED — lives in the measurement registers, which the +// charger reports and the Anker app sets. So this card offers controls for the +// four and shows the rest as what they are: a readback. +const draftAmps = ref(16); +const draftSeconds = ref(120); +const draftPhase = ref(0); + +// Seeded from the charger, not from the last thing typed. refreshCtl runs after +// every write, so the form ends up showing what the charger took — which is not +// always what was asked for, since it clamps the current to its own rating. +function syncSettingsDraft() { + const set = mb.value.settings || {}; + if (isSet(set.maxCurrentA)) draftAmps.value = Math.round(set.maxCurrentA); + if (isSet(set.timeoutSeconds)) draftSeconds.value = set.timeoutSeconds; + if (isSet(set.phaseSetting)) draftPhase.value = set.phaseSetting; +} + +const boostOn = computed(() => !!(mb.value.settings || {}).boost); + +// The charger pauses below 6 A rather than charging slowly, and the server +// refuses that case outright, so the slider does not offer it. The ceiling comes +// from the charger's own rating where it reports one. +const LIMIT_FLOOR = 6; +const limitCeiling = computed(() => Math.round(mb.value.maxCurrentA || 32)); + +// The timeout the charger falls back on its own strategy after. The spec's floor +// is "more than five seconds"; a minute of slack above it is a sane lower bound +// for a control that is set by hand. +const TIMEOUT_FLOOR = 6; + +// The settings rows there is no register to write. Same labels and formatting as +// the readings card's settings block, minus the four that have controls above. +const modbusSettingsReported = computed(() => { + const s = mb.value; + const set = s.settings || {}; + return rows([ + ["lastCommand", enumLabel("command", set.lastCommand)], + ["chargingMode", enumLabel("chargingMode", s.chargingMode)], + ["loadBalancing", yesNo(s.loadBalancing)], + ["solarBalancing", yesNo(s.solarBalancing)], + ["ledBrightness", isSet(s.ledBrightness) ? `${s.ledBrightness} %` : null], + ]); +}); + const modbusDevice = computed(() => { const s = mb.value; return rows([ @@ -701,6 +750,7 @@ async function refreshCtl() { // one sitting in the field, where saving would move it to the wrong charger. modbusHost.value = ctl.value?.modbusHost || ""; modbusPort.value = ctl.value?.modbusPort || 502; + syncSettingsDraft(); } catch (e) { ctlError.value = e.message; ctl.value = null; @@ -1121,13 +1171,104 @@ onMounted(async () => {
- -
- -
+ +
+ + +
+

{{ t("charging.modbus.limitFloorHint", { amps: LIMIT_FLOOR }) }}

+ +
+
+ + +
+ + +
+ +
+
+

{{ t("charging.modbus.boostSet") }}

+

{{ t("charging.modbus.boostHint") }}

+
+ +
+ + +
+ +
+ + +
+

{{ t("charging.modbus.timeoutHint", { n: TIMEOUT_FLOOR }) }}

+
+ + +
+

{{ t("charging.modbus.settingsReported") }}

+
+ +
+

{{ t("charging.modbus.settingsReportedHint") }}

+
+ +

{{ ctlError }}

@@ -1319,6 +1460,16 @@ onMounted(async () => { +
+

{{ t("charging.modbus.settings") }}

+
+ +
+
+

{{ t("charging.modbus.device") }}