diff --git a/goodwe_controller/test_modes_p4.py b/goodwe_controller/test_modes_p4.py index c308e57..957fa05 100644 --- a/goodwe_controller/test_modes_p4.py +++ b/goodwe_controller/test_modes_p4.py @@ -672,9 +672,24 @@ check("Cheap hours with a sun forecast charges as with none (P3)", and top > 80.0) # The forecast goes away mid-run: Leftover solar stops at once (P3 option A). +# OPT-27: a sensor that is unavailable is covered by its last fresh reading +# for up to 3 h (`solarfc.Hold`), so "lost" here is a hold that never covers; +# the next scenario is the same loss with the hold (test_opt27.py). DROP = UTC0 + timedelta(hours=1, minutes=30) + + +class _NoHold(solarfc.Hold): + def stand_in(self, *a, **k): + return None + + +def seed_profile_no_hold(c): + seed_profile(c) + c.fc_hold = _NoHold() + + rows, st_, c = run_day(SF_LS, fc=lambda now: 1.0 if now < DROP else None, - soc0={"a": 60.0, "b": 10.0}) + soc0={"a": 60.0, "b": 10.0}, patch=seed_profile_no_hold) invariants("forecast lost mid-run", rows, c) before = [r for r in rows if r["now"] < DROP and r["action"] == arbitrage.CHARGE] @@ -682,6 +697,15 @@ late = [r for r in rows if r["now"] > DROP + timedelta( seconds=2 * 60 + 10) and r["action"] == arbitrage.CHARGE] check("forecast lost mid-run: Leftover solar was charging, and stops within " "a forecast read (60 s) and a cycle", before and not late) +rows, st_, c = run_day(SF_LS, fc=lambda now: 1.0 if now < DROP else None, + soc0={"a": 60.0, "b": 10.0}) +invariants("forecast unavailable mid-run, covered", rows, c) +check("forecast unavailable mid-run (OPT-27): the last reading covers it, so " + "Leftover solar goes on charging past the point where it was lost", + [r for r in rows if r["now"] < DROP and r["action"] + == arbitrage.CHARGE] + and [r for r in rows if r["now"] > DROP + timedelta( + seconds=2 * 60 + 10) and r["action"] == arbitrage.CHARGE]) # The forecast is far too high (40 kWh, and there is no sun at all): no grid # charge, so B covers less of the evening - a money risk, never a safety one. diff --git a/goodwe_controller/test_opt27.py b/goodwe_controller/test_opt27.py index fd28a8c..7f9cb0e 100644 --- a/goodwe_controller/test_opt27.py +++ b/goodwe_controller/test_opt27.py @@ -100,7 +100,7 @@ check("22:00 with the sensors stale since 17:05: the 17:05 figures are used", fc is not None and fc.kept and fc.tomorrow.kwh == 12.0 and fc.at == D1_READ and gaps == ()) check("... today has no sun left in it, tomorrow's is counted from 06:00", - fc.between(EVE, at(4, 7, 0)) is not None + fc is not None and fc.between(EVE, at(4, 7, 0)) is not None and 0.0 < fc.between(EVE, at(4, 7, 0)) < 0.5 and fc.today.kwh == 0.4) fc2, _w, _g = rs(GONE, GONE, EVE, h) @@ -146,12 +146,16 @@ check("02:59: tomorrow's 12.0 kWh is now today's, there is no tomorrow", fmid is not None and fmid.today.kwh == 12.0 and fmid.tomorrow is None and fmid.kept and fmid.today.day0 == at(4, 0, 0)) check("... its sun is counted on the day it is for (06:00-20:00 on the 4th)", - fmid.between(at(4, 3, 0), at(4, 7, 0)) is not None + fmid is not None and fmid.between(at(4, 3, 0), at(4, 7, 0)) is not None and fmid.between(at(4, 3, 0), at(4, 7, 0)) > 0.0) check("03:01 (midnight + 3 h): not used any more", rs(GONE, GONE, at(4, 3, 1), h2)[0] is None) check("... and not the next evening either (older than one night)", rs(GONE, GONE, at(4, 22, 0), h2)[0] is None) +fe = rs(st(0.1, at(4, 21, 50)), GONE, at(4, 22, 0), h2)[0] +check("... the next evening, with today fresh and tomorrow's sensor down, " + "there is no tomorrow: the 4th's figure is not the 5th's", + fe is not None and fe.today.kwh == 0.1 and fe.tomorrow is None) # Never today's figure for tomorrow. h4 = solarfc.Hold() rs(st(3.0, at(3, 17, 0)), st(12.0, at(3, 17, 0)), at(3, 17, 5), h4)