Add solar surplus car charging to divert excess PV to EV#3791
Add solar surplus car charging to divert excess PV to EV#3791Pezmc wants to merge 15 commits intospringfall2008:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a core “solar surplus car charging” mode that reuses Predbat’s existing car-charging slot signaling to divert excess PV export into EV charging, with new config controls and observability.
Changes:
- Introduces 3 new config entities to enable/shape surplus-charging behavior (master switch, W threshold, ignore SoC limit).
- Updates
execute_plan()to detect live surplus export with hysteresis and publish surplus-related binary sensors/attributes. - Extends execute/test infrastructure and documentation to cover the new feature.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/car-charging.md | Documents the new solar surplus charging feature, configuration, and sensors. |
| apps/predbat/config.py | Adds three new configuration items for surplus charging. |
| apps/predbat/fetch.py | Reads the new configuration items into runtime fields. |
| apps/predbat/execute.py | Implements surplus detection, battery-discharge hold integration, and sensor publishing. |
| apps/predbat/predbat.py | Initializes car_charging_solar_surplus_active in reset state. |
| apps/predbat/tests/test_infra.py | Adds defaults/reset fields for the new config/state in tests. |
| apps/predbat/tests/test_execute.py | Adds scenarios validating surplus activation/inhibition rules and status text. |
| .cspell/custom-dictionary-workspace.txt | Adds “deadband” to spelling dictionary. |
… shape Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@springfall2008 I've addressed the copilot feedback, both suggestions were valid edge cases |
|
Hi Pezmc, One question I have, why is this hard-wired to inverter 0? There's a bit cleanup required, in that reset() should be updated to reset the self.* attributes used in this code and then the getattr() can also be removed. Do you want me to try to clean up or would you like to have a go? |
It's hard-wired to inverter.id == 0 solely so it only runs once (since it uses global state, not anything per-inverter). Following the pattern file e.g. the status_extra, for example. Happy to move it up above the inverter loop instead if you'd prefer!
I've attempted to address, and applied a slight refactor to handle number of cars changing. If you'd like further changes or want to make some tweaks, please have at it! Thanks for taking the time! |
|
Thanks for this work! I have been missing this and this will be an awesome addition. My 2 cents for two of items being added:
It would be awesome if instead the car charge rate were to be adjusted so that the car would charge with the correct surplus. It would have a minimum/maximum car charge rate.
Like soc min/recommended, it would be nice if we could define an upper limit that would be respected. This way we could continue to set a SoC target for Predbat to manage while allowing any surplus to charge the car slightly more. In short this would convert the Boolean (100%) value to a configurable integer. |
|
@bpinto thanks, appreciate the thoughts!
If your charger supports variable current, I think this is actually better handled in your own HA automation than inside Predbat. The automation already has to flip the charger on and off and can read One thing Predbat could usefully do is expose the surplus amount it's currently seeing as an attribute, so the automation doesn't have to recompute it. But actually modulating the rate from inside Predbat feels like feature creep to me, given Predbat can't actuate the charger directly anyway.
Yeah, that one's fair, turning the boolean into a configurable cap makes sense. It's a boolean to KISS for now. @springfall2008 do you think we should make this change now, or handle as a separate issue? |
|
Having sat on this overnight I'm going to change the boolean to a limit that defaults to 100%, to save needing to introduce backwards incompatible changes or having to maintain backwards compatibility going forward. Thanks for the suggestion @bpinto |
|
@Pezmc thanks for taking into consideration my comments! By no means, I think any of that is a blocker, but I think it's an extra/improvement.
I think it would make sense for predbat to be responsible for this since predbat does similar to battery/inverter settings. For instance, it automatically sets the SoC target for the batteries and can also configure the charging rate. Many of predbat's input support either a fixed value or a sensor, I haven't read how That said, not everyone uses chargers that supports variable charging rate, so the ideas are not exclusive since they support a different group of users. |
|
|
|
@springfall2008 Updated to use This is ready for re-review and any tweaks you wish to make! |
I've been running a Home Assistant automation that wraps Predbat to divert excess solar to my EV for about a month now and it's been working well, so I'm proposing this as a core feature.
In default Predbat when the home battery is full (or solar exceeds battery charge rate + house load), any surplus current is exported to the grid at likely low feed-in rates. This PR adds an option to automatically enable car charging during those periods instead, using Predbat's existing car charging infrastructure.
Closes #632, relates to #3316, #3489
Changes
Three new configuration items control the feature. When enabled,
execute_plan()checks live inverter data each cycle.switch.predbat_car_charging_solar_surplusinput_number.predbat_car_charging_solar_surplus_thresholdinput_number.predbat_car_charging_solar_surplus_limitExecute plan changes:
car_charging_from_batteryhold logic appliesThe feature sets
binary_sensor.predbat_car_charging_slotto ON when surplus is detected, so existing HA automations that watch that sensor work without modification. A dedicatedbinary_sensor.predbat_car_charging_solar_surplussensor is also published for observability.