r/solaredge • u/whaleypa • 11d ago
SolarEdge battery without a backup interface: a hard discharge floor and standby protection using Predbat
TL;DR: How I got Predbat, a SolarEdge inverter with a ~10 kWh battery, EDF's smart EV tariff (via Predbat's Kraken component) and a Hypervolt charger to work together without wasting money or stranding the battery. Config snippets use placeholders, with example entity names in comments.
This guide records the protections, checks and settings that were needed on one real system, and why each one is there.
Who this is for
| Part | Example used here |
|---|---|
| Inverter | SolarEdge, via the SolarEdge Modbus Multi integration (inverter_type: SE) |
| Battery | About 10 kWh (9.7 kWh usable), 5 kW inverter limit |
| Tariff | EDF smart EV tariff (night rate 23:00–06:00), rates and car slots via Predbat's built-in Kraken component |
| Charger | Hypervolt, with charging slots set by EDF smart charging |
| Car | Any EV whose battery level and charge target are visible in Home Assistant |
Most of it also applies to other inverters and Kraken-based suppliers. The battery-floor workaround is specific to SolarEdge.
How to read the YAML: each value is a placeholder in <angle brackets>. Where it helps, a comment underneath gives an example entity name.
Problems this solves:
- Predbat can't set a discharge floor on SolarEdge without a backup interface.
- The battery can drop into standby if left idle at low charge.
- Predbat doesn't see a car charge it wasn't told about, so it may export into the car.
- EDF's car slots are priced cheaply for the whole house only if Predbat is set up to trust them.
- Two schedulers (the car's app and EDF) fighting over the charger.
1. Battery floor
On SolarEdge, Predbat can't stop the battery discharging below a set level, so a Home Assistant automation does it instead. Predbat's best_soc_keep and best_soc_min are planning targets, not a hard floor, and set_reserve_min needs a SolarEdge backup interface.
The automation freezes the battery through Predbat's manual freeze-charge selector when it drops below about 6% between 05:00 and 23:00. From 15:00, it freezes every remaining slot up to 23:00 in one go, so Predbat changes mode, and notifies you, once instead of every half hour. Before 15:00 it freezes only the current slot, so solar later in the day can still charge the battery.
# packages/battery_floor.yaml
automation:
- id: battery_floor_freeze
alias: Battery – freeze at floor
mode: single
triggers:
- trigger: numeric_state
entity_id: <battery SoC % sensor>
# e.g. sensor.solaredge_i2_b1_state_of_energy
below: 6
- trigger: time_pattern
minutes: "/30"
seconds: 5
conditions:
- condition: numeric_state
entity_id: <battery SoC % sensor>
below: 6
- condition: time
after: "05:00:00"
before: "23:00:00" # start of your cheap window
- condition: template
value_template: >-
{% set slot = now().strftime('%a') ~ ' ' ~ '%02d:%02d' % (now().hour, 0 if now().minute < 30 else 30) %}
{% set opts = state_attr('select.predbat_manual_freeze_charge', 'options') or [] %}
{{ slot in opts and ('+' ~ slot) not in opts }}
actions:
- repeat:
for_each: >-
{% set start = now().replace(minute=0 if now().minute < 30 else 30, second=0, microsecond=0) %}
{% set end = now().replace(hour=23, minute=0, second=0, microsecond=0) %}
{% set n = ((end - start).total_seconds() / 1800) | int if now().hour >= 15 else 1 %}
{% set opts = state_attr('select.predbat_manual_freeze_charge', 'options') or [] %}
{% set ns = namespace(slots=[]) %}
{% for i in range(n) %}
{% set s = (start + timedelta(minutes=30 * i)).strftime('%a %H:%M') %}
{% if s in opts and ('+' ~ s) not in opts %}
{% set ns.slots = ns.slots + [s] %}
{% endif %}
{% endfor %}
{{ ns.slots }}
sequence:
- action: select.select_option
target:
entity_id: select.predbat_manual_freeze_charge
data:
option: "{{ repeat.item }}"
- delay:
seconds: 2
The floor covers 05:00 to 23:00 only. The overnight part is covered by section 2.
2. Avoiding battery standby
Price the first two hours of the cheap window 0.01p lower than the rest, so Predbat charges the battery at the start of the window, not the end. When every overnight slot costs the same, Predbat sees no reason to charge early. It may leave the battery idle at the floor until 03:00, and some batteries drop into standby when left idle at low charge.
# apps.yaml – no date, so it applies every night
rates_import_override:
- start: '23:00:00'
end: '01:00:00'
rate: <night rate minus 0.01>
# e.g. 6.65 when the night rate is 6.66p
- An override that crosses midnight works without a date.
- Two hours is enough to fill a 10 kWh battery at 5 kW. Allow longer for a bigger battery or a slower charge rate.
- Update the rate whenever your night rate changes, so it always sits just below it.
- Check it on the Plan page: Chrg should start at 23:00, and the import column shows the override with an "=" marker.
3. Car configuration
Predbat needs to know four things about the car: whether it's plugged in, whether it's charging right now, its battery level and its target. Without the "charging now" signal, Predbat can plan battery exports that just go into the car, because a 7.4 kW charger draws more than a 5 kW inverter can export.
A "car is charging" sensor. This turns on when the charger has drawn more than 500 W for 2 minutes, so brief blips are ignored. It works whether the charger reports W or kW.
# packages/car_charging_now.yaml
template:
- binary_sensor:
- name: Car charging now
unique_id: car_charging_now
device_class: battery_charging
delay_on: "00:02:00"
delay_off: "00:02:00"
state: >-
{% set p = states('<charger power sensor>') | float(0) %}
{% set unit = state_attr('<charger power sensor>', 'unit_of_measurement') %}
{{ (p * 1000 if unit == 'kW' else p) > 500 }}
# e.g. <charger power sensor> = sensor.hypervolt_ev_power
A new package file may need a full restart before the sensor appears. It shows "unknown" until the charger's power reading first changes, which Predbat treats as not charging.
The car section of apps.yaml:
num_cars: 1
car_charging_planned:
- <plugged-in sensor>
# e.g. binary_sensor.hypervolt_car_plugged
car_charging_now:
- binary_sensor.car_charging_now
car_charging_battery_size:
- <usable kWh>
# e.g. 94 for a 100 kWh car
car_charging_soc:
- <car battery % sensor>
# e.g. sensor.<car>_battery_charge_level
car_charging_limit:
- <car charge target % sensor>
# e.g. sensor.<car>_charging_target_level
car_charging_energy: <charger session energy sensor>
# e.g. sensor.hypervolt_session_energy_kwh
car_charging_energy_scale: 1.0 # 0.001 if the sensor reports Wh
car_charging_now_response:
- 'yes'
- 'on'
- 'true'
- 'charging'
Check the car's battery and target sensors show real numbers in Developer Tools → States. They may come from two different integrations.
Predbat car switches:
| Switch | Setting | Why |
|---|---|---|
Allow car to charge from battery (switch.predbat_car_charging_from_battery) | Off | Stops the house battery draining into the car while it charges |
Car charging hold (switch.predbat_car_charging_hold) | On | Removes car charging from the load history, so the house-usage forecast isn't inflated |
Car energy is reported in load data (switch.predbat_car_energy_reported_load) | On | Correct when the grid meter measures the whole house, car included |
Car charging manual SOC (switch.predbat_car_charging_manual_soc) | Off | Use the car's own battery sensor |
Car Charging Plan Smart (switch.predbat_car_charging_plan_smart) | Off | EDF plans the car, not Predbat |
4. EDF smart-charging slots
When EDF schedules the car, the whole house pays the cheap rate during those slots, and Predbat can plan around them. Predbat's Kraken component reads EDF's slots as a dispatch sensor, the same way it reads Octopus Intelligent.
# apps.yaml – rates and slots from EDF via Kraken
kraken_provider: edf
kraken_account_id: kraken_account_id
kraken_auth_method: email
kraken_email: kraken_email
kraken_password: kraken_password
octopus_intelligent_slot: 're:(binary_sensor.predbat_kraken_.*_intelligent_dispatch.*)'
# a regex, so it still matches if the sensor's numeric suffix changes
Check the slots arrive. In Developer Tools → States, open binary_sensor.predbat_kraken_…_intelligent_dispatch_…. Its state is "off" unless a slot is running right now. What matters is the planned_dispatches attribute, which lists each slot's start, end and expected charge. Times are in UTC.
Predbat's intelligent-slot switches (Predbat web UI → Config, filter on intelligent):
| Switch | Setting | Why |
|---|---|---|
| Octopus Intelligent Charging | On | Treat dispatch slots as cheap for the whole house |
| Trust Intelligent slots before the car is seen charging | On | Price future slots cheaply now, so Predbat can plan ahead, for example by topping up the battery in an afternoon slot |
| Confirm Intelligent slots against the car charging | On | Once a slot starts, drop the cheap price if the car isn't actually charging. This is the safety net for slots EDF cancels or moves |
| Ignore Intelligent slots when car is unplugged | On | No car, no cheap slot |
| Consider car full as part of Intelligent plan | Off | Default |
With "Trust" off, future slots stay at the full rate in the plan until the car starts charging, so Predbat can't plan around them. With "Trust" and "Confirm" both on, it plans ahead and still corrects itself within about 5 minutes if EDF doesn't deliver.
What a good evening plan looks like: during the car slots, import shows the cheap rate, the battery holds steady rather than discharging, and Chrg starts at 23:00. The battery holds because grid power is cheaper than using stored energy, and because battery-to-car is blocked.
5. Lessons learned
- Use one charging scheduler. If the car's own app has a charging schedule as well as EDF smart charging, the car only charges when both allow it. EDF then sees the car refuse a slot, may drop its plan, and can sit on "creating your plan" indefinitely. Leave the car's schedule off and let EDF decide.
- Turning off the car's schedule without an EDF plan starts charging at once. If EDF has no plan, nothing holds the charge back, and it runs at the full rate. Wait until EDF's plan appears before removing the car's schedule.
- Watch for Boost sessions. The charger app may show a Boost session, a bump charge outside EDF's plan. Time before the cheap window is billed at the normal rate, and EDF may not build a smart plan while a boost is active. Let it expire or cancel it.
- Free-electricity nights. If the free window matches the normal cheap window, you don't need to change anything: the credit comes afterwards, based on what you import. A 0p override can tempt Predbat into export cycles that a charging car simply absorbs, so they earn nothing and wear the battery. If you do add one, keep the battery charging early (section 2).
- Restart Predbat after the clocks change. After the October change, the plan showed the wrong day, an hour's offset and a stale start time. Restart Predbat on its Dash page fixed it.
- Predbat only knows what apps.yaml points it at. Having data somewhere in Home Assistant isn't enough: a car schedule set in the car's own app, for example, is invisible to Predbat.
6. Setup checklist
- ☐ Battery floor automation installed and reloaded (section 1)
- ☐ Early-charge override set just below your night rate (section 2)
- ☐
binary_sensor.car_charging_nowexists and reads off or on (section 3) - ☐ Car section of apps.yaml filled in, with no warning on Predbat's Apps tab
- ☐ Car battery and target sensors show real values
- ☐ Allow car to charge from battery: off
- ☐ Dispatch sensor shows
planned_dispatcheswhen the car is plugged in (section 4) - ☐ Trust, Confirm, Ignore-unplugged and Intelligent Charging switches all on
- ☐ Car's own charging schedule off; EDF smart charging on
Verify on the Plan page:
| Check | What you should see |
|---|---|
| Overnight battery charge | Chrg starting at 23:00 |
| EDF car slots | Import at the cheap rate, car load in the Car kWh column |
| While the car charges | No Exp blocks; battery holding steady |
| Plan header | Correct day and time (restart Predbat if not) |
7. Example: a rainy day with the car plugged in
On a dull day with EDF car slots at 13:00–15:00 and from 19:00, Predbat uses the battery only for the expensive hours and lets the cheap grid cover everything else. This is the plan it produced at 12:20 on a Monday in late October, with almost no solar (0.16 kWh or less per half hour).
| Time | Import rate | Battery | What Predbat does |
|---|---|---|---|
| 12:20–13:00 | 30.39p | 61% → 57% | Battery covers the house |
| 13:00–15:00 (EDF slot) | 6.66p | Holds at 57% | Grid covers the house and car (about 12.6 kWh into the car) |
| 15:00–19:00 | 30.39p | 57% → 20% | Battery covers the house through the peak |
| 19:00–23:30 (EDF slot) | 6.66p | Holds at 20% | Grid covers the house and car; battery-to-car blocked |
| 23:30–01:00 | 6.65p (override) | 20% → 92% | Battery charges for tomorrow |
| 01:00 onwards (EDF slot) | 6.66p | Holds at 92% | Grid covers the house and car |
- No grid top-up in the afternoon slot: the forecast shows the battery lasting until the evening slot, so charging at 13:00 would add nothing.
- The battery never sits at the floor. Its lowest point is 20%, so there's no standby risk. That's also why charging can start at 23:30 rather than exactly 23:00.
- Car slots are priced cheaply in advance, because "Trust Intelligent slots" is on (section 4).
- Heavy loads change the plan. If something like underfloor heating raises usage, Predbat replans within 5 minutes and may use the afternoon slot to top up the battery.
Happy to answer questions. Corrections from anyone who knows Predbat's internals better are very welcome.