If you use solar inverters, heat pumps, and energy meters, then Modbus is required in your homelab. It works, but to get basic stats you need to write custom YAML and hunt for hex addresses. This is the main reason Modbus is a headache for many homelabbers. The Home Assistant 2026.9 update fixes this. They have introduced "device-aware" Modbus integrations.
The first major integration to use this new architecture is Fronius, which now gets optional Modbus TCP support.
I don't have a Fronius inverter on my network, but I wanted to see exactly how this works under the hood. So, I spun up my 2026.9.4 Docker container, built a virtual GEN24 inverter that supports the Fronius Solar API and SunSpec Modbus, and wired it up. Here I found all the details for controlling hardware through Home Assistant, which I have included in my article.
Why the Old Way Was Broken
The classic modbus: YAML integration forces you to define every single register manually.
modbus:
- name: my_inverter
type: tcp
host: 192.168.1.50
port: 502
sensors:
- name: "Solar Power"
address: 40083
data_type: float32
scan_interval: 10
If you spread it across too many strings, phases, and scale factors, then your configuration.yaml file turns into a mess.
The biggest problem is connection limits. Many inverters restrict you to a single Modbus session. If you have custom YAML sensors reading data and a separate vendor integration trying to write controls, they fight over that single socket, causing timeouts and dropped data.
The New Architecture: Connection Sharing
Home Assistant 2026.9 resolves the session limit issue by using the Modbus integration as a broker.
It depends on two parts: tmodbus and modbus-connection.
When the Fronius integration needs Modbus data, it calls async_get_unit() and requests a shared connection. Home Assistant manages the socket. If another integration already has the device locked with different connection settings, then Fronius logs the conflict and gracefully skips Modbus, falling back to HTTP data, rather than breaking your inverter's connection limit.
How Fronius Modbus TCP Works in Home Assistant
Modbus does not replace the HTTP integration for Fronius; it runs alongside it.
Here is the setup flow:
- You configure the host and Modbus port (default 502) in the UI.
- Home Assistant validates the host via the HTTP Solar API (using GetLoggerInfo or GetInverterInfo).
- Home Assistant discovers the inverters via HTTP, then calls _init_modbus_inverter() for each one.
- It assigns a unit ID. GEN24 models always use unit ID 1. Older Datamanager setups map the SolarNet ID.
- The fronius-modbus library scans the SunSpec register map starting at base address 40000, looking for the SunS marker.
- It builds Home Assistant entities for every supported SunSpec model it finds.
Importantly, this library re-verifies the model headers on every poll. If a firmware update changes your register map, the library detects it, throws a SunSpecMapShiftError, and instead of silently logging garbage data, it rediscovers the layout.
These are the specific SunSpec models the 0.2.0 library looks for:
| Model | Function |
|---|---|
| 1 | Common (Manufacturer, model, serial) |
| 111-113 | Inverter (Float mode) |
| 101-103 | Inverter (Integer + scale factor mode) |
| 123 | Immediate Controls (AC power limits) |
| 160 | Multiple MPPT (Per-string DC data) |
| 124 | Basic Storage Control (Battery limits) |
What You Actually Get
Once configuration is done, the integration exposes data that the HTTP API simply does not provide.
New Modbus Sensors:
- MPPT 1 / 2 DC Power
- MPPT 1 / 2 Energy (Lifetime total per string)
- PV Energy Total (Aggregate of all strings)
- Per-string Voltage and Current (These are disabled by default in Home Assistant to reduce clutter, but you can enable them in the device page).
New Modbus Controls:
- AC Power Limit (Slider percentage) and AC Power Limiting (Toggle)
- Battery Charge/Discharge Power Limits (Sliders) and toggles
- Battery Grid Charging toggle
- Battery Minimum Reserve
This means you can natively throttle your inverter export during negative pricing events, or force battery charging during off-peak hours, directly from Home Assistant without custom scripts.
Setting Up the Test Environment
To prove this works without physical hardware, I set up an Ubuntu server running Docker, locked down behind UFW.
I spun up Home Assistant 2026.9.4.
mkdir -p ~/homeassistant/config && cat > ~/homeassistant/compose.yaml <<'EOF'
services:
homeassistant:
container_name: homeassistant
image: ghcr.io/home-assistant/home-assistant:stable
volumes:
- ./config:/config
- /etc/localtime:/etc/localtime:ro
environment:
- TZ=Asia/Kolkata
network_mode: host
restart: unless-stopped
EOF
cd ~/homeassistant && sudo docker compose up -d
sudo ufw allow 8123/tcp comment 'Home Assistant'
I checked the integration source inside the container and found fronius_modbus/testing.py. The developers ship a build_sunspec_map() helper function specifically for generating valid register maps.
Using that, I wrote a Python script to run alongside Home Assistant. It binds to 127.0.0.1 and runs a virtual GEN24 inverter. It serves the Solar API v1 on port 18080 and a SunSpec Modbus TCP server on port 15020. It simulates 2 MPPT strings and a battery, applying a slow sine wave to the power values so I can watch them update in Home Assistant. It also dumps all Modbus WRITE commands to a log.
Verifying the Register Writes
When I added the Fronius integration connected to my local virtual inverter (http://127.0.0.1:18080), it pulled down 47 entities, including the individual MPPT string power sensors. The numbers exactly matched the calculations from my simulator script.
The real test was the write controls. I turned AC power limiting on, set the slider to 54%, and then adjusted the battery charge power limit.
Here is what Home Assistant sent to my simulated inverter over Modbus:
12:34:39 WRITE unit 1 reg 40141 = 1 12:34:43 WRITE unit 1 reg 40221 = 5000 12:37:33 WRITE unit 1 reg 40137 = 5400 12:37:33 WRITE unit 1 reg 40141 = 1 12:37:38 WRITE unit 1 reg 40137 = 5000 12:37:38 WRITE unit 1 reg 40141 = 1 12:37:41 WRITE unit 1 reg 40221 = 0
Mapping those addresses back to the SunSpec layout:
| Register | SunSpec Field | Purpose |
|---|---|---|
| 40137 | WMaxLimPct |
AC Power Limit (5400 = 54.00%) |
| 40141 | WMaxLim_Ena |
AC Power Limit Enable Toggle |
| 40221 | InWRte |
Max Battery Charge Rate (5000 = 50.00%) |
Home Assistant handles the -2 percentage scale factors automatically. You'll notice that adjusting the AC power limit (40137) immediately triggers a second write to the enable toggle (40141). It forces the limit active so things don't get stuck in memory.
Preparing a Real Inverter
If you have physical hardware, you need to enable Modbus TCP. On a GEN24, you do this in the local web interface under Communication > Modbus:
- Set Slave as Modbus TCP
- Set Modbus port to 502
- Set SunSpec Model Type to float or int+SF (Home Assistant handles both)
- Enable Inverter control via Modbus (Required if you want the power/battery sliders to work)
If things stay grayed out in Home Assistant, check your network firewall. If Home Assistant can't hit port 502, it silently reverts to HTTP.
Why this matters
This isn't just a Fronius thing. Sofar Inverter Modbus and Flexit are using this shared-connection setup in 2026.9 too.
Getting granular MPPT data locally means you can ditch the cloud portal to figure out which panel is shaded. Adding native write access for curtailment means your homelab actually controls your energy grid now.