Integration of sonnenCharger/Etrel Inch Home/Duo Wallbox into Home Assistant

Sonnen does not allow access to the sonnenCharger without a login and password, which it does not make public. However, access via Modbus (port 502) is possible. Here I describe how I have implemented this in Home Assistant (tested with version 2024.5).

Reading and writing values in the wallbox is done via register addresses. A list of the registers and their meaning can be found at ETREL:
[https://landisgyr-evsolutions.atlassian.net/wiki/spaces/Home/pages/2236121092/Modbus+Communication+with+Inch+products]
I have created the following file modbus.yaml and called it via “modbus: !include modbus.yaml” in config.yaml.

My modbus.yaml file:

- name: sonnenCharger
  type: tcp
  host: <IP of your wallbox>
  port: 502
  sensors:
    - name: charge_status_int
      address: 0
      input_type: input
      data_type: int16
    - name: no_of_phases
      address: 1
      input_type: input
      data_type: int16
    - name: active_power
      address: 26
      input_type: input
      data_type: float32
      device_class: power
      state_class: measurement
      unit_of_measurement: kW
      precision: 2
    - name: session_energy
      address: 30
      input_type: input
      scan_interval: 10
      data_type: float32
      device_class: energy
      state_class: total_increasing
      unit_of_measurement: kWh
    - name: session_duration
      address: 32
      input_type: input
      data_type: int64
      unit_of_measurement: s
    - name: departure_time_unix
      address: 36
      input_type: input
      data_type: int64
      unit_of_measurement: s
    - name: serial_number
      address: 990
      input_type: input
      data_type: string
      count: 10
    - name: model
      address: 1000
      input_type: input
      data_type: string
      count: 10
    - name: HW_version
      address: 1010
      input_type: input
      data_type: string
      count: 5
    - name: SW_version
      address: 1015
      input_type: input
      data_type: string
      count: 5

The values are then available as sensors in Home Assistant. Here some examples in my file sensors.yaml:

- platform: template
  sensors:
    wb_active_power:
      unique_id: "wb_active_power"
      value_template: >-
        {% if (states('sensor.active_power') | float) > 0 %}
          {{(states('sensor.active_power') | float)}}
        {% else %}
          {{ 0.0 }}
        {% endif %}
- platform: integration
  source: sensor.wb_active_power
  name: i_wallbox_total_energy
  method: right
  round: 2
- platform: template
  sensors:
    wallbox_total_energy:
      unique_id: "wallbox_total_energy"
      friendly_name: "Wallbox gesamt geladenen Energie"
      value_template: >-
        {% set number = (states('sensor.i_wallbox_total_energy') | float) %}
        {{number}}
      device_class: energy
    wallbox_session_duration:
      unique_id: "wallbox_session_duration"
      friendly_name: "Dauer"
      value_template: >-
        {% set uptime = states("sensor.session_duration") | int %} 
        {% set hours = (uptime % 86400) // 3600 %}
        {% set minutes = (uptime % 3600) // 60 %}
        {% set seconds = (uptime % 60) %} 
        {{ '{:02} Std {:02} Min'.format(hours, minutes) }}
    wallbox_departure_time:
      unique_id: "wallbox_departure_time"
      friendly_name: "Abfahrtszeit in der Wallbox"
      value_template: >-
        {% set departure = as_datetime(as_datetime(states('sensor.departure_time_unix')) | as_local).strftime("%a. den %d.%m.%Y um %H:%M") %}
        {{departure}}

For the integration it is important to use the method “right”, because the value in active_power of the last charge is still in the register (on the left, so to speak).

The sensors can now be displayed as cards on the dashboard. Here is an excerpt from my vertical-stack where two cards are only shown when they make sense:

  - type: markdown
    title: Wallbox
    content: >-
      <h3> {% if states('sensor.charge_status_int') == '1' %} <ha-alert
      alert-type="info">Wallbox bereit</ha-alert> {% elif
      states('sensor.charge_status_int') == '2' %} <ha-alert
      alert-type="info">Warten auf Verbindung zum Auto</ha-alert> {% elif
      states('sensor.charge_status_int') == '3' %} <ha-alert
      alert-type="info">Warten, dass Laden startet</ha-alert> {% elif
      states('sensor.charge_status_int') == '4' %} <ha-alert
      alert-type="success">Auto lädt seit:
      {{states('sensor.wallbox_session_duration')}}</ha-alert> {% elif
      states('sensor.charge_status_int') == '5' %} <ha-alert
      alert-type="info">Auto pausiert Laden</ha-alert> {% elif
      states('sensor.charge_status_int') == '6' %} <ha-alert
      alert-type="info">Wallbox pausiert Laden</ha-alert> {% elif
      states('sensor.charge_status_int') == '7' %} <ha-alert
      alert-type="info">Laden beendet</ha-alert> {% elif
      states('sensor.charge_status_int') == '8' %} <ha-alert
      alert-type="alert">Systemfehler</ha-alert> {% elif
      states('sensor.charge_status_int') == '9' %} <ha-alert
      alert-type="success">Laden wird fortgesetzt</ha-alert> {% elif
      states('sensor.charge_status_int') == '10' %} <ha-alert
      alert-type="alert">Nicht verfügbar</ha-alert> {% else %} <ha-alert
      alert-type="alert">Ststus unbekannt</ha-alert> {% endif %} </h3>
      <small>Software-Version {{states('sensor.sw_version')}}, Hardware-Version
      {{states('sensor.hw_version')}}</small>
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: sensor.charge_status_int
        above: 3
        below: 7
    card:
      type: vertical-stack
      cards:
        - type: sensor
          name: Leistung
          entity: sensor.active_power
          graph: line
          detail: 2
          min: 0
          max: 11.1
        - type: entity
          name: aktuell geladenen Energie
          icon: mdi:ev-station
          entity: sensor.session_energy
  - type: entity
    entity: sensor.wallbox_total_energy
    name: gesamte geladenen Energie
    icon: mdi:ev-station
    unit: kWh

You can also write values to the wallbox. This is particularly interesting if you want to charge the car with the PV system over several days. The departure time is saved as a UTC Unix timestamp in the box. Here is my automation, which reads the set time from helper input_datetime.departure_time and writes it to the box when triggered by the button input_boolean.wallbox_set_time. The timestamp must be divided into four 16-bit integer values. The calculation in the automation reaches up to the year 2106.

In my automations.yaml:

- id: '1716879434742'
  alias: set departure time in wallbox
  description: setting the expected departure time of the car
  trigger:
  - platform: state
    entity_id:
    - input_boolean.wallbox_set_time
  condition: []
  action:
  - service: modbus.write_register
    data:
      hub: sonnenCharger
      address: 4
      slave: 0
      value: >-
        {% set dt = as_timestamp(states('input_datetime.departure_time', 0)) | round(0)%}
        {% set reg1 = (dt | float / 65536) | round(0, "floor") | int%}
        {% set reg2 = (dt - (reg1 * 65536)) | int%}
        [0, 0, {{reg1}}, {{reg2}}]
  mode: single

Setting the time is only possible when the car is connected to the wallbox and only makes sense when “Smart mode” is selected. Smart and Power mode are not saved in the wallbox. They are transmitted to the company Sonnen, which then controls charging depending on the available energy in PV system and sonnenBatterie.

Here you can see what my dashboard looks like when the car is loading: