Skip to content

Transient GetConfiguration failure permanently caps charging current (stack_level falls back to 1 and is out-ranked) #2148

Description

@bruxy70

Describe the bug

set_charge_rate() re-reads ChargeProfileMaxStackLevel from the charger on every limit
write, and falls back to stack_level = 1 on any exception (ocppv16.py:603-609):

try:
    stack_level_resp = await self.get_configuration(
        ckey.charge_profile_max_stack_level
    )
    stack_level = int(stack_level_resp)
except Exception:
    stack_level = 1

On a charger that reports a high stack level (mine reports 10), a single failed read makes the
next ChargePointMaxProfile land at level 1, where the already-installed level-10 profile
out-ranks it. Since the composite limit is the minimum across installed profiles, the charger
goes on enforcing the old ceiling while new profiles are accepted a

Nothing surfaces the problem: SetChargingProfile returns Acceptem_current
updates, and the except Exception is bare and unlogged. Charging is silently degraded until the
profiles are cleared. In my case it capped charging at 8 A for two

The tell is that clipping is asymmetric — limits below the stale e
they are the minimum of the composite schedule:

commanded sensor.charger_current_offered actually drawn
6 A 6 A 6.5 A ✔
7 A 7 A 7.06 A ✔
9 A 8 A 8.06 A ✘
10 A 8 A 8.06 A ✘
11 A 8 A 8.06 A ✘

It survived a charger power cycle, an integration reload, two OCPP session stop/restarts, and
both of my EVs — as expected, since charging profiles are persisten

A related detail removes the safety net: on Accepted the ChargePons
immediately and no TxProfile is written, so an accepted-but-out-ranked profile has no fallback
that could still take effect for the running transaction.

To Reproduce

Steps to reproduce the behavior:

  1. Use a charger that reports ChargeProfileMaxStackLevel greater ationwith keyChargeProfileMaxStackLevel; mine reports 10`).
  2. Set number.charger_maximum_current to some value — say 8 A. A
    installed at stack level 10 with that limit.
  3. Cause one GetConfiguration for that key to fail. In the wild t
    reconnect; to force it deterministically, temporarily make get_configuration() raise, or
    block the message.
  4. Set number.charger_maximum_current to a higher value, e.g. 16 A. The new profile is
    written at stack level 1 and the charger answers Accepted.
  5. Watch sensor.charger_current_offered and the actual current: both stay at 8 A. Lower values
    (6-7 A) still apply normally.
  6. Call ocpp.clear_profile and write the limit again — it now applies immediately.

Expected behavior

A transient GetConfiguration failure should not permanently degrade charging.

Specifically:

  • The stack level should be cached per connection (read once after BootNotification), not
    re-read on every limit write, where repeated reads accumulate fai
  • On a read failure the last known good level should be reused. Falling back to 1 — the lowest
    possible level — is the one value guaranteed to be out-ranked by .
  • The fallback should be logged at WARNING with the exception, and the bare except Exception
    narrowed. Silent loss of charging power is a bad failure mode to
  • Optionally, send ClearChargingProfile for that purpose before installing at a different stack
    level, so a level change cannot leave an out-ranking profile behiied
    limit with GetCompositeSchedule after a write.

Screenshots

N/A — not a UI issue. The relevant evidence is the commanded-vs-offered table above.

Desktop (please complete the following information):

N/A — the bug is in the integration's charging-profile handling, not in the frontend.

Smartphone (please complete the following information):

N/A.

Additional context

Versions:

  • OCPP integration: 0.11.4
  • Home Assistant: 2026.9.2
  • Charger: Wallbox Pulsar Plus, firmware 6.7.41, OCPP 1.6J

Relevant charger configuration:

ChargeProfileMaxStackLevel: 10
MaxChargingProfilesInstalled: 30
ChargingScheduleAllowedChargingRateUnit: Current
MeterValueSampleInterval: 60
ConnectorSwitch3to1PhaseSupported: false

The charger is driven by a PV-surplus energy-management system, so limit writes are frequent
(a few hundred per day) — which is probably why I hit this and most

What I have not proven: I have no debug logs from the moment it
GetConfiguration is inferred rather than observed. The correlation: the last limit above 8 A
applied at 2026-09-14 11:52; the charger went unavailable (websocfrom
that point nothing above 8 A ever applied again, until the profiles were cleared two days later.
A reconnect is exactly when that read would fail.

One part of the mechanism is implementation-dependent and I could nde:
whether this charger keys profile replacement on chargingProfileId or on
(purpose, stackLevel). That decides whether later level-10 writes
succeeding again) should have recovered it on their own. Observed behaviour says they did not.

After clearing, the charger tracked setpoints promptly and correctly again:

11:35:24  commanded 8 A → car drew 8.05 A within 12 s
11:36:24  commanded 9 A → car drew 9.04 A within 14 s

Workaround for anyone else hitting this: call `ocpp.clear_profiit.
Do not do it during an active transaction — clearing removes the cap and the charger falls
back to its installation maximum until a new profile lands. When I
charger offered 16 A against a commanded 8 A and the car drew 15-16 A in bursts for about four
minutes, pulling 1.5-2.8 kW from the grid, before the per-minute reen
sessions instead.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions