MAINT-11: start or stop the maintenance cycle per battery
checks / unit (pull_request) Successful in 38m46s
infra-pin / fresh (pull_request) Successful in 1s

maint_start and maint_abort take an optional unit. With two or more batteries
each Batteries card has Start cycle and Stop cycle buttons. One slot per site
stays: a start while a battery cycles is refused and names it (no queue). A
battery that is switched off, an unknown unit, and a stop on a battery that is
not cycling are refused with a plain message. A request with no unit is the
old call. The scheduler and the phase machine are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
This commit is contained in:
glenn schrooyen
2026-10-13 21:37:36 +02:00
co-authored by Claude
parent 79dd0770f9
commit 21d8a944bc
9 changed files with 521 additions and 1 deletions
+28 -1
View File
@@ -336,6 +336,32 @@
finally { packBusy = false; }
await refresh(true);
}
// MAINT-11: start or stop ONE battery's cycle. Two or more batteries only;
// the add-on refuses a second cycle and says which battery is cycling.
async function cycleAct(what, b) {
if (packBusy) return;
packBusy = true;
try {
const r = await fetch("action", { method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action: what, unit: b.name }) });
const j = await r.json().catch(() => ({}));
const own = j.code && t("batteries.cycle." + j.code);
if (!r.ok) toast(own ? say(own.description, { name: j.unit || b.name }) : (j.error || t("toast.failed")));
else toast(say(t("batteries.cycle.toast." + (what === "maint_start" ? "started" : "stopped")), { name: b.name }));
} catch (e) { toast(t("toast.failed")); }
finally { packBusy = false; }
await refresh(true);
}
function cycleButtons(b, i) {
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")));
}
function onSwitch(b, i, where, lid) {
const sw = h("button", { type: "button", class: "switch", role: "switch", id: "bsw-" + where + i,
"aria-checked": String(b.use), "aria-labelledby": lid,
@@ -370,7 +396,8 @@
h("dt", {}, t("batteries.level")), h("dd", {}, lvl + stored),
h("dt", {}, t("batteries.now")), h("dd", {}, b.now),
h("dt", {}, t("batteries.today")), h("dd", {}, todayLines(b)),
h("dt", {}, t("batteries.why")), h("dd", { class: "muted" }, b.why || "–")));
h("dt", {}, t("batteries.why")), h("dd", { class: "muted" }, b.why || "–")),
S.batteries.length > 1 ? cycleButtons(b, i) : null);
}
function simpleRow(b, i) {
const lid = "bsname-" + i;