Skip to content

feat: Add Deye EV Charger (SUN-EVSE22K01-EU) support#1073

Open
DJm00n wants to merge 1 commit into
davidrapan:mainfrom
DJm00n:feat/evse
Open

feat: Add Deye EV Charger (SUN-EVSE22K01-EU) support#1073
DJm00n wants to merge 1 commit into
davidrapan:mainfrom
DJm00n:feat/evse

Conversation

@DJm00n

@DJm00n DJm00n commented May 21, 2026

Copy link
Copy Markdown

Adds support for the Deye AC EV Charger (SUN-EVSE11K01-EU/SUN-EVSE22K01-EU) with full profile, autodetection, and automatic slave ID discovery.

This PR extends the EVSE support that was partially introduced in PR #1065 (inverter-based control via the LoRa communication channel).

NOTE: Deye has not published an official Modbus specification for the EV Charger. All register mappings in this profile are based on reverse engineering. Special thanks to @Stormhand for his reverse engineering work that made this possible and @ZajoCZ for MCU9_EV_C020_EU_3_26.bin firmware file.

Profile (deye_evse.yaml):

  • Info: device type (0x0904), serial, firmware/protocol version, model class (22 kW / 11 kW EV Charger from register 0x0013 hi-byte)
  • Configuration: inverter LoRa link status, date/time, charging start mode, communication mode, LoRa channel, grid voltage protection thresholds, off-grid charge inhibit
  • Time Of Charge: 4 scheduled charge time points with enable switches
  • Control: charging power limit setpoint (0–22000 W, IEC 61851 clamping noted)
  • State: Charging Control State and Charging Operational State (register 0x0039), split by high/low byte, state machine values from firmware handlers, update_interval: 2 to capture short-lived transitions
  • Grid: Grid Capacity (0x0038, dynamic firmware-computed power cap), Grid Voltage L1–L3
  • Fault: Charger Fault binary_sensor (0x0036), 11-bit bitmask with confirmed bit assignments; Stuck Relay (bit 6) is a current-based relay detector, Overtemperature (bit 7, ≥110°C), Undertemperature (bit 8, <−40°C)
  • Charging: per-phase and total power, charging voltage L1–L3, charging current L1–L3, Relay Temperature 1/2 (0x001D, two NTC sensors packed in one register, −50°C offset)
  • Energy: total and daily EV charge counters

Autodetection (const.py):

  • Register value 0x0904 mapped to deye_evse.yaml via AUTODETECTION_DEYE_EVSE

Slave ID discovery (device.py):

  • setup() retries slave IDs 1→2→3 if ProfileProvider.init() fails, so the
    EVSE (which uses slave 3) is found automatically without user configuration

Parser/time fixes:

  • parser.py, time.py: support mul parameter for time entity encoding,
    required by EVSE Time Of Charge registers (5-minute slots, dec: 12, mul: 5)
image

EVSE State Diagram:

stateDiagram-v2
        direction TB
        [*] --> Available : system init

        Available     : Available · 0x01, CP = +12V DC  (IEC A)
        Finishing     : Finishing · 0x02, CP = +12V DC
        Preparing     : Preparing · 0x04, CP = +12V DC  (IEC B, EVSE not ready)
        SuspendedEV   : Suspended EV · 0x08, CP = PWM  (IEC B, EVSE ready)
        SuspendedEVSE : Suspended EVSE · 0x10, CP = +12V DC  (IEC B, EVSE withdrawn)
        Charging      : Charging · 0x20, CP = PWM · relay ON  (IEC C)
        Faulted       : Faulted · 0x40, relay OPEN

        Available  --> Preparing      : EV plugs in, [FUN_0000b44c]
        Available  --> SuspendedEVSE  : auth received before EV, [FUN_0000b484]
        Available  --> Faulted        : relay fault, [FUN_0000ba24]

        Finishing  --> SuspendedEV    : EV plugs in (immediate PWM), [FUN_0000b44c]
        Finishing  --> Charging       : auth active + current ≥ 6A, [FUN_0000b484]
        Finishing  --> Faulted        : relay fault

        Preparing  --> Available      : EV unplugs, [FUN_0000b50c]
        Preparing  --> SuspendedEV    : auth OK (2s timer), [FUN_0000b5c8]
        Preparing  --> Faulted        : relay fault

        SuspendedEV   --> Charging      : EV closes S2 (2s timer · auth · current ≥ 6A), [FUN_0000b698]
        SuspendedEV   --> Finishing     : EV unplugs, [FUN_0000b50c]
        SuspendedEV   --> Preparing     : auth revoked / current < 6A, [FUN_0000b8cc]
        SuspendedEV   --> Faulted       : relay fault

        SuspendedEVSE --> Available     : EV unplugs, [FUN_0000b55c]
        SuspendedEVSE --> Available     : auth revoked (no EV), [FUN_0000b968]
        SuspendedEVSE --> Charging      : EV closes S2 (unconditional · no auth check), [FUN_0000b74c]

        Charging   --> Finishing      : normal stop (EV unplugs), [FUN_0000b55c]
        Charging   --> SuspendedEVSE  : auth revoked (relay open · 3s PWM timer set), [FUN_0000b7c0→b880]
        Charging   --> SuspendedEV    : EV opens S2 (CP B · relay open), [FUN_0000b84c]
        Charging   --> Faulted        : relay fault, [FUN_0000ba24]

        Faulted    --> Available      : relay opens (auto-recovery), [+0x66=1]
Loading

@Stormhand

Copy link
Copy Markdown

Hey @DJm00n , nice work.
Since then i’ve poked a bit more on the same box and a few things showed up that aren’t in your file yet, might be useful.

Energy: 0x0022 and 0x0023 look like total kWh and today’s charge (today is ×0.1). I matched them to the app counters. You don’t have those as sensors yet.

Protection on 0x003B — bit0 leakage(Leakage Current Detection in the app), bit1 grounding(Grounding Detection in the app). Tracks the app toggles and you can write them with FC16. Didn’t see that in the yaml.

On reg18 — it’s not just a limit slider. Anything above 0 still clamps to ~6A minimum. Zero is a real hard stop and often throws reg57 into that 641 fault-ish state. with Tesla i sometimes got the red port. Also when you flip WiFi/LoRa on reg21, firmware rewrites reg18 on its own (LoRa tends to slam it to 0, back to WiFi it jumps to 22000), so worth re-setting the limit after a mode change.

I also watch reg36 next to reg57 — rough pattern 9 while charging, 4 idle, 0 on EVSE stop. reg44 goes to zero when it’s really stopped. And reg69–72 mirror the schedule times in reg25–28, same numbers.

The 76/77/78 block — i log them but honestly still guessing. 77 often goes 1 on EVSE-side stop or in LoRa-y states, usually 0 when charging on WiFi. Tried writing 77 and 78 — ACK but read-back stays 0, so probably status not control. 76 mostly sits at 1, not much movement. Wouldn’t wire them as switches yet, more like extra context when you’re debugging reg57.

Raw reg57 fingerprints i kept for diffs: 416 charging, 392 done/idle, 385 unplugged, 641 EVSE 0W stop, 897 saw once on LoRa flip. Your byte split on 57 is probably the right way to show it — i just kept the raw numbers.

One naming trap: your Program 1 time on 0x0019 matches what i have (that’s decimal reg 25, time code hour*12 + min/5). Your off-grid switch is on 0x0025 though — that’s reg 37, not 25. Easy to read "25" in the hex and think it’s the first schedule slot. I haven’t confirmed off-grid on my side yet (reg37 was usually 0), but if you’ve seen that bit follow the inverter UI it’s probably right — just worth calling out so nobody mixes the two addresses.

Happy to sync or dump snapshot refs if any of it helps. I did a lot of snapshots of all the regs 0-79 while changing the car/evse/charger port states. The most awful part is the lack of "clean" stop/suspend of the charging. Ive sent a feedback to their support and waiting for solution.

@DJm00n

DJm00n commented May 21, 2026

Copy link
Copy Markdown
Author

Hey @Stormhand, thanks a lot, your findings were really valuable. We've incorporated all of them into the PR.

On the 0x0081 transition - yes, I've seen the same thing. When the EVSE drops into that state mid-session (due to low PV in Solar-only mode for example), my e-Tron actually tries to close its charge port flap, which means the car sees it as a completed session. Looks like a state machine bug on the EVSE side rather than anything we can work around in the profile. Interestingly, Deye support reached out a couple of days ago and offered a firmware update - I agreed, but it hasn't been pushed to my unit yet. Fingers crossed they've already fixed this in the new build; will report back once it lands.

Right now I have:
EVSE Firmware: C01F
Logger Firmware: MW4_01_5429_SS_04_00.00.00.07

@Stormhand

Copy link
Copy Markdown

My EVSE firmware version is the same.
But this is different: MW4_01_5429_SS_04_00.00.00.0B
I explained them all the issues, they even requested a video which Ive sent. No answer for the last 3 days so I'll push them again tomorrow.
As temporary solution ill use HA and manage the charging rate using these evse registers but when it has to stop it I'll use the tesla's integration instead.
After all it might be even better considering the reported oscillation when the inverter commands it...

@Stormhand

Copy link
Copy Markdown

@DJm00n i found that the times are not displayed properly because of the strange deye format for the minutes(*5) so if i have start time 10:30 its gonna be displayed as 10:06.
Unfortunately we cant fix it in the same config. It needs either external helper or template sensor. The other way os lookup like this but its too verbose:

- name: "Program 1 Time"
        platform: select
        rule: 1
        registers: [0x0019]
        icon: "mdi:sun-clock"
        lookup:
          - key: 0
            value: "00:00"
          - key: 6
            value: "00:30"
          - key: 12
            value: "01:00"
          - key: 18
            value: "01:30"
          - key: 24
            value: "02:00"
          - key: 30
            value: "02:30"

@DJm00n

DJm00n commented May 22, 2026

Copy link
Copy Markdown
Author

@Stormhand should work in latest revision. Added code-fix for this case.
image
image

@Stormhand

Copy link
Copy Markdown

Do I need to wait for a new solarman version?

@DJm00n

DJm00n commented May 22, 2026

Copy link
Copy Markdown
Author

@Stormhand yes. Or maybe you can sync to the latest testing and replace corresponding files on your home assistant instance with files from this PR.

@DJm00n DJm00n force-pushed the feat/evse branch 3 times, most recently from a1f2a8e to 05aacb8 Compare May 26, 2026 13:26
@Jairo2106

Copy link
Copy Markdown

Hola. Soy un poco nuevo en esto de las integraciones. Me podéis ayudar?

He creado el archivo deye.evse.yaml y he pegado el código. Pero ahora no se como tengo que agregar el dispositivo. Tengo el cargador conectado por LORA al inversor. Que ip tengo que poner? La del cargador o la del inversor? El inversor lo tengo bien agregado. Y que mas datos tengo que poner para agregar el cargador?

Muchas gracias de antemano.

@DJm00n

DJm00n commented May 26, 2026

Copy link
Copy Markdown
Author

@Jairo2106 you have to put ip address of evse that is connected to your wifi. also, dont forget to set "modbus slave id" 3 under additional options:
image

@Jairo2106

Copy link
Copy Markdown

Si pongo la ip del EV no carga, me da error. Será porque lo tengo con protocolo LORA ???

@DJm00n

DJm00n commented May 26, 2026

Copy link
Copy Markdown
Author

@Jairo2106 No, the EV charger can operate simultaneously via both LoRa and Wi-Fi. If “Working mode 2 - LoRa” is selected, the Wi-Fi connection will be used for monitoring, and LoRa will be used for control from the inverter. Please refer to the manual.
BManualSUN-EVSE11-22K01-EU-AC20260415en.pdf - see "6. Setting of communication mode"

WiFi configuration.pdf

@Jairo2106

Copy link
Copy Markdown

Ya lo he conseguido integrar y puedo ver los parámetros del EV.

Pero ahora tengo otro problema. No consigo separar el consumo de mi hogar del EV. Me mezclan los parámetros. Mi hogar esta en GRID y el EV en LOAD. Cuando consume del inversor me da unos datos y cuando consume de la red me da otros.

Screenshot_20260527_003728_Deye Cloud Screenshot_20260527_004215_Home Assistant

Adds support for the Deye AC EV Charger (SUN-EVSE11K01-EU/SUN-EVSE22K01-EU)
with full profile, autodetection, and automatic slave ID discovery. All register
interpretations and state machine values are based on direct firmware
reverse-engineering (MCU9_EV_C020_EU_3_26.bin).

Profile (deye_evse.yaml):
- Info: device type (0x0904), serial, firmware/protocol version, model class
  (22 kW / 11 kW EV Charger from register 0x0013 hi-byte)
- Configuration: inverter LoRa link status, date/time, charging start mode,
  communication mode, LoRa channel, grid voltage protection thresholds,
  off-grid charge inhibit
- Time Of Charge: 4 scheduled charge time points with enable switches
- Control: charging power limit setpoint (0–22000 W, IEC 61851 clamping noted)
- State: Charging Control State and Charging Operational State (register 0x0039),
  split by high/low byte, state machine values from firmware handlers,
  update_interval: 2 to capture short-lived transitions
- Grid: Grid Capacity (0x0038, dynamic firmware-computed power cap),
  Grid Voltage L1–L3
- Fault: Charger Fault binary_sensor (0x0036), 11-bit bitmask with confirmed
  bit assignments; Stuck Relay (bit 6) is a current-based relay detector,
  Overtemperature (bit 7, ≥110°C), Undertemperature (bit 8, <−40°C)
- Charging: per-phase and total power, charging voltage L1–L3, charging
  current L1–L3, Relay Temperature 1/2 (0x001D, two NTC sensors packed in
  one register, −50°C offset)
- Energy: total and daily EV charge counters

Autodetection (const.py):
- Register value 0x0904 mapped to deye_evse.yaml via AUTODETECTION_DEYE_EVSE

Slave ID discovery (device.py):
- setup() retries slave IDs 1→2→3 if ProfileProvider.init() fails, so the
  EVSE (which uses slave 3) is found automatically without user configuration

Parser/time fixes:
- parser.py, time.py: support `mul` parameter for time entity encoding,
  required by EVSE Time Of Charge registers (5-minute slots, dec: 12, mul: 5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@DJm00n DJm00n changed the title feat: Add Deye EV Charger (SUN-EVSE**K01-EU-AC) support feat: Add Deye EV Charger (SUN-EVSE22K01-EU) support Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants