I’ve been running a Growatt SPH inverter with two GBLI 6.5kWh batteries (13kWh total) on Octopus Agile, using Predbat to optimise battery charging during the cheapest half-hour slots. Getting the configuration right took some trial and error, so here’s the complete setup.
The Hardware
- Growatt SPH hybrid inverter
- 2× GBLI 6.5kWh batteries (13kWh total)
- Solar Assistant (for Modbus communication with the inverter)
- Home Assistant with Predbat
Why Predbat?
Octopus Agile prices change every 30 minutes, with prices published day-ahead. Predbat analyses these prices alongside your solar forecast (via Solcast) and historical consumption to determine the optimal times to charge your battery from the grid. On Agile, this can mean charging at 5p/kWh overnight instead of 30p/kWh during peak hours.
Solar Assistant Entities
Solar Assistant exposes your Growatt inverter to Home Assistant via MQTT. The key entities you’ll need are:
Sensors (Read-only)
| Purpose | Entity |
|---|---|
| Battery Power | sensor.growatt_sph_battery_power |
| Battery SoC | sensor.growatt_sph_battery_state_of_charge |
| Battery Voltage | sensor.growatt_sph_battery_voltage |
| Battery Temperature | sensor.growatt_sph_battery_temperature |
| PV Power | sensor.growatt_sph_pv_power |
| Grid Power | sensor.growatt_sph_grid_power |
| Load Power | sensor.growatt_sph_load_power |
| Load Energy Today | sensor.growatt_sph_load_energy |
| Grid Import Today | sensor.growatt_sph_grid_energy_in |
| Grid Export Today | sensor.growatt_sph_grid_energy_out |
| PV Energy Today | sensor.growatt_sph_pv_energy |
Controls (Writable)
| Purpose | Entity |
|---|---|
| Work Mode | select.growatt_sph_work_mode_priority |
| Charge Rate % | number.growatt_sph_battery_first_charge_rate |
| Discharge Rate % | number.growatt_sph_grid_first_discharge_rate |
| Charge Target SoC | number.growatt_sph_battery_first_stop_charge |
| Discharge Stop SoC | number.growatt_sph_grid_first_stop_discharge |
| Reserve SoC | number.growatt_sph_load_first_stop_discharge |
| Charge Slot 1 Start | select.growatt_sph_battery_first_slot_1_start |
| Charge Slot 1 End | select.growatt_sph_battery_first_slot_1_end |
| Charge Slot Enable | switch.growatt_sph_battery_first_slot_1_enabled |
The apps.yaml Configuration
Here’s the complete Predbat configuration for this setup:
pred_bat:
module: predbat
class: PredBat
prefix: predbat
timezone: Europe/London
currency_symbols:
- '£'
- 'p'
threads: auto
# ===========================================
# INVERTER CONFIGURATION
# ===========================================
inverter_type: "SA"
num_inverters: 1
# Battery capacity (2x GBLI 6.5kWh = 13kWh)
soc_max:
- 13
# Max charge/discharge rate in Watts
battery_rate_max:
- 3600
# Inverter limits
inverter_limit: 3600
inverter_limit_charge:
- 3600
inverter_limit_discharge:
- 3600
# ===========================================
# ENERGY SENSORS (Daily totals)
# ===========================================
load_today:
- sensor.growatt_sph_load_energy
import_today:
- sensor.growatt_sph_grid_energy_in
export_today:
- sensor.growatt_sph_grid_energy_out
pv_today:
- sensor.growatt_sph_pv_energy
# ===========================================
# POWER SENSORS (Real-time)
# ===========================================
battery_power:
- sensor.growatt_sph_battery_power
battery_voltage:
- sensor.growatt_sph_battery_voltage
battery_temperature:
- sensor.growatt_sph_battery_temperature
soc_percent:
- sensor.growatt_sph_battery_state_of_charge
pv_power:
- sensor.growatt_sph_pv_power
load_power:
- sensor.growatt_sph_load_power
grid_power:
- sensor.growatt_sph_grid_power
# ===========================================
# GRID POWER DIRECTION
# ===========================================
# IMPORTANT: Check which way your sensor reports!
# Predbat expects: negative = importing, positive = exporting
# My Growatt reports positive when importing, so invert it
grid_power_invert:
- True
# ===========================================
# CHARGE/DISCHARGE RATE CONTROL
# ===========================================
charge_rate_percent:
- number.growatt_sph_battery_first_charge_rate
discharge_rate_percent:
- number.growatt_sph_grid_first_discharge_rate
# ===========================================
# SOC LIMITS
# ===========================================
reserve:
- number.growatt_sph_load_first_stop_discharge
battery_min_soc:
- number.growatt_sph_load_first_stop_discharge
charge_limit:
- number.growatt_sph_battery_first_stop_charge
discharge_target_soc:
- number.growatt_sph_grid_first_stop_discharge
# ===========================================
# TIME SLOT CONTROL
# ===========================================
charge_start_time:
- select.growatt_sph_battery_first_slot_1_start
charge_end_time:
- select.growatt_sph_battery_first_slot_1_end
scheduled_charge_enable:
- switch.growatt_sph_battery_first_slot_1_enabled
# ===========================================
# SERVICE CALLS FOR MODE SWITCHING
# ===========================================
charge_start_service:
service: select.select_option
entity_id: select.growatt_sph_work_mode_priority
option: "Battery first"
charge_stop_service:
service: select.select_option
entity_id: select.growatt_sph_work_mode_priority
option: "Load first"
discharge_start_service:
service: select.select_option
entity_id: select.growatt_sph_work_mode_priority
option: "Grid first"
discharge_stop_service:
service: select.select_option
entity_id: select.growatt_sph_work_mode_priority
option: "Load first"
# ===========================================
# INVERTER TIME
# ===========================================
inverter_time:
- sensor.date_time
# ===========================================
# HISTORICAL DATA
# ===========================================
days_previous:
- 7
- 14
- 21
days_previous_weight:
- 1.0
- 0.7
- 0.5
# ===========================================
# OCTOPUS AGILE
# ===========================================
metric_octopus_import: "re:sensor.octopus_energy_electricity_.*_current_rate"
metric_octopus_export: "re:sensor.octopus_energy_electricity_.*_export.*_current_rate"
octopus_intelligent_slot: "off"
# ===========================================
# FORECAST SETTINGS
# ===========================================
# forecast_hours: how far ahead Predbat models solar/load predictions
# Keep at 48 for good solar and load forecasting
forecast_hours: 48
# forecast_plan_hours: how far ahead Predbat plans charging slots
# Set to 30 for Agile - prices are released at ~16:30 for 23:00 that day
# through 23:00 next day, giving ~30 hours of known prices
forecast_plan_hours: 30
# ===========================================
# SOLCAST
# ===========================================
pv_forecast_today: "re:sensor.solcast_pv_forecast_forecast_today"
pv_forecast_tomorrow: "re:sensor.solcast_pv_forecast_forecast_tomorrow"
pv_forecast_d3: "re:sensor.solcast_pv_forecast_forecast_day_3"
pv_forecast_d4: "re:sensor.solcast_pv_forecast_forecast_day_4"
# ===========================================
# NO EV (covered in separate post)
# ===========================================
num_cars: 0
Key Configuration Decisions
Grid Power Inversion
This caught me out initially. Predbat expects grid power to be negative when importing and positive when exporting. My Growatt via Solar Assistant reports it the opposite way (positive = importing), so grid_power_invert: True is essential. Check your own sensor in Developer Tools → States while the grid is importing to see which way yours reports.
Forecast Hours vs Forecast Plan Hours
These are two different settings that confused me initially:
forecast_hours: 48- How far ahead Predbat models your solar production and household load. Keep this at 48 hours for accurate predictions.forecast_plan_hours: 30- How far ahead Predbat actively plans charging slots. This should match your known price window.
For Agile, prices are released around 16:30 each day, covering 23:00 that evening through to 23:00 the following day - roughly 30 hours of known prices. Setting forecast_plan_hours higher would have Predbat planning with estimated prices, which is less accurate. But you still want the full 48-hour forecast for solar and load modelling.
Historical Weighting
The days_previous setting tells Predbat to look at your consumption from 7, 14, and 21 days ago to predict today’s usage. The weights (1.0, 0.7, 0.5) give more importance to recent data. Make sure you have as many weights as you have days.
Time Slot Control vs Service Calls
The Growatt SPH supports both approaches. Time slot control (charge_start_time, charge_end_time, scheduled_charge_enable) lets Predbat set specific charging windows. Service calls (charge_start_service, etc.) switch the inverter’s work mode. Using both gives Predbat maximum flexibility.
Enabling the Date/Time Sensor
If inverter_time shows red in the Predbat web interface, you need to enable the time_date sensor. Add this to your configuration.yaml:
sensor:
- platform: time_date
display_options:
- 'date_time'
Then restart Home Assistant.
Home Assistant Settings (Predbat Dashboard)
Beyond the apps.yaml file, Predbat exposes many settings as Home Assistant entities that you configure via the UI or the Predbat dashboard. Here are the key ones:
Core Settings
| Setting | Value | Reason |
|---|---|---|
select.predbat_mode |
Control charge | Discharge disabled - only optimising charging |
switch.predbat_set_reserve_enable |
ON | Allows Predbat to set the reserve/minimum SoC |
input_number.predbat_set_reserve_min |
20 | Minimum battery SoC to maintain |
switch.predbat_set_charge_freeze |
ON | Holds SoC during cheap periods if already charged |
input_number.predbat_best_soc_keep |
0.5 | Buffer in kWh to keep above target |
Agile-Specific Settings
| Setting | Value | Reason |
|---|---|---|
input_number.predbat_forecast_plan_hours |
30 | Matches Agile price availability (~30 hours ahead) |
switch.predbat_combine_charge_slots |
ON | Combines consecutive cheap slots into one charge window |
switch.predbat_combine_export_slots |
ON | Same for export (when enabled) |
switch.predbat_calculate_inday_adjustment |
ON | Adjusts plan as Agile prices update |
Load Prediction
| Setting | Value | Reason |
|---|---|---|
switch.predbat_load_filter_modal |
ON | Uses modal filtering for load prediction |
input_number.predbat_metric_self_sufficiency |
1.0 | Adds 1p/kWh virtual value to self-consumed energy |
Optional but Recommended
| Setting | Value | Reason |
|---|---|---|
switch.predbat_expert_mode |
ON | Exposes additional settings |
switch.predbat_inverter_hybrid |
ON | Correct for Growatt SPH |
switch.predbat_metric_pv_calibration_enable |
ON | Improves solar predictions over time |
switch.predbat_set_export_freeze |
ON | Holds SoC during high export rate periods |
Verifying the Configuration
After saving your apps.yaml:
- Wait a few minutes for Predbat to reload (it runs every 5 minutes)
- Access the Predbat web interface
- Click apps.yaml to see any mismatched entities (shown in red)
- Check
predbat.statusfor errors
What I’m Not Covering Here
I’ve deliberately excluded EV charging configuration from this post - that’s covered separately as it adds significant complexity with Tesla Fleet integration and charging automations.
I’ve also disabled discharge/export (switch.predbat_set_discharge_enable = off) for now. On Agile there are periods where exporting makes sense, but I wanted to get charging working reliably first.
Results
With this configuration, Predbat successfully identifies the cheapest overnight slots on Agile and charges the battery accordingly. On a typical day, it might charge at 2am-4am when prices are around 7p/kWh, then the battery powers the house during the 4pm-7pm peak when prices hit 30p+/kWh.
The Predbat dashboard shows the planned charging windows and predicted costs, making it easy to verify it’s working as expected.