One ceiling, one slider, in the card that owns it

The current limit sat in the control card and in the settings card at
once. Over the cloud those were not two settings that happen to agree:
the "limit" command builds its frame from the same table entry the
maxCurrentA setting does, so it was one wire field with two controls.
The control card gives it up wherever a settings card can take it —
which, now that the cloud has one, is both transports that read the
charger.

OCPP keeps its slider. A charging profile is not a setting the charger
reports, so there is no settings card to move it to, and clearing a
limit is OCPP's alone: the cloud sets a ceiling and has no message for
"no ceiling". Taking the control away there would have left those modes
unable to set a limit at all.

It arrives in the settings card as the slider it was, not as the number
box the table gave it. Slider rows stack — label and the value it is at
on one line, the track beneath, the floor hint under that — because a
ceiling is a thing you slide between two known ends rather than type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-03 18:53:48 +02:00
co-authored by Claude Opus 5
parent dde5410788
commit e190364c77
+40 -14
View File
@@ -602,7 +602,10 @@ const MQTT_SETTING_BLOCKS = [
id: "charging",
title: "blockCharging",
fields: [
{ key: "maxCurrentA", at: "settings.maxCurrentA", label: "maxCurrentSet", type: "number", min: LIMIT_FLOOR, max: null, step: 1, unit: "A" },
// A slider rather than a box, like the control card had and the Modbus
// settings card still has: it is the same value they set, and a ceiling
// is a thing you slide between two known ends rather than type.
{ key: "maxCurrentA", at: "settings.maxCurrentA", label: "maxCurrentSet", type: "slider", min: LIMIT_FLOOR, max: null, step: 1, unit: "A", hint: "limitFloorHint" },
{ key: "autoStart", at: "settings.autoStart", label: "autoStart", type: "switch" },
{ key: "randomDelay", at: "settings.randomDelay", label: "randomDelay", type: "switch" },
{ key: "plugLock", at: "settings.plugLock", label: "plugLock", type: "switch" },
@@ -2013,14 +2016,18 @@ onMounted(async () => {
</button>
</div>
<!-- The limit, everywhere but the local path. There the settings card
owns it: it is the charger's own ceiling register, read back and
written, and the same slider in two cards was the same register
twice. Neither OCPP nor the cloud has a settings card to move it to
a charging profile is not a setting the charger reports so here
it stays. Clearing it is OCPP's alone: the cloud sets a ceiling and
has no message for "no ceiling". -->
<div v-if="!ctlIsModbus" class="mt-3">
<!-- The limit, only where nothing else owns it. Both transports that
read the charger have a settings card now, and on both the limit is
the charger's own ceiling the same register over Modbus, the same
wire field over the cloud, where "limit" and the maxCurrentA
setting are built from one table. The same slider in two cards was
that one value twice.
OCPP is the exception and keeps it: a charging profile is not a
setting the charger reports, so there is no settings card to move
it to. Clearing it is OCPP's alone too — the cloud sets a ceiling
and has no message for "no ceiling". -->
<div v-if="!ctlReadsDevice" class="mt-3">
<label class="dh-label flex justify-between">
<span>{{ t("charging.control.limit") }}</span><span class="data text-body">{{ limitAmps }} A</span>
</label>
@@ -2415,11 +2422,29 @@ onMounted(async () => {
<section v-for="block in mqttSettingBlocks" :key="block.id" class="mt-2 rounded-control bg-sunken p-3">
<h4 class="eyebrow">{{ t(`charging.modbus.${block.title}`) }}</h4>
<div class="mt-2 flex flex-col gap-2">
<div
v-for="f in block.fields"
:key="f.key || f.from"
class="flex items-center justify-between gap-3"
>
<template v-for="f in block.fields" :key="f.key || f.from">
<!-- A slider needs the width, so its row stacks: the label and
the value it is at on one line, the track under them. -->
<div v-if="f.type === 'slider'">
<label class="dh-label flex justify-between" :for="`set-${f.key}`">
<span>{{ t(`charging.modbus.${f.label}`) }}</span>
<span class="data text-body">{{ mqttDraft[f.key] }} {{ f.unit }}</span>
</label>
<input
:id="`set-${f.key}`"
v-model.number="mqttDraft[f.key]"
type="range"
:min="f.min"
:max="fieldMax(f)"
:step="f.step"
class="w-full accent-[var(--accent)]"
/>
<p v-if="f.hint" class="mt-1 text-[11px] text-muted">
{{ t(`charging.modbus.${f.hint}`, { amps: f.min }) }}
</p>
</div>
<div v-else class="flex items-center justify-between gap-3">
<label class="dh-label !mb-0 min-w-0 grow" :for="`set-${f.key || f.from}`">
{{ t(`charging.modbus.${f.label}`) }}
</label>
@@ -2478,6 +2503,7 @@ onMounted(async () => {
/>
</span>
</div>
</template>
</div>
<p v-if="block.warning" class="mt-2 text-[11px]" style="color: var(--warning-600)">