This is a custom Home Assistant integration for intelligent control of multiple separate battery storage systems. It was originally designed for Marstek Venus E systems, but it can be used with any battery system that can be controlled via corresponding entities in Home Assistant.
The integration optimizes energy usage by managing multiple batteries as a unified, intelligent storage pool. Instead of simply turning batteries on or off, it uses advanced control theory (PID & Feed-Forward) and dynamic staging to maximize efficiency, protect battery health, and achieve near-zero grid balance.
- Flexible number of batteries: Control any number of batteries (fully scalable via the UI).
- Dynamic Power level switching: Uses one, two, or more batteries depending on demand or surplus. Configurable thresholds determine when to add or remove a battery from the active pool.
- Smart SoC Prioritization: Automatically rotates battery usage. When charging, the emptiest battery is prioritized to ensure even wear; when discharging, the fullest battery is used first.
- Explicit Entity Selection: No strict naming conventions required. Select the exact entities for each battery directly via Home Assistant dropdown menus.
- Grid power smoothing: Prevents rapid switching by averaging grid power over a configurable time window.
- Optional wallbox integration: Smart pausing of battery charging during high PV surplus. Charging resumes when the car is full or charging at maximum power to avoid wasting energy.
- Configurable limits: Set upper and lower SoC limits to protect battery lifespan.
- Minimum charge/discharge power: Configurable thresholds that define from which surplus/consumption the batteries start charging/discharging to improve efficiency.
- Easy configuration: Fully configurable via the Home Assistant UI config flow.
- PID control: Optional PID control for precise power regulation.
- Feed-Forward Control: Pre-emptively reacts to household load changes before the grid sensor even registers a significant error, leading to much faster response times.
- Service call caching: Prevents sending the same Home Assistant service call (same entity + same value) too frequently.
- Event-driven control loop: The coordinator runs on relevant sensor updates (instead of a fixed polling loop) and is throttled by a configurable minimum interval.
- Grid Export Prevention: Proactively limits battery discharge if the system detects that it would result in exporting battery energy to the grid.
- PV-based charge limiting (optional): An optional PV power sensor can be configured to cap commanded charging power to current PV production to avoid charging from the grid due to short sensor glitches.
- Max charge and discharge power per battery: Set the max charge and discharge rate per battery.
If you find this integration helpful and want to support its further development, I'd highly appreciate a coffee! ☕
This integration does not control the batteries directly via a vendor-specific API. Instead, you must already have entities in Home Assistant for each battery in order to:
- Read the state of charge (SoC) (e.g.,
sensor.marstek_l1_battery_soc). - Read the current AC power (e.g.,
sensor.marstek_l1_ac_power). A positive value means discharging, a negative value means charging. - Control the charge power value (e.g.,
number.marstek_l1_modbus_set_forcible_charge_power). - Control the discharge power value (e.g.,
number.marstek_l1_modbus_set_forcible_discharge_power). - Set the Force Mode to control current direction (e.g.,
select.marstek_l1_modbus_force_mode). - Toggle the RS485 Control Mode (e.g.,
switch.marstek_l1_modbus_rs485_control_mode). - Max charge and discharge power: If your batteries need different max discharge and charge rates. You can define those per battery.
- You can define the max State of Charge for the whole integration or optional select the Max SoC Entity per battery(e.g.,
sensor.marstek_l1_battery_max_soc).
During configuration, you will first choose how many batteries you want to control. In the next step, the UI will present you with dropdown menus to explicitly select these 6 entities for every single battery. You have to select them manually. That way, there is no strict naming convention you need to follow.
As a basis for integrating a Marstek energy storage system, the Modbus integration from https://github.com/ViperRNMC/marstek_venus_modbus was used. Depending on the use case it may be useful to reduce the scan intervals in the Modbus integration settings.
- Add this GitHub repository to HACS as a "Custom repository".
- Search for "Marstek Venus HA" and install the integration.
- Restart Home Assistant.
- Download the folder
custom_components/marstek_venus_hafrom this repository. - Copy it into the
custom_componentsdirectory of your Home Assistant installation. - Restart Home Assistant.
After installation you can add the integration via the Home Assistant UI:
- Go to Settings > Devices & Services.
- Click Add integration and search for "Marstek Venus HA".
- Follow the configuration dialog. Fields for the wallbox or optional sensors can be left empty to disable the corresponding functionality.
| Parameter | Description | Example |
|---|---|---|
| Number of batteries | Choose how many battery systems you want to control. | 6 |
| CT Mode | When CT mode is enabled, power regulation by the script is disabled and relies on the default Marstek logic. Only the wallbox logic remains active to override the batteries when the car is charging. | False |
| Grid power sensor | The sensor ID that measures current grid import (+) or export (-) in watts. | sensor.power_meter_power |
| PV power sensor (optional) | The sensor ID that measures current PV production power in watts. Used to cap battery charging power to avoid grid import glitches. | sensor.pv_power |
| Smoothing window | Time window (seconds) used to compute the average grid power. Set to 0 to disable smoothing. | 0 |
| Minimum surplus | Minimum power surplus in watts required to start charging. | 200 |
| Minimum consumption | Minimum consumption in watts required to start discharging. | 200 |
| Max. limit breaches | Max consecutive cycles below minimum limits before setting batteries to 0W. | 10 |
| Battery X: [Entity Type] | Dropdown fields to select the specific AC Power, SOC, Charge, Discharge, Force Mode, and RS485 entities for each battery. | (Selected via UI) |
| Minimum state of charge (%) | Batteries will no longer discharge once their SoC reaches this value. | 10 |
| Maximum state of charge (%) | Batteries will no longer charge once their SoC reaches this value. | 100 |
| Max Discharge Power (W) | Maximum discharge power sent to a single battery. | 2500 |
| Max Charge Power (W) | Maximum charge power sent to a single battery. | 2500 |
| Power threshold: X to Y batteries | Absolute power threshold (W) at which the system steps up to use more batteries. | 1500 |
| Power stage offset / hysteresis | Offset used to switch power levels with less toggling (e.g., jump to 2 batteries at Threshold + Offset, drop to 1 at Threshold - Offset). | 300 |
| Priority interval | Interval in minutes at which battery priorities are re-evaluated based on SoC. | 15 |
| Wallbox power sensor (optional) | Sensor that measures wallbox charging power. | sensor.wallbox_power |
| Wallbox minimum surplus (W) | If PV surplus exceeds this value, battery charging is paused for car charging. | 1500 |
| Wallbox cable connected (optional) | A binary sensor (on/off) that indicates whether a charging cable is connected. |
binary_sensor.wallbox_cable |
| Wallbox stability settings | Various thresholds and delays to determine if the wallbox power is stable enough to allow home batteries to resume charging. | (See UI for details) |
| Coordinator update interval | Minimum seconds between executions of the logic update cycle. | 3 |
| Service call cache TTL | Seconds to cache identical service calls to prevent spamming the battery API. | 30 |
| PID control enabled | Enables PID-based power control to continuously adjust battery power to drive the real grid power towards 0W. |
False |
You can configure up to 5 charge-level caps and 5 discharge-level caps to limit the maximum power commanded to each battery depending on its State of Charge (SoC). This helps protect battery lifetime by reducing charge/discharge currents near the top and bottom of the SoC range.
Default example values (configurable via the integration options):
-
Charge caps (applied when charging, checked from highest SoC down):
Level 1(SOC >= 98%): 1500 WLevel 2(SOC >= 95%): 1800 WLevel 3(SOC >= 91%): 2000 WLevel 4(SOC >= 86%): 2200 WLevel 5(SOC >= 80%): 2400 W
-
Discharge caps (applied when discharging, checked from lowest SoC up):
Level 1(SOC <= 13%): 1500 WLevel 2(SOC <= 15%): 1800 WLevel 3(SOC <= 19%): 2000 WLevel 4(SOC <= 25%): 2200 WLevel 5(SOC <= 30%): 2400 W
How these caps are used: During distribution, the coordinator computes a per-battery cap from these values based on each battery's current SoC. The requested power is allocated among the active batteries respecting these caps. If a battery reaches its absolute SoC limit, the priority list is immediately recalculated and the next available battery takes over.
The PID (Proportional-Integral-Derivative) controller is the "brain" of the regulation. It continuously calculates the difference between your current grid power and the target (0W) and adjusts the battery output accordingly.
- When you have PV surplus (grid export), the controller will increase charging power.
- When you have grid import, the controller will increase discharging power.
- Anti-Windup Logic: The controller is aware of physical limits (like max battery power or current PV production). If a limit is reached, the "Integral" part stops accumulating to prevent massive overshooting when the situation changes (e.g., when a cloud passes).
- Feed-Forward (Disturbance Rejection): This feature allows the system to "anticipate" the needed power. By looking at the raw house load, the controller can instantly adjust the battery output by a percentage of the load change (controlled by the
Gainvalue), leaving the PID to only clean up the remaining small error.
- Start with
Kd = 0. - Set
Feed-Forward Gainto approx0.8. This covers 80% of any load jump immediately. - Increase
Kp(e.g.,0.2to0.5) until the response is fast but not oscillating. - Add a small
Ki(e.g.,0.01to0.05) to remove the remaining permanent offset from 0W.
To ensure you never "waste" battery energy by sending it back to the public grid, the integration includes a safety guard. It monitors the raw grid data and caps the discharge power if it would push the grid balance into negative (export) territory. This is especially useful during rapid load drops (e.g., when a stove turns off).
- Discharging: The battery with the highest SoC is used first. This ensures that the system always tries to utilize the most "full" energy source.
- Charging: The battery with the lowest SoC is charged first. This levels the SoC across all batteries over time.
- A battery is removed from the priority list when it reaches its upper/lower SoC limit.
The absolute grid power determines the number of active batteries. Instead of constantly turning batteries on and off, the integration uses a dynamic offset (hysteresis) based on your configured powerstage_X_to_Y thresholds:
- Step Up: If the requested power is greater than
Threshold + Offset, an additional battery is activated. - Step Down: If the requested power is less than
Threshold - Offset, a battery is deactivated.
- Discharge protection: As soon as the wallbox draws power, discharging of all batteries is stopped immediately to prevent the home battery from draining into the car.
- Charging priority for the car: If the real PV surplus exceeds the configured threshold, home battery charging is paused to prioritize the EV.
- Intelligent charge resume: Battery charging is released again when the EV stops drawing fluctuating power (e.g., because the car is full or has hit its max charging speed). Discharging remains blocked as long as the wallbox is charging.
The integration provides dedicated switches in Home Assistant to manually override behaviors:
- Control Batteries (
switch.*._control_switch): Master switch to enable/disable all battery control logic. When disabled, all batteries are set to 0W and the integration becomes idle. - Charging Allowed (
switch.*._charging_switch): Manually allow/block the batteries from charging. - Discharging Allowed (
switch.*._discharging_switch): Manually allow/block the batteries from discharging into the house. - Wallbox Priority (
switch.*._wallbox_priority_switch): Enable/disable the logic that prioritizes the EV over home batteries. - Block battery discharge while car charging (
switch.*._discharge_blocker_cc_switch): Standard protection. If disabled, your home batteries are allowed to discharge into your EV.
