M-Bus to MQTT bridge with Home Assistant integration.
Read your wired M-Bus meters (water, heat, gas, electricity) and send the data to Home Assistant or any MQTT broker.
Disclaimer: This software is provided 'as-is' without any guarantees.
- Reading M-Bus devices via serial M-Bus Master connected over:
- TTL (UART)
- USB
- IPv4 TCP (using for example ser2net)
- Automatic discovery of connected M-Bus meters
- Publishing meter data to MQTT
- Home Assistant MQTT Discovery integration:
- Automatic device and entity creation
- Device availability reporting
- Bridge device entity with controls (rescan, log level, poll interval)
- Template system to support different M-Bus device types
- Docker support with pre-compiled libmbus
- Systemd service for native Linux installation
- Requirements
- ⚡Quick Start with Docker (recommended)
- Native Installation
- Configuration
- Running libmbus2mqtt
- Home Assistant Integration
- Device Templates
- Hardware Setup
- Troubleshooting
- Supported Devices
- M-Bus Master adapter - converts TTL/USB signals to M-Bus protocol
- TTL to M-Bus converter (example from AliExpress)
- USB to M-Bus adapter (example from AliExpress)
- Any Linux host device with access to M-Bus Master via:
- UART (TTL)
- USB
- IPv4 TCP
- Docker / Docker Compose
- Python 3.11+ for native installation
Docker is the easiest way to get started - libmbus is pre-compiled in the image.
mkdir libmbus2mqtt
cd libmbus2mqttnano docker-compose.ymlPaste the following content:
services:
libmbus2mqtt:
image: nilvanis/libmbus2mqtt:latest
container_name: libmbus2mqtt
restart: unless-stopped
volumes:
- ./data:/data
devices:
# Change this to match your M-Bus adapter
- /dev/ttyUSB0:/dev/ttyUSB0
environment:
TZ: Europe/LondonSave and exit (Ctrl+O, Ctrl+X).
mkdir -p data/config
nano data/config/config.yamlPaste the following content and edit to match your setup:
mbus:
device: /dev/ttyUSB0 # Your M-Bus adapter device (or IPv4:port for TCP masters)
mqtt:
host: 192.168.1.100 # Your MQTT broker IP address
# username: user # Uncomment if authentication required
# password: secret
homeassistant:
enabled: true # Set to true for Home Assistant integrationSave and exit (Ctrl+O, Ctrl+X).
Note
Full config options are described here.
docker compose up -ddocker compose logs -fYou should see your M-Bus devices being discovered and data published to MQTT.
For running directly on Linux without Docker.
libmbus2mqtt can install libmbus for you:
# Install dependencies first (Debian/Ubuntu)
sudo apt-get install git build-essential libtool autoconf automake
# Clone and install libmbus2mqtt
git clone https://github.com/nilvanis/libmbus2mqtt
cd libmbus2mqtt
pip install .
# Install libmbus
sudo libmbus2mqtt libmbus installOr install libmbus manually - see libmbus on GitHub.
# Create config directory
sudo mkdir -p /data/config
# Generate example configuration
libmbus2mqtt config init --config /data/config/config.yaml
# Edit the configuration
sudo nano /data/config/config.yaml# Check if your M-Bus device is accessible
libmbus2mqtt device-info
# Scan for connected meters
libmbus2mqtt scanlibmbus2mqtt runTo run libmbus2mqtt automatically at startup:
sudo libmbus2mqtt installThis creates a systemd service that starts on boot. To manage the service:
# Check status
sudo systemctl status libmbus2mqtt
# View logs
sudo journalctl -u libmbus2mqtt -f
# Stop the service
sudo systemctl stop libmbus2mqtt
# Remove the service
sudo libmbus2mqtt uninstallConfiguration is stored in a YAML file. By default, libmbus2mqtt looks for /data/config/config.yaml.
Only two settings are required:
mbus:
device: /dev/ttyUSB0
mqtt:
host: 192.168.1.100# M-Bus Interface
mbus:
device: /dev/ttyUSB0 # REQUIRED - Serial device path OR IPv4:port (TCP master)
# Serial examples: /dev/ttyUSB0, /dev/ttyACM0, /dev/ttyAMA0
# TCP example: 192.168.1.50:9999 (IPv4 only, no hostnames)
baudrate: 2400 # M-Bus baudrate (300, 2400, or 9600) - ignored for TCP
poll_interval: 60 # Seconds between polling cycles
startup_delay: 5 # Seconds to wait before first poll/scan
timeout: 5 # Seconds to wait for device response
retry_count: 3 # Number of retries on failure
retry_delay: 1 # Seconds between retries
autoscan: true # Scan for devices on startup
# MQTT Broker
mqtt:
host: 192.168.1.100 # REQUIRED - Broker IP or hostname
port: 1883 # Broker port
username: # Username (if required)
password: # Password (if required)
client_id: # Client ID (auto-generated if empty)
keepalive: 60 # Connection keepalive in seconds
qos: 1 # Message quality of service (0, 1, or 2)
base_topic: libmbus2mqtt # Base MQTT topic
# Home Assistant Integration
homeassistant:
enabled: false # Enable Home Assistant MQTT Discovery
discovery_prefix: homeassistant
# Devices (optional)
# Must be defined if 'mbus' -> 'autoscan' is set to 'false')
# Can be also used as override for specific devices
devices:
- id: 1 # M-Bus address (0-254)
name: "Water Meter Kitchen" # Friendly name
enabled: true # Set false to ignore this device
template: # Template name (auto-detect if empty)
# Device Availability
availability:
timeout_polls: 3 # Consecutive failures before marking offline
# Logging
logs:
level: INFO # Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL
save_to_file: false # Enable file logging
file: data/log/libmbus2mqtt.log # Log file path
max_size_mb: 10 # Max file size before rotation (1-1000 MB)
backup_count: 5 # Number of rotated backup files (0-100)Environment variables can be used to provide config values (handy in Docker). When both the YAML file and an environment variable set the same field, the environment variable takes precedence and the override is logged.
Env var naming convention is based on config.yaml: LIBMBUS2MQTT_SECTION_OPTION. Below is complete list:
LIBMBUS2MQTT_MBUS_DEVICE
LIBMBUS2MQTT_MBUS_BAUDRATE
LIBMBUS2MQTT_MBUS_POLL_INTERVAL
LIBMBUS2MQTT_MBUS_STARTUP_DELAY
LIBMBUS2MQTT_MBUS_TIMEOUT
LIBMBUS2MQTT_MBUS_RETRY_COUNT
LIBMBUS2MQTT_MBUS_RETRY_DELAY
LIBMBUS2MQTT_MBUS_AUTOSCAN
LIBMBUS2MQTT_MQTT_HOST
LIBMBUS2MQTT_MQTT_PORT
LIBMBUS2MQTT_MQTT_USERNAME
LIBMBUS2MQTT_MQTT_PASSWORD
LIBMBUS2MQTT_MQTT_CLIENT_ID
LIBMBUS2MQTT_MQTT_KEEPALIVE
LIBMBUS2MQTT_MQTT_QOS
LIBMBUS2MQTT_MQTT_BASE_TOPIC
LIBMBUS2MQTT_HOMEASSISTANT_ENABLED
LIBMBUS2MQTT_HOMEASSISTANT_DISCOVERY_PREFIX
LIBMBUS2MQTT_AVAILABILITY_TIMEOUT_POLLS
LIBMBUS2MQTT_LOGS_LEVEL
LIBMBUS2MQTT_LOGS_SAVE_TO_FILE
LIBMBUS2MQTT_LOGS_FILE
LIBMBUS2MQTT_LOGS_MAX_SIZE_MB
LIBMBUS2MQTT_LOGS_BACKUP_COUNT
Docker Compose example with environment variables:
services:
libmbus2mqtt:
image: nilvanis/libmbus2mqtt:latest
environment:
LIBMBUS2MQTT_MBUS_DEVICE: /dev/ttyUSB0
LIBMBUS2MQTT_MQTT_HOST: 192.168.1.100
LIBMBUS2MQTT_MQTT_USERNAME: user
LIBMBUS2MQTT_MQTT_PASSWORD: secret
LIBMBUS2MQTT_HOMEASSISTANT_ENABLED: "true"
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
volumes:
- ./data:/data# Start the main daemon
libmbus2mqtt run
# Scan for M-Bus devices (one-time)
libmbus2mqtt scan
# Show M-Bus adapter information
libmbus2mqtt device-info
# Show version
libmbus2mqtt version
# or
libmbus2mqtt --version
# Validate configuration file
libmbus2mqtt config validate
# Generate example configuration
libmbus2mqtt config init
# Install/uninstall systemd service
libmbus2mqtt install
libmbus2mqtt uninstall
# Install libmbus (native installation only)
libmbus2mqtt libmbus installlibmbus2mqtt run --config /path/to/config.yaml
libmbus2mqtt scan --config /path/to/config.yamlWhen homeassistant.enabled is set to true, libmbus2mqtt automatically creates devices and entities in Home Assistant using MQTT Discovery.
libmbus2mqtt creates a "Bridge" device in Home Assistant with these entities:
| Entity | Type | Description |
|---|---|---|
| Discovered Devices | Sensor | Number of M-Bus devices found |
| Online Devices | Sensor | Number of devices currently responding |
| Firmware Version | Sensor | libmbus2mqtt version |
| Last Scan | Sensor | When devices were last scanned (disabled by default) |
| Uptime | Sensor | How long the bridge has been running (disabled by default) |
| Last Poll Duration | Sensor | Duration of last polling cycle in ms (disabled by default) |
| Rescan Devices | Button | Trigger a new device scan |
| Log Level | Select | Change logging level (DEBUG, INFO, WARNING, ERROR) |
| Poll Interval | Number | Change polling interval (10-3600 seconds) |
Important
Settings changed via MQTT (e.g. Log level) are changed only for the current runtime, meaning it will revert to config.yml setting (or default if not set) after restart of libmbus2mqtt.
Each discovered M-Bus meter appears as a separate device in Home Assistant, linked to the Bridge device. The entities created depend on:
- Device template - If a template exists for your meter, entities are created with proper names, units, and icons
- Generic fallback - If no template exists, generic sensors are created for each data record
Data is published to these MQTT topics:
libmbus2mqtt/bridge/state # Bridge availability (online/offline)
libmbus2mqtt/bridge/info # Bridge status JSON
libmbus2mqtt/device/{id}/state # Device data JSON
libmbus2mqtt/device/{id}/availability # Device availability
libmbus2mqtt/command/rescan # Trigger rescan (send any message)
libmbus2mqtt/command/log_level # Change log level (DEBUG/INFO/WARNING/ERROR)
libmbus2mqtt/command/poll_interval # Change poll interval (10-3600)
Templates define how M-Bus data records are mapped to Home Assistant entities. They provide friendly names, icons, units, and device classes.
libmbus2mqtt includes templates for common devices. Check the Supported Devices section.
You can create custom templates for your devices. Place them in /data/templates/:
/data/
└── templates/
├── index.json # Maps devices to templates
└── my_custom_meter.json # Your template file
Maps device information to template files:
{
"my_custom_meter.json": {
"Manufacturer": "ACME",
"ProductName": "Water Meter Pro"
}
}Defines entities for each data record:
{
"0": {
"name": "Serial Number",
"icon": "mdi:identifier",
"component": "sensor",
"value_template": "{{ value_json.serial_number }}",
"entity_category": "diagnostic"
},
"1": {
"name": "Total Volume",
"icon": "mdi:water",
"component": "sensor",
"device_class": "water",
"state_class": "total_increasing",
"unit_of_measurement": "m³",
"value_template": "{{ value_json.volume }}"
},
"custom-daily": {
"name": "Daily Usage",
"icon": "mdi:water-outline",
"component": "sensor",
"unit_of_measurement": "L",
"value_template": "{{ value_json.daily_usage }}"
}
}The numbers ("0", "1", etc.) correspond to <DataRecord id="X"> in the M-Bus XML output. You can also create additional, custom sensors. Every custom sensor JSON section name must be preceded with custom-. All section names must be unique.
Since custom sensors does not have it's own DataRecord in libmbus xml output, value must be derived from other data.
Common use case is creating dedicated sensors from data in Manufacturer Specific field value.
Template lookup order:
- libmbus2mqtt first checks
/data/templates/index.jsonfor a matching entry and template file. - If no match is found in the user index, it falls back to the bundled index/templates.
This lets you add templates without blocking built-in ones; if you want to override a built-in, add a matching entry to your user index and provide the file in
/data/templates/.
To see what data your meter provides, run:
# Using libmbus2mqtt
libmbus2mqtt scan
# Or directly with libmbus
mbus-serial-request-data -b 2400 /dev/ttyUSB0 1This shows the XML output with all data records. Use this to create a custom template.
Need help? Open an issue with your XML output, and we can help create a template.
Important: Disconnect all power before wiring!
sudo raspi-configNavigate to: 3 Interface Options → Serial Port → No (login shell) → Yes (hardware enabled)
Reboot the Pi.
Connect the TTL-to-M-Bus adapter to the Raspberry Pi GPIO:
| Raspberry Pi | TTL-to-M-Bus |
|---|---|
| Pin 2 (5V) | TTLVCC |
| Pin 4 (5V) | VIN |
| Pin 6 (GND) | GND |
| Pin 8 (TX) | TXD |
| Pin 10 (RX) | RXD |
See pinout.xyz for GPIO pin locations.
After powering on, the device should be available at /dev/ttyAMA0 or /dev/serial0.
Simply plug in the USB adapter. It will typically appear as /dev/ttyUSB0 or /dev/ttyACM0.
Check with:
ls -l /dev/ttyUSB* /dev/ttyACM*You can run libmbus2mqtt on one device and access M-Bus Master connected to another one. Below is an example how to configure that:
Use the information above and note the device path.
sudo apt update && sudo apt install ser2net -y
sudo nano /etc/ser2net.yaml
Remove existing ser2net config if not used. Create new entry for your M-Bus Master (example for Raspberry Pi UART):
connection: &uart0
accepter: tcp(nodelay=true),0.0.0.0,4001
enable: on
options:
kickolduser: true
chardelay: false
connector: serialdev,/dev/ttyAMA0,2400e81,local
4001is the TCP port number over which your M-Bus Master will be available.0.0.0.0means that the ser2net will open port on all IP interfaces configured on the linux host. You can specify a single IP here if needed./dev/ttyAMA0is the M-Bus master device path2400e81- serial settings:- speed:
2400baud - parity:
e(even) - data bits:
8 - stop bits:
1
- speed:
Start the ser2net service:
sudo systemctl enable ser2net
sudo systemctl start ser2net
You might also need to open firewall if used (inbound TCP/4001)
In libmbus2mqtt config.yaml file you just need to set mbus.device to IP:4001, e.g.:
mbus:
device: '192.168.1.50:4001'Important
M-Bus scan over TCP can take up to 20 minutes! Currently it should take around 5 minutes if using Docker.
Add your user to the dialout group:
sudo usermod -aG dialout $USERLog out and back in for the change to take effect.
-
Check wiring - Ensure M-Bus devices are properly connected
-
Check baudrate - Most M-Bus devices use 2400, but some use 300 or 9600
-
Test with libmbus directly:
mbus-serial-scan -b 2400 /dev/ttyUSB0
-
Check power - M-Bus devices need power from the M-Bus master
- Check if the device is physically connected
- Increase
availability.timeout_pollsin config - Check for loose wiring
- Verify
homeassistant.enabled: truein config - Check MQTT broker connection
- Verify
discovery_prefixmatches Home Assistant (default:homeassistant) - Check Home Assistant MQTT integration is enabled
- Check mosquitto addon logs
# Docker
docker compose logs -f libmbus2mqtt
# Systemd service
sudo journalctl -u libmbus2mqtt -fYou can set log level to DEBUG temporarily in Home Assistant MQTT device "libmbus2mqtt Bridge".
You can also set the log level permanently using either:
- config.yaml
log.leveloption LIBMBUS2MQTT_LOGS_LEVELenv variable
Enable file logging in your configuration:
logs:
save_to_file: true
file: data/log/libmbus2mqtt.log # Log files will be hereLog files are automatically rotated when they reach max_size_mb (default: 10 MB). Old files are kept up to backup_count (default: 5).
Note
Log level changes via MQTT (libmbus2mqtt/command/log_level) are not persisted - they reset to the config value on restart. To permanently change the log level, edit logs.level in your config file.
Also, make sure the data/ directory is writable by libmbus2mqtt user if using Docker
libmbus2mqtt includes templates for these devices:

Data sheet: https://se.itron.com/o/commerce-media/accounts/-1/attachments/3809944

Data sheet: https://api.apator.com/uploads/oferta/woda-i-cieplo/systemy/przewodowy/apt-mbus-na/apt-mbus-na-1-catalogue.pdf
If your device isn't listed, libmbus2mqtt should still work - it creates generic sensors for all data records. For a better experience, you can create a custom template or request one.
Contributions are welcome! Please open an issue or pull request on GitHub.
- Open an issue for bugs or feature requests
- Include your M-Bus XML output when requesting device support



