Skip to content

Add Enovates charger - #30477

Merged
andig merged 6 commits into
masterfrom
feat/enovates
Jun 4, 2026
Merged

Add Enovates charger#30477
andig merged 6 commits into
masterfrom
feat/enovates

Conversation

@andig

@andig andig commented Jun 3, 2026

Copy link
Copy Markdown
Member

Fix #30476

Adds an Enovates charger via Modbus TCP, controlled through its EMS-over-Modbus current limit. Covers Enovates ENO ONE and its Shell Recharge "Home Advanced" rebrand, a common alternative to the Alfen-based Shell/NewMotion units. Register map follows the official Enovates Modbus integration; control writes the EMS limit register, status is derived from the IEC 61851 Mode 3 state.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In CurrentPower, the register is documented as int16 but is decoded via rs485.RTUInt16ToFloat64, which will mis-handle negative values; consider using a signed conversion (e.g. cast via int16(binary.BigEndian.Uint16(b))) to reflect the documented type.
  • The Status method passes string(b) directly to api.ChargeStatusString, which will include any NUL padding from the two registers; trimming trailing \x00 (similar to trimModbusString) before conversion would make the status decoding more robust.
  • In MaxCurrentMillis, curr := uint16(current * 1e3) will silently wrap for negative or overly large currents; adding bounds checking (e.g. clamp to [0, math.MaxUint16] or the charger’s max register value) would prevent unintended values being written to the EMS limit register.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `CurrentPower`, the register is documented as `int16` but is decoded via `rs485.RTUInt16ToFloat64`, which will mis-handle negative values; consider using a signed conversion (e.g. cast via `int16(binary.BigEndian.Uint16(b))`) to reflect the documented type.
- The `Status` method passes `string(b)` directly to `api.ChargeStatusString`, which will include any NUL padding from the two registers; trimming trailing `\x00` (similar to `trimModbusString`) before conversion would make the status decoding more robust.
- In `MaxCurrentMillis`, `curr := uint16(current * 1e3)` will silently wrap for negative or overly large currents; adding bounds checking (e.g. clamp to [0, math.MaxUint16] or the charger’s max register value) would prevent unintended values being written to the EMS limit register.

## Individual Comments

### Comment 1
<location path="charger/enovates.go" line_range="152-153" />
<code_context>
+var _ api.ChargerEx = (*Enovates)(nil)
+
+// MaxCurrentMillis implements the api.ChargerEx interface
+func (wb *Enovates) MaxCurrentMillis(current float64) error {
+	curr := uint16(current * 1e3)
+
+	err := wb.setCurrent(curr)
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against negative or excessively large `current` values before converting to `uint16`.

Casting `current * 1e3` directly to `uint16` causes negative values to wrap and large values to overflow silently. Please validate or clamp the value before conversion (e.g. `<= 0` → 0, `> maxSupported` → max) or return an error on out-of-range input to avoid unexpected EMS limits at the charger.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread charger/enovates.go
Comment on lines +152 to +153
func (wb *Enovates) MaxCurrentMillis(current float64) error {
curr := uint16(current * 1e3)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Guard against negative or excessively large current values before converting to uint16.

Casting current * 1e3 directly to uint16 causes negative values to wrap and large values to overflow silently. Please validate or clamp the value before conversion (e.g. <= 0 → 0, > maxSupported → max) or return an error on out-of-range input to avoid unexpected EMS limits at the charger.

@premultiply premultiply changed the title Enovates: add charger Add Enovates charger Jun 3, 2026
@premultiply premultiply added the devices Specific device support label Jun 3, 2026
Comment thread charger/enovates.go
Co-authored-by: premultiply <4681172+premultiply@users.noreply.github.com>
@andig

andig commented Jun 3, 2026

Copy link
Copy Markdown
Member Author

Add rfid reading @copilot

Copilot AI commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Add rfid reading @copilot

Implemented in e00b502: Enovates now exposes RFID/token reading via the Identifier capability by reading Modbus register 401 (identify()).

Copilot finished work on behalf of andig June 3, 2026 20:50
Comment thread charger/enovates.go Outdated
Copilot finished work on behalf of andig June 3, 2026 21:16
The Identifier registration is unconditional, so a plain method plus interface assertion suffices. With no conditional capabilities left, drop the implement.Caps embed and implement.New().
@andig
andig enabled auto-merge (squash) June 4, 2026 06:20
@andig
andig merged commit c0d501c into master Jun 4, 2026
7 checks passed
@andig
andig deleted the feat/enovates branch June 4, 2026 06:21
toeklk pushed a commit to toeklk/evcc that referenced this pull request Jun 4, 2026
@Knuppel1983

Copy link
Copy Markdown

Can't get this to work.
modbus.tcp.enabled=true
modbus.tcp.ems.enabled=true
modbus.tcp.port=502

But there is still no modbus server running on the CP.

@andig

andig commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

Then we're probably out of luck :(

@Knuppel1983

Copy link
Copy Markdown

Then we're probably out of luck :(

version=1.13.1.3, name=ChargePointControllerLight, groupID=com.enovates.lccl, artificatID=ChargePointControllerLight, buildNumber=1744126097163, buildDate=2025-04-08 15:28 +0000

I'm guessing the 'ChargePointControllerLight' means it's handicapped... I'll try to gain some info, but i just noticed Enovates filed for bankrupcy about a week. Hoping firmware updates get released but not counting on it.

Thanks for your efforts though!

@ZerR2D0

ZerR2D0 commented Jun 7, 2026

Copy link
Copy Markdown

Shell Recharge Advanced 2.1 no luck, Modbus is refused.
modbus.tcp.enabled=true
modbus.tcp.ems.enabled=true
modbus.tcp.port=502

have i missed something?

@Knuppel1983

Copy link
Copy Markdown

Shell Recharge Advanced 2.1 no luck, Modbus is refused. modbus.tcp.enabled=true modbus.tcp.ems.enabled=true modbus.tcp.port=502

have i missed something?

You haven’t. The service is probably removed because the controller is too light to run it. Take the fakexemex route or use your car to change the amps if you can.

@dries007

Copy link
Copy Markdown

Hey 👋 I made the HA integration and library. I may be able to help, but I must mention I do not (currently) represent Enovates.
I have only recently started using EVCC mysef. Feel free to ping me if there are issues.
I don't have a token, as I use the HA integration instead, can I apply for a contributors one based on this and providing some support for this?

If you are on firmware 2.13 or later, the Modbus options should be there and working. Do not assume things based on the internal package versions/names, they are not meant for your eyes 😉.

I will also drop this totally unrelated link here with a small word of warning: Only upgrade your firmware via the official channel provided by your CPO, otherwise you may lose all support or warranty. But isn't it suspicious that the fw_upgrade zip on that page has the word generic in the name? 🙈

@andig

andig commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

@dries007 always happy to hand out tokens to new contributors! For this specific integration I'm lacking a clear picture what the status is. It seems it was never tested in the first place since even basic modbus TCP connectivity wasn't working.

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

Labels

devices Specific device support

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enovates charger Modbus TCP

6 participants