Local Energy Management: Integrating PV Inverters and Battery Storage via Modbus TCP
Part 5 of 5 in the series Home Assistant without the cloud

Contents
Local Energy Management: Integrating PV Inverters and Battery Storage via Modbus TCP
Modern residential and enterprise microgrids require instantaneous energy routing to maximize self-consumption and prevent grid export losses. Relying on cloud-based manufacturer APIs for photovoltaic (PV) inverters and battery storage systems introduces unacceptable latency, rate limits, and external network dependencies. Establishing a robust Local Energy Management System (LEMS) requires querying telemetry directly over the local network using Modbus TCP or SunSpec protocols. Achieving sub-second response times enables precise surplus diversion into domestic hot water (DHW) heating or electric vehicle (EV) charging infrastructure.
1. High-Frequency Telemetry via Modbus TCP and SunSpec Register Maps
Direct local polling of hardware inverters (such as Fronius Symo or hybrid Deye units) and smart bidirectional energy meters eliminates cloud polling restrictions. By addressing standardized holding registers over port 502, an automation controller can read real-time power production, battery state-of-charge (SoC), and net grid exchange at intervals under one second.
- Deterministic Polling Cycles: Querying active power registers via Modbus TCP provides raw 16-bit or 32-bit signed integer values without JSON parsing overhead or TLS handshake delays.
- SunSpec Compliance: Standardized SunSpec register blocks (such as Model 101 or Model 103) allow uniform integration logic across different inverter manufacturers without rewriting low-level register addresses.
2. Automating Excess Solar Utilization: Dynamic Load Shedding and Diversion
Once real-time net grid export is computed locally, surplus energy can be dynamically routed to high-capacity thermal or chemical storage before triggering grid export limiters.
- Proportional DHW Heating: Controlling immersion heaters via thyristor power regulators or smart relays allows modulating water heating power precisely to match available solar excess.
- Dynamic EV Charging Control: Interfacing with OCPP-compliant or Modbus-enabled EV charging stations permits adjusting charging current between 6 A and 16 A per phase dynamically, absorbing excess PV production without drawing from the utility grid.
# Example: Home Assistant Modbus TCP sensor configuration for inverter telemetry
modbus:
- name: "pv_inverter_local"
type: tcp
host: 192.168.10.150
port: 502
sensors:
- name: "Inverter Active Power"
slave: 1
address: 40083
input_type: holding
data_type: int16
unit_of_measurement: "W"
device_class: power
state_class: measurement
scale: 10
3. Ensuring Control Loop Stability
Implementing rapid surplus diversion loops requires careful hysteresis planning. Introducing moving average filters and deadband thresholds prevents rapid cycling of mechanical relays or EV charging contactors during transient cloud cover.
Summary
Local energy management utilizing Modbus TCP guarantees zero-cloud dependency, sub-second telemetry, and maximum PV self-consumption. Combining direct inverter register polling with intelligent load modulation for hot water and electric vehicle charging transforms a standard solar installation into an autonomous, highly efficient microgrid.
Home Assistant without the cloud
- Zigbee2MQTT vs. ZHA: Building a Highly Reliable Mesh Network in Home Assistant
- ESPHome and ESP32 in Practice: Building Cloud-Free Environmental Sensors
- Presence Detection 2.0: mmWave Radars (LD2410/LD2450) in Lighting Automation
- WireGuard & Tailscale DMZ: Secure External Access to Home Assistant and Self-Hosted Servers
- Local Energy Management: Integrating PV Inverters and Battery Storage via Modbus TCP
2 comments
Section 3 is three sentences long and it is the whole game. Our immersion heater cycled every forty seconds under broken cloud until we put a filter in front of it.
What I never resolved: is a moving average sufficient, or does the deadband have to be asymmetric — a different threshold for switching on than for switching off?
Asymmetric, and the reason is that the load changes the very quantity the loop is measuring.
A symmetric band around a single threshold cannot hold, because switching on a two-kilowatt heater removes two kilowatts of export within a second. The condition that turned it on is then immediately false, the loop switches it off, the export returns, and the cycle repeats — the filter only decides how fast. That is the forty-second cycle, and no amount of smoothing removes it.
The rule that does hold: the switch-off threshold sits below the switch-on threshold by at least the power of the load being switched, plus a minimum on-time so that a genuinely brief cloud cannot trigger a full cycle. The moving average smooths the input, the asymmetry accounts for the loop’s own effect on it, and the minimum on-time protects the contactor. Modulating loads — a thyristor regulator rather than a relay — sidestep the problem differently, by not having a discrete step to fall over.