This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Fronius Gen24 Modbus TCP to MQTT bridge for Home Assistant. Reads inverter data via SunSpec Modbus TCP registers, publishes to MQTT with HA auto-discovery, and accepts commands from HA back to the inverter over Modbus.
# Run locally
pip install -r requirements.txt
cp .env.example .env # edit with your inverter/broker IPs
python -m src
# Docker
docker compose up -d --build
# Logs
docker compose logs -f fronius-modbussrc/
config.py - Env-var-based configuration (Config class)
registers.py - SunSpec model & register definitions (absolute 40xxx addresses)
modbus_client.py - pymodbus wrapper: read/write registers, scale factor handling
mqtt_client.py - paho-mqtt wrapper: HA discovery, state publishing, command subscriptions
greengrid.py - Green Grid uniform command handler (SPEC §6): JSON command -> verified SunSpec-124 register sequences
main.py - Main loop: poll modbus -> publish MQTT, handle MQTT commands -> write modbus
Two command paths: the original fronius/command/<register> (raw register writes,
used by HA) and the optional Green Grid uniform interface greengrid/inverter/<id>/command
(high-level {mode, power_w, ttl_s} → verified register sequences, gated by
GG_ENABLE). See README "Green Grid uniform command interface" and src/greengrid.py.
Modbus I/O is serialized by a lock in modbus_client since the poll loop and the MQTT
command callback run on different threads. Force-writes use FC16 (write_registers_fc16);
FC06 is silently ignored by the storage model. Requires pymodbus 3.6.x (slave= kwarg).
Data flow:
- Inverter → (Modbus TCP read) →
modbus_client→ scale factor applied →mqtt_client→ MQTT broker → Home Assistant - Home Assistant → MQTT command topic →
mqtt_client→main.handle_command→modbus_client→ (Modbus TCP write) → Inverter
- Register addresses are absolute (40001-based, matching Fronius documentation). Fronius Gen24 uses raw Modbus addresses, so the pymodbus offset is computed via
Register.addressproperty (start - 1). - Scale factors are read once at startup, then refreshed every 60 poll cycles. Many are "auto-scaled" by the inverter and can change.
- Two register maps: non-storage (MPPT with 2 PV modules) and storage (MPPT with 4 modules + storage model 124). Controlled by
HAS_STORAGEenv var. - MQTT topics: state at
fronius/<model>/state(JSON), commands atfronius/command/<register_name>(single value). - HA Discovery: sensors for read-only registers,
number/selectentities for writable registers. Device grouping by inverter serial number.
| Model | ID | Addr Range | Purpose |
|---|---|---|---|
| Common (1) | 1 | 40003-40069 | Device info (manufacturer, model, serial) |
| Inverter (10x) | 101/103 | 40070-40121 | AC/DC measurements, operating state |
| Nameplate (120) | 120 | 40122-40149 | Device ratings |
| Settings (121) | 121 | 40150-40181 | Configuration (read-only) |
| Status (122) | 122 | 40182-40227 | Connection status, lifetime energy |
| Controls (123) | 123 | 40228-40253 | Power limit, PF, VAR control (R/W) |
| MPPT (160) | 160 | 40254-40303/40343 | PV string data + battery channels |
| Storage (124) | 124 | 40344-40369 | Battery control (charge/discharge, SoC) |
Key controls: conn (connect/disconnect), w_max_lim_pct + w_max_lim_ena (power limiting), out_pf_set + out_pf_set_ena (power factor), var_max_pct + var_pct_ena (reactive power).
Key controls: storage_ctrl_mode (bit 0=charge, bit 1=discharge), storage_out_w_rte / storage_in_w_rte (charge/discharge rate %), storage_min_rsv_pct (min battery reserve), storage_cha_gri_set (grid charging enable).