From 5ee10613982d36fc1a6cbb3c0fe7d5f1300d8dd1 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Tue, 13 Oct 2026 23:18:21 +0200 Subject: [PATCH] MAINT-11 round 2: disable the cycle buttons that cannot work Stop cycle is disabled on a battery that is not cycling. Start cycle is disabled while any battery cycles and on a switched-off battery. Page only; the add-on still refuses each of these. DECISIONS notes the one-battery case. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC --- .../DECISIONS/MAINT-11-cycle-per-battery.md | 3 +++ goodwe_controller/app/static/app.js | 11 +++++++++-- goodwe_controller/test_maint11.py | 9 +++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/goodwe_controller/DECISIONS/MAINT-11-cycle-per-battery.md b/goodwe_controller/DECISIONS/MAINT-11-cycle-per-battery.md index 730c5b4..e10dfc5 100644 --- a/goodwe_controller/DECISIONS/MAINT-11-cycle-per-battery.md +++ b/goodwe_controller/DECISIONS/MAINT-11-cycle-per-battery.md @@ -32,3 +32,6 @@ queues by waiting time, so nothing is lost. (SAFETY-73); its drain waits. The per-battery start does the same. - A start on the battery that is already cycling is refused as "busy", where the bare button restarts its drain. The bare button is unchanged. +- At one battery, a request that names the battery uses the new path, so a + second start is refused instead of restarting the drain. Nothing sends a name + at one battery, and the bare request is unchanged. diff --git a/goodwe_controller/app/static/app.js b/goodwe_controller/app/static/app.js index 868d9db..0897f15 100644 --- a/goodwe_controller/app/static/app.js +++ b/goodwe_controller/app/static/app.js @@ -352,15 +352,22 @@ finally { packBusy = false; } await refresh(true); } + // A button that cannot work is disabled (the add-on still refuses it): Stop + // needs this battery cycling; Start needs no battery cycling and this one on. + function cycleState(b) { + const cycling = (x) => x.maintenance !== "idle"; + return { start: !S.batteries.some(cycling) && b.use !== false, abort: cycling(b) }; + } function cycleButtons(b, i) { + const can = cycleState(b); const ask1 = (key, what) => () => { const w = t("batteries.cycle." + key); ask({ name: say(w.name, { name: b.name }), description: say(w.description, { name: b.name }), yes: w.yes }, () => cycleAct(what, b)); }; return h("div", { class: "cyc", style: "display:flex;gap:8px;flex-wrap:wrap;margin-top:10px" }, - h("button", { type: "button", class: "btn", id: "bcs-" + i, onclick: ask1("start_dialog", "maint_start") }, t("batteries.cycle.start")), - h("button", { type: "button", class: "btn danger", id: "bca-" + i, onclick: ask1("abort_dialog", "maint_abort") }, t("batteries.cycle.abort"))); + h("button", { type: "button", class: "btn", id: "bcs-" + i, disabled: !can.start, onclick: ask1("start_dialog", "maint_start") }, t("batteries.cycle.start")), + h("button", { type: "button", class: "btn danger", id: "bca-" + i, disabled: !can.abort, onclick: ask1("abort_dialog", "maint_abort") }, t("batteries.cycle.abort"))); } function onSwitch(b, i, where, lid) { const sw = h("button", { type: "button", class: "switch", role: "switch", id: "bsw-" + where + i, diff --git a/goodwe_controller/test_maint11.py b/goodwe_controller/test_maint11.py index 102c420..8451135 100644 --- a/goodwe_controller/test_maint11.py +++ b/goodwe_controller/test_maint11.py @@ -365,6 +365,15 @@ check("both buttons post the battery's name and ask first", check("a refusal is shown in the page's language, by its code", 'const own = j.code && t("batteries.cycle." + j.code);' in JS and 'toast(own ? say(own.description' in JS) +check("Stop is disabled on a battery that is not cycling", + 'abort: cycling(b) };' in JS and "disabled: !can.abort" in JS) +check("Start is disabled while any battery cycles", + "start: !S.batteries.some(cycling) && " in JS + and "disabled: !can.start" in JS) +check("Start is disabled on a switched-off battery (use false, modes on)", + "&& b.use !== false, abort" in JS) +check("a battery counts as cycling when its phase is not idle", + 'cycling = (x) => x.maintenance !== "idle"' in JS) check("the old header buttons still post a bare request", 'ask("maint_start", () => simple("maint_start"))' in JS and "body: JSON.stringify({ action: what }) });" in JS)