Modbus-to-MQTT bridge for Kermi heat pumps with Home Assistant auto-discovery.
- Full monitoring - All heat pump sensors published via MQTT
- Bidirectional control - Change settings (mode, preset, DHW temp) via MQTT
- Home Assistant integration - Zero-config auto-discovery with climate & water_heater entities
- Unified device - All entities grouped under single "Kermi X-Center" device in HA
- Safety-first - Only exposes user-safe controls with validation
- Kubernetes ready - Helm chart included for easy deployment
- Async/efficient - Low resource usage (<50MB RAM)
- Kermi heat pump with Modbus TCP/RTU interface
- MQTT broker (Mosquitto, Home Assistant, etc.)
- Python 3.12+ or Docker
# Create config file
cp config.example.yaml config.yaml
# Edit config.yaml with your Modbus and MQTT settings
# Build and run
docker build -t kermi2mqtt .
docker run -d --name kermi2mqtt \
-v $(pwd)/config.yaml:/config/config.yaml:ro \
kermi2mqttOr with Docker Compose:
docker-compose up -d# Add your values
cat > my-values.yaml << EOF
config:
modbus:
host: "xcenter.local"
port: 502
mqtt:
host: "mqtt.local"
port: 8883
tlsEnabled: true
mqttAuth:
username: "kermi"
password: "your-password"
EOF
# Install
helm install kermi2mqtt ./charts/kermi2mqtt -f my-values.yamlSee charts/kermi2mqtt/values.yaml for all options.
# Install from PyPI
pip install kermi2mqtt
# Or install from source
git clone https://github.com/jr42/kermi2mqtt
cd kermi2mqtt
pip install -e .- Copy
config.example.yamltoconfig.yaml - Configure your Modbus connection:
modbus: host: 192.168.1.100 # Your heat pump IP port: 502 mode: tcp
- Configure your MQTT broker:
mqtt: host: localhost port: 1883
- Set device ID (or leave blank for auto-detection):
integration: device_id: my_heat_pump poll_interval: 30.0
docker-compose up -d
docker-compose logs -f kermi2mqttpython -m kermi2mqtt --config config.yaml# Copy service file
sudo cp kermi2mqtt.service /etc/systemd/system/
sudo systemctl daemon-reload
# Enable and start
sudo systemctl enable kermi2mqtt
sudo systemctl start kermi2mqtt
# Check status
sudo systemctl status kermi2mqttkermi/{device_id}/sensors/outdoor_temp → Outdoor temperature
kermi/{device_id}/sensors/supply_temp → Supply temperature
kermi/{device_id}/sensors/cop → Coefficient of Performance
kermi/{device_id}/sensors/power_total → Thermal power output
kermi/{device_id}/sensors/power_electrical → Electrical power consumption
kermi/{device_id}/heating/actual → Current heating temperature
kermi/{device_id}/heating/setpoint → Heating setpoint
kermi/{device_id}/heating/circuit_status → Heating circuit status
kermi/{device_id}/water_heater/actual → DHW actual temperature
kermi/{device_id}/water_heater/setpoint → DHW setpoint
kermi/{device_id}/water_heater/single_charge → One-time heating active
kermi/{device_id}/availability → online/offline
# Monitor all topics
mosquitto_sub -h localhost -t 'kermi/#' -v
# Monitor specific sensor
mosquitto_sub -h localhost -t 'kermi/my_heat_pump/sensors/outdoor_temp'Entities automatically appear in Home Assistant with appropriate types:
- Climate entities for heating/cooling control
- Water Heater entities for domestic hot water
- Sensor entities for temperature, power, COP readings
- Switch entities for on/off controls
- Binary Sensor entities for status indicators
All entities are grouped under a single Device in Home Assistant.
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Kermi Heat │ Modbus │ kermi2mqtt │ MQTT │ Home Assistant │
│ Pump (x-center)│◄───────►│ Bridge │◄───────►│ / MQTT Clients │
└─────────────────┘ └──────────────┘ └─────────────────┘
│
┌───────▼────────┐
│ py-kermi-xcenter│
│ (Modbus lib) │
└─────────────────┘
kermi2mqtt exposes a lightweight HTTP server (default 0.0.0.0:8080) with three endpoints intended for Kubernetes probes or external monitoring:
| Path | Purpose | 200 when | 503 when |
|---|---|---|---|
/healthz |
Liveness | MQTT and device clients are connected and a poll cycle succeeded within stale_after_seconds |
any of those is false |
/readyz |
Readiness | MQTT and device clients have each completed at least one successful connect (sticky) | either client has never connected yet |
/status |
Debug JSON | always 200 — returns the raw signals ({mqtt, device, last_poll_seconds_ago, ...}) |
— |
The staleness check on /healthz is the load-bearing part: is_connected can report True while a socket is wedged (DNS flap, CNI identity drift, upstream hostname change), so liveness additionally requires that poll_and_publish() has completed recently. This is exactly the failure mode — logs fill with reconnect errors but nothing is published and the pod stays Ready 1/1 — that a plain process-alive check misses.
health:
enabled: true # set false to disable the server entirely
host: "0.0.0.0"
port: 8080
stale_after_seconds: null # null -> max(2 * poll_interval, 60)Starting with chart 0.1.3, livenessProbe, readinessProbe, and startupProbe are enabled by default pointing at the new endpoints. This is a behaviour change: pods will now be restarted by Kubernetes on persistent connectivity failure (~2.5 min of /healthz 503s at default settings). If you were running an earlier chart and have custom probe values in your values.yaml, review them. To keep the previous "no probes" behaviour, set config.health.enabled: false.
# Clone repository
git clone https://github.com/jr42/kermi2mqtt
cd kermi2mqtt
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # or `.venv\Scripts\activate` on Windows
# Install with dev dependencies
pip install -e ".[dev]"# Run tests
pytest
# Run tests with coverage
pytest --cov=src/kermi2mqtt --cov-report=html
# Run linters
ruff check src/ tests/
mypy src/kermi2mqtt/
black --check src/ tests/kermi2mqtt/
├── src/kermi2mqtt/ # Main package
│ ├── __main__.py # Entry point
│ ├── config.py # Configuration loading
│ ├── modbus_client.py # Modbus wrapper
│ ├── mqtt_client.py # MQTT wrapper
│ ├── bridge.py # Main bridge logic
│ ├── safety.py # Safety validation
│ ├── ha_discovery.py # HA discovery payloads
│ ├── mappings.py # Attribute definitions
│ └── models/ # Data models
├── charts/kermi2mqtt/ # Helm chart for Kubernetes
├── tests/ # Test suite
├── Dockerfile # Container image
└── config.example.yaml # Example configuration
This integration only exposes user-safe controls equivalent to the heat pump's physical interface:
✅ Safe to modify:
- Temperature setpoints (40-60°C for DHW)
- Operating modes (heating/cooling)
- One-time water heating
- Heating schedules
❌ Not exposed (hardware safety):
- Compressor controls
- Refrigerant valve positions
- System pressure
- Low-level firmware parameters
See specs/001-modbus-mqtt/safety.md for detailed safety documentation.
# Test Modbus connection
python -c "from kermi_xcenter import KermiModbusClient, HeatPump; import asyncio; asyncio.run(test())"
# Check MQTT broker
mosquitto_sub -h localhost -t '#' -v# Docker logs
docker-compose logs -f kermi2mqtt
# Systemd logs
journalctl -u kermi2mqtt -f
# Increase log verbosity in config.yaml
logging:
level: DEBUG-
"No response from heat pump"
- Check network connectivity:
ping <heat_pump_ip> - Verify Modbus port 502 is accessible
- Check firewall rules
- Check network connectivity:
-
"MQTT connection failed"
- Verify broker is running:
systemctl status mosquitto - Test broker:
mosquitto_sub -h localhost -t test - Check credentials in config.yaml
- Verify broker is running:
-
"Entities not appearing in Home Assistant"
- Check MQTT discovery prefix matches HA config (default:
homeassistant) - Verify kermi2mqtt is publishing:
mosquitto_sub -t 'homeassistant/#' - Restart Home Assistant after first discovery
- Check MQTT discovery prefix matches HA config (default:
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run linters and tests
- Submit a pull request
Apache License 2.0 - see LICENSE for details.
- Built with py-kermi-xcenter by @jr42
- Uses aiomqtt for async MQTT
- Designed for Home Assistant
This software is not affiliated with or endorsed by Kermi. Use at your own risk. Always ensure changes are safe for your equipment.