A new tab beside Home and Batteries for today and tomorrow: the solar forecast, the solar measured so far, the house's expected use and each battery's expected level, with the plan's own charge and give blocks under it. - Blocks are Home's: today's are the plan's `runs`, tomorrow's come from the same builders (`homeview.runs_for()`), in the plan lines' words. - The sun is what solarfc reads; a daily total is spread over the bell and the tab says so. The house is HouseProfile.estimator(), as the plan reads it. - The level is new: `daycurve.curve()` gives each battery the role `modetrade.decide()` gives it and moves it hour by hour inside the plan's floor and ceiling. It is an estimate and counts on `solar_trust_pct`. - Built on request (`GET forecast`), no Home Assistant call. Measured solar is kept per hour in memory from the reading control already takes. - A part not known yet is a note: modes off, no forecast, learning, no forecast for tomorrow, a monthly cycle, no level, stopped, shadow. No decision, command, timing or option key changes. Found, not changed: `dayplan.plan()` over-counts a state-only forecast (DECISIONS/UX-54). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
3.7 KiB
UX-54 - a Forecast tab: the sun, the house and each battery's level, per hour
Status: DONE 2026-10-26 (owner go 2026-10-26: "Day curve", "New tab").
Decision
A tab beside Home and Batteries shows today and tomorrow, per hour: the solar forecast, the solar measured so far, the house's expected use and each battery's expected level with its blocks. It follows the add-on, as Home does (UX-53), and decides nothing.
- Blocks are Home's. Today's are the
runsof the same plan Home draws (grid, give, monthly cycle). Tomorrow's come from the same builders (homeview.runs_for()). The words are the plan lines'. A block is on the tab exactly where Home has one, so the UX-53 gate is followed too. - The sun is what
solarfcreads. An hourly split (SolcastdetailedForecast, Open-Meteowh_period) is used as given. A daily total (Forecast.Solar, and any other state-only sensor, the owner's three "today remaining" and three "tomorrow") is spread over the 06:00-20:00 bell, and the tab says so. - The house is
HouseProfile.estimator(), never below the night median, as the plan reads it. - The level did not exist (the plan holds targets and run blocks only), so
daycurve.curve()derives it: each battery has the rolemodetrade.decide()gives (follow the house, hold, give, charge from the grid, rest), spare solar goes to Solar first batteries first, a shortfall is met by those that may give, both atdayplan.EFF, inside the plan's floor and ceiling, from the level now. It counts onsolar_trust_pctof the sun, as the plan does, and says so. Grid charging goes tomodetrade.charge_to(). It is an estimate. - Measured solar is kept per local hour in memory (
SolarToday), from the reading the control cycle already takes; nothing is read back. - On request.
GET forecastbuilds the tab (Controller.forecast_payload()reads/status' plan first, so today's blocks are the very ones Home has). No Home Assistant call, nothing cached, nothing in the cycle but one in-memory add. The page asks every 30 s while the tab shows. - Not known yet is a note, not an empty chart: modes off, no forecast set, forecast missing or old, learning, no tomorrow forecast, a monthly cycle, no level reading, control stopped, shadow.
What does not change
No decision, command, timing or option key. The one write in the control path
is SolarToday.add() after the solar read, in a try, in memory.
Limits
- Prices are planned about a day ahead (
priceruns.HORIZON), so tomorrow's blocks appear as the day comes near. The tab says so. - A give run drawn on a battery that is empty now has no block on Home (no energy to give), but the curve gives in it if the sun fills it first.
- Tomorrow shows price blocks only. A monthly cycle is drawn today, where the curve then stops for that battery.
- The curve uses the arbitrage floor and ceiling, as the plan does, not the battery's own cut-out level. The house profile is a median per hour, net of the solar the house used.
- Measured solar starts when the add-on starts; the tab says from when.
Found, not changed
dayplan.plan() reads the forecast hour by hour with Forecast.between(s, e).
For a state-only forecast between() takes its start as the moment the "left
today" figure was read, so each later hour is spread over what is left of the
bell from that hour: on the owner's shape 14.4 kWh left gives 57 kWh over the
hours to midnight (test_ux54.py part 1). The plan's sun is overcounted for
those sites. It does not show on a site whose batteries are all Solar first
(no level is planned for them). The tab measures both ends from now
(dayview.sun_kwh()), which keeps the total and gives the same hours for an
hourly split. A card should fix the plan.