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:
- Use a charger that reports
ChargeProfileMaxStackLevel greater ationwith keyChargeProfileMaxStackLevel; mine reports 10`).
- Set
number.charger_maximum_current to some value — say 8 A. A
installed at stack level 10 with that limit.
- 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.
- 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.
- Watch
sensor.charger_current_offered and the actual current: both stay at 8 A. Lower values
(6-7 A) still apply normally.
- 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.
Describe the bug
set_charge_rate()re-readsChargeProfileMaxStackLevelfrom the charger on every limitwrite, and falls back to
stack_level = 1on any exception (ocppv16.py:603-609):On a charger that reports a high stack level (mine reports
10), a single failed read makes thenext
ChargePointMaxProfileland at level 1, where the already-installed level-10 profileout-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:
SetChargingProfilereturnsAcceptem_currentupdates, and the
except Exceptionis bare and unlogged. Charging is silently degraded until theprofiles 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:
sensor.charger_current_offeredIt 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
Acceptedthe ChargePonsimmediately and no
TxProfileis written, so an accepted-but-out-ranked profile has no fallbackthat could still take effect for the running transaction.
To Reproduce
Steps to reproduce the behavior:
ChargeProfileMaxStackLevelgreater ationwith keyChargeProfileMaxStackLevel; mine reports10`).number.charger_maximum_currentto some value — say 8 A. Ainstalled at stack level 10 with that limit.
GetConfigurationfor that key to fail. In the wild treconnect; to force it deterministically, temporarily make
get_configuration()raise, orblock the message.
number.charger_maximum_currentto a higher value, e.g. 16 A. The new profile iswritten at stack level 1 and the charger answers
Accepted.sensor.charger_current_offeredand the actual current: both stay at 8 A. Lower values(6-7 A) still apply normally.
ocpp.clear_profileand write the limit again — it now applies immediately.Expected behavior
A transient
GetConfigurationfailure should not permanently degrade charging.Specifically:
BootNotification), notre-read on every limit write, where repeated reads accumulate fai
1— the lowestpossible level — is the one value guaranteed to be out-ranked by .
WARNINGwith the exception, and the bareexcept Exceptionnarrowed. Silent loss of charging power is a bad failure mode to
ClearChargingProfilefor that purpose before installing at a different stacklevel, so a level change cannot leave an out-ranking profile behiied
limit with
GetCompositeScheduleafter 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:
Relevant charger configuration:
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
GetConfigurationis inferred rather than observed. The correlation: the last limit above 8 Aapplied at 2026-09-14 11:52; the charger went
unavailable(websocfromthat 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
chargingProfileIdor on(purpose, stackLevel). That decides whether later level-10 writessucceeding again) should have recovered it on their own. Observed behaviour says they did not.
After clearing, the charger tracked setpoints promptly and correctly again:
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.