doladeni odpoledniho dobiti
Some checks failed
CI and deploy / migration-check (push) Failing after 22s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-21 16:18:30 +02:00
parent c9149babd3
commit e295e55770
4 changed files with 91 additions and 15 deletions

View File

@@ -106,7 +106,13 @@ def _select_charge_slots(
eta = float(getattr(battery, "charge_efficiency", 1.0) or 1.0)
max_p_w = float(getattr(battery, "max_charge_power_w", 0.0) or 0.0)
per_slot_full_wh = max_p_w * eta * INTERVAL_H
if current_soc_wh >= reserve_wh:
soc_max_wh = float(getattr(battery, "soc_max_wh", 0) or 0)
if charge_buf > 0:
charge_target_wh = min(
max(energy_to_fill, 0.0) * charge_buf,
max(soc_max_wh - float(current_soc_wh), 0.0),
)
elif current_soc_wh >= reserve_wh:
charge_target_wh = max(energy_to_fill, 0.0)
else:
charge_target_wh = min(
@@ -129,13 +135,26 @@ def _select_charge_slots(
selected: set[int] = set()
grid_filled_wh = 0.0
buf_mult = charge_buf if charge_buf > 0 else 1.0
cap_am = (
max(1, min(_MAX_GRID_CHARGE_CAP, int(chg_am / per_slot_full_wh) + 1))
max(
1,
min(
_MAX_GRID_CHARGE_CAP,
int(chg_am / per_slot_full_wh * buf_mult) + 1,
),
)
if per_slot_full_wh > 0
else 6
)
cap_pm = (
max(1, min(_MAX_GRID_CHARGE_CAP, int(chg_pm / per_slot_full_wh) + 1))
max(
1,
min(
_MAX_GRID_CHARGE_CAP,
int(chg_pm / per_slot_full_wh * buf_mult) + 1,
),
)
if per_slot_full_wh > 0
else 6
)
@@ -166,6 +185,15 @@ def _select_charge_slots(
cum += per_slot_full_wh
grid_am += 1
grid_filled_wh += cum
chg_pm = max(chg_pm, charge_target_wh - grid_filled_wh)
if per_slot_full_wh > 0:
cap_pm = max(
cap_pm,
min(
_MAX_GRID_CHARGE_CAP,
int(chg_pm / per_slot_full_wh * buf_mult) + 1,
),
)
pm_candidates = [
(t, getattr(slots[t], "is_predicted_price", False), float(slots[t].buy_price))
@@ -462,6 +490,24 @@ class SelectChargeSlotsTests(unittest.TestCase):
self.assertNotIn(0, out, "Při malém rozpočtu má přednost levnější NT, ne VT 1.49")
self.assertTrue({1, 2} & out, "NT slot(y) mohou být vybrány")
def test_pm_grid_gets_unused_am_wh_budget(self) -> None:
"""Nečerpaný AM rozpočet → odpolední levné PM sloty mohou dostat allow_charge."""
base = datetime(2026, 5, 22, 6, 0, tzinfo=timezone.utc)
slots = [
_slot(buy=0.55, sell=-0.2, hour_utc=6, interval_start=base),
_slot(buy=0.58, sell=-0.2, hour_utc=7, interval_start=base + timedelta(hours=1)),
_slot(buy=0.52, sell=-0.25, hour_utc=14, interval_start=base + timedelta(hours=8)),
_slot(buy=0.50, sell=-0.25, hour_utc=15, interval_start=base + timedelta(hours=9)),
_slot(buy=5.5, sell=3.8, hour_utc=20, interval_start=base + timedelta(hours=14)),
]
battery = _battery(charge_buf=1.3, uc_wh=64_000.0)
out = _select_charge_slots(slots, battery, current_soc_wh=0.12 * battery.usable_capacity_wh)
pm_cheap = {2, 3}
self.assertTrue(
pm_cheap & out,
"po levném AM má PM dostat grid charge z nevyčerpaného rozpočtu",
)
def test_ote_slots_prioritized_over_predicted(self) -> None:
"""Při stejné ceně má OTE (is_predicted=false) přednost před predikovaným."""
slots = [