Files
goodwe-addon/goodwe_controller/DECISIONS/TARIFF-24-modes-need-the-tariff-task.md
T
glenn schrooyenandClaude 1c9fefd9dd Sync 0.19: the Forecast tab extends a cheap run with control's gap (TARIFF-24)
UX-54's Forecast tab (release/1.0) re-plans tomorrow's cheap run as control
will, with the gap `required`. It still added the "no weather" x1.5 always,
while control (TARIFF-24, from master) drops it when a forecast is read. In
the UX-54 scenarios the tab asked 0.225 where control needs 0.15. It now asks
`arbitrage.weather_uncertainty(True, c.solar_fc)`, so the two agree again, as
they did on release/1.0 alone. Display only; no control change. The tab's
blocks in every UX-54 scenario, every 2 h, are identical to release/1.0.

test_ux54.py pins it (the old line fails it). The TARIFF-24 DECISIONS note
names the tab as the fourth place that reads the rule.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:11:42 +01:00

4.0 KiB

TARIFF-24 - battery modes need the tariff task

Status: FIXED in 0.19.1 (hotfix from 0.19.0, owner go 2026-10-28). Follows TARIFF-10 (fetching is not trading). Tests: test_tariff24.py.

The defect. The control cycle gets prices only from Controller.tariff_plan, which only run_tariff_refresh() stores. amain() starts that task when tariff_refresh_wanted(opts), which was provider_configured(opts) or arbitrage.enabled(opts). Battery modes read the plan but were not a term. With battery modes on, a day/night or fixed static_profile, price_provider: off and arbitrage_enabled: false (the site trade switch is not read with modes on) no task ran. Then fresh_tariff_plan() was None, _price_runs() was None and mode_runs was None, so Leftover solar and Cheap hours never charged from the grid. The Batteries screen said "No prices, so it follows the house" while Home priced the day from static_plan() itself (homeview.build_plan()).

The fix (main.py, tariff_refresh_wanted()). packmode.enabled(opts) is a third term. The gate stays a widening: nothing that started the task before stops. No HTTPS call is added: with price_provider: off, resolve_tariff() is the pure static_plan().

Not changed. Modes off, arbitrage off, no feed still starts no task. The control law, the price runs and every message are untouched.

One rule, not two. Gating on "modes on and a static profile" was considered. It saves one arithmetic pass every 30 min on a site with modes on and no prices at all, and it adds a second condition that has to track what resolve_tariff() accepts. With no profile and no feed the plan is no_data, exactly what the cycle sees without the task.

UX-55. Home said "no prices have arrived" (mode_runs None) with every battery on Solar first. The same cause, and this fix clears it for a site with a static profile (test_tariff24.py part 5). A site with no profile and no feed still has no prices, and Home still says so.

Round 2 (AUDIT-02 F1): the forecast replaces the "no weather" doubt

Decided 2026-10-28 (owner), in the same hotfix. The price gap a mode needs was cycle_cost_threshold_eur x 1.5 (Uncertainty.NO_WEATHER) whether or not a solar forecast was read. At the shipped cost 0.10 that is 0.15, so a day/night gap (0.08) and many dynamic days never traded, and the screens said only "Prices differ too little".

Why the doubt goes with a forecast. The widening stands in for not knowing the weather. The forecast is that knowledge, and the day plan already trusts it for how much to charge. Trusting it for the plan but doubting it for the gap was one input judged twice.

The rule (arbitrage.weather_uncertainty()). With battery modes on and a usable forecast (solar_fc not None, the day plan's own test) the doubt is not added: the gap needed is the cycle cost, widened by the price product only. No forecast, or a stale or unusable one: 1.5 x, as before. It is read in four places that must agree: the run plan (_price_runs()), the charge gate (_mode_roles()), Home's mirror (homeview.trade_verdict()) and the Forecast tab's longer cheap run (dayview.py, from release/1.0). The forecast is read at most every 60 s and the plan key already holds "forecast or not", so the gap follows in the cycle the read changes, both ways, with one exception: a run planned with a forecast that is under way keeps that gap to its end (Controller.weather_held()), so a sensor that goes unavailable does not stop or restart a Cheap hours charge (a Leftover solar battery still stops: without a forecast it has no planned level). The wider gap applies from the next run. A run that has started runs to its end: a give run that starts during the hold is kept after it (keep_started()), with no grid charge or sale.

Not changed. Modes off (the site trade) keeps the doubt always. The shipped cost 0.10 still needs a gap of 0.10 even with a forecast: a 0.08 day/night site must set Battery cycle cost to 0.08 or less. Showing the two numbers on the screens (AUDIT-02 F1 option b) is a separate card.