balloob/fronius-modbus

Async Python library for the Modbus TCP (SunSpec) interface of Fronius inverters, built on modbus-connection

★ 0Forks 0GitHub ↗Compare

README

fronius-modbus

Async Python library for the Modbus TCP (SunSpec) interface of Fronius inverters, built on modbus-connection.

Warning

Alpha version — mostly untested. So far this has been verified against a single Symo GEN24 10.0 Plus (firmware 1.40.9-1) and a SnapINverter Symo behind a Datamanager 2.0. Write support (power limiting, battery controls) is only tested against mocks. Expect breaking API changes. Use at your own risk — especially the write operations, which change inverter behavior. Feedback and test reports are very welcome!

Supports both Fronius device generations:

  • GEN24 / Tauro — the inverter itself serves Modbus TCP (unit ID 1)
  • SnapINverter via Datamanager 2.0 — the Datamanager gateways all inverters of a Fronius Solar Net ring (unit ID = inverter number, 00 → 100)

The SunSpec register map of Fronius devices is dynamic: model addresses shift with the configured data type (float vs int + SF) and differ between generations. This library discovers the model chain at runtime, so no register configuration is needed — and the data type setting is detected automatically.

Currently implemented (read-only):

  • SunSpec Common Model (1): manufacturer, model, software version and serial number.
  • SunSpec inverter models (101-103 / 111-113) in both encodings: AC power, frequency, lifetime energy, per-phase currents and voltages, apparent/reactive power, power factor, DC totals, operating state and vendor state, event flags.
  • SunSpec Multiple MPPT Inverter Extension Model (160): DC current, voltage, power and lifetime energy per MPP tracker, with module role classification (PV string / storage charge / storage discharge) and derived totals (PV-only energy, battery charging/discharging energy).
  • SunSpec Basic Storage Control Model (124), read-only values: state of charge, battery status, nominal charge/discharge power reference.

Usage

The library only consumes a ModbusUnit — connection lifecycle stays with the caller (or with Home Assistant's modbus_connection integration):

import asyncio

from fronius_modbus import FroniusModbusInverter, GEN24_UNIT_ID
from modbus_connection.tmodbus import connect_tcp


async def main() -> None:
    connection = await connect_tcp("192.168.1.50", port=502)
    inverter = FroniusModbusInverter(connection.for_unit(GEN24_UNIT_ID))
    await inverter.discover()
    print("Data type:", "float" if inverter.float_mode else "int+SF")
    print("Storage:", inverter.has_storage)  # auto-detected during discovery

    identity = await inverter.read_device_identity()
    print(identity.manufacturer, identity.model, identity.serial_number)

    ac_dc = await inverter.read_inverter()
    print("AC power:", ac_dc.ac_power, "state:", ac_dc.operating_state)

    if inverter.has_mppt:
        data = await inverter.read_mppt()
        for module in data.modules:
            print(module.index, module.id_str, module.role, module.power)
        print("PV energy total:", data.pv_energy_total)
        print("Battery charged:", data.storage_charge_energy_total)
        print("Battery discharged:", data.storage_discharge_energy_total)

    if inverter.has_storage_model:
        storage = await inverter.read_storage()
        print("SoC:", storage.state_of_charge, "state:", storage.state)

    await connection.close()


asyncio.run(main())

Modbus must be enabled on the inverter web interface (GEN24: Communication → Modbus → Slave as Modbus TCP; Datamanager: Settings → Modbus → Data output via Modbus → TCP).

Testing on real hardware

Two scripts in scripts/ help test the library against an inverter (clone the repo and run them with uv):

read_inverter.py — a one-shot dump of everything the library reads, plus optional write commands. Good for quick checks and scripting:

uv run scripts/read_inverter.py <inverter-ip>
uv run scripts/read_inverter.py <inverter-ip> --set-power-limit 80 --revert 60
uv run scripts/read_inverter.py <inverter-ip> --probe-write

monitor.py — an interactive terminal UI that polls live at a configurable interval. It shows the data panels and a status log, a live power graph with a selectable series list (AC power, per-MPPT and battery charge/discharge), and a write-command dialog (press s) for running every write command with immediate status feedback:

uv run scripts/monitor.py <inverter-ip> --interval 2

Write commands require "inverter control via Modbus" to be enabled on the device web interface. Limit writes default to a 60 second auto-revert as a safety net.

Testing support

fronius_modbus.testing provides build_sunspec_map() to build SunSpec register maps for modbus_connection.mock.MockModbusUnit:

from fronius_modbus.testing import MpptModuleSpec, build_sunspec_map
from modbus_connection.mock import MockModbusConnection

connection = MockModbusConnection()
connection.for_unit(1).holding.update(
    build_sunspec_map(
        [MpptModuleSpec(id_str="String 1", current=82, voltage=4021, power=3300)],
        float_mode=True,
    )
)

Disclaimer

This is an unofficial library, not affiliated with Fronius International GmbH. Based on the public Fronius Modbus TCP & RTU operating instructions and the Fronius SunSpec register maps (fronius.com/QR-link/0006).

Contributors

farmio

Issues