long

If you're a Romande Energie customer, you likely have a Landis+Gyr E350 or E450 smart meter with an AD-FU module. This meter exposes real-time electricity data, power, energy, voltage, current on all three phases via a wired M-Bus interface on an RJ12 connector. Here's how to get that data into Home Assistant running in Docker on a Debian machine.

Reading Your Romande Energie Smart Meter (Landis+Gyr E450) with Home Assistant

What You Need

  • A Landis+Gyr E350/E450 with AD-FU module
  • An M-Bus slave USB dongle (FC722-based, available on AliExpress for ~€10)
  • An RJ12 cable wired to the dongle's M-Bus screw terminals (pins 3 and 4)
  • A Linux machine running Home Assistant in Docker
  • A Mosquitto MQTT broker (Docker)
flowchart LR meter["Landis+Gyr E450\n(M-Bus master)"] dongle["USB M-Bus Dongle\n(FC722 / PL2303)"] collector["smartmeter-datacollector\n(Docker)"] mqtt["Mosquitto MQTT\n(Docker)"] ha["Home Assistant\n(Docker)"] meter -->|"RJ12 / M-Bus\n2400 baud, 8O1"| dongle dongle -->|"USB-Serial\n/dev/ttyMBUS"| collector collector -->|"MQTT publish\nevery 5s"| mqtt mqtt -->|"MQTT subscribe"| ha

Understanding the Interface

The first thing to know is that the meter itself is the M-Bus master. Your USB dongle acts as the slave. This means you don't need to send any requests: the meter pushes data every 5 seconds automatically.

The protocol is DLMS/COSEM over HDLC, not plain M-Bus telegrams. Standard M-Bus tools like libmbus won't parse this directly. The serial parameters are:

  • 2400 baud
  • 8 data bits
  • Odd parity (not even, as is common with standard M-Bus)
  • 1 stop bit

reference: Landis Gyr E450 technical specifications

Hardware Setup

The USB dongle shows up as a Prolific PL2303 USB-to-serial adapter:

[dmesg] usb 1-1.4: Product: USB-Serial Controller
[dmesg] pl2303 converter now attached to ttyUSB1

To make the device path stable across reboots, reference it by USB serial number in Docker rather than using a udev symlink:

$ ls -la /dev/serial/by-id/
usb-Prolific_Technology_Inc._USB-Serial_Controller_BOCJb116L17-if00-port0 -> ../../ttyUSB1

The Right Tool: smartmeter-datacollector

Rather than writing a custom DLMS/HDLC parser (trust me, we tried), use smartmeter-datacollector, an open source tool funded by EKZ and specifically built for the Landis+Gyr E450 over wired M-Bus. It handles multi-frame HDLC reassembly, DLMS/COSEM parsing, and MQTT publishing out of the box.

Install it in a Python virtual environment to test (we'll use a docker container later):

python3 -m venv /opt/mbus-reader
source /opt/mbus-reader/bin/activate
pip install smartmeter-datacollector

Generate and edit the config:

smartmeter-datacollector --saveconfig

Edit datacollector.ini:

[reader0]
type = lge450
port = /dev/ttyMBUS
baudrate = 2400
key =

[sink0]
type = logger
name = DataLogger

[sink1]
type = mqtt
host = 127.0.0.1
port = 1883
tls = False
username =
password =

[logging]
default = INFO
collector = INFO
smartmeter = INFO
sink = INFO

Gotchas: 1) Do not include client_cert_path or client_key_path lines in the config — even empty, they trigger TLS mode due to a quirk in the config parser. 2) host will need to be updated when we'll use this configuration in the container.

Containerising the Setup

Create a smartmeter/ folder alongside your docker-compose.yml:

smartmeter/Dockerfile:

FROM python:3.13-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir smartmeter-datacollector

COPY datacollector.ini /etc/smartmeter/datacollector.ini

CMD ["smartmeter-datacollector", "--config", "/etc/smartmeter/datacollector.ini"]

smartmeter/datacollector.ini: same as above, but set host = mosquitto (Docker internal DNS).

docker-compose.yml additions:

mosquitto:
  image: eclipse-mosquitto:2
  container_name: mosquitto
  volumes:
    - ./mosquitto/config:/mosquitto/config
    - ./mosquitto/data:/mosquitto/data
  restart: unless-stopped

smartmeter:
  build: ./smartmeter
  container_name: smartmeter
  restart: unless-stopped
  devices:
    - /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_BOCJb116L17-if00-port0:/dev/ttyMBUS
  depends_on:
    - mosquitto

mosquitto/config/mosquitto.conf:

listener 1883
allow_anonymous true
persistence true
persistence_location /mosquitto/data/

Build and start:

docker compose up -d --build smartmeter

MQTT Topics

Once running, smartmeter-datacollector publishes to topics like:

smartmeter/00000000/ACTIVE_POWER_P    {"value": 259.0, "timestamp": 1773999751}
smartmeter/00000000/ACTIVE_POWER_N    {"value": 0.0, "timestamp": 1773999751}
smartmeter/00000000/ACTIVE_ENERGY_P   {"value": 13053210.0, "timestamp": 1773999751}
smartmeter/00000000/ACTIVE_ENERGY_N   {"value": 0.0, "timestamp": 1773999751}
smartmeter/00000000/CURRENT_L1        {"value": 0.48, "timestamp": 1773999751}
smartmeter/00000000/CURRENT_L2        {"value": 1.66, "timestamp": 1773999751}
smartmeter/00000000/CURRENT_L3        {"value": 0.13, "timestamp": 1773999751}
smartmeter/00000000/VOLTAGE_L1        {"value": 243.0, "timestamp": 1773999751}
smartmeter/00000000/VOLTAGE_L2        {"value": 242.0, "timestamp": 1773999751}
smartmeter/00000000/VOLTAGE_L3        {"value": 244.0, "timestamp": 1773999751}

The topic prefix uses your meter's serial number (00000000). Check the smartmeter logs to find your serial number, e.g. mine:

smartmeter  | INFO:DataLogger:00000000 - 2026-03-21T22:08:40.944365+00:00 - Active Energy -: 0.0 Wh

Home Assistant Configuration

First, add the MQTT integration in HA: Settings → Devices & Services → Add Integration → MQTT. Use mosquitto as the broker address.

Then add the sensors to configuration.yaml:

mqtt:
  sensor:
    - name: 'Power Import'
      state_topic: 'smartmeter/00000000/ACTIVE_POWER_P'
      value_template: '{{ value_json.value }}'
      unit_of_measurement: 'W'
      device_class: power
      state_class: measurement

    - name: 'Power Export'
      state_topic: 'smartmeter/00000000/ACTIVE_POWER_N'
      value_template: '{{ value_json.value }}'
      unit_of_measurement: 'W'
      device_class: power
      state_class: measurement

    - name: 'Energy Import Total'
      state_topic: 'smartmeter/00000000/ACTIVE_ENERGY_P'
      value_template: '{{ (value_json.value / 1000) | round(3) }}'
      unit_of_measurement: 'kWh'
      device_class: energy
      state_class: total_increasing

    - name: 'Energy Export Total'
      state_topic: 'smartmeter/00000000/ACTIVE_ENERGY_N'
      value_template: '{{ (value_json.value / 1000) | round(3) }}'
      unit_of_measurement: 'kWh'
      device_class: energy
      state_class: total_increasing

    - name: 'Current L1'
      state_topic: 'smartmeter/00000000/CURRENT_L1'
      value_template: '{{ value_json.value | round(2) }}'
      unit_of_measurement: 'A'
      device_class: current
      state_class: measurement

    - name: 'Current L2'
      state_topic: 'smartmeter/00000000/CURRENT_L2'
      value_template: '{{ value_json.value | round(2) }}'
      unit_of_measurement: 'A'
      device_class: current
      state_class: measurement

    - name: 'Current L3'
      state_topic: 'smartmeter/00000000/CURRENT_L3'
      value_template: '{{ value_json.value | round(2) }}'
      unit_of_measurement: 'A'
      device_class: current
      state_class: measurement

    - name: 'Voltage L1'
      state_topic: 'smartmeter/00000000/VOLTAGE_L1'
      value_template: '{{ value_json.value | round(1) }}'
      unit_of_measurement: 'V'
      device_class: voltage
      state_class: measurement

    - name: 'Voltage L2'
      state_topic: 'smartmeter/00000000/VOLTAGE_L2'
      value_template: '{{ value_json.value | round(1) }}'
      unit_of_measurement: 'V'
      device_class: voltage
      state_class: measurement

    - name: 'Voltage L3'
      state_topic: 'smartmeter/00000000/VOLTAGE_L3'
      value_template: '{{ value_json.value | round(1) }}'
      unit_of_measurement: 'V'
      device_class: voltage
      state_class: measurement

Restart HA and head to Settings → Energy to add your meter as the grid consumption source using the Energy Import Total sensor.

Pitfalls and Lessons Learned

  • Empty config values trigger TLS: in smartmeter-datacollector's config, omit client_cert_path and client_key_path entirely rather than leaving them blank.
  • Use /dev/serial/by-id/: more reliable than /dev/ttyUSBx or custom udev symlinks for identifying the dongle in Docker.

Result

You now have real-time electricity data from your Romande Energie meter flowing into Home Assistant every 5 seconds, fully containerised and stable across reboots. The energy dashboard will start accumulating historical data immediately, giving you a clear picture of your consumption and any solar export.

About me

Stuff I do with my brain, my fingers and an editor:

(Front-end) development - typescript, Vue, React, svelte(kit) web architectures web performance API design pragmatic SEO security Accessibility (A11y) front-end dev recruitment and probably more...
Feel free to , check out my open source projects, see the music I listen to, or just read the things I write on this site.