Automatically curtail solar production when your battery is full and feed-in prices are zero or negative, preventing you from paying to export electricity to the grid.
- Price-Aware Curtailment: Automatically shuts down solar when exporting at zero/negative prices
- Battery Protection: Only curtails when Powerwall/battery is ≥99% full
- Smart Restart: Automatically resumes when:
- Battery needs charging
- Home is consuming power
- Feed-in prices become profitable
- Manual Override: Dashboard controls for manual start/stop
- Configurable Thresholds: Adjust price and battery thresholds via UI
- Safety Features: Always re-enables at sunset for next day
- Notifications: Alerts when curtailment occurs
- Sungrow SG5.0RS string inverter (or compatible SG-series model)
- WiNet-S WiFi/Ethernet dongle (or direct LAN connection)
- Home Assistant instance
- Battery system with HA integration (Tesla Powerwall, etc.)
- Dynamic pricing sensor (Amber Electric, etc.)
- Home Assistant 2024.1 or newer
- Modbus integration (built-in)
- Teslemetry Integration - For Tesla Powerwall battery data
- Amber Electric Integration - For dynamic pricing data (Australian users)
Note: You can use alternative battery and pricing integrations - see docs/INTEGRATIONS.md for details.
# Clone this repository
git clone https://github.com/Artic0din/sungrow-sg5-price-curtailment.git
cd sungrow-sg5-price-curtailment
# Copy files to Home Assistant config
cp config/packages/sungrow_control.yaml /config/packages/
cp config/integrations/modbus_sungrow.yaml /config/integrations/
cp config/sungrow_customize.yaml /config/Add to /config/secrets.yaml:
sungrow_modbus_host_ip: 192.168.x.x # Your inverter IP
sungrow_modbus_port: 502
sungrow_modbus_slave: 1Add to /config/configuration.yaml:
homeassistant:
packages:
sungrow: !include packages/sungrow_control.yaml
customize: !include sungrow_customize.yaml
modbus: !include integrations/modbus_sungrow.yamlEdit /config/packages/sungrow_control.yaml and update these sensor names to match your system:
# Line ~110: Update to your battery sensor
sensor.powerwall_percentage_charged
# Line ~115: Update to your feed-in price sensor
sensor.sandhurst_estate_feed_in_priceha core restartConfigure via UI in Settings → Helpers or directly in your dashboard:
| Setting | Default | Description |
|---|---|---|
| Curtail Below Price | $0.00/kWh | Shutdown when feed-in price ≤ this value |
| Restart Above Price | $0.05/kWh | Resume when feed-in price > this value |
| Battery Threshold | 99% | Only curtail when battery ≥ this level |
Curtail Threshold ($0.00/kWh):
- Set to
0.00to curtail during zero and negative pricing - Set to
0.05to curtail when price is below 5 cents - Set to
-0.10to only curtail when you'd pay more than 10c to export
Restart Threshold ($0.05/kWh):
- Should be HIGHER than curtail threshold to prevent flapping
- Set based on when exporting becomes profitable for you
- Consider your import tariff when setting this
Scenario 1: Negative Pricing Event
Time: 2:00 PM
Feed-in: -$0.05/kWh (you pay to export!)
Battery: 100%
Export: 2000W
→ 2:03 PM: Solar shuts down automatically
→ Saves you ~$0.10/hour
→ 3:00 PM: Price returns to +$0.10/kWh
→ 3:02 PM: Solar restarts automatically
Scenario 2: Zero Feed-In
Time: 11:00 AM
Feed-in: $0.00/kWh (no payment)
Battery: 99%
Export: 1500W
→ 11:03 AM: Solar shuts down
→ Reduces inverter wear, extends equipment life
→ 11:45 AM: Battery drops to 94%
→ 11:47 AM: Solar restarts (battery needs charge)
Scenario 3: High Peak Pricing
Time: 5:00 PM
Feed-in: $0.25/kWh (peak rate)
Battery: 100%
Export: 3000W
→ Solar STAYS ON (above restart threshold)
→ Earns ~$0.75/hour from export
Dashboard Controls:
input_select.set_sg_inverter_run_mode- Manual start/stopinput_boolean.sungrow_curtailment_enabled- Enable/disable automation
Via UI:
- Go to your dashboard
- Find "Sungrow Inverter Run Mode" dropdown
- Select "Shutdown" or "Enabled"
Check Current Status:
- Developer Tools → States - Search "inverter" to see all sensors
- Settings → Automations → Traces - View automation execution history
- Notifications - Receive alerts when curtailment occurs
Key Sensors:
sensor.inverter_total_dc_power- Current solar productionsensor.inverter_run_mode- Current inverter statesensor.inverter_work_state- Detailed inverter statusbinary_sensor.pv_generating- Solar generation indicator
View detailed execution logs:
- Go to Settings → Automations & Scenes
- Click on "Sungrow: Shutdown when Powerwall full..."
- Click ⋮ → Traces
- See exact conditions and actions taken
Add these to your Lovelace dashboard:
# Manual Control Card
type: entities
title: Solar Control
entities:
- entity: input_select.set_sg_inverter_run_mode
name: Inverter Mode
- entity: input_boolean.sungrow_curtailment_enabled
name: Auto Curtailment
- entity: sensor.inverter_run_mode
name: Current State
- entity: sensor.inverter_total_dc_power
name: Solar Production
# Threshold Settings Card
type: entities
title: Curtailment Settings
entities:
- entity: input_number.sungrow_curtailment_price_threshold_low
name: Shutdown Price
- entity: input_number.sungrow_restart_price_threshold_high
name: Restart Price
- entity: input_number.sungrow_curtailment_powerwall_threshold
name: Battery Threshold
# Current Conditions Card
type: entities
title: Current Conditions
entities:
- entity: sensor.powerwall_percentage_charged
name: Battery Level
- entity: sensor.sandhurst_estate_feed_in_price
name: Feed-in Price
- entity: sensor.inverter_meter_power
name: Grid Power
- entity: binary_sensor.pv_generating
name: GeneratingSee examples/dashboard.yaml for a complete dashboard configuration.
Check conditions:
input_boolean.sungrow_curtailment_enabledis ON- All sensors are available (not "unknown" or "unavailable")
- Conditions sustained for full 3 minutes
- Currently daytime (between sunrise/sunset)
Debug:
- Go to automation → ⋮ → Traces to see why it didn't fire
- Check template conditions in Developer Tools → Template
Verify modbus connection:
- Developer Tools → Services
- Test direct modbus write:
service: modbus.write_register data: hub: SungrowSHx slave: 1 address: 5005 value: 206
- Check if
sensor.inverter_total_dc_powerdrops to 0W
Common issues:
- WiNet-S IP address changed (check router DHCP)
- Modbus slave ID incorrect (should be 1)
- Firewall blocking port 502
- WiNet-S requires write-enable setting (check web interface)
Check sensor names match:
- Open
/config/packages/sungrow_control.yaml - Find the
template:section - Verify all sensor names exist in Developer Tools → States
Common mismatches:
sensor.total_dc_powervssensor.inverter_total_dc_powersensor.meter_powervssensor.inverter_meter_power
Fix:
- Edit template sensor names to match your actual entities
- Reload: Developer Tools → YAML → Template Entities
Cause: Multiple modbus configurations loaded
Fix:
- Search
/config/for othermodbus_sungrow*.yamlfiles - Check Settings → Devices & Services for old Sungrow integration
- Remove duplicates, restart Home Assistant
- Delete unavailable entities: Settings → Entities → Filter: unavailable
- Modbus TCP has no authentication - ensure WiNet-S is on a trusted network
- Consider firewall rules to restrict modbus access
- Use Home Assistant's built-in authentication for dashboard access
- Keep Home Assistant updated for security patches
┌─────────────────────────────────────────────────────────┐
│ Home Assistant │
│ │
│ ┌──────────────┐ ┌───────────────┐ │
│ │ Dashboard │───▶│ Automations │ │
│ │ Controls │ │ & Logic │ │
│ └──────────────┘ └───────┬───────┘ │
│ │ │
│ ┌──────────────┐ ┌───────▼───────┐ │
│ │ Sensors │◀───│ Modbus TCP │ │
│ │ (battery, │ │ Integration │ │
│ │ pricing) │ └───────┬───────┘ │
│ └──────────────┘ │ │
└──────────────────────────────┼──────────────────────────┘
│ Port 502
┌──────▼──────┐
│ WiNet-S │
│ Dongle │
└──────┬──────┘
│ RS485
┌──────▼──────┐
│ Sungrow │
│ SG5.0RS │
│ Inverter │
└─────────────┘
-
Monitoring Loop (every 30 seconds):
- Read modbus sensors from inverter
- Check battery level
- Check feed-in price
- Evaluate curtailment conditions
-
Curtailment Decision (when conditions met for 3 minutes):
- Battery ≥ threshold
- Exporting > 100W
- Price ≤ threshold
- Set
input_selectto "Shutdown"
-
Modbus Write (triggered by input_select change):
- Write value
206to register5005 - Inverter receives shutdown command
- Solar production stops
- Write value
-
Restart Decision (when any condition met for 1-2 minutes):
- Battery < 95%, OR
- Not exporting, OR
- Importing from grid, OR
- Price > restart threshold
- Set
input_selectto "Enabled"
-
Modbus Write (re-enable):
- Write value
207to register5005 - Inverter receives enable command
- Solar production resumes
- Write value
| Register | Address | Function | Write Values |
|---|---|---|---|
| 5006 | 5005 | Run Mode Control | 206=Shutdown, 207=Enabled |
| 5007 | 5006 | Power Limit Toggle | 85=Disabled, 170=Enabled |
| 5008 | 5007 | Power Limit % | 0-1000 (×0.1%) |
See docs/MODBUS_REGISTERS.md for complete register map.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- mkaiser's Sungrow Integration - Modbus register documentation
- SunGather Project - Register definitions
- Sungrow official documentation - Communication protocols
- Home Assistant community
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Home Assistant Community: Forum Thread
- Support for additional Sungrow models (SG3.0RS, SG6.0RS)
- Historical analytics dashboard
- Mobile app integration
- Multi-inverter support
This software controls your solar inverter. While tested and working, use at your own risk. Always monitor the system for the first few days after installation. Not affiliated with Sungrow.
Made with ☀️ for the Home Assistant community
Star this repo if it saves you money! ⭐