mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-11-13 09:13:30 +01:00
feat(battery): plan a periodic full charge of the stationary battery (#1486)
LFP batteries can only recalibrate their state of charge close to full charge, so many systems expect a full charge every few weeks and some start one on their own at an arbitrary, possibly expensive time. EOS can now plan it instead (GENETIC only, off by default). New battery settings: full_charge_interval_days (unset = off), full_charge_soc_percentage, full_charge_detect_percentage, full_charge_allow_grid and the informational cell_chemistry. The last full charge is read from the measured SoC history; nothing is stored. When the next one is due within the horizon (or overdue), the maximum SoC is raised to the target. With full_charge_allow_grid the optimizer must reach the target once before the deadline, which is at least 24 h away (penalty battery_full_charge_miss on the best SoC reached), so it picks the cheapest time itself. Without it, PV surplus may fill the battery but the target is not enforced. Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b33953d445
commit
a06473b6d6
@@ -65,7 +65,12 @@ config path from ``self.device_id`` without needing an external index.
|
||||
1.0
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false
|
||||
}
|
||||
},
|
||||
"max_batteries": 1,
|
||||
@@ -102,7 +107,12 @@ config path from ``self.device_id`` without needing an external index.
|
||||
1.0
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false
|
||||
}
|
||||
},
|
||||
"max_electric_vehicles": 1,
|
||||
@@ -174,6 +184,11 @@ config path from ``self.device_id`` without needing an external index.
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false,
|
||||
"measurement_key_soc_factor": "bat0-soc-factor",
|
||||
"measurement_key_power_l1_w": "bat0-power-l1-w",
|
||||
"measurement_key_power_l2_w": "bat0-power-l2-w",
|
||||
@@ -223,6 +238,11 @@ config path from ``self.device_id`` without needing an external index.
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false,
|
||||
"measurement_key_soc_factor": "ev0-soc-factor",
|
||||
"measurement_key_power_l1_w": "ev0-power-l1-w",
|
||||
"measurement_key_power_l2_w": "ev0-power-l2-w",
|
||||
|
||||
@@ -69,7 +69,12 @@
|
||||
1.0
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false
|
||||
}
|
||||
},
|
||||
"max_batteries": 1,
|
||||
@@ -106,7 +111,12 @@
|
||||
1.0
|
||||
],
|
||||
"min_soc_deadline_datetime": null,
|
||||
"min_soc_max_duration_h": null
|
||||
"min_soc_max_duration_h": null,
|
||||
"cell_chemistry": null,
|
||||
"full_charge_interval_days": null,
|
||||
"full_charge_soc_percentage": 100,
|
||||
"full_charge_detect_percentage": 99,
|
||||
"full_charge_allow_grid": false
|
||||
}
|
||||
},
|
||||
"max_electric_vehicles": 1,
|
||||
|
||||
@@ -213,6 +213,11 @@ The behavior of the genetic algorithm can be customized using the following conf
|
||||
A value of `1.0` (default) means the penalty equals the actual economic loss in €.
|
||||
Use larger values (e.g. `3.0`) to make the optimizer more aggressively avoid unprofitable
|
||||
AC charging, or `0.0` to disable this penalty entirely.
|
||||
|
||||
- `battery_full_charge_miss`:
|
||||
Applies a penalty per percent the battery stays below its target when a periodic full
|
||||
charge is due and enforced (see [Periodic full charge](#periodic-full-charge)).
|
||||
Default: `10`.
|
||||
:::
|
||||
|
||||
#### Value Formats
|
||||
@@ -384,6 +389,57 @@ The inverter supports separate AC↔DC conversion efficiencies:
|
||||
}
|
||||
```
|
||||
|
||||
##### Periodic full charge
|
||||
|
||||
LFP batteries have an almost flat voltage curve in the middle of their range. The battery
|
||||
management system can only recalibrate the state of charge close to full charge, so many
|
||||
manufacturers expect a full charge every few weeks, and some batteries start one on their own
|
||||
at an arbitrary time. A periodic full charge lets EOS plan it instead, preferably from PV
|
||||
surplus or at a cheap price.
|
||||
|
||||
The feature is off by default. It is enabled per stationary battery with
|
||||
`full_charge_interval_days` (GENETIC only):
|
||||
|
||||
- `full_charge_interval_days`: maximum number of days between two full charges. `null` (default)
|
||||
disables the feature. For LFP batteries 21 to 25 days are a reasonable start; check what the
|
||||
manufacturer requires.
|
||||
- `full_charge_soc_percentage` (default `100`): the target. It may be above `max_soc_percentage`,
|
||||
which keeps applying on all other days.
|
||||
- `full_charge_detect_percentage` (default `99`): the measured state of charge from which the
|
||||
battery counts as full. Many battery management systems never report exactly 100 %.
|
||||
- `full_charge_allow_grid` (default `false`): with `false`, EOS only raises the maximum state of
|
||||
charge while the full charge is due, so that PV surplus can fill the battery; the target is not
|
||||
enforced. With `true`, the optimizer has to reach the target and charges from the grid if
|
||||
needed, at the cheapest time before the deadline.
|
||||
- `cell_chemistry` (`LFP`, `NMC`, `NCA`, `LTO` or `null`): informational only, it does not
|
||||
enable anything.
|
||||
|
||||
The last full charge is taken from the measured state of charge of the battery
|
||||
(`<device_id>-soc-factor`), so a full charge from PV or one the battery started itself counts as
|
||||
well. Nothing else is stored. Without a full charge in the last interval, the interval counts
|
||||
from the oldest measurement, so a new installation is not forced into a full charge on its first
|
||||
day. The next full charge is due one interval after the last one. EOS acts once that deadline
|
||||
falls within the optimization horizon or has passed, and logs an INFO message for every run in
|
||||
which the full charge is due. The optimizer always gets at least 24 hours to finish it, so that
|
||||
a close or overdue full charge can still use the cheapest time.
|
||||
|
||||
```json
|
||||
{
|
||||
"devices": {
|
||||
"batteries": {
|
||||
"battery1": {
|
||||
"device_id": "battery1",
|
||||
"capacity_wh": 10000,
|
||||
"max_soc_percentage": 90,
|
||||
"cell_chemistry": "LFP",
|
||||
"full_charge_interval_days": 21,
|
||||
"full_charge_allow_grid": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Home appliance simulation configuration
|
||||
|
||||
**Example:**
|
||||
|
||||
Reference in New Issue
Block a user