API Changelog
Chronological public API changes (newest first). Contract = /docs spec + OpenAPI. AI overview: /docs/llms.txt.
This page documents changes to the public SpotpriceAPI in chronological order (newest first). Breaking changes are announced at least 30 days before removal, with a deprecation notice.
2026-10-22
- Storage size: In the annual cost simulation, a dark stretch before the first sun stays ahead of a dearer later hour of the same day. Only a later hour that is cheaper or the same price moves in front. A larger pack no longer buys the expensive morning while that night is cheaper. The EMS plan does not use this order.
decision_enginestayshours_demand_v1.38. All plants/keys. PicoC unchanged. - Hours PV headroom: Grid charge that would push later PV out of the battery at 110 % of the forecast, or that no longer fits in the same hour after 110 % of the PV, is shortened only by those kWh. The minimum is checked on the raw forecast.
decision_enginestayshours_demand_v1.38. KillHOURS_PV_EXPORT_HEADROOM=0. All plants/keys. PicoC unchanged.
2026-10-21
- Hours interim prefix: With an imminent interim slot, that slot stays the EMS chain. The decide hour that buys finite grid kWh stays in the prefix, including sibling rows of that same hour. The prefix stops at the first gap, and demand is that count. Hour 23 stays outside before 23:00. JSON
true, NaN, and infinity do not count. All plants/keys. PicoC unchanged.
2026-10-20
- PV midnight SoC:
loxone.d0_soc_eodstarts from live SoC. PV and planned grid kWh of the open hour count only the minutes still ahead. Load in that hour and every later hour stay whole. Grid kWh only from a freshday_planwithenergy_kwh > 0. RollbackLOXONE_EOD_OPEN_HOUR_FRACTION=0. All plants/keys. PicoC unchanged.
2026-10-18
- Hours price ban: The current hour is shortened once. When cover refine already stored the remainder, the price ban and the crumb cut keep that remainder. Otherwise they shorten the full hour by the remaining fraction.
decision_enginestayshours_demand_v1.38. All plants/keys. PicoC unchanged. - Hours load: Three matching meter hours raise or lower those same clock hours over the next 24 hours. The cap applies only upward, at twice the typical hour. A single spike or dip does not count. A falling battery is not a quiet hour. The current hour counts only for its remainder when the cover refine has already shortened it. The daily total and
d1_load_kwhstay on typical.decision_enginestayshours_demand_v1.38. KillHOURS_LOAD_HOUR_BRIDGE=0. All plants/keys. PicoC unchanged.
2026-10-17
- Hours crumb: A grid charge under the crumb threshold (
max(0.5 kWh, 2% of capacity)), whose grid port is larger, is dropped only when the floor hours across all windows up to the eighth sunrise are the same with and without it. A shift of the floor keeps it. A missing or non-finite state of charge leaves the plan unchanged. A smaller port,must, andreserve_breachstay. It is not raised. Decide, DA replan, and the annual simulation.decision_enginestayshours_demand_v1.38. KillHOURS_CHARGE_CRUMB_DROP=0. All plants/keys. PicoC unchanged. - Week glance: A charge row with 0 kWh is not shown as “1 h planned”.
hours_neededandhours_needed_dayscount only hours that buy grid energy. The same row does not setcharge_nowand is not the next charge. An older snapshot is counted the same way in the display, including the plant-page week glance and day chips. The seven days start on the Vienna issue day. Explain clocks and the full hour count omit that empty row; a clock with no energy field stays a charge hour. All plants/keys. PicoC unchanged. - Docs:
llms.txtand OpenAPI name the live Hours engine tag (defaulthours_demand_v1.38). PV PicoC in llms is DebugPvForecast v1.6 and ExtractPvDaily v2.7. Seven Loxone detail pages arenoindexbecause anonymous visitors only see the login gate. - Hours grid charge: No grid charge into the battery during the most expensive hours of a fully priced day. A missing hour or a spread under 5 €/MWh means no ban. If 110 % of the PV forecast shows export within the next 24 hours and the minimum still holds on the raw forecast without the grid charge before that export, that charge is dropped. Decide and the annual simulation. Charge from that hour on stays. Otherwise at most one top-up: the cheapest hour that fully covers the shortfall. Energy that would not arrive is not bought. If no hour covers it fully, the largest share that does arrive is used. Discharge during expensive hours is unchanged.
decision_enginestayshours_demand_v1.38. KillHOURS_PRICE_PEAK_CHARGE_BAN=0. All plants/keys. PicoC unchanged.
2026-10-14
- Portal/Sparvo — Plant: Lookback/Survive slot clocks are ISO strings in the week glance, the snapshot, and the plant JSON. A raw
datetimemade the refresh return HTTP 500, so the page stopped updating; the day line would otherwise show 0 kWh. All plants/keys. PicoC unchanged.
2026-10-13
- Hours discharge: Below the curve top (at a 20 % floor, from 32 %) discharge power falls as the state of charge falls and rises as it rises, including when a later or active charge is planned. At or below the minimum it is 0. Inside a taken hold above the minimum the slot cap stays.
decision_enginestayshours_demand_v1.38. All plants/keys. PicoC unchanged.
- Hours cover: A dark stretch before the first sun does not outrank later cheap hours of the same day. Grid charge stays only when, after round-trip efficiency, it beats the prices it can still avoid before the next sun or in the rest of that calendar day (ε 3 €/MWh). Otherwise the house imports directly. A larger battery therefore does not buy extra expensive night power.
decision_enginestayshours_demand_v1.38. All plants/keys. PicoC unchanged.
- Portal/Sparvo — storage size: Annual € and the import average follow the last 90 days of spot (70–150 % of the 12-month mean). The Spot Ø 12 months and Regime Ø 90 days chips stay the raw windows. Kill
STORAGE_HORIZON_PRICE_REGIME_SCALE=0. Hours Decide: annual fields stay null. All plants/keys.
- Hours weak PV: A storage purchase on a sunny hour is dropped when it does not cut later house import in the same sun window. The check uses the served hold cap, not a full discharge. End SOC on a surplus charge hour includes the PV. A purchase that still covers a later import stays. A price below zero stays.
decision_enginestayshours_demand_v1.38. KillHOURS_WEAK_PV_REDUNDANT_PEEL=0andHOURS_WEAK_PV_FORWARD_RENT=0. All plants/keys. PicoC unchanged.
2026-10-12
- Hours grid charge: Booked cover, prep, survival, and lookback kWh stay only when removing them raises house import over the rest of the horizon. Each drop is judged against the plan that remains. A needed remainder below the minimum slot size is raised to that size, not left as the original full charge. A lookback hour before a fill day is skipped when the full port there does not cut that import. The one follow-up starts at the current hour and adds new kWh as their own row. Storage kWh on a floor hour move to earlier cheaper hours that still reach the floor; the cost check counts house import plus storage grid charge and keeps the smallest amount with the lowest bill. The kilowatt-hour clip leaves that charge in place for the move.
decision_enginestayshours_demand_v1.38. KillHOURS_CHARGE_SURVIVES_USE=0andHOURS_LOOKBACK_FILL_DAY_BLOCK=0. All plants/keys. PicoC unchanged.
- Hours PV day: A short tail dropped by the PV-day split stays its own block. Charge before a full battery does not cover a later trough. A missing series price does not become 0 ct; the row keeps its price.
decision_enginestayshours_demand_v1.38. KillHOURS_CHEAP_REACH_DEFER=0. All plants/keys. PicoC unchanged.
- Hours PV day: The first trough's pack depth in a PV day, divided by efficiency, sits on the cheapest reachable hour of that day. An earlier PV day opens only when that day cannot hold minimum SOC. A full port with nothing missing is dropped.
decision_enginestayshours_demand_v1.38. KillHOURS_CHEAP_REACH_DEFER=0. All plants/keys. PicoC unchanged.
- Hours night charge: Needed grid charge sits on the cheapest reachable hours of today and tomorrow. Gaps stay empty. From D+2 only when those two days cannot hold minimum SOC. Slot caps stay unless they raise the grid bill. Discharge across midnight runs through the end of the next calendar day.
decision_enginestayshours_demand_v1.38. KillHOURS_CHEAP_REACH_DEFER=0. All plants/keys. PicoC unchanged.
2026-10-09
- Portal/Sparvo — fixed tariff: The daily fetch reads at most one step per hour. The hour is fixed from the postcode and the 100 kWh step, not per plant. Turning the comparison on in the plant profile still fetches at once. Kill
ECONTROL_TK_STAGGER=0. All plants/keys.
- Portal/Sparvo — fixed tariff: The first page after the toggle is turned on does not stay on “being read” once the quote is in the cache. The plant-outlook cache (120 s) re-reads the tariff row on the next request. The page reloads at most 16 times, every 15 s. All plants/keys.
- Portal/Sparvo — fixed tariff: A postcode with only one grid operator already has that field set and disabled. It is not selected again. Otherwise the fetch stores no quote and the tile stays on “being read”. All plants/keys.
- Portal/Sparvo — fixed tariff: A missing complete quote (energy, all-in, supplier) for that postcode is fetched at once, including inside 23 h. The tile says it is being read, instead of the 11.4 ct fallback. The same postcode is not queued again while that scrape is running. A heavy-ML skip does not count as an attempt. An attempt that stores nothing waits 10 minutes. All plants/keys.
- Portal/Sparvo — fixed tariff: The all-in price stays the year total for the 100 kWh step. The cent list also shows the grid standing charge. The energy standing charge stays the €/month line. Energy plus the cent list plus that monthly amount times 12 divided by the step is the all-in price.
bis einschließlich N Jahrefrom 18 andN- bis M-Jährigeare an age limit, including next to a price guarantee. All plants/keys.
- Sparvo — plant info: Saving with the fixed-tariff comparison on or off stays a response. The active plant key is read before the write. All plants/keys.
- Portal/Sparvo — fixed tariff: The benchmark stays the form. A 12-month price guarantee stays. An age limit, a consumption cap under 100,000 kWh/year, and a new-customers-only tariff are left out, and only inside that product’s own text. The company home page does not count. The 0–100,000 kWh band and a new-customer bonus stay. A finished scan with no eligible fix removes that step, then the tile shows 11.4 ct without the supplier note. A failed or unfinished scan keeps the previous row. Kill
ECONTROL_TK_HOUSEHOLD_ONLY=0. All plants/keys.
- Portal/Sparvo — storage size: The annual average is grid charge plus other grid import from the same run. Grid charge is booked from the start of the day on the cheapest hour in the sun window, not on the cheapest clock hour of the calendar day. The sum stays the annual grid import. All plants/keys. Hours Decide has no annual euros.
- Portal/Sparvo — fixed tariff: The benchmark comes from the form on e-control.at/tarifkalkulator (postcode and grid import in 100 kWh steps). The partner API and
rc-public-restare not called. The tile reads the cache. A quote stays visible for up to 7 days. The energy price is stored only when it is below the all-in price in the cent column. A year total in euros is not stored as ct/kWh. A higher detail price does not replace the list price. Under the product name, the branded tile says that age, new-customer and promotion conditions are on the supplier page. That applies to every plant with E-Control branding. The three fixed-vs-dynamic labels stay. All plants/keys.
- Hours sun window: The weak remainder and the lookback delta move to an hour at least 5 €/MWh cheaper inside the same sun window (morning sun to the next morning sun), and not before the current hour. In the week cost, only the lookback delta moves, and it is scored on the full charge map. JSON
trueis not an hour. The chart stays on the calendar day. Survival without lookback, MUST, prefill, spill, interim, prep, and opp stay put. Kill staysHOURS_COVER_EARLY_PLACE. All plants/keys. PicoC unchanged.
2026-10-08
- Sparvo Plan: When capacity and state of charge are known, an hour is priced only on the extra grid import after PV and the battery. Sequential mode may overlap when that extra import is 0 in the shared hour. A near tie prefers more PV, then an hour between 07:00 and 22:00. A frozen day stays until it is replanned. A missing state of charge is not replaced with 30 % and is not passed to the next day. From the current hour, the savings card compares only with hours that can still be chosen. The SNAP bias does not turn a still-positive exchange price into income. When devices run together only because the price is below zero, the reason says so. JSON
true/falseis not a price, a kWh value, a state of charge, a base load, a grid connection, a PV-day total, or a device power; an invalid value falls back to the preset. A blocked hour, a must-run hour, or a weekdaytrueis not hour 1 and not Tuesday. KillSPARVO_PLAN_BATTERY_COVER=0. All plants/keys.
- Flex Windows: JSON
true/falseis not hours already run and not a kWh value in the recipe. A weekday or a windowtrueis not Tuesday and not 01:00. Invalid numbers count as 0. All plants/keys. PicoC unchanged.
- Display: JSON
true/falseand invalid numbers are not a spot amount. As a markup or kWh they count as 0. All plants/keys.
- Discharge, week cost, storage size: JSON
true/falseis not a discharge power (the 9 kW default stays), not a state of charge, and not a week price of 1 €/MWh. In the storage recommendation it is not a 1 kWh capacity and not 1 kWp. All plants/keys. - Hours explain and stockpile: JSON
true/falseis not a night or horizon hour, not a stockpile price, and not a 1 € ceiling. It is not a dual-contract day and not an EMS-clear counter. A preserve or hold indextrueis not hour 1. A session countertrueis not a kWh value and not an hour. The night state-of-charge roll does not count it as pack capacity or load. Explain does not count it as an hour, a kWh value, a window, or a buffer. A profile fieldtrueis not a state of charge, not a grid connection, not an annual load, not a meter total, not a kWp value, and not 100 % efficiency. In the lookback merge and on the week map it is not 1 kWh. All plants/keys. PicoC unchanged.
- Hours top-up: A top-up before the floor stays only when that hour is at least 5 €/MWh cheaper than the floor hour. Otherwise the house buys in the floor hour. A missing floor price skips the top-up. The sun window then stays open for the next floor. All plants/keys. PicoC unchanged.
- Hours lookback: A daytime hour blocked by the soft cap is used when it is at least 5 €/MWh under the cheapest night hour and it does not curtail PV. A morning above min+10 pp does not keep that hour blocked. A further hour that would curtail PV together with energy already placed is skipped. The probe adds only the free grid port, discharges a conserve hour only up to its cap, and stops at the plan target SoC. JSON
true/falseis not a price, a kWh value, or a PV block (shared price read including P20, Hold, Weak, moves and P33, efficiency, PV recovery, and sun windows). Feed-in, residual carry, interim, prep reserve, the discharge move, and the cheapest-hour sort do not count it as 1 kWh. In the Decide price series, the Weak fill, and the day price,truedoes not fill forward as 1 €/MWh. Neighborhood and thin-cover carry, and lookback-thin, do not count it as house load or charge power. Cover deficit, the autark gate, early cover, and the discharge move do not count it as capacity or charge power. The hold pack and the aligned PV series do not count it as kWh. Cold-start PV and the remaining day load do not count it as kWh. The night card does not count it as hours or kWh. The empty-reason does not count it as charge power. The cover window and the horizon load do not count it as kWh. Weak DP and the Decide mix do not count it as capacity, charge power, or a grid-connection limit, and cap-skip does not treat the pack as full. Must-run, prep, and lookback hour indexes do not counttrueas hour 1, and the charged energy total does not count it as 1 kWh. All plants/keys. PicoC unchanged.
2026-10-07
- Sparvo Plan: The day plan uses the device schedule (weekday, from–to, duration).
00:00–00:00stays the whole calendar day. A window that crosses midnight continues on the next date; those kWh count only there, and a partial last hour stays partial. A pinned night stays put when the budget repair replaces hours. Dynamic reshape scores hours after midnight on the next day's curve and does not pull a device in front of the one it must follow. A new run starts only after that device's own continuation. Must-run hours belong to that same run and are not booked twice. Touching hours of that run show as one block. A must-run hour that does not fit the window or the duration is left out and named on the plan. Flex Windows stays price-only. The request and response of/v1/decision/windowsand/v1/plant/device-schedulesare unchanged. All plants/keys.
- Hours slot placement: The top-up before PV recovery searches from the first hour of the PV block, not only after dusk. A full grid port or a battery at cap takes the next cheaper hour. The top-up is seated again after the early move on the conserve roll, so a conserve hour does not invent a floor. An hour that already grid-charges stays a charge hour on that roll. If the floor is already on the first search hour, the sun window stays open for the next floor. Plain cover moves to the earlier cheaper hour inside the same sun window (
HOURS_COVER_EARLY_PLACE, default on), after neighborhood rebalance and before planned house draw. A frozen row on the target hour stays; the kWh sit beside it. The annual cost tile moves only cover that was frozen beside weak-PV. The “no earlier hour” chip applies to weak-PV only.decision_enginestayshours_demand_v1.38. KillHOURS_COVER_EARLY_PLACE=0. All plants/keys. PicoC unchanged.
2026-10-04
- Feed-in is the day total: Today's
export_feed_kwhis energy already fed in plus what is still forecast, not only the remainder from the current hour. The battery chart still starts from live SOC at the current hour. Completed hours come from SOC pings (with a battery and no ping those hours are not invented; with no battery the surplus counts). D1…D6 stay the full day. Display unchanged, 1 decimal place. Not in the Hours JSON and not in the Loxone text. All plants/keys.
2026-10-03
- Feed-in visible on every plant: The same
export_feed_kwhroll for every active key. The line and the day chips always show the kWh, including 0.0. 0.0 means load and the battery take the PV (or there is no PV). All plants/keys.
- Feed-in over the 7-day horizon:
export_feed_kwhis the PV surplus (AC kWh) that load and the battery cannot take that day. As of this day: the remainder from the current hour. From 2026-10-04 the figure is the day total (entry above). Shown on Sparvo Plant and portal plant outlook (PV · Load · Feed-in, day chips and day buttons always, including 0.0). One decimal place. Not in the Hours JSON and not in the Loxone PV text. All plants/keys.
- Hours horizon: Conserving into the next morning applies across the full 168 h without buying extra kWh. The pre-sunrise top-up runs for every night of the 7-day PV forecast, not only the first morning. Cross-day purchases stay on the lead band through D+2. Load, PV and price are the same hourly vectors. All plants/keys.
- Hours pre-sunrise top-up: The extra kWh are their own
prefill_breachrow (stillsurvival). Charge already planned on that hour stays movable. Day consolidate does not drop this row and does not move it past the shortfall; another consolidate move is kept only when the floor still holds before PV recovery. All plants/keys.
- Hours storage plan v1.38 (default on): One hold wrapper for Decide, weak-PV and family B. The battery is reserved for later peaks, discharge moves onto dearer hours without extra kWh, and a cheap night may be conserved into the next morning only through D+2. Price peaks add to the clock peaks only when the day's prices are complete. A top-up before PV recovery stays inside the morning cap. Lookback may use a daytime hour only when it is cheaper than the night, the morning cap holds, and it does not curtail PV. Negative prices (below 0, lead band 0) do not depend on the plant opportunistic switch. The day foot shows minimum-SOC grid draw separately; the planned total stays X+Y+Z.
decision_enginehours_demand_v1.38. KillsHOURS_HOLD_LATER_PEAK_RESERVE,HOURS_HOLD_DISCHARGE_PING_PONG,HOURS_HOLD_PRICE_PEAKS,HOURS_HOLD_MIDNIGHT_CARRY,HOURS_PREFILL_BREACH_TOPUP,HOURS_LOOKBACK_DAY_STAMP_PROBE,HOURS_OPP_NEGATIVE_PRICEeach=0(thenv1.37again while the lead band is on). All plants/keys. PicoC unchanged.
2026-10-02
- Portal/Sparvo — gross/net: Fixed-tariff energy and standing charge follow the VAT toggle on the prices page (same user setting, all plants). The E-Control working price is net; gross is that value × 1.20, not divided again.
- Portal/Sparvo — fixed-tariff query: E-Control is asked for the expected residual grid import (whole kWh), not the plant's annual consumption. No fetch without that residual. All plants/keys.
- Portal/Sparvo — storage payback: Hardware = 160 €/kWh net × 1.20 (installed, linear). No 16 kWh pack price and no second EMS lump. Visible years and euros use the same basis as the bars (surcharge/VAT). If the battery is above the sweet-spot bucket, the short explanation says “above the rule of thumb”, not “inside the range”. All plants/keys.
- Hours weak-PV forward rentability (default on): A weak-PV grid charge stays only when the house imports it actually avoids are more expensive than the purchase after efficiency (ε 3 €/MWh, full discharge to the floor, no supplier markup). The evening-average gate still admits hours. A flat price tail after an already covered evening is dropped. Note
weak_pv_forward_unrentable. KillHOURS_WEAK_PV_FORWARD_RENT=0. Decide, DA-DP and family B. A neighborhood move that shrinks the rentable remainder is reverted (neighborhood_forward_rent_revert). All plants/keys. PicoC unchanged.
- Hours econ lead band (default on): absolute euros only through D+2; from D+3 only the within-day shape. Note
econ_lead_band,decision_enginehours_demand_v1.37. Point price unchanged; dump scenario is not the control price. A typ-week without forecaststartsstays price-ordered. The bridge target hour is band 0 only when horizon days are supplied (a D+6 price under the dump threshold does not stretch the trough). KillHOURS_ECON_LEAD_BAND=0restoreshours_demand_v1.36and the previous price order. Spec HOURS §6.8.19 + wiki Hours. All plants/keys.
2026-10-01
- Hours weak-PV redundant peel: Grid charge that does not reduce house import inside the same PV day (sunrise to sunrise) is dropped — typically the inverter-port remainder just before PV fills the battery on its own. Necessary cheaper hours before that stay. Kill
HOURS_WEAK_PV_REDUNDANT_PEEL=0. Explain grid on a charge row is charge kWh plus house residual (portal and Sparvo, same card). Holds are replanned from the fill that remains after the peel. All plants/keys. PicoC unchanged.
2026-09-29
- Portal/Sparvo — E-Control fixed tariff (try-mode live): Cheapest household fix (
GUARANTEE) for every plant with an active API key + AT postcode, 24 h cache, timerspotforecast-fix-tariff-refresh(HA primary, distinct PLZ×band — load band plus lower bands/existing cache keys, because Outlook prefers import band). Layer-A tile energy+all-in + attribution; expand excl./incl. VAT + To supplier (changeLink.link); layer B admin (pending only when Fixtarif path is active, not non-AT); opt-outfix_tariff_display_enabled(Sparvo power toggle On/Off, default on; save last-of-duplicate hidden-0+checkbox-1; guest"0"= off; Portal/Sparvo save+geocode/reverse including early exitsempty_address/not_enough_input/invalid_coords/invalid_postal/profile errors; Sparvo geocode ignores bad form coordinates). KillECONTROL_TK_ENABLED=0. Fetch fail-closed: 4-digit PLZ only, finite annual kWh (no JSON bool/inf), HTTP body cap, skip malformed cost objects; live compare without fakecost_dyn(fix×import). Cache/compare ct finite (poison cache → fallback; bad env floats do not crash Hours import). Hours Decide unchanged (basis.storage_horizonhas no E-Control fetch / annual €). Spec plant-profile + Fixtarif concept. All plants/keys.
2026-09-20
- Storage-size Outcome Guard fail-closed (SSR): Missing/invalid outcome dict for the active scenario → fail_closed; Kill=
0ignores leftover dicts (onlyoutcome_guard_enabled); JSON attrs single-quoted. Spec plant-profile Hybrid. All plants/keys.
- Storage-size Outcome Guard: Display title/cap/€/delta/bar-3 only from
display_outcome_{pv,bridge}(never costlier tip cap; tip only outcomesinnvoll; collapse/fits/warn/grid/fail). KillSTORAGE_HORIZON_OUTCOME_GUARDdefault on (=0→ legacy worked example). Soft-link = candidate ∧ edit; citizen fixed-tariffcollapse|fix. Hours Decide unchanged (outcomes null). Spec plant profile + HOURS §6.6. All plants/keys.
- Storage-size Hybrid-UI audit-clean: Incomplete/empty toggle defaults to PV-only; Portal horizon-html soft-link gate = plant-location owner (same as page); toggle refreshes bar Ø-ct + delta € with VAT; bar-3 label = right title; amort-extra only on profile scenario. Spec plant-profile Hybrid. All plants/keys.
- Storage-size hybrid UI + dual-annual: PV-only / with-grid-charge toggle + 2 columns + 3 bars (Portal/Sparvo/guest); dual annual € for PV/bridge additive; kill
STORAGE_HORIZON_DUAL_ANNUALdefault on; “Fits” when scenario cap ≈ current; upsizing = copy gate; soft-link owner/edit only; fail-closed “—” without dual €. Hours Decide unchanged (annual fields null). Spec plant profile + HOURS §6.6. All plants/keys.
2026-09-17
- Storage-size dual scenarios (Portal/Sparvo/guest): Grid-charge On|Off status; always both if-off / if-on blocks (incl. incomplete + empty hydrate stub); “Fits” only when grid charging is on; upsizing gate;
assumptions.annual_load_sourcemeter|profile. Hours Decide unchanged (annual fields null). Spec plant profile + HOURS §6.6. All plants/keys.
- Storage dual-kWh / bridge sweetspot live (Phase 3): Additive
sweet_spot_pv_kwh/sweet_spot_bridge_kwh/recommendation_mode(grid_off|pv|bridge). Code kill default remains off (unset/=0→ active = PV). User-Go 2026-09-17: hosts HP+Lenovo=1inaccount.env→ with grid charging on, bridge is the active recommendation (sweet_spot_capacity_kwh). Grid-off: no upsizing. Rollback:=0+ restartaccount-portalon both hosts. Evidence:storage_bridge_sweetspot_evidence_20260917. All plants/keys.
2026-09-16
- Hours Soft-Cap Weak-resim + Spill-€ JSON-bool scrub: Soft-Cap post-trim
end_socresim, Econ-PP Soft-Cap SoC/stamp maps, Soft-Cap-Spill euro proxy, gate-filter/Econ-redistrib charge maps scrub JSON-bool/NaN→0 (nofloat(True)=1). Spec HOURS §6.8.15/§6.7 + wiki Hours. All plants/keys.
- Hours Soft-Cap SoC-path JSON-bool scrub: Shared Soft-Cap SoC path (
roll_soc_hour/forward_soc_path/stamp/Lookback stress+place/O4 refresh/plan_roles/NB+Thin residual) treats JSON-bool/NaN as 0 (nofloat(True)=1Soft-Cap Σ). Spec HOURS §6.8.15/§6.7 + wiki Hours. All plants/keys.
- Hours Soft-Cap Cover-worsen + Spill scrub: Consolidate Soft-Cap gate rejects Cover Soft-Cap-worsen even when Soft-Cap already broken on orig (pre-stamp Cover Σ↑); in-horizon Soft-Cap trim scrubs JSON-bool even when
soft_rel=None; Soft-Cap spill/clip/same-day maps JSON-bool→0. Spec HOURS §6.7/§6.8.15 + wiki Hours. All plants/keys.
- Hours Soft-Cap survival/MUST peel + consolidate Cover gate: Soft-Cap peel exempts lookback/survival/MUST (
softcap_may_peel_row); consolidate Soft-Cap gate always runs — Cover-only Soft-Cap break → reject (survival delta may Soft-Cap-breach); Econ-PP base/frozen JSON-bool→0. Spec HOURS §6.7/§6.8.15 + wiki Hours. All plants/keys.
- Hours Soft-Cap boundary JSON-bool scrub: Soft-Cap trim early-hold exit scrubs
energy_kwh/cover_merged;softcap_stamp_holds/in-horizon map/LThin map+lookback_added/resolve Soft-Cap-adjacent charge maps treat JSON-bool/NaN as 0 (nofloat(True)=1Soft-Cap Σ). Spec HOURS §6.7/§6.8.15 + wiki Hours. All plants/keys.
- Hours consolidate JSON-bool kWh: Day-consolidate
_energy/ prep Σ treat JSON-bool/NaN as 0 (nofloat(True)=1Soft-Cap/budget inflate); week-status charge Σ likewise. Spec HOURS §6.8.15/§6.7 + wiki Hours. All plants/keys.
- Hours Soft-Cap relocate JSON-bool floor:
split_cover_merged_on_relocate+ Neighborhood/Thin-Defer/LThin Σ treat JSON-boolcover_merged/energy_kwhas 0 (nofloat(True)=1floor on relocate). Spec HOURS §6.7/§6.8.15/§6.8.18 + wiki Hours. All plants/keys.
- Hours Soft-Cap JSON-bool kWh (shared): Soft-Cap identity/peelable/trim/Lookback-Thin maps treat JSON-bool/NaN kWh as 0 (no
float(True)=1); merge normalizes str map indices. Spec HOURS §6.7/§6.8.15 + wiki Hours. All plants/keys.
- Hours Lookback merge JSON-bool / MUST-kind / non-cover role: JSON-bool
energy_kwhcounts as 0 (nofloat(True)=1Soft-Cap floor); coalesce setscharge_kind=mustwhen MUST (no leftover darf/weak_pv); non-coverslot_role(e.g. floor) stays frozen; new Lookback slots sanitize NaN/Inf load/PV. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge non-finite kWh: Sibling/map
energy_kwh/cover_merged/lookback_addedthat are NaN/Inf/unparseable count as 0 (no Soft-Cap floor poison, no merge crash). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge abs-map unparseable/out-of-window: Unparseable or out-of-
starts-window non-spill rows are zeroed/dropped (no Soft-Cap Σ inflate); spill stays exempt. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge freeze-role coalesce: Prep/Opp/Bridge∪Weak same-hour coalesce keeps freeze
slot_roleon the sink (Neighborhood / Thin-Cover-Defer stay frozen); blankstartrows dropped. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge survival coalesce: Cover∪Weak same-hour coalesce keeps sibling
survivalon the sink (Thin-Now / Neighborhood / Defer stay frozen). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge interim-omit + MUST coalesce: Interim omitted from the absolute map keeps the pin row but zeros energy (no Soft-Cap Σ inflate); same-hour coalesce ORs
mustonto the sink. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge abs-map + zero-Δ lookback: Hours omitted/0 in the absolute map are dropped (no leftover Soft-Cap Σ);
lookback=Trueonly when lookback Δ>ε (zero-Δ no longer peel-exempts Weak). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge coalesce naive-tz: Naive slot
startassumes map/start0/Vienna tz (not UTC) so siblings coalesce and Soft-Cap Σ cannot inflate. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge coalesce by logical hour: Sibling match uses parsed UTC hour (not string equality) so
str(dt)vsisoformat()cannot leave a second row and inflate Soft-Cap Σ; sinkstartnormalized to map iso. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge Soft-Cap floor on coalesce: Same-start Cover∪Weak coalesce keeps Cover energy as
cover_merged_kwhfloor (no bareweak_pv→ Soft-Cap Natural reopen); spill flags transfer to sink. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback merge sibling coalesce + Familie B Soft-Cap baseline carry:
merge_lookback_into_charge_hourscoalesces same-start siblings to absolute map Σ (no Soft-Cap inflate); Familie B LThin Natural only when Weak overlay ∧ ¬Cover fill ∧ ¬Lookback movable;week_had_*survives PP forced re-sim. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback-Thin Weak per-index + Familie B Soft-Cap baseline: Weak Soft-Cap identity is per movable hour (Cover siblings no longer inherit
weak_pv); Familie B LThin Natural only when Weak overlay filled and no Cover fill remains (not stamp presence). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback-Thin Weak Soft-Cap identity + split clamp: Rebuild keeps
weak_pvon peelable movable; prior-Weak frozen remnant keepsweak_pv(Cover-floor remnant still clears it);split_cover_merged_on_relocateclamps floor ≤ source energy; Familie B LThin Soft-Cap Natural when Weak-week stamps present. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Consolidate Soft-Cap plan-level + Lookback-Thin floor/probe: Soft-Cap mode = day pool + fixed
other_slots(Lookback Cover no longer day-scoped Weak-only→Natural); Lookback-Thin carriescover_mergedon drop/relocate and probessoftcap_stamp_holdsCover/Mixed absolute / Weak-only Natural (Thin/NB parity). Spec HOURS §6.8.15/§6.7 + wiki Hours. All plants/keys.
- Hours Consolidate min_head overshoot + Lookback-Thin cover_merged: Cover crumb
< min_headkeepscover_merged_kwh(Mixed absolute, never bare Weak→Natural); Soft-Cap demote clampscover_merged≤energy; Lookback-Thin frozen/movable split viasplit_cover_merged_on_relocate. Spec HOURS §6.8.15/§6.7 + wiki Hours. All plants/keys.
- Hours Consolidate Mixed Weak-budget: Weak budget excludes
cover_merged_kwhfloor (softcap_weak_peelable_kwh) — counting full Mixed energy as Weak stamped Cover→weak_pvand Soft-Cap Natural reopen after day-consolidate. Spec HOURS §6.8.15 + wiki Hours. All plants/keys.
- Hours Thin/NB Mixed cover_merged relocate: Relocate splits/carries
cover_merged_kwh(Weak first, floor travels with Cover energy) — full Mixed move dropped the marker and reopened Soft-Cap Natural. Spec HOURS §6.7/§6.8.18 + wiki Hours. All plants/keys.
- Hours Soft-Cap Mixed post-apply absolute:
trim_weak_pv_charges_to_softcap+ Thin/Neighborhood/Consolidate Soft-Cap mode viasoftcap_charge_rows_weak_only— Cover/Mixed (incl.cover_merged_kwh) absolute / Weak-only Natural; always-Natural after Cover∪Weak merge let Soft-Cap reopen. Thin trim Σ-guard → revert (Neighborhood parity); Why Neighborhood moved > dest-skip. Spec HOURS §6.8.15/§6.7/§6.8.18 + wiki Hours. All plants/keys.
- Hours Econ-PP Soft-Cap dest-skip note hygiene: Intermediate
weak_econ_ping_pong_softcap_dest_skipis dropped after a successful move (Neighborhood parity); Why prefers moved > dest-skip > no_move. Spec HOURS §6.8.15/§6.8.10 + wiki Hours. All plants/keys.
- Hours Explain Why hydrate + Thin Soft-Cap revert: Lookback codes
lookback_fill_day_block/lookback_far_softcap_guardin the Explain JS i18n blob (hydrate dropped SSR chips whenwhy_*was empty); Thin post-move Soft-Cap revert writes notethin_cover_softcap_revertinto snapshot/plan_why_chain(Neighborhood parity). Spec HOURS §6.7/§6.8.10 + wiki Hours. All plants/keys.
- Hours Thin-Cover Soft-Cap dest-skip Why: Probe fail without a move writes note
thin_cover_softcap_dest_skipinto snapshot/plan_why_chain(Neighborhood parity); Explain DE+EN. Spec HOURS §6.7/§6.8.10 + wiki Hours. All plants/keys.
- Hours Econ-PP Mixed Soft-Cap absolute: Dest Soft-Cap probe = Cover∪Weak stamp (
softcap_stamp_holds); Cover/Mixed absolute / Weak-only Natural; frozen Cover in port/SoC — Natural Weak-only map let Mixed Soft-Cap reopen. Spec HOURS §6.8.15 + wiki Hours. All plants/keys.
- Hours Mixed Soft-Cap absolute (Thin/Neighborhood): Dest Soft-Cap probe is plan-level Weak-only (not source-row); Mixed Cover+Weak stays absolute; same-flag sibling merge (no Cover→
weak_pvflip). Spec HOURS §6.7/§6.8.18 + wiki Hours. All plants/keys.
- Hours Cover Soft-Cap absolute on Thin/Neighborhood: Neighborhood + Thin-Cover-Defer Soft-Cap stamp probe = Cover/Mixed absolute / Weak Natural (
softcap_stamp_holds, consolidate parity) + post-move Soft-Cap check → revert — Natural probe let Cover Soft-Cap reopen above morning_cap. Spec HOURS §6.7/§6.8.18 + wiki Hours. All plants/keys.
- Hours stop display bool-guard (Why + Explain JS + chip merge): Why page and Explain JS reject JSON bool stop (no
fmt_num(true)/Number(true)=1); chip mergecoerce_charge_stop_soc_pct; Spec §6.2 EMS default Extract v1.23/Fetch v1.17; client session-poll regression tests. Spec HOURS §6.2/§6.7 + wiki. All plants/keys.
- Hours
charge_nowclient bool-guard + PicoC spec: Portal/Sparvo/live-telem session poll only whencharge_now === 1/"1"(neverNumber(true)=1); Explain Jinja stop not|inton JSON bool; Spec §14 PicoC Fetch v1.17 / Extract v1.23. Spec HOURS §6.7/§14 + wiki Hours. All plants/keys.
- Hours Familie-B Soft-Cap stamp rebuild: Jahres-€ Lookback/Lookback-Thin Soft-Cap stamps = Weak-week ∪ sun→sun
plan.hi(merge_softcap_stamps_with_rebuild) — Weak-off left_lb_softcap=Noneand Soft-Cap probe no-op. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours
charge_nowbool-guard (UI/live-telem/webhook): Sharedcoerce_charge_now— JSONtrue/falseneverint(True)=1on snap write, Explain UI, live-telem, session-hide, webhook payload/material key, EMS prefix, and session-complete. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback-Thin Soft-Cap stamp probe: Relocate dest Soft-Cap probe (
softcap_natural_baseline_holds) — peel-exempt Lookback must not fill Soft-Cap daytime after Far Guard; Decide + Familie B. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours stop-reader bool-guard (session-complete + UI): Session-complete replan and session-complete overlay/
charge_stop_soc_pctUI read stop only viacoerce_charge_stop_soc_pct(JSONtrue/falsenever stop=1);charge_nowbool → idle. FetchPlantHours comment + wiki O12 engine v1.36. Spec HOURS §6.7 + wiki Hours/Loxone. All plants/keys.
- Hours Thin Soft-Cap stamp probe (Cover): Thin-Cover-Defer Soft-Cap-probes dest before commit (
softcap_natural_baseline_holds, Neighborhood parity) — Cover Soft-Cap not reopened when Weak is off; Weak trim remains safety net. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Soft-Cap stamps Thin/Neighborhood (Weak-off): Decide passes Soft-Cap stamps = Weak defer ∪ Near∪Dawn∪each sun→sun
plan.hi(merge_softcap_stamps_with_rebuild) to Lookback and Thin-Cover-Defer / Neighborhood — when Weak is off, Cover Soft-Cap still gets stamp probe. Spec HOURS §6.8.18/§6.2b + wiki Hours. All plants/keys.
- Hours sticky stop writer bool-guard: Prior-snap/publish stop (
coerce_charge_stop_soc_pct) on latch sticky republish, snapshot write, webhook payload, material key, and timeline stamp — JSONtrue/falsenever becomes stop=1. Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Lookback Far Soft-Cap stamp rebuild: Soft-Cap stamps for Far guard = Weak defer ∪ Near∪Dawn∪each sun→sun
plan.hi(rebuild_lookback_softcap_stamps) — dawn-only left Soft-Cap daytime peel-exempt in Far JIT (Slack↑). Spec HOURS §6.2b + wiki Hours. All plants/keys.
- Hours Dual warm-up Geo
cache_only:estimate_pv_series_kwh(cache_only=True)/ Dual stitch never Open-Meteo refresh — missing cache →sun_sun_ready=false. Spec HOURS §14.5b + wiki Hours. All plants/keys.
- Hours numeric input bool-guard (series + constraints): Client
load.hours/pv.hours/energy_next_24h_kwh, constraintsmax_hours*/min_hours, snap stop, and PV-arraykwpfail-closed like battery/horizon/latch — JSONtrue/false→invalid_inputor field omit (neverfloat(True)=1/int(True)=1). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours numeric input bool-guard: Request numbers (
battery.*/ meter / PV scalars / opp /horizon_hours) and latch live/stop accept only int/float/str/Decimal — JSONtrue/false→invalid_inputor latchincomplete_hold(neverfloat(True)=1/int(True)=1/float(False)=0). Spec HOURS §6.7 + wiki Hours. All plants/keys.
- Hours Familie-B Lookback soft-cap stamps: Week-€ path passes soft-cap stamps into Lookback (Decide parity / Far-guard still needs
dawn_cap→ no-op without dawn; Fill-Day block is independent of soft-cap). Spec §6.2b. All plants/keys.
- Hours Lookback Dawn-Scope + Fill-Day + Soft-Cap-Far + Dual warm-up (
hours_demand_v1.36): Intentional engine jump fromv1.32. O2 first stress cluster + O5b Far-JIT 36 h (HOURS_LOOKBACK_DAWN_SCOPE); Fill-Day survival block (HOURS_LOOKBACK_FILL_DAY_BLOCK); Far Soft-Cap cohort + natural probe (HOURS_LOOKBACK_FAR_SOFTCAP_GUARD); warm-up Dual-Cycle calendar ≥18/24 ∧ sun→sun (HOURS_COLD_START_DUAL_CYCLE, default on; ops first Dual=0). Spec HOURS §6.2b/§14.5b + wiki Hours. All plants/keys.
- Hours latch survival-below-floor rearm: Sticky stop−H no longer holds below the reserve floor (live < min_soc−0.5 pp) — Decide opens with
rearm_below_floor(prefixcharge_now); above floor unchanged hold until stop−H. Extract v1.23 follows server SoT (charge_now=1clears local). Spec HOURS §6.7 + wiki Hours/Loxone. KillHOURS_CHARGE_HYSTERESIS=0unchanged. All plants/keys.
- Hours grid-off Speicherschonen evidence: Display strip requires real hold evidence (
hold_hours/hold_hours_local/status=hold) — stalehold_hours_nalone does not invent Speicherschonen clocks. Portal explain: live-telemcharge_now=0also under cold-start pause. Sticky?api_key_id=syncs the session only for ACL+active keys. Spec HOURS §14.5b + ANLAGENPROFIL. All plants/keys.
2026-09-15
- Hours Cold-Start / Warm-up Pause: Until
load_profile_ready(meter Typical ≥1 full day, client load, orallow_flat_default), Decide publishes OK with empty charges/holds,charge_now=0, latch false,charge_stop_soc_pct: null(snap+webhook explicit), O12 max/at_min(+ EV-flex), syntheticweek_statuswhy_code=cold_start. Pause on meter warming (profile stamps/PlantLoadSample) or annual-only <72 h (missing activation stamps / ApiKey lookup fail → fail-closed pause; meter-count DB errors do not invent warming). Warm-up exit clears sticky snappause_reason(Explainempty_cold_startonly while paused).load_covered_by_soc_pv=false+ Outlook/empty_reason=cold_start— week glance D0 Warm-up, never Covered; Explain chip/headlinechip_cold_start/headline_cold_start(notchip_none). Pause ∩ grid-off → pause wins only whilehours_control_paused(top-level/basis; stickynotesalone are not enough): display strip setscharge_kind=none, clears holds, and rewritesweek_statuswhy/empty_hint/groups tocold_start(nevergrid_off/hold/covered). Pause with grid still on: same display mute without inventinggrid_off(UI format + Outlook snapshot-first/live-hydrate forcehours_needed*/energy_charge_kwh/next_charge_*to 0; live-telemcharge_now=0). Grid-off only: half-healedstatus=chargewith hold evidence → Speicherschonen (hold), not autark. Tombstone webhook setshours_control_paused=false. KillHOURS_COLD_START_PAUSE=0(default on). Webhook fieldhours_control_paused. Portal/Sparvo warm-up banner + Explainempty_cold_start. Spec HOURS §14.5b. All plants/keys.
- Hours max grid-charge Cap (
hours_demand_v1.32): Profile fieldmax_grid_charge_kw(default 8,NULL→8 without backfill) as hard ceiling:available_kw = min(inverter, connection×0.80−base, Cap). O11/MUST = available; Darf and Weak-PVP_econ≤ Cap (Cap 0 → 0, no invented C×capacity). Cap/headroom 0 with an uncovered night → UIcharge_empty_reason=no_charge_power/ week glance “No charge power” (never a false “Covered”). Portal/Sparvo/Guest slider + plant-outlook rules bar; material key includescharge_power_kw. Full-form blank→NULL (engine 8); partial forms without Cap field preserve DB value. Spec ANLAGENPROFIL/HOURS + wiki Hours/Loxone/Webhooks. All plants/keys.
- Grid charge off deep-audit: Decide-OK without a persisted Hours snap flashes
error_stale(never silent ok). Outlook hydrate publishes on grid-off + uncleared snap (prefix_*/today slots/plan_hour_rolescharge/energy_charge_kwh/nestedday_planincluded;resolved_clearfromresult.hours_needed) instead of silent persist. Snapshot-first + DA replan force a clear when the profile is off but the snap is still charged (grid_off_uncleared_snap/ fail-closedgrid_off_clear_check_failed). Non-ok DA under grid-off + charged snap → new tombstone + one webhook. Live-telem /hours_decision_ui_payloadandui_payload_from_resolvedforcecharge_now=0/ empty Demand narration (incl. charge roles/week_status/hours_needed_days/next_charge_*/projected_soc_pct_*_with_grid) while the profile is off; display strip also heals half-healed days (status=charge/charge_hold/charge_kwhdespitewhy=grid_off; hold evidence →status=hold) tostatus=autark/charge_kwh=0, rebuildsgroups, drops staleteaser/lines(no localize “Charge …” teaser); clear-gate checks week_status charge residue; Outlook does not reinject Mit-Netz SoC under grid-off/warm-up;empty_reasonprefersgrid_off(warm-up first).
- Grid charge off → EMS Demand/LoadNow: Server clears plan/
charge_nowimmediately; Portal/Sparvo flash whenplant.plan_updatedis missing (webhook_not_configured/webhook_failed). Telemetry partialgrid_charge_allowed=0echoescharge_now/prefix_hours_needed_today/hours_needed*= 0. EMS without webhook: next Hours Decide. All plants/keys.
- Portal Community-only (temporary): Paid self-serve pricing/checkout hidden (
PORTAL_HIDE_PAID_PLANS). Community stays visible; Professional/OEM in Auth/_index listed as on request (no €)./preise→ Contact. Partner upsell title/lead soft-copy without self-serve €. API contract unchanged.
2026-09-14
- Hours Explain Why-Details deep-audit #5 clean: Unknown timeline kinds no longer coerced to charge (omit later/body); drop dead
softcap_boundkey; CSShx30. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details deep-audit #4 clean:
floor_prefilluses start SoC; Soft-Cap hint without double later; softcap-move before hint; Specno_legal_later. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details Phase-2 Lite (audit polish): Why hints without Soft-Cap jargon (
details_hint_*); FAQ typo; CSS tokens on.hours-why; pinhx29. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details Phase-2 Lite: UI hero/role colors/FAQ; per-segment
later_code(precedence + dedupe); always-on later removed; conserve/η copy; CSShx28. Spec §6.8.10. All plants/keys. PicoC unchanged.
2026-09-13
- Hours Explain Why-Details deep-audit #3 clean: Hold Cap≈
HOLD_KW→cheap_hold(weak_legacyparity with snapshot/O12; no false load-share label); Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details deep-audit #2 clean: Details link after shell hydrate (
data-why-detailsarm=kill+href, visibility=available); Stopp-SoC only on prefix stamp (no global fallback); hold nuance fromhold_kw→hold_reason_for_cap; clienthx54. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details deep-audit clean: Hint parity with Explain (adaptive-off + EV O12); kill fail-closed in partial; Portal runtime tests guest/kill/shared-user/owner; Spec §6.8.12
HOURS_WHY_DETAILS. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details (Phase 1) audit-complete: Sparvo login
next=/plant/hours-whywhitelist + SSO path; route tests guest/shared-user/owner/kill + snapshot-only (noresolve_hours_demand); i18n DE=EN parity. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain Why-Details (Phase 1): Shared auth page Sparvo
/plant/hours-why· Portal/plant-outlook/hours-whynarrates the snapshot (use_live_resolve=False); sibling “Details” link beside week teaser; shared-user access like Explain; killHOURS_WHY_DETAILS(default on); clienthx53/hx27. Spec §6.8.10. All plants/keys. PicoC unchanged.
- Hours Explain deep-audit #4 clean: Jinja fail-closed
discharge_power_kwvia.get; SSRuntil_pv_energy_label; wiki Pack→battery/Speicher (+ PV day-pack SoT); client pinhx52. Spec §6.8.10. All plants/keys. PicoC unchanged. - Hours Explain deep-audit #3 clean: EN wiki roles
PV + storage(was PV+pack); DE Pack-Laden→Speicherladen; SSR↔JSshow_restbars fallback; i18n blob dedupe; Spec §6.8.10 hydrate note. Portal pinhx51; SWhz6/v28. All plants/keys. PicoC unchanged. - Hours Explain deep-audit #2 clean: SSR↔JS hydrate parity (ops before Stopp-SOC;
until_pv_energy_kwh+today_autarkfor until-none); EN Battery conserve in week_status engine + chart JS fallbacks + tip; wiki Pack-Reserve→Speicherreserve; Sparvo base pinhx50, SW pinshz6/v28. Spec §6.8.10. All plants/keys. PicoC unchanged. - Hours Explain deep-audit clean: SSR
_today_cover/_rest_daysset (rest/chip SSR); EN hold copy Pack conserve→Battery conserve; fold title/tip aligned; EN wiki week_status; fold edge tests. Spec §6.8.10. All plants/keys. PicoC unchanged. - Hours Explain UI: two folds: Grid charging and “Until tomorrow morning · following days” collapse independently (shared Portal+Sparvo partial; both default closed). Spec §6.8.10 + wiki. All plants/keys. PicoC unchanged.
- Hours Explain/API heal (audit-clean): Decide top-level Thin echo (
lookback_placed_kwh/lookback_thin_*/lookback_placed_effective_kwh= Σlookback=True);plan_why_chain—econ_darf_closedonly ¬autark∧¬weak,dual_contractonly D0=0∧later>0, NB Soft-Cap probe →neighborhood_softcap_dest_skip; UI Pack→battery reserve; Sparvo pins laterhx49/hx26. Spec §6.8.10/§14.6 + wiki. All plants/keys. PicoC unchanged. - Hours Neighborhood Rebalance default ON (User-Go Dual-Gate):
HOURS_NEIGHBORHOOD_REBALANCEdefault on (kill=0). Decide-only forward move radius 18 h (clamp 4…24); Soft-Cap stamp probe + peel→revert; orthogonal to Econ-PP/Thin-Defer. Spec §6.8.18/§14.6 + wiki. All plants/keys. PicoC unchanged. Familie-B follow-up. - Hours Explain Engine Pack (WP1+WP3): UI
plan_why_chain(autark/¬Darf/Cap-Skip/PP/Thin/dual-contract chips DE+EN); Econ-PP Soft-Cap stamp probe before dest commit (softcap_dest_skip, Decide+Familie B); Neighborhood module later default ON (see entry above); Lookback early-exit PARK. Soft-Cap unchanged. Spec §6.8.10/§6.8.15/§6.8.18 + wiki. All plants/keys. PicoC unchanged. - Hours portal overlay foot X+Y+Z: Session-hide sets
grid_draw_kwh= hold+charge+direct (no proportional charge scale); spot € scales charge share only; syncsweek_grid_draw_kwh. Spec §6.7 + tests. All plants/keys. - Hours portal overlay date-aware chip strip: Session-hide ≤now is date-aware (ISO/
DD.MM.); next-day chips with the same clock stay; future ISO start wins over bareclock. Footgrid_draw_kwhscales with truncate. Spec §6.7 + tests. All plants/keys. - ExtractHoursDemand v1.23 (O9 stop-headroom) + overlay hide without capacity: O9 headroom to session stop/O4 (not target_soc); latch → rest=0. Portal session-hide even when capacity=0; sync foot
charge_kwhafter timeline truncate; drop chip hours ≤now. Spec §6.7 + wiki/samples. All plants/keys. - Hours portal overlay merged-timeline truncate: Session-hide truncates merged
plan_timelinecharge segments when now ∈ \[start…end_start] (incl. mid-span), not only exact labels — Portal Explain matches EMS LoadNow=0. Spec §6.7 + tests. All plants/keys. - ExtractHoursDemand v1.22 docs/llms sync + dead-var hygiene: llms.txt / docs-llms / overview tests / README / sample stub / retired ParsePlantHours → v1.22; remove unused
lastInChargeSlot(no behavior change). Wiki/samples DE+EN. All plants/keys. - ExtractHoursDemand v1.22 (remaining-energy baseline) + portal overlay latch-hide: API
prefix_energyis remaining SoT — Extract re-baselines SoC every run while charging;energy>0clearschargeDoneTodaybefore baseline (fix: double-subtract vs session-start SoC → Demand/O3/O9=0 despitecharge_now=1). Portal overlay hides whencharge_latchedor live≥stop−0.5 and strips current-hour charge fromplan_timeline. Wiki/samples DE+EN. All plants/keys. - ExtractHoursDemand v1.21 (charge_now SoT): Parses
charge_latched; when APIcharge_now=1, clear local session latch (Demand/O3/LoadNow follow open Decide). Else whencharge_latched=true, set local latch. Fix: wide H band left Demand/LoadNow=0 while Portal/API showedcharge_now=1. Wiki/Samples DE+EN. All plants/keys. - ExtractHoursDemand v1.20 (O4 sync):
sessionStopSoctracks APIcharge_stop_soc_pcton every extract; a mid-session raise updates O4 and re-arms the latch while live SoC is still below the new stop (fixes Loxone target 74% vs portal stop 77% drift). No live+energy invent. Wiki/samples DE+EN. All plants/keys. - Hours Soft-Cap cover-merge floor (deep-audit #3): Post-apply Soft-Cap peels Weak remainder only;
cover_merged_kwhis a floor (never strip merged Darf cover). Econ-PP exception also rolls back notes. Spec §6.8.15/§14.6 + wiki. All plants/keys. - Hours Econ-Ping-Pong deep-audit #2 (default ON): Familie-B forced-PP honors intended per-hour cap (
weak_hour_cap) even with role-aware Soft-Cap (no chronological day-budget dump into first port). Spec Status-LIVE + §6.8.15. All plants/keys. - Hours Econ-Ping-Pong deep-audit (default ON): Familie B — prior Near \(H_{gate}\) → Occupied ban (dest∉); forced-PP Soft-Cap only on Weak/PP hours (cover/prep role-aware; no false Σ-revert); flag whitespace/empty → ON. Spec §6.8.15/§14.6 + wiki. All plants/keys.
- Hours Econ-Ping-Pong default ON (User-Go):
HOURS_WEAK_EARLY_PLACEdefault on (kill=0). Decide + Familie B live. Spec §6.8.15/§14.6 + wiki. All plants/keys. - Hours Econ-Ping-Pong Familie-B Soft-Cap peel live (shipped flag-OFF; superseded default ON): Forced-PP apply uses Soft-Cap ceiling (never Cap);
week_charge= actual applied; peel=gained-dest∨Σ → revert; survival-cover occupied ban. Spec §6.8.15. All plants/keys. - Hours Econ-Ping-Pong audit-clean (shipped flag-OFF; superseded default ON): Decide exception→pre-PP restore; post-PP Cap0+Hold-Strip; no Cap fallback; Familie-B Soft-Cap Σ-revert + per-source gate + Weak target ceiling; wiki Hours DE+EN Econ paragraph. Spec §14.6. All plants/keys.
- Hours Econ-Ping-Pong audit fix (shipped flag-OFF; superseded default ON): Familie-B Lookback after PP forced re-sim; Near sticky via Occupied ban (not global
gate_hi); prep occupied ban + hold-aware SoC; Decide Soft-Cap ceiling (never Cap); Soft-Cap peel → revert; exception logging. Spec §6.8.15/§14.6. All plants/keys. - Hours SoT stop-SOC display + Econ-Ping-Pong (shipped flag-OFF; superseded default ON): Prefix charge timeline end (
soc_pct) = publishedcharge_stop_soc_pct/ O4 (one target; Hold/pv_pack half_up unchanged). Sharedapply_weak_econ_ping_pong(HOURS_WEAK_EARLY_PLACE): move necessary Weak kWh earlier (move≠add, η/Soft-Cap/gate/Cap-Skip); Cover apply merge not strip; Decide+Familie B. Spec §6.7/§6.8.15/§14.6 + wiki. All plants/keys. - Hours session-stop SoT / O4 parity (
hours_demand_v1.31, Extract v1.19): Decide publishes one stop — snapshot + material webhook (charge_stop_soc_pct) + Portal/Sparvo timeline stamp + Loxone O4. Resolve refreshessoc_projected_*after picker mutations; no live+energy invent (fail-closedNone/ latch sticky); O4 =floor(prefix-end);plan_rolescharge =roll_soc_hour; Explainhx46. Spec §6.7/§8 + wiki DE+EN. All plants/keys. - Hours+Quota consolidate load-first path + clip target + orphan finalize: Consolidate SOC trough/emit load-first (Cover parity); clip
upper=min(cap,target); Lookback-Thin stress/s0 passtarget_soc; middleware finalizes reserved exceptions asaccess_denied; finalize keepsduplicateagainst softcap overwrite. Spec §6.5 + MONITORING + changelog. All plants/keys.
2026-09-12
- Hours+Quota load-first shared headroom + softcap 4xx demote: Shared
grid_headroom_after_natural_hour_kwh(Lookback/Thin/Defer/Spill/Interim); Interime_gapnot clipped by decide SoC; Lookback delivery capmin(cap,target); Weak R2; consolidate shortfall = caps; reserved 4xx demotes softcap too; basic without IP → 429. Spec/wiki/MONITORING. All plants/keys. - Hours consolidate load-first + PV-fill grid-gate + Weak inject finite: P1-v2 consolidate headroom load-first (
soc_nat, plantarget, R2 on full port); PV-fill skip onlyroom_grid ≤ min_head; Weak inject omits NaN/±inf price. Spec §6.5/R2/§6.8.15 + wiki. All plants/keys. - Hours+Quota PV-fill η + burns Remaining heal: Cover PV-fill lookahead returns grid kWh (
pack/η); Darf Preis_Tag skips non-finite;check_rate_limitstoresburns; reserved demote (4xx and 200access_denied) restoresX-RateLimit-Remaining; auth deferred pathrstrip("/"). Spec/wiki/MONITORING. All plants/keys. - Hours+Quota Thin ±inf + Cover η + duplicate-IP heal: Lookback-Thin/Thin-Cover-Defer dest
math.isfinite(−inf not “cheap”); Cover headroom grid(upper−soc)/η(Lookback/Spill parity); slot emitters no fake0€/MWh; anonymous duplicate coalesce onlyapi_key_id IS NULL; reserved 4xx restoresX-RateLimit-Remaining. Spec/wiki/MONITORING. All plants/keys. - Hours+Quota interim pack + IP anonymous heal: Interim picker pack headroom includes planned Cover (
already·η, R2 on full port); finite prices in Interim/Rebalance/Consolidate/Weak; basic IP counter onlyapi_key_id IS NULL; responsequota_classfollows coalesce/demote. Spec §6.8.5 + MONITORING + wiki spill R2. All plants/keys. - Hours+Quota spill R2 + reserved-4xx/3xx heal: Soft-Cap spill Partial-Port on full inverter port (Cover/Lookback parity); Prep
_hour_price/prices_prepfinite; middleware skips unreserved 3xx and demotes reserved billable 4xx →access_denied. Spec §6.2b + MONITORING. All plants/keys. - Hours+Quota finite-price + duplicate finalize heal: Soft-Cap spill/Lookback/Thin/
_rank_indices/Resolve skip NaN/±inf (0€/MWh valid); finalize keepsduplicate(no re-bill after coalesce);check_rate_limitsyncsrequest.state.quota_class; telemetry semanticinvalid/incompletedemotes softcap; Spec Force + DE auth. All plants/keys. - Hours+Quota fail-closed deep-audit: Incomplete Hours echoes
grid_charge_allowed=falsewhen profile/field unknown; Survival/Force-Near/_try_takeskip non-finite prices (NaN/±inf) and treat0.0€/MWh as valid; Windows POST parses before reserve; telemetry parses before softcap reserve; PV/Weather require key before reserve; middleware deferred paths honor trailing slash; finalize can demotequota_class; softcapX-RateLimit-Remainingcounts reserve. Spec/wiki auth EN + MONITORING. All plants/keys. - Portal auto-geocode deep-audit parity: Locality keep preserves
nominatim_reverse/nominatimwhen form≈DB (portal always posts lat/lon); guest forward fail clears coords and persists address (owner parity). Spec ANLAGENPROFIL. API JSON unchanged. All plants/keys. - Hours Soft-Cap abandon spill pack headroom (
hours_demand_v1.31): Spill take pack roomupper−soc_i−already·η(Lookback/Defer parity; no cover plan-ceiling overshoot when stacking). Spec §6.2b + wiki. All plants/keys. - Portal auto-geocode on address save: Save (portal + Sparvo owner/guest) checks/fills lat/lon from the address (Nominatim); drift >2 km with locality gate; kill-switch
PLANT_AUTO_GEOCODE_ON_SAVE=0. Wiki weather/PV location note. API JSON unchanged. All plants/keys. - Quota deferred unreserved 4xx + Hours invalid grid_off echo: Middleware skips deferred paths (Hours/Windows/PV/Weather/…) without reserve and without
quota_classon HTTP ≥400 (no default-decisionbill); Windows POST gates before body parse;invalid_input/EMS prefix fail-closedgrid_charge_allowed=falsewhen unknown; incomplete echo unchanged. Wiki telemetry/auth/rate-limits + MONITORING. All plants/keys. - Quota Windows defer + duplicate-at-ceiling + Force-Near price guard:
/v1/decision/windowsdeferred (access/access_deniedbefore reserve, Hours parity); Loxone twin GET at Fair Use ceiling → freeduplicateinstead of 429; Force-Near cover rank fail-closed onNone/OOB price; incomplete Hours basis echoesgrid_charge_allowed. Spec/wiki auth DE + MONITORING. All plants/keys. - Hours Lookback Place pack headroom + η clamp (
hours_demand_v1.31): Place subtractsalready·ηfrom pack headroom (no overfill when stacking on existing dest kWh; LThin/Defer parity); Lookback Place/stress/size_e_bridge+ Decideroundtrip_efficiency+ SOC slots use sharedclamp_charge_eta. Spec §6.2b + wiki. All plants/keys. - Hours residual-aware carry + η parity (
hours_demand_v1.31): Thin-Cover-Defer / Thin-Now carry usesresidual_carry_step(net drain > room above floor → refuse; incomplete horizon fail-closed) instead of vacuous floor clamp; sharedclamp_charge_etafor Lookback-Thin / Defer headroom /roll_soc_hour. Spec §6.7 + wiki. All plants/keys. - Unified billable pool + Community 200: Productive calls (Forecast, Hours, Decision, Windows/Flex, PV, Weather) share one quota pool per tier — Community 200/day (Vienna), Professional 600/h account, OEM 3000/h. Telemetry + duplicates remain exempt (telemetry softcap 2000/10000). PV/Weather softcaps 48/288 only via kill-switch
QUOTA_UNIFIED_BILLABLE=0. Portal dashboard breakdown (telemetry = Vienna day)./v1/statusand HTTP 429 without reserve do not count. Backlog (not in this ship): Device-schedules may bill in parallel with Windows. Docs/wiki/llms DE+EN. - Hours Lookback-Thin + Thin-Cover-Defer (
hours_demand_v1.31): Lookback-Thin — post-Lookback drop-first/relocate only lookback-eligible kWh (incl. underreserve_breach; siblings frozen/movable split; mergelookback_added_kwhfreezes absorbed cover and keepssurvivalagainst Defer undo; pack headroom+port) when stress-delta conjunction holds (sum≤∧earliest≥); own commit; residual note andlookback_reason_codesonly when \(S≠∅\); fieldslookback_thin_*; killHOURS_LOOKBACK_THIN=0. Thin-Cover-Defer — then relocate-only for non-spill Cover/Weak onto a cheaper hour in the same Near or Weak[lo,hi]window (residual-aware carry + port + pack headroommin(cap[,target])/η+ partial port incl. R2/_partial_port_take_ok); dest avoids Spill ∪ Cap-Skip; freeze spill/lookback/survival/MUST/reserve_breach/latch; one commit with Thin-Now; Soft-Cap not reopened; killHOURS_THIN_COVER_DEFER=0. Spec §6.7 / §6.2b. All plants/keys.
2026-09-11
- sparvo.at hybrid marketing (IA CLEAN): Public marketing site hybrid
/·/ems·/services(+ legal). Claim ampel Live / with connection / outlook. Shared app tour keys (hoursinstead of savings check). SEO sitemap/hreflang/llms; Umami website id16c2ee7f-…(consent-gated). Portal landing Hours-first unchanged — see 2026-08-27 history. - Hours Lookback Spec 5b + slider bool-guard: Spec table 5b placeable
\\ Cap-Skip; storage-rules Jinjasameasalso for min/target/cover (no|float(True)=1). Engine remainshours_demand_v1.29. All plants/keys. - Hours latch H Jinja/form_num type-guard: Storage-rules slider (
sameas true/false→ default) + Sparvo plant_form_num/build prefill viacoerce_hysteresis_pp. Engine remainshours_demand_v1.29. All plants/keys. - Hours latch H form-prefill type-guard: Portal/Sparvo storage-rules prefill (
charge_hysteresis_pp_for_form, guest form/_pct) usescoerce_hysteresis_pp— bool/Mock → default, never"1"for persist. Engine remainshours_demand_v1.29. All plants/keys. - Hours latch H webhook/guest type-guard:
plant.plan_updatedpayload + material key and Sparvo guest profile usecoerce_hysteresis_pp(bool/Mock → omit/default). Engine remainshours_demand_v1.29. All plants/keys. - Hours latch H surface type-guard: Tombstone UI, live-telem, snap write, EMS prefix, and Decide request (
battery.charge_hysteresis_pp) usecoerce_hysteresis_pp/ bool reject — nofloat(True)=1. Family-B Cap-Skipblocked_indicesimmediately after mix. Engine remainshours_demand_v1.29. All plants/keys. - Hours latch H snap type-guard: Snap fallback/tombstone for
charge_stop_hysteresis_ppuses the same fail-closed coerce as profile (int/float/str/Decimal; bool/Mock → default/omit). Engine remainshours_demand_v1.29. All plants/keys. - Hours OpenAPI/samples/version-notes sync (
hours_demand_v1.29): OpenAPI stub, code-sample guides DE+EN, changelog version notes, docs-concept badge →v1.29. All plants/keys. - Hours PicoC/wiki engine-tag sync (
hours_demand_v1.29): Fetch/Extract.pico+ code samples + wiki Extract header →v1.29. All plants/keys. - Hours live engine-tag sync (
hours_demand_v1.29): API tests/docstring, llms.txt, Loxone PicoC/wiki O12, webhooks “live” tag →v1.29(changelog history unchanged). All plants/keys. - Hours Cap-Skip deep-audit CLEAN (
hours_demand_v1.29): Lookbackblocked_indices+ latch H type-guard (Decimalok, Mock/bool reject); worklist/wiki field table. All plants/keys. - Hours Weak Cap-Skip Lookback block (
hours_demand_v1.29): Cap-Skip windows in Lookbackblocked_indices(Decide + Familie B) — no Soft-Cap-exempt refill under later-day stress; Spec §6.8.15 + tests. All plants/keys. - Hours latch H type-guard:
profile_charge_hysteresis_ppaccepts only int/float/str/Decimal (not bool/Mock) — tombstone keeps snap H. All plants/keys. - Hours Weak Cap-Skip wiki kill-switch wording: Surplus-Cap kill (
HOURS_HOLD_CONTINUOUS_CAP) ≠ Cap-Skip kill (HOURS_WEAK_PV_NEED_FULL_PV_CAP_SKIP); Cap-Skip from v1.27 / audit v1.28. All plants/keys. - Hours Weak Cap-Skip audit heal (
hours_demand_v1.28): DA DP is a no-op after Cap-Skip (holds kept);eve_start=n_wfail-closed no skip; Spec §6.8.15 + tests. All plants/keys. - Hours Weak-PV need_full Cap-Skip (
hours_demand_v1.27): When PV-only (no Hold) reaches Cap before evening without residual import →need_full=0, no weak grid charge; Hold kept; noteweak_pv_need_full_pv_cap_skip(Decidebasis.notes+ Snapshotnotes). KillHOURS_WEAK_PV_NEED_FULL_PV_CAP_SKIP=0. Spec §6.8.15. All plants/keys. - PV meter day-cap UI heal: When actual already exceeds 1.2× model day forecast (
day_cap_kwp_forecast/ abs), no scale — but Sparvo/Portal/pv_kindkeep chart actual +meter_blend(same as kill-switch). Only mean power > 1.2×kWp clears the chart. Spec §14.2.1. All plants/keys. - Hours Soft-Cap heal live default on (
hours_demand_v1.26):HOURS_DARF_SOFTCAP_S1,HOURS_LOOKBACK_LATE_BIAS,HOURS_SOFTCAP_ABANDON_SPILLdefault on (standard path); Kill each with=0. Targets needless early grid charge / Near Soft-Cap before cheaper S1. All plants/keys. - Hours Soft-Cap Darf-S1 + Lookback late-bias (
hours_demand_v1.25): FlagHOURS_DARF_SOFTCAP_S1(then default off) — Darf Near budget=0 → S1 Soft-Cap cohort (shared spill helper, Gate OR without Spill flag, no S0 fallback); FlagHOURS_LOOKBACK_LATE_BIAS(+…_EURdefault 5) — pre-stress late-bias in price band; Thin-Now does not dropsoftcap_spillNow; Spec §6.2b/§6.8.3–4/§14.6. All plants/keys. (superseded: defaults on from v1.26) - Hours Soft-Cap v1.25 full-audit heal: Decide top-level echo
softcap_abandon_spill_*/darf_softcap_s1*; Interim ¬breach Darf-S1 →darf_softcap_s1_skip_near; Spec §6.8.5 + samples. All plants/keys. Flags remain default off. - Hours Soft-Cap spill skip + Darf echo heal: distinguish
clippedvseuro_cap; Darf-S1 echo onno_grid/autark; DE wiki skip enum + §6.8.14. All plants/keys. - Hours Soft-Cap spill R2-continue heal: after €-shrink
best>0but R2 fail → continue cohort (no falseeuro_capstop); Spec §1 placement order + DE lookback peel-exempt; Gate-OR/late-bias/snapshot tests. All plants/keys. - Hours Soft-Cap docs Flag/Darf-S1 parity: Spec placement strings + wiki DE/EN teaser/cover narrative
(flag/Darf-S1)/ Gate OR. No code change. All plants/keys. - PV Forecast serve deep-audit #5 (clean): Soft gaps closed — sync short-est test; wiki
timezone= pack/serve day boundaries. No further code bugs on estimate→pack→serve paths. Spec §6.7. All plants/keys. - PV Forecast serve deep-audit #4: Decide/
build_pv_decide_packre-estimates a full week whenpv_estlacks D0…D6 (no zero-fill); sync same; GET envelopetimezone= serve/pack TZ (query must not override day boundaries). Spec §6.7. All plants/keys. - PV Forecast serve deep-audit #3: Live estimate always 7×24 h before pack sync; query
daysonly truncates responsedays/hours/totals(loxoneweek kept). Stops zero-fill D3…D6 on?days<7. Spec §6.7. All plants/keys. - PV Forecast serve deep-audit #2: Post-midnight age-ok: pad horizon to 7, strip pre-today hours, synthetic D0 if today missing; SoC refresh builds
loxonefrom aligned view (packdaysstay FC). Spec §6.7. All plants/keys. - PV Forecast serve deep-audit clean: Pack not fresh on empty
hours/ calendar roll (days[0]≠ today); Serve aligns D0 by calendar date (not index[0]);loxone.d0_kwh= today’s row. Spec §6.7. All plants/keys. - PV Forecast served D0 = actual+forecast (all keys): Pack in DB stays model FC (
pv_hashFC).GET /v1/plant/pv-forecast/loxone.d0_kwhand Portal/Sparvo apply the same calendar meter blend (additivepv_kind/pv_meter_today_kwh). Meter ticks do not fireplant.plan_updated. KillHOURS_PV_METER_REFINE=0. Spec §6.7 / §14.2.1. All plants/keys. - Plant Outlook PV chart seam audit-clean: Wiki kill-switch wording matches Spec (UI actual + Decide unscaled); Portal
now_hourNaN guard; Outlook comment seam Δ > 0. All plants/keys. - Plant Outlook / Sparvo PV chart seam (all keys): Current hour stays forecast until meter Δ > 0;
pv_kind=meter_blendeven with current-hour-only samples; bars green = PV (base), soft green = forecast with meter; legend no longer “bars = PV”. Spec §14.2.1 / wiki telemetry. All plants/keys. - Hours/Telemetry audit-clean #4 (P2 polish): Webhook sample
charge_stop_hysteresis_ppdefault 2.0; Sparvostorage_rules_linkDE+EN mentions charge hysteresis. All plants/keys. - Hours/Telemetry audit-clean #3: Loxone Hours wiki latch H = profile
charge_hysteresis_pp(not cover/grid buffer); joint partialinvalid_inputincludesupdated.*for landed sibling writes; latch module drops cover-default re-export. All plants/keys. - Plant Telemetry joint partial heal: Import-meter errors no longer skip sibling
pv_production_kwh_total(independent ingest; invalid_input carries both results). Guest plant-settings payload keepscharge_hysteresis_pp. Wiki ceiling list + historical latch changelog superseded note. All plants/keys. - Hours Phase-1 split + target ceiling: Profile
charge_hysteresis_pp(default 2, clamp 1…20) separate fromcover_buffer_pct; latch/EMScharge_stop_hysteresis_ppfrom profile H; material key includes H; Cover/Bridge/Survival/Lookahead/Lookback/Consolidate/Interim/Spill/Rebalance plan ceilingmin(cap,target); Weak/chrono Cap kept. KillHOURS_CHARGE_HYSTERESIS=0= latch only. All plants/keys. - Plant Telemetry PV production meter (Phase-2): Partial POST
meter.pv_production_kwh_total→plant_pv_samples+ profilemeter_pv_*; response siblingpv_meter/updated.pv_meter(quota T); D0 refine completed hours = actual + rest × scale 0.5…1.5 vs calendar-day forecast (HOURS_PV_METER_REFINE, ε≥1 kWh, N≥2); no Decide on PV-Δ; Portalpv-meter-reset; Loxone VO 1dFetchPlantTelemetryPvMeter(1c stays GridAllow). Spec §14.2.1. All plants/keys.
2026-09-10
- Hours latch docs deep-audit #5 (
hours_demand_v1.24): Plant-profile tombstone H7 latch carry + material-key latch; Loxone Hours wiki O12 cell DE+EN aligned toactive_charge/v1.24. All plants/keys. - Hours plant-profile O12 prose sync (
hours_demand_v1.24): Adaptive-discharge paragraph aligned to §6.8.7/active_charge(dropped “full cap while charging” without latch). All plants/keys. - Hours latch/active_charge docs sync (
hours_demand_v1.24): Wiki webhooks DE+EN, plant-profile concept, FORECAST-WEBHOOK, portal i18n adaptive hint aligned to §6.8.7/active_charge. All plants/keys. - Hours Spec §6.8.7 latch/active_charge sync (
hours_demand_v1.24): Canonical Hold-power + PicoC section aligned to §6.7/active_charge(full cap only while actively charging; latch → Hold Cap). All plants/keys. - Hours O12 Hold-Cap deep-audit #4 (
hours_demand_v1.24): Hold gate usesactive_charge(latched/grid-off → Hold Cap despite stalecharge_now=1); tombstone wiki H7 latch carry; O12 wiki caveat. All plants/keys. - Hours latch/O12/tombstone deep-audit #3 (
hours_demand_v1.24): O12 latch guard fail-closed (charge_nowno longer bypassesblock_recharge_vollcap); tombstone webhook + UI carry sticky latch/stop (H7 parity); Decide disclaimer + Spec §6.7 materiality. All plants/keys. - Hours latch/Thin-Now deep-audit #2 (
hours_demand_v1.24): Thin-Now Hold rebuild fail-closed (locals + prior Hold restore); never drop MUST/reserve_breachCover; sticky stop from prior snap published; §14.8/wiki triggers include latch. All plants/keys. - Hours latch/Thin-Now deep-audit heal (
hours_demand_v1.24): Sticky latch fail-closedincomplete_hold(prior latched + missing stop/SoC); latch stop from prior snap; Thin-Now cover-only + Hold rebuild fail aborts drop; material key includescharge_latched; wiki/spec §6.7/§14.8/webhooks. All plants/keys. - Hours latch docs/samples heal (
hours_demand_v1.24): Wiki plant-telemetry DE+EN stickycharge_latched/ stop−H (Decide SoT); code samples all languages print latch fields; AGENTS PicoC Extract v1.18 / Fetch v1.17. All plants/keys.
- Loxone FetchPlantHours v1.17 (Community 120 cadence): Decide triggers = hourly :00 (+ :05) · SOC 5% bands both ways · Latch/Stop Extract O5→I5+I6 · Manual I5/I6. Smart-15 Decide removed (I7 :15/:30/:45 telemetry only; :00 still Telem+Decide). Class-T softcap spec aligned to code (Community 2000/day · Pro 10000/day). Extract stays v1.18. All plants/keys. Miniserver: re-paste Fetch v1.17.
- Hours latch/Thin-Now audit-clean (
hours_demand_v1.24): Live UI/ui_payload_from_resolved+ live-telem carry latch fields; Thin-Now commits only after sync/recount (+ hold rebuild); webhook payload additivecharge_latched/charge_stop_hysteresis_pp; O12 latch only suppressesrecharge_plannedfull cap (house Adaptive remains); wiki PicoC stand Extract v1.18. All plants/keys. - Hours SOC-stop latch / Thin-Now / Extract v1.18 (
hours_demand_v1.24): Sticky latch in Decide publish (overlay display-only); hysteresis stop−H then fromcover_buffer_pct(1–20 pp, empty→2) — superseded by Phase-1 split → profilecharge_hysteresis_pp; Thin-Now beforeplan_roles; O12 short-circuit while latched; material key +charge_now/charge_stop_soc_pct; snap additivecharge_latched/charge_stop_hysteresis_pp. Extract v1.18 O5 PulseSessionStop → Fetch I5+I6 (no mid-poll fetch); Fetch was v1.16 at latch ship, cadence later v1.17. KillHOURS_CHARGE_HYSTERESIS/HOURS_THIN_NOWdefault on. All plants/keys. - Hours Soft-Cap abandon spill deep-audit #2 (
hours_demand_v1.23, flag default off): Placement freeze (no Prep/Opp/Far top-up on spill); Need-Cap pre-spill baseline; Peak-Hold display-tz;no_gridpf↔basis skip parity; tests. All plants/keys. - Hours Soft-Cap abandon spill deep-audit (
hours_demand_v1.23, flag default off): Spill indices rebalance-/consolidate-/lookback-fixed; Weak apply keeps spill/lookback; post-clip Need-Cap sync; resolve echo fail-closed; wiki sync; tests. All plants/keys. - Hours Soft-Cap abandon spill (B5b, flag default off,
hours_demand_v1.23): After Near Soft-Cap abandon (Recovery\|Darf), optional residual ≤ abandoned into S1 cohort (price-first, Soft-Cap@S1, Dawn-Cap, €-cap peak-hold proxy ε=0). KillHOURS_SOFTCAP_ABANDON_SPILL=0→ fields absent / exact v1.23. Additivesoftcap_abandon_spill_*+charge_hours[].softcap_spill. No Soft-Cap slack↑ / Far→100%. Spec §6.2b freeze exception. All plants/keys.
2026-09-09
- Hours Explain SOC clarity audit-clean (UX, Sparvo
hx45): SSR uses sharedformat_plan_soc_token; JS end-only without hard-coded DE fallback; DE/EN prefix; parity tests. All plants/keys. - Hours Explain SOC clarity (UX, Sparvo
hx44): Timeline{soc_pct}shows start→period end (~a %→~b %) or end-only /—; additive segmentsoc_start_pct(soc_pctstays end). Live gauge ≠ timeline SOC; overlay does not rewrite segments. Non-breaking. All plants/keys. - Hours Lookback-Rebalance port/role fix (
hours_demand_v1.23): Place respectsport−already(Cover parity); merge forcesslot_role=coveron lookback; Decide skips at charge power 0 likeapply_*; wiki duplicate removed +HOURS_LOOKBACK_PASSESin field text. All plants/keys. - Hours Lookback-Rebalance deep-audit (
hours_demand_v1.23): Exception-echo parity; residual note = final stress set; Soft-Cap trim test forlookback=True; Python/JS/TS/Loxone samples; Familie B Dawn/Hold approx documented; env table + slot field in wiki. All plants/keys. - Hours Lookback-Rebalance audit-clean (
hours_demand_v1.23): Decide residual after last place/Hold rebuild (not pre-place); flag-on skip echoeslookback_applied=false+lookback_skip; Familie B exceptions logged; dead place-roll removed. Wiki/spec sync. All plants/keys. - Hours Lookback-Rebalance (
hours_demand_v1.23): After Hold, survival ping-pong — stress (floor ∧ residual) → cheapest pre-stress slots (Cover ranker + R2) → full Hold rebuild; Soft-Cap does not peellookback=True; passes default 4 (HOURS_LOOKBACK_PASSES); kill-switchHOURS_LOOKBACK_REBALANCE=0. Additive fieldslookback_applied/lookback_passes/lookback_stress_remaining_kwh+reason_codes. Familie B shared helpers. Spec §6.2b/§14.6. All plants/keys. - Hours Interim wide-pick + deferred contract (
hours_demand_v1.22, Sparvohx43): Interim slot picker now searches up tomin(breach, next_cheap-1)instead of a fixed 5h pick cap; legacy behavior is available viaHOURS_INTERIM_PICK_CAP_H=5. Deferred interim (interim_decision=charge) stays valid withinterim_period_active=false; EMS period shrink/today pin only apply when imminent (h < 5). Deferred skip for already scheduled survival energy fail-closes tointerim_decision=none,interim_reason=covered_by_scheduled_survival, and scrubsinterim_slot_start/interim_cost_*. Snapshot/Explain hints (reason_codes,show_interim_charge_hint,hidePlan) now followinterim_decision=charge. Onpass, both econ costs (interim_cost_charge_eur+interim_cost_pass_eur) stay; onlynone/r2_rejected/covered scrub clears them. All plants/keys. - Hours Folgetage fail-soft hydrate (
hx42): Folgetage SSR is replaced only after ≥1 day paints successfully; total paint failure restores SSR or hides the empty shell (no hint-only card). Sparvohx42. All plants/keys. - Hours Folgetage empty after hydrate (
hx41):statusChipsHtmlwas nested underuseSummary— Folgetage supply-cost paint threwReferenceErrorand the per-day catch wiped every day (card title/hint stayed). Helper hoisted next todisplayPrefs; Sparvo cachehx41. All plants/keys. - Hours Interim week_status label (
hours_demand_v1.21): Interim slots carryinterim=True;week_status/rest-daycharge_kind=interim(“bridge”) instead of false MUST/reserve. Samples/API tests engine tagv1.21. Spec §6.8.5. All plants/keys. - Hours Interim Explain reason gate (
hours_demand_v1.21): Snapshotreason_codesemitsinterim_chargeonly wheninterim_period_active(parity with Explain footnote/hidePlan). Regression tests for empty_min-today-only + JS interim gate. All plants/keys. - Hours Interim R2 weak-consolidate pin (
hours_demand_v1.21): Weakstage=futureday-consolidate receivesinterim_slot_start(no relocate across midnight interim);basis.interim_*synced with Decide top-level after pass/r2 scrub. Spec §6.8.5 + tests. All plants/keys. - Hours Interim R2 audit-clean (
hours_demand_v1.21, Sparvohx40): Post-consolidate EMS pin keepscharge_hours_todayto one interim slot;r2_rejectedclears phantominterim_cost_*and setsinterim_decision=none; Explain empty_min hint only for today’s empty_min (not horizon-wide); bridge footnote/hidePlanonly wheninterim_period_active. Decide+DA shared. All plants/keys. - Hours Interim R2 + empty_min Explain (
hours_demand_v1.21): Interim insert fail-closed via_partial_port_take_ok(no sub-min_headroomtake unless Partial Port);charge_kwhaligned; Decide+DA shared. Explain: Soft-Cap abandon + empty_min-after-peak hints; bridge-charge footnote only when interim is active today. Soft-Cap+Hold→empty_min = accepted tradeoff (no Far→100 %/slack↑). Docs/AGENTS engine tag v1.21; Sparvo Explain JShx40. All plants/keys. - Snapshot refresh + follow-day chips + Today basis (audit-clean hx38):
load_raw_snapshotcallsdb.refreshagainst identity-map stale reads (hydrate/DA see concurrent tombstone/heal); follow-day supply-cost keeps prefs chips after JS paint; Sparvo Today savings note hydrates basis prose (data-sv-savings-basis). All plants/keys. - Wiki tombstone O12: Webhooks DE+EN + Anlagenprofil: profile-save clear sends
discharge_power_kw=0/charge_now=0; UI—only for missing/nullfield, numeric0→0,0 kW/is-off. All plants/keys. - Tombstone O12 + hydrate issued_at (audit-clean hx37/hz5): Profile-save tombstone webhook sets
discharge_power_kw=0+charge_now=0(avoid PicoC fallback 9); snapshot carries the same zeros; outlook hydrate skips silent persist whenissued_atadvances during resolve; Portalhorizon-htmlsuppliestariff_markup_ct_kwh; lead/explain € without chip wipe; Sparvo SW shellplant-hydrate-hz5/nav-center-1/sparvo-shell-v27. All plants/keys. - Battery-size prefs hydrate (audit-clean): Lead basis without chip wipe; explain/delta € (
savings_vs_no/ sensible) follow prefs toggle; horizon-html supplies profiletariff_markup; tombstone webhook setscharge_now/discharge_power_kw=0; hydrate does not clobber a newer snapshot; spec/wiki DA redispatch on any non-ok Decide. Cachehx37/plant-hydrate-hz5. All plants/keys. - Hours Explain follow-days + O12 + horizon prefs (audit-clean hx36): Folgetage cost paint uses decision
d(not undefineddata) — otherwise empty follow-days whencost_net_eurlacks day markup; alwayspaintPlantNowDischarge(clear even when available + null kW); storage horizon hydrates lead/basis note/transparency/explain{basis}/{tax}on prefs toggle. Cachehx36. All plants/keys. - Hours Explain follow-days (bugfix):
displayPrefstop-level again — otherwise strict-mode ReferenceError after cost paint emptied Folgetage. All plants/keys. - Battery-size tiles prefs chips: Current/Fits cards show Exchange + with/without surcharge + with/without VAT (hydrate on toggle). All plants/keys.
- Battery-size tile net/gross labels: Headline + bar Ø follow VAT toggle (
{tax}→ net/gross); markup alone stays net. Client hydrate on prefs change. All plants/keys. - Sparvo/Portal plant outlook hydrate (audit-clean hz4): Snapshot-first accepts empty
day_plan(autark/covered); tombstone/missingdischarge_power_kwclears O12 chip (—/is-off); cachehx34/plant-hydrate-hz4. All plants/keys. - Sparvo/Portal current discharge + horizon (client): Explain JS before Sparvo; O12 chip before Explain render (try/catch); horizon fetch decoupled + parallel kick on skeleton; inline fallback if Explain shell missing. All plants/keys.
- Horizon HTML prefs + hydrate: Portal
/plant-outlook/horizon-htmlinjects account display prefs (SSR); after AJAX inject Portal/Sparvo callSpotDisplayCost.hydrateDom(VAT/markup/chips/{tax}). DA redispatches tombstone clear on any non-ok Decide. All plants/keys. - Profile-save tombstone audit (hydrate/fingerprint/DA): Hydrate reloads snapshot after resolve before persist/publish (concurrent tombstone → publish; advanced
issued_at→ no silent persist); material fingerprint only on push OK (not onnot_configured/HTTP fail); DA withincomplete_input+ tombstone redispatches empty clear; flash copy mentions EMS retry. All plants/keys. - Profile-save → Hours Decide (audit): Tombstone + empty webhook; material fingerprint only on successful push (HTTP fail → retry on next Decide/DA); Outlook/Sparvo hydrate over tombstone uses
publish_hours_and_pv_after_decide(no silent persist); DA checks tombstone before incomplete cache; flasherror_staleif clear fails. All plants/keys. - Profile-save → Hours Decide: After location/storage rules/dump/
grid_charge_allowed, immediate shared Decide; on incomplete/error, tombstone (profile_refresh, empty charges,hours_unavailable) +plant.plan_updatedincludinggrid_charge_allowed; snapshot best-effort, webhook best-effort; if snapshot write fails → flashplan=error_stale; Sparvo device freezes invalidated; DA replan forces recompute. Guest session-only (no Decide). Public Decide JSON unchanged. All plants/keys. - Sparvo/Portal plant outlook hydrate (audit-clean):
/…/dataskips storage horizon (include_horizon=False); horizon only via…/horizon-html; shared TTL + snapshot persist after preview resolve (no webhook, except hydrate over profile-save tombstone → publish incl. webhook); soft-busy includesfetch_timeout/ pending without usable Decide; loader cleared when Decide isavailable. Fixes stuck “Computing plan/horizon”. All plants/keys. - Sparvo/Portal plant outlook hydrate (resume/horizon): Visibility abort resumes immediately when loader/skeleton is visible; horizon retries no longer blank the mount (soft-retry). All plants/keys.
- Sparvo/Portal plant outlook hydrate: Soft-busy
status=computingkeeps polling (no stuck “Computing plan… Retry”); horizon loading skeleton stays (cost tile gap fixed); loader removed when Decide is ready; current discharge fromdischarge_power_kweven if Explain is hidden. Portal+Sparvo. All plants/keys. - Day-ahead Accept hardening: Fresh overlay attribution without blind
awattar_api; integrity_reopen counts Vienna D+1 hours; inference-gate doc = expected (not ≥20). All plants/keys. - Day-ahead Accept audit fix: D+1 from parquet
awattar_apiafterreceived_for(bridge / no live overlay);fresh_api_tsnever mints stale; Forecast/Hours wiki: label only afterreceived_for. All plants/keys. - Day-ahead Accept (Pass 9): Live-tick vs parquet bridge (bridge only on aWATTar
failed|stale); D+1 market label only afterreceived_for/ predicate; Hours immediate replan only whenreceivedandreceived_for=tomorrow. Public forecast fields (price_source) unchanged. All plants/keys. - Global cost display — Hours Explain + horizon no-batt hydrate: Hours Explain keeps the last decision and re-applies on
sf-display-prefs-changed(boot hydrate from#hours-explain-decision); horizon no-battery bar ct withdata-horizon-bar-calc; plant outlook + Sparvo chart listen to cross-widget prefs. All plants/keys. - Global cost display — Landing/Charts + audit-clean: PDN/bands/forecast chart/
/beste-stunden//live-strompreiseshare prefs (cross-widgetsf-display-prefs-changed); hour table + horizon market/bar ct hydrated; Sparvo no double-apply;estimated_ctincludes markup; dead helpers removed; partner embeds exchange-only; Forecast/Windows wiki: prefs UI-only. All plants/keys. - Global cost display — audit follow-up: Sparvo Today markup kWh =
device_energy(arb) /from_grid+grid_charge(plan); week summary + horizon/today client hydrate for guest/adminlocalStorage; admin FCsource=admin; fix-vs-dyn net≠gross; PDN disclaimer from i18ndata-*. Public API still has no prefs flags. All plants/keys. - Global cost display (Portal/Sparvo): Account prefs
display_markup_on/display_vat_on(both default off) control surcharge·VAT on Hours foot, supply pie, storage horizon, charts, charge chips, Sparvo Today and FC verification. Profilegrid_surcharge_ct_kwh= amount (net, excl. VAT). Payload stays spot (spot_cost_eur); engine/gates/public API/PicoC unchanged. Timeline ops all roles:(PV, battery, grid, SOC). All plants/keys. - Battery-size tile spec/UI sync: Anlagenprofil annual-spot + horizon explain (portal/Sparvo) document Surplus-Cap Hold
min(net,Cap,s−floor)and profilemax_discharge_kwlike Decide; SEO meta Peak-Pack→Surplus-Cap; testsimulate_week_gridforwardsmax_discharge_kw. Code path live since audit fix. All plants/keys. - Hours docs sync Surplus-Cap: Spec §6.8 intro/6.8.6–9/Family B + wiki Webhooks/Hours (Cap-vector materiality, hold-load, planned_direct since v1.19) + PicoC comments/sample prints aligned to live
hours_demand_v1.20. All plants/keys. - Hours audit fix Cap labels + P_max parity:
_hold_maps_subsetlabels Cap>0.2 without reason ashold_share(notcheap_hold); Cap≈HOLD_KW→cheap_hold(O12/Resolve/Snapshot); Cap0 refresh keeps Weak/legacy only; Weak + Family B Peak-Pack + Outlookcompute_storage_horizonget profilemax_discharge_kw; Hold-Load slot Cap readsstart|target_start|time; conserve-SOC copy avoidsSOC: — %. Spec §6.4.2 / §6.8.16 / §6.8.17. All plants/keys. - Hours Explain pack-conserve kW+SOC: Hold segments in
plan_timelineshowbattery conservation (battery: ~x kW, grid: ~y kW, SOC: ~z %)— mean planned pack takemin(net,Cap,s−floor)+ house residual (1 dp) + SoC at period end; Cap remains inhold_kw/discharge_cap_kw. PicoC unchanged. Spec §6.8.17. All plants/keys. - Hours Hold-Load Cap-aware + Weak Peak Cap copy: Telemetry Hold-Load compares live import vs
max(0, plan_rate−Cap)(no false Decide underhold_share); Weak SoC-roll no longer clips Peak Share Caps; Cap-raise-Guard thresholdPACK_RESERVE_KW_MAX(0.2); Decide Peak-Pack uses profilemax_discharge_kw. Spec §6.4.2 / §6.8.16. All plants/keys. - Hours surplus Cap + hold-load replan (
hours_demand_v1.20): Continuous load-weighted surplus Cap replaces Cap0/Liberate for off-peak deficit before peaks; reasonshold_share/pack_reserve/cheap_hold; O12=slot Cap with floor→reserve_emptybefore Cap; Family Bmin(net,Cap); webhook material includes Cap vector; telemetry hold-load Decide (Session→Near-Min→Hold-Load). Kill-switchesHOURS_HOLD_CONTINUOUS_CAP,HOURS_HOLD_LOAD_REPLAN. PicoC unchanged (O12 only). Spec §6.8.16. All plants/keys.
2026-09-08
- Hours v1.19 wiki polish: Public Hours wiki documents optional
battery.grid_surcharge_ct_kwh; research Peak-Pack note marked SUPERSEDED (live enginev1.19). No API/PicoC break. All plants/keys. - Hours v1.19 hygiene: Secondary docs/PicoC header → engine tag
hours_demand_v1.19; week-glance dead code removed. No API/PicoC contract break. All plants/keys. - Hours v1.19 audit fix: Foot € only X/Y/Z (no
empty_mincatch-all); markup0ct stays0(is not None); requestbattery.grid_surcharge_ct_kwhfail-closedinvalid_input; Why{energy}= Decide-horizon X+Y+Z (not Folgetage 1A filter); Sparvohours_enginemirrorsweek_grid_draw_kwh/tariff_markup_ct_kwh. PicoC unchanged. Spec §6.8.17. All plants/keys. - Hours full-day plan + planned house grid draw (
hours_demand_v1.19): After final Cap0/Hold/\(H_{gate}\), Decide tags residuals asplanned_direct(calendar-day P33 +spot ≤ p_ersetztwithout η/markup; never on charge/hold). Explain full-day roles + foot X/Y/Z (hourly costs); week-glance Why = EMS today hours + sum X+Y+Z (168 h). Supplier surchargegrid_surcharge_ct_kwh(requestbattery.grid_surcharge_ct_kwh→ profile → 1.5 ct/kWh) display-only. Kill-switchHOURS_PLANNED_DIRECT=0. PicoC/day_planunchanged. Spec §6.8.17. All plants/keys. - Day-ahead fetch / immediate Decide: aWATTar market-day
start/end(no more midday 15/24); live overlay from 13:00 Vienna with smartENERGY gap-fill (aWATTar preferred); smartENERGY early-accept live only (no stale→received); Hours immediate replan only when pollstatus=received(catch-up 15–23 Vienna).price_sourcecontract unchanged. All plants/keys. *(Superseded 09.09.: Pass 9 live-tick/bridge + Sofort onlyreceived∧received_for=tomorrow.)* - Hours Weak price-first + Cover partial port: Rentable Day-Fill assigns Band∩η∩R2-port in \(W\) (sun→sun) price-first (SoC roll + greedy chrono-deliver, grid budget (target−soc)/η, not chrono-first); rebalance does not reopen pv_will_fill skips;
net≤0does not block grid when port>ε; Coverpv_fillsskip only when port≈0 (partial port allowed); timeline until-none respectscharge_by_date, no phantom kWh. PicoC unchanged. Spec §6.3 / §6.8.15 / §8. All plants/keys. - Hours Explain plan timeline + supply pie: “This week” prose merged into the plan tile (
plan_timeline: charge + contiguous pack-conserve; unique-start merge for sibling slots; autark/no_hold_gapsilent; until-none only when today is truly autark); cover label low-price charge hour / DE *günstige Lade-Stunde*; pie 4th segment grid→battery (flow denominator, pct sum 100);basis.week_statusAPI unchanged;plan_timelineUI-only. PicoC unchanged. All plants/keys. - Hours Soft-Cap sibling EMS prefix (10): Weak+Cover split same
startno longer breaks EMS contiguous prefix;hours_needed/day_plan/today cap = unique starts; compact EMS merges sibling energy; SoC chain after split. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Soft-Cap Mixed budget split (9): Mixed relocate splits the last hour at the Weak budget — Cover remainder without
weak_pv; Mixed Soft-Cap gate stays absolute. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Soft-Cap relocate Natural/Mixed (8): Weak-only relocate Soft-Cap = Natural-Baseline; Mixed keeps
weak_pvbudget (no blanket on Cover); Cap0 Hold map always a dict ({}=0). PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Soft-Cap relocate mixed-day (7): Consolidate relocate keeps
weak_pvon mixed Cover/Weak days; Soft-Cap check includesplan.hi. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Soft-Cap post-apply plan.hi + Cap0 notes (6): Post-apply Soft-Cap stamps = Near/Dawn ∪ Weak
plan.hi; Cap0 notescharge_fill/nofillmutually exclusive. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Familie B Soft-Cap calendar-scope (5): Soft-Cap
end_socresim after deferred merge; Cover/Prep wipe only for calendar Weak (not deferred-only); Soft-Cap note only on peel. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Familie B Soft-Cap calendar-scope (4): Soft-Cap peels only calendar
h < hi(post-midnight deferred); Weak except does not revive Cover after Soft-Cap attempt; Spec Heuristik Cap0 empty-fill. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Cap0 after gate/DP (3): Cap0+end_soc always after final fill (DP / \(H_{gate}\) / Soft-Cap incl. empty fill); Familie B Cap0 refresh after Soft-Cap; Decide Hold map without
{} or Nonefallback. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Cap0/Soft-Cap audit (2): Cap0 energy-map fail-closed (missing key=0); Weak Cap0 resim after fill/Soft-Cap peel; Familie B Natural Soft-Cap without Dawn kill-gate + Cover/Prep energy map. PicoC unchanged. Spec §6.8.15/§6.8.16. All plants/keys.
- Hours Peak-Pack Cap0 charge-fill audit: Cap0/
fill_beforeuses plannedcharge_kwh_by_index(not flat kW alone); Weak mix re-plans Cap0 after rentable fill (no empty-fill Cap0 re-closing Liberate); Soft-Cap at eachplan.hieven without Dawn stamps. PicoC unchanged. Spec §6.8.15/§6.8.16. All plants/keys. - Hours Weak Soft-Cap Natural-Baseline: Weak trim (
trim_weak_pv_charges_to_softcap/ in-horizon) peels only charge abovemax(min+10 pp, natural[stamp])— a high live SoC no longer wipes Rentable Day-Fill; Cover/consolidate Soft-Cap stays absolute. PicoC unchanged. Spec §6.8.15. All plants/keys.
2026-09-07
- Hours Soft-Cap path/parity audit: Soft-Cap rolls from
plan.lo(no hour-0 fail-open); stamp== n= SoC after last hour; Decide horizon keeps Soft-Cap at eachplan.hi; Familie B Soft-Cap Dawn-kill + wipe clears Cover/Prep (no revive). PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Familie-B Soft-Cap parity: Weak after gate filter also Soft-Cap-trims in
simulate_week_gridat sun→sun end (plan_hi) — Decide §6.8.15. Near-autark:cover_dawn_cap_hoursonly when stamp> H_{gate}. PicoC unchanged. All plants/keys. - Hours Weak×Near-Autark Soft-Cap audit: Under Near-
autark_ok, Dawn Soft-Cap stays active (anchormax(pv_cover_end, H_{gate})→hi(next_pv), stamps> H_{gate}); Near stamps ≤ \(H_{gate}\) stay excluded.cover_dawn_cap_hoursrestored in basis. PicoC unchanged. Spec §6.8.15. All plants/keys. - Hours Weak×Near-Autark:
autark_okno longer skips Weak-PV for the whole 168 h horizon (Decide + Familie B; B: week-gate for cover/prep, sticky Near \(H_{gate}\)+resim+Soft-Cap for Weak/Hold — no midnight reset). Near = 0 until \(H_{gate}\); follow days: full sun→sun → filter → Soft-Cap; energy_* from finalcharge_hours. Dual contract documented. PicoC unchanged. Spec §6.0 / §6.8.15. All plants/keys. - Hours Familie B Weak hold-only + Sparvo 1A: Hold-only Weak no longer wipes cover/prep charge and does not force Cap headroom (
weak_overlayonly for rentable; Cap0 hold merge separate; Decide parity); Sparvo PV navalways_include= wall-clock today (not issued/as_of). Week-glance docs: no “Next …” chip, why line only. PicoC comments engine v1.18. All plants/keys. - Hours peak-pack Familie B charge-fill + snapshot 1A: Annual sim re-plans Peak-Pack Hold after planned charge (
charge_abs_indices); snapshotweek_status1Aalways_include= wall-clock today (not only issued day); week glance drops duplicatemetric_next/why_later(wg14). PicoC unchanged. All plants/keys. - Portal/Sparvo week glance empty dedup: Why and strip-empty are mutually exclusive (
wg13) — no duplicate “No grid charging in the 7-day plan”. API/PicoC unchanged. All plants/keys. - Hours peak-pack v1.18 audit fix (2): Weak union keeps Peak Cap0 (
preserve_indices);hours_engine.week_statusshares 1A filter with Folgetage; fill-preview injects charge; orphan Cap →pack_reserve(no Cap1 invent). PicoC unchanged. All plants/keys. - Hours peak-pack v1.18 audit fix: Charge hours in peak-pack SoC roll no longer drain the pack (c∧d=0 inject); Cap0 drain deducted from
drain_budget; Liberate Cap1 guard shared (Decide + Familie B); visibility 1A = Folgetagedate∪[hour_lo,hour_hi)∪ local today (D0 stays). PicoC unchanged. All plants/keys. - Hours peak-pack v1.18 + visibility 1A + follow-days D+6: Engine
hours_demand_v1.18— sequential peak blocks; fill only before each block; Hold-Set = Cap0/pack_reserveonly; Liberate = empty Hold (adaptive). Weak must not re-close Liberate with Cap1. UI: Folgetage dates gatekeep PV nav / week glance / week_status (everywhere or nowhere); Hold→Folgetage by[hour_lo, hour_hi); copy residual import ≠ charge + bars-vs-pie tip;REST_DAY_UI_MAX=7(incomplete trim kept). Markup stays display-only. PicoC unchanged (live O12). Spec §6.8.16. All plants/keys. - Portal/Sparvo week glance copy: Title “Current week glance” (no “Hours”); removed Cover/search-horizon/EMS layers line; covered+Hold why is only “Pack conserve in hold hours is not charging.” (no Hold → empty-plan text). Lead + EMS hint stay (
wg14). API/PicoC unchanged. All plants/keys. - Portal/Sparvo week glance: Removed redundant summary chips “Week N h” / “Today (EMS) N h” (day strip + EMS hint are enough; week total misled on hold-only days). Later also dropped “Next …” chip — next charge only in why (
why_later;wg8→wg14). API/PicoC unchanged. All plants/keys. - Hours peak-pack audit (
hour_is_cheap+ night UI):hour_is_cheap= cheapest N only (price signal), not hold membership; O12 still viahold_hours/slot Cap. Snapshot no longer restores raw night scalars (night_direct/night_floor). Spec: thin-price gate only for cheap-clock, not peak-pack hold set. Wiki/SEO. PicoC unchanged. All plants/keys. - Hours peak-pack O12 Hold-Set: Live O12 = slot Cap whenever Now ∈
hold_hours— no secondcover_neededgate (Weak-PV hold vs chrono cover-to-sun). Legacy cheap-clock without hold membership still needs a cover gap. Snapshot no longer restores stalenight_hold_hoursfrom raw. Explain Hold copy = hold/off-peak hours (not “cheapest”). Spec §6.8.7. PicoC unchanged. All plants/keys.
2026-09-06
- Loxone ExtractPvDaily v2.7 + DebugPvForecast v1.6: Extract re-reads ~every 20 s; Txt1 always
v2.7 soc=…; SoC sidecarspotprice_pv_soc.txtfrom Debug after a fresh fetch. Display = Extract O11. Re-paste both PicoCs, then tap fetch. All plants/keys. - Loxone ExtractPvDaily v2.6: PicoC audit —
setoutput(10)=O11(0-based), no%.1f/ no heavytxt_d0stack; key search without escapes; force re-read ~10 s; idle deltad0_kwh+d0_soc_eod; Txt1OK soc=…when SoC parsed. Display = Extract O11. Re-paste PicoC. All plants/keys. - Loxone ExtractPvDaily v2.5: While Debug O1=10/12, forced re-read ~every 10 s; idle delta also watches
txt_d0. Txt1/setlogtextshowsOK soc=…. Display must use Extract O11 (not a stale Memory). Re-paste Extract PicoC. All plants/keys. - Plant-weather morning slot: Freshness 05:00–11:59 requires a same-day fetch at/after ~05:00 — overnight on-demand no longer skips the 05:20 timer. Deploy starts the refresh only on stale/schema gaps (fail-open on check errors; no journal spam). Wiki Weather/PV DE/EN. All plants/keys.
- Hours peak-pack UI night-scope: Night chip/headline only from
night_hold_hours+ live O12 (follow-daypack_reservemust not override Cap1 tonight). Wiki slot-Cap wording. All plants/keys. - Hours peak-pack UI/samples: Explain chip/headline/empty Cap-aware (
pack_reserve+{kw});pack_reserve_anyviahold_reason_for_cap; portal PicoC samples O12=slot Cap. All plants/keys. - Hours peak-pack audit: Cap union fail-closed (Weak must not loosen peak-pack); Cap0/
pack_reservelabel even whenHOURS_HOLD_KW≤0.2; Weak mix/DP exclude peak Hold; week_status/API prefer tightest Cap. Spec/PicoC docs. All plants/keys. - Hours peak-pack Hold (
hours_demand_v1.17): On cover gap + tight drain budget (off-peak atHOURS_HOLD_KWwould empty the pack before peaks), discharge Cap is 0–0.2 kW (HOURS_PACK_RESERVE_KW, default 0) instead of flat 1 kW — peaks (18–21 / 06–08) stay off Hold.hold_hours[].discharge_cap_kw+reason∈ {cheap_hold,pack_reserve}; Weak-PV union Caps fail-closedmin(peak, weak)(incl. DA DP polish); follow-day Explain uses slot caps. O12 = live Cap (PicoC unchanged). Research:docs/research/peak_pack_allocation_v117_20260906.md. All plants/keys. - PV
d0_soc_eodlive SoC: A fresh Decide pack previously froze midnight SoC until the next Decide (up to 6 h).GET /v1/plant/pv-forecastand telemetry SoC now recomputeloxone.d0_soc_eod/txt_d0from the current live SoC (PV days/pv_hashunchanged; persist after meter ingest; no clobber of newer Decide packs). Loxone ExtractPvDaily v2.4: file-delta also watchesd0_soc_eod(v2.3 onlyd0_kwh→ O11 stayed stale). Re-paste Extract PicoC. All plants/keys. - Hours Rentable Day-Fill (
hours_demand_v1.16): Weak-PV mix replaces evening-peak withneed_full+ spot η-gatep/η < p_ersetzt − ε(ε=0.3 ct; markup display-only). Soft-Cap after Weak inject (fail-closed trim); flat without blind charge (Hold stays); grid_off: Hold without charge; keep clipped PV-day tails; Familie B same sun→sun predicate; Explain piesupply_*/cost_*+ prose. API shape/PicoC unchanged. All plants/keys. - Hours week_status list filter + date: “This week” lists only plan days (Hold/Charge/Charge+Hold) — Autark/idle/
no_hold_gap/opp_*omitted fromlines(kept indays[]). HeadingThu 10.09.:(weekday +dd.mm.). PicoC unchanged. All plants/keys. - Hours Copy B audit fix 4: Residual beats only near hints (
no_charge_covered/interim_pass), not horizon-globalopp_off/opp_not_triggered.charge_holdwithout Hold evidence after 1-dp heal → charge-only (no invented “Plus pack conserve”). PicoC unchanged. All plants/keys. - Hours Copy B audit fix 3: Idle residual beats
empty_hint=no_charge_covered→no_hold_gap(no false “covered”).localizehealsdirect_*before Hold-clock backfill; charge status beats stalehold_*why. Chart tip: bare clocks incharge_starts_plan/night/restonly today (dated labels still match by date). PicoC unchanged. All plants/keys. - Hours Copy B audit fix 2:
localize_week_statushealsdays[](why_code/direct_*) to 1-dp like the lines;_line_for_week_daycheckscharge_holdbefore Hold. Chart tip: barecharge_starts(14:00) only on today (no false Folgetag Netzladung). Explaintip_p33+ rest-day Hold strings + wiki chip → pack conserve. PicoC unchanged. All plants/keys. - Hours Copy B / chart tip audit fix:
why_code=hold_directanddirect_hoursfollow 1-dp display (micro-kWh < 0.05 →hold, not import header). Portal/Sparvo plan keyword Charge > Hold matches display strings (charge_starts*) and day chips before Hold; tip/aria escaped. Explain Hold chips = pack conserve.localize_week_statuswithout fakemin_soc=20. PicoC unchanged. All plants/keys. - Hours week_status Copy B + plant day chart PV/load: Hold line = one clock list (
HH:00 X,X kWhorno import· total = displayed 1-dp); wording Pack conserve / no battery charge from grid. Portal and Sparvo day chart: PV+load toggles (default both on; both off forbidden), tipPV · load · plan keyword(Charge>Hold fromhours_decision), Sparvo payloadhourly_load_*+now_hour. Outlook SOC-roll η = profileroundtrip_efficiency(default 0.90 like Hours Decide). PicoC/engine unchanged. All plants/keys. - Hours week_status copy 1A+2C:
basis.week_status= one plain-language line per calendar day withwhy_code,direct_hours(kWh/h),hold_hours_local,charge_*,teaser. No grouped “Grid import:” line. Hold line lists all hold clocks; residual import only when display kWh>0 (residual ⊆ hold / fielddirect_hours). Legacy withouthold_hours_local: clocks fromdirect_hours. Idle with residual but no hold →no_hold_gap(not false Autark). Covered/autark_okbeatsinterim_passhint (same as Explain). Hold-kW copy fromHOURS_HOLD_KW. Pack-conserve / residual-import prose only under “This week”; “cheap third” only fordarf(§6.8.10). Hold with no import ≠ Autark. Every Decide rolls[now, now+H≤168). Empty days: honest hints (grid_off/interim_pass/no_charge_covered/opp_off/opp_not_triggered/no_hold_gap). Netzladung Explain + week-glance echo default collapsed. Optionalhold_hours[].direct_grid_kwh. PicoC unchanged. All plants/keys. - Hours Weak-PV Mix v1.15 (
hours_demand_v1.15): Weak-PV days (morning sun→morning sun, day-by-day across 168 h): pre-peak grid charge, morning conserve, hold bands, c∧d=0.charge_kind=weak_pv;basis.week_status(plain-language week lines on Portal/Sparvo, snapshot+outlook). Hold-only windows do not strip Darf cover; EMS hold excludes concurrent charge hours. Explain/empty_reason=holdonly when night residual-import kWh>0. DP polish only on the existing DA replan (allow_weak_pv_dp, killHOURS_WEAK_PV_DP); no extra polls. Weak vs MUST-survival dedupe; residual-import UI only when kWh>0. API remains calendar day 00:00–23:59. PicoC unchanged (live O12 /charge_nowonly). All plants/keys. - Loxone PV after Decide — Extract stale: Server Decide pack was already SoT (
GET /v1/plant/pv-forecast= Portal/Sparvo). Wiki wrongly said Hours O7 → VO FetchPvForecast; VO alone updates the file while Extract keeps old kWh. Fix: wire O7 → DebugPvForecast I1; ExtractPvDaily v2.3 also re-reads JSON ~15 s whend0_kwhchanges. Re-paste Extract PicoC; check O7 wiring. All plants/keys.
2026-09-05
- Hours Hold / Family B boundary: Explain UI gate (Hold only when night uncovered) does not change annual € on the Battery size & spot cost tile; Family B already gates Hold in the shared builder. No Hold without storage; with storage, Hold import on gap hours (v1.14). Docstring fix O12≠annual-sim Hold. PicoC unchanged. All plants/keys.
- Hours Hold explain audit fix:
charge_empty_reasonuses recomputednight_hold_ui(not stale raw); Week Glance/Explain downgrade hold→covered when the night window is covered. PicoC unchanged. All plants/keys. - Hours Hold explain UI: Status chip/headline lives inside the Until tomorrow morning card; Hold copy only when night load is uncovered (
load > storage + PV), not from follow-day holds alone. Headline no longer says “cheap third” (Darf only). PicoC unchanged. All plants/keys. - Hours Darf Near-only even when ¬recovery: Soft-Cap Far-skip/abandon for Darf independent of
cover_recovery_ok; consolidatenear_cover_hours/cover_softcapwithout Dawn stamp for Darf; interim/relocate sync cover kWh. PicoC unchanged. All plants/keys. - Hours Darf Near-only (v1.14 audit): Recovery Soft-Cap back on for Darf (Far-skip + leftover abandon); only Dawn Soft-Cap stamp stays off. Hold
n_takeuses full charge power (not P_econ). Relocate syncsenergy_deficit_cover_kwh. Wiki PicoC Hold headers DE/EN. PicoC logic unchanged. All plants/keys. - Hours Darf cover — live picker fix (v1.14 audit): Darf sized as N×P_econ (not survival bulk); Near budget without chrono/Dawn zero-clip; Dawn Soft-Cap stamp only for MUST/Stretch;
basis.charge_power_kw=P_econon Darf; Soft-Cap abandon syncs cover kWh. Resolve e2e under Recovery. PicoC unchanged. All plants/keys. - PV pack-sync audit fix: Live fail after weather invalidate → age-ok pack (fingerprint + ≤6 h) instead of
weather_missing. Outlook daypv_kwhfrom packdayswhen pack SoT. Decide publishes the same estimate (pv_est/ today-past +display_tz). Freshness weather exception fail-closed. Loxone DebugPvForecast v1.5:pendingFetchfor Hours O7 while busy/coolDown. Darf v1.14: cover energy = N×P_econ (fill-capped); Recovery Soft-Cap no longer zeros Darf placement. All plants/keys. Re-paste DebugPv PicoC. - PV pack sync UI↔API: Cover short-term refine no longer changes displayed day
pv_kwh(no hydrate jump). When the weather cache is stale/midday or newer than the pack: live estimate → pack is persisted — Portal/Sparvo andGET /v1/plant/pv-forecaststay aligned. All plants/keys. - Hours horizon-hold / MUST·Darf v1.14 (
hours_demand_v1.14): Autarky → no hold and no MUST/Darf. MUST =reserve_breach; Darf = ¬breach ∧(day_price/η)≤P33(η default 0.90; C-rate sweet 0.25 for Darf only). Hold set = cheapest N − charge; live O12 = 1 kW only in hold hours; pack conserve (O12=1 kW) = normal case; floor = emergency. API:hold_hours,day_plan[].hold_hours,direct_grid_hold_kwh,basis.reserve_breach/econ_charge_ok/charge_kind. Webhook materiality includes hold starts. UI “cheap third of the week” only whencharge_kind=darf(never say “P33” to end users). PicoC unchanged (live O12). Family B: Decide O12=1 kW vs annual-sim import-without-drain. All plants/keys. No PicoC repaste. - Loxone PicoC O4 convention: Debug* blocks drive the VO trigger (usually O4) high only during fetch/poll, then back to 0 (VO OR-safe). DebugPvForecast v1.4, DebugSpotpriceFile v5.7, DebugPlantWeather v1.3; Hours FetchPlantHours v1.16 O7→FetchPvForecast OR-safe. Re-paste wiki DE+EN PicoC blocks; samples synced.
- Loxone DebugPvForecast v1.4: After poll, O4 returns to 0 (previously stayed at 1 and masked Hours O7→FetchPvForecast on the same VO). Wiki DE+EN + code sample. Re-paste Debug PicoC; Extract v2.2 unchanged.
- Hours/PV Decide SoT — audit fix 2: Session-complete overlay strips real Portal/Sparvo payloads (
charge_days_*/ dated chips), not only testcharge_hours_today. All plants/keys. - Hours/PV Decide SoT — audit fix: Outlook pack hourly ISO
startparsed todatetime(no/plant-outlookcrash); overlaycharge_startsno longer matches TZ offset+02:00at hour 02; session-completecharge_now==1+ 120 s debounce documented in wiki. All plants/keys. - Hours/PV Decide SoT: After Decide OK the server persists the Hours snapshot and a PV pack (
last_pv_forecast_*); webhookplant.plan_updatedincludespv_forecast_url/pv_hash. GET PV + Outlook prefer a fresh pack (≤6 h). Fill-day with residual Cap need is not a Prep gap (fill_day_not_gap). Session-complete replan + UI overlay when SoC ≥ session stop. Loxone FetchPlantHours v1.16 pulses O7→FetchPvForecast after Decide. All plants/keys. Re-paste Hours PicoC if wiring O7; PV:08remains backup. - Auth — key-owner tier: Wiki note: quotas/
accessfollow the account that owns the API key. After a plant handover to a partner, the same key uses the partner / Professional quota. No API contract change.
2026-09-04
- Hours Dawn-Cap — completion sync: Portal samples (
cover_dawn_cap_hours), wiki/concept examples, Bridge leftover abandon past dawn, kill-switch behavior test. Enginehours_demand_v1.13. - Hours Dawn-Cap — ≤168 h universality: Cover/Bridge must not place at/after
hi(next_pv)(any weekday, not weekends only); leftover is policy abandon. Spec/wiki + midweek tests. Engine remainshours_demand_v1.13. - Hours Dawn-Cap v1.13 — docs audit: Decide-pipeline footnotes (§6.0 / §6.1 step numbers) aligned to Recovery→Sizing→Autark→Dawn→Picker. Docs only. All plants/keys.
- Hours Dawn-Cap v1.13 — stem audit: Consolidate feasibility sync after kWh drop (survival defer); Soft-Cap stamps Near+Dawn on consolidate; Decide pipeline + wiki field tables (
energy_cover_dawn_kwh,energy_abandoned_cover_kwh). All plants/keys. No PicoC repaste. - Hours Dawn-Cap v1.13 — deep audit:
energy_deficit_cover_kwhfollows the Dawn-clipped picker budget; day-consolidate (flag) Soft-Cap-checks Near/Dawn stamp before accepting a relocate; Stretch docs calendar-agnostic; Go/Rust/C# samples printcover_dawn_cap_hours. All plants/keys. No PicoC repaste. - Hours Dawn-Cap v1.13 — Soft-Cap layering audit: Cover/Bridge Soft-Cap applies all active stamps (
minof rooms): overnight + Recovery daytime + Dawnhi(next_pv)— Dawn does not replace Near Soft-Cap. Bridge leftover under Recovery (even with Dawn kill-switch off) is policy abandon. PHP sample:cover_dawn_cap_hoursindependent of Recovery. All plants/keys. No PicoC repaste. - Hours Dawn-Cap v1.13 (
hours_demand_v1.13): Cover/Bridge Soft-Cap andpicker_coverclip athi(next_pv)(PV-day morning-sun→morning-sun) even under stretch/recovery false — stops far pre-charge to ~100 % before the follow day. Hold-to-min may breach the cap; Opp unchanged. Kill-switchHOURS_COVER_DAWN_CAP=0→ v1.12. Additive basis:cover_dawn_cap_hours,energy_cover_dawn_kwh. All plants/keys. No PicoC repaste. - Hours Cover-Recovery v1.12 — audit fix rebalance/feasibility: When
cover_recovery_ok, same-day rebalance must not move Cover past Near (accept-if-better had recreated same-calendar-day Far Cover); day-consolidate (flag default OFF) gets the same Near gate. Soft-Cap cover abandon is notstorage_undersized(plan_feasibility.energy_abandoned_cover_kwh). Decide + explain, all plants/keys. No PicoC repaste. - Hours DA replan — journal/
by_reason: Batch log and state distinguishalready_current(idempotent after first decide) fromincomplete_input/incomplete_input_cachedand gate skip (by_reason.gate). No API contract change; wiki Hours DA section + ops concept §6.4. - Hours Cover-Recovery v1.12 — audit fix far spill: When
cover_recovery_ok, Soft-Cap/leftover remainder after Near Cover must not place as Far/D+1 cover or bridge (would reintroduce Sat→Sun pre-charge); D+1 force indices cleared; rebalance dawn Soft-Cap only on recovery. Decide + explain, all plants/keys. No PicoC repaste. - Hours Cover-Recovery v1.12 (
hours_demand_v1.12): When the next PV day holds the chrono trough with margin (basis.cover_recovery_ok), reserve stretch and D+1 union are skipped — no far pre-charge to ~100 % before a strong follow day. Daytime cover Soft-Cap before morning sun only on recovery; leftover shortens cover take; cover rebalance is bidirectional (accept-if-better; prep/opp/bridge fixed). Kill-switchHOURS_COVER_RECOVERY=0. All plants/keys. No PicoC repaste. - Hours cover — cheaper hour after pack-full (audit): Load/PV-first roll + clip + same-day rebalance. Rebalance uses load-first room even without earlier grid kWh (PV-full pack) and shifts from dearer hours (including earlier→later when accept-if-better); clip pass-2 no longer wipes all later hours; end SoC via
roll_soc_hour. Decide + explain, all plants/keys. No PicoC repaste. - Hours prep/Vorrat — start SoC above prep floor:
prep_take_under_ceil_kwhchecks the SoC peak from the candidate hour forward (not the full horizon). When the pack was already above the prep floor at decide time (e.g. 99 % with floor 85 %), every later prep hour was previously forced to 0 kWh — follow-day plans showed only thin far cover (e.g. Thu only 13:00 · 6.2 kWh). Also: near cover → prep → far cover so Vorrat claims cheap hours before far cover. Decide + explain. All plants/keys. No PicoC repaste. - Portal/Sparvo follow-days — usable battery: The Usable battery bar at morning sun follows the plant profile Allow grid charging (
grid_charge_allowed): on → kWh includes planned grid charge up to that sun (same path as with-grid trajectory); off → without grid charge as before. Applies to all plants/keys (portal + Sparvo). Snapshot fieldrest_day_balances[].usable_soc_kwh(UI-only, not Decide API). Without·with grid comparison unchanged. - Audit-fix Hours placement docs + role merge: Docstring/wiki/API text: placement near-cover → prep → far-cover → bridge → opp (sizing still dump-first). Role-merge rank
cover > bridge > opp > prep(bridge no longer tied with cover).
2026-09-03
- Hours cover — keep mid-price hour: Removed dominated-hour guard. When 14:00/15:00 are already full, the next-cheapest hour (e.g. 13:00) stays in the plan — remaining need no longer spills into worse hours (e.g. 16:00). Price rank unchanged. Decide + explain. All plants/keys. No PicoC repaste.
- Portal/Sparvo — skipped hours full list: “Next planned charge” once per day as its own bullet at the bottom; hour lines without repetition. DE+EN.
- Audit fix skip footer: Day footer uses
next_timeof the day’s last skipped hour (not the last non-empty). Explain preview/JS: same footer including “no further grid charge”. Poll assethx24.
2026-08-31
- Sparvo PWA — homescreen launch: Service worker no longer intercepts document navigations (only
/static/*, manifest,sw.js). Fixes raw HTML/JS source shown on cold start from the app icon; SWsparvo-shell-v26, registrationupdateViaCache: none. - Sparvo — “Savings” menu hidden: Bottom nav, home tiles, and favorites omit
/savings; direct URL redirects to/. Off by default — re-enable with envSPARVO_SAVINGS_SECTION_ENABLED=1(both hosts). Day-plan savings on Today unchanged. - Portal/Sparvo — battery size & spot cost (role UI): Fixed vs dynamic chip, amortisation hint, and expandable dynamic vs fixed block only for plant administrator (API key owner). Primary member and co-users still see current/sensible sizing, cost bars, and “briefly explained”.
fix_tariff_compare/ amort fields instorage_horizonunchanged — UI visibility only. All plants/keys (same role logic as Hours “skipped” tile).
2026-08-30
- Portal/Sparvo — battery size & spot cost (sweet spot + fixed compare): Rule-of-thumb sizing; fits when current ≥ sweet spot. Inverter standby 0.5 kWh/day. Fixed vs dynamic:
fix_tariff_comparefallback./fc-verification:tariff_context. Copy fix (evening): PV hint chip only when clearly undersized; summer grid+inverter one line (cover-days text removed). All plants/keys. - Sparvo/Portal — WhatsApp referral: “Share via WhatsApp” / referral WhatsApp forms use
data-sf-no-page-loading— no “Loading …” overlay before redirect to WhatsApp (fix: overlay stuck after cancel/back in the PWA). Additionally: loading modal dismisses onvisibilitychangeback to the app when the page was already fully loaded. - Sparvo PWA — resume after idle: Shared
fetch-timeout.js(20 s API timeout;TimeoutError→ existing retry backoff). Plant/outlook/live telemetry refresh on foreground only when due or hidden >5 min; in-flight fetches abort on background. Service workersparvo-shell-v25, assetsresume-fetch-1/fetch-timeout.js?v=1. Fix: first reopen after long idle hung (stale TCP + blind refresh); kill+restart was the workaround.
2026-08-28
- Hours v1.11 (
hours_demand_v1.11): Reserve (prep) sizing after planned cover/bridge/opp kWh (SOC path, not natural trough only) —basis.forward_prep.energy_kwhnever above v0; optionalnotes:sized_after_cover. Decide + storage tile family B (simulate_week_grid, same predicates). Cover slots unchanged (non-regression). Kill-switchHOURS_PREP_AFTER_COVER=0. Audit-fix:plan_feasibilityrefreshed after prep trim — cover shortfall stayspartial, prep-only trim does not falsestorage_undersized. All plants/keys. No PicoC repaste. - Hours cover slot rank (refactor,
hours_demand_v1.10): Cover picker: per calendar day price rank, global cheapest day-head merge — same rule all days. Dominated-hour guard everywhere. Decide + explain. Family B: policy intent (autark, prep predicates, port room) — not the full picker/dominated guard insimulate_week_grid. - Portal/Sparvo — dump-price toggle removed: Day navigator (
/live-strompreise, login, admin), Sparvo/prices,/plant,/plant-outlook, and/code-samplesnow display and calculate with model/point price only (price_ct_kwh/price_eur_mwh) — bars, min/max/avg, switch-on planning, grid-charge ranking, FC verification savings sim (/fc-verification, admin), landing#planung-live. No No dump correction | Dump price toggle, no midday hatch overlay.price_view=scenarioin URLs is ignored. API fieldsdump_risk*/dump_scenario_*remain optional onGET /v1/forecast(unchanged). FC verification detail + CSV/PDF dump history unchanged. Cleanup: deleteddump-view-pref.js, removed dead i18n/CSS/JS remnants, code samples no longer inject dump helpers. - Wiki audit (lead + PicoC): Regression tests for lead blockquote on all
PAGE_SLUGSDE+EN and PicoC ``fences alongsidedocs-lead` (copy-paste windows unchanged). Concept [API-DOCUMENTATION-PUBLIC-KONZEPT.md](../../API-DOCUMENTATION-PUBLIC-KONZEPT.md) § llms + wiki lead updated. - Wiki detail guide lead blockquotes: All Loxone PicoC guides (Hours, telemetry, Flex, legacy Windows, Forecast, PV, Weather) plus Plant telemetry, Webhooks, Weather, Errors, Download and Changelog DE+EN get lead
>summaries (endpoint, PicoC version, Community 120/day, Loxone-first). Rendererdocs-leadunchanged. - llms.txt v2 + wiki agent optimization:
/llms.txt,/llms-full.txt, and new/docs/llms.txtfollow llmstxt.org (blockquote summary, markdown links, pricing numbers, endpoint overview, cURL/Python/Loxone examples). Wiki hub and lead pages DE+EN consolidated (Loxone as primary EMS path, open REST API). Sparvo/llms.txtwith GEO keywords, PWA/no-login, EV/heat-pump USPs. - Hours explain cover label: Cover slots show “economical cover hour” only — suffix “not the cheapest hour of the day” removed (portal/Sparvo, poll
hours-decision-explain.js). - Hours cover slot rank (hotfix 2b / hotfix,
hours_demand_v1.10): Dominated-hour guard on follow days; price rank all calendar days. Family B: policy shared, not full picker. No PicoC repaste. - Hours autarky gate v1.10 (
hours_demand_v1.10): When PV+battery cover the horizon without grid charging (basis.autark_ok), planned near charging is 0 kWh (cover, prep, dump, bridge) — Decide and the portal “battery size & spot cost” tile share the same policy. Gate window \(H_{gate}\) = rest-of-today from decide + next calendar day until morning sun (gate_hinautark_notes). *Follow-up 2026-09-07:* Weak-PV may still charge after \(H_{gate}\) (no week-wide skip). All plants/keys. Additive:basis.autark_ok,basis.autark_notes. Kill-switchHOURS_AUTARK_GATE=0. No PicoC repaste (Extract v1.17 / Fetch v1.15). - Hours cover short-term v1.9 (
hours_demand_v1.9): “Until this morning” SOC projection uses meter actuals (partial hour + 2–4 h recent rate) for cover load/PV and the grid-charge engine — consistent in Portal/Sparvo Hours explain and API (basis.load_refine,hourly_load_cover_kwh). Follow-day load profile unchanged. No PicoC repaste (Extract v1.17 / Fetch v1.15 unchanged). - Hours v1.9 audit fix: No double partial-hour in slot picker/consolidate after refine;
awaiting_first_deltakeeps meter-budget norm; plant-outlook chart uses the same cover load as Hours; SOC slice indices clamped to real hours. Extract v1.17:periodup to 48, no 40 h reset oflastOkAbs. ExtractPvDaily v2.2: optional O8–O13 zeroed when JSON fields missing. - Hours v1.9 audit fix (2): Snapshot-first outlook patches cover load like live resolve; cross-midnight cover writes D+1 hours;
load_kwhrecomputed after patch. Extract v1.17 fallback O2 = hours in window (inclusive end hour). - Hours explain SoC sync: “Now” in grid-charge explain uses live telemetry SoC (not older decide snapshot); forecast stamps shift by SoC drift. Live-telem poll patches explain client-side.
- Hours SOC sync audit fix: No cumulative projection drift on telem polls; idempotent server overlay (
*_at_decidebaselines); outlook patch recomputespv_kwh. - Loxone offline fallback (Forecast · PV · Flex): PicoC fetch blocks use
rename()backup instead of truncating JSON; on API outage O1=12 (Forecast/PV) and switching from last JSON (Flex boot phase 30). Versions: DebugSpotpriceFile v5.6, ParseSpotpriceForecast v18, DebugPvForecast v1.3, ExtractPvDaily v2.2, SpotpriceFlexWindows v0.6.11. Wiki DE+EN + code samples updated. No REST API change.
2026-08-27
- Page-loading modal: Arm on
submitonly after sync handlers (setTimeout 0) — cancelled forms (empty admin password / dismissedconfirmon members) no longer leave “Loading …” stuck over the page. - Landing + Sparvo marketing IA (Hours-first): Lower funnel Proof → Sparvo (guest
app.sparvo.at/try) → Steps → wiki band/docs→ FAQ (7) → CTA. Lead products Hours · PV · Windows Flex · 168h; Weather API not marketed. Portal SEO defaults + legacy refresh on portal boot. Then: sparvo.at FAQ/VideoObject, wiki footer, hero “specs in the wiki”. Current (2026-09-11): hybrid/·/ems·/services— see changelog 2026-09-11. - Page-loading modal (Sparvo/Portal/websites): Same-origin navigations and slow first paints show a “Loading …” dialog until the HTML is ready (
DOMContentLoaded). Fast pages (<≈280 ms) stay without overlay. Opt-out:data-sf-no-page-loadingon body/link/form. Embeds unchanged. - Lazy-load Anlagenprognose (Sparvo/Portal): First paint uses
mode=shell(PV + telem, no Hours Decide); Hours via/api/plant-outlook/dataor/plant-outlook/data(mode=full,include_horizon=Falsesince 2026-09-09 audit-clean); storage horizon via…/horizon-html. Hours resolve capped byHOURS_RESOLVE_MAX_PARALLEL(default 2, non-blocking); busy slots return JSONstatus=computing+ HTTP 200 +Retry-Afterwith client backoff. Soft-busy:status=computing/ pending without usable Decide /fetch_timeout(see 2026-09-09 audit-clean). Incomplete/failed shells setpending_hours=false. HTTP 503 (includingoutlook_failed) and 429 use the same backoff. Fetch/horizon chains use generation+abort; real fragments stay; horizon loading skeleton is no longer wiped on soft-busy/retry (soft-retry — see 2026-09-09 resume/horizon);charge_hours_sourcewarning updates on hydrate. Horizon HTML: empty partial → empty fragment. TTL cache returns deep copies. Empty explain/horizon templates are shell-safe (fmt_numUndefined guard). Docs Markdown renderer process-lru_cache. Public API JSON contracts unchanged. - Hotfix PV-day on /plant: Precomputed outlook PV (midnight→…) is realigned from now for Hours/cover/follow-days — morning-sun stamps no longer show 23:00/00:00.
- Audit fix plant primary / plant-weather: Claims transfer on set-primary runs in the same DB transaction as the primary flag; portal invalidates plan freezes like Sparvo; remove/leave/delete primary likewise. Secondary no longer counts
skipped_secondaryas a weather error (incl. geo-save). Wiki Weather/PV: refresh 05:20 + 12:20 Vienna (HA primary). - Battery size & spot cost (outlook/Sparvo): Chips split price basis (4 typical spot weeks / fallback) and load & PV (meter seasons / hybrid / estimate); 12‑month spot Ø remains meta. Season price state
data/state/season_price_basis_latest.json(HA primary soft-fail). PV weather cache 2×/Vienna day (05:20 + 12:20), on-demand writes primary only. Citizen explain for the scale-up./plantshort-TTL + PV/forecast/meter dedupe. Hours Decide:basis.storage_horizonannual fields remain null. Sparvo/llms.txt+/ai.txt(marketing + app). - Plant primary user: Setting primary always moves device care and EMS connection (confirm dialog); clear-primary UI removed. Deactivating/deleting a primary returns claims to the plant administrator.
- Hours auto-decide near min SoC (bidirectional): SOC ping at 10 pp / 5 pp / floor relative to
min_soc_pct, and the mirror on recovery (floor → 5 pp → 10 pp → headroom / full O12). Internal async Decide (snapshot +plant.plan_updatedon delta). Sparvo/Portal live; webhook immediately; Loxone Extract on the next Hours Decide. No Decision quota. Kill-switchHOURS_SOC_NEAR_MIN_REPLAN=0.
2026-08-26
- Hours reserve v1.8 (
hours_demand_v1.8): Cap =min(target, max(min+10 pp, min(target−10 pp, 85 %))). Lookback onlyvorrat_wuerdig(P20 ∧ absolute guard; dump on → ≤ limit incl. 0). Fill-day blocks prep including night. Dump before reserve (sizing + placement). Sticky lookback ≤P25. Force D0+Near ∪ D+1. Family B: Cap+port+same predicate. Soft-Stop overnight only cover/prep kWh. PicoC wiring unchanged (O1/O4 values follow engine). - Hours v1.8 audit fix: Sticky reads top-level
forward_prep(snapshot shape); Force no longer bypasses Fill-day; Dump placement ≤ limit; Dump rank without Force; layer-B onlyvorrat_wuerdig; bridge separate; Family-B Cap clip. Docs:charge_nowinstead ofcharge_active. PicoC wiring unchanged. - Hours v1.8 follow-up: Sticky works outside P20 (≤P25); Family-B cheap picks stay in the calendar day; Sticky price from live series; storage tile kWh with 1 decimal.
- Grid charge O11 vs connection rating:
charge_power_kw(Loxone Extract O11) ismin(inverter/max_charge, connection × 0.80 − base load)— not the raw grid connection rating. Portal/Sparvo plant profile shows the computed power under the connection slider. Thin Decide withoutbattery.max_charge_kw; after saving the profile, tap Fetch I6. Planned hourly kWh may stay SoC/reserve-limited (not always full cap). - Portal navigation Plant: Under Plant, new Devices (SSO → Sparvo
/devices, preconfigure devices) and EMS connection (/settings/ems, PicoC slots). EMS connection no longer under Account. - Plant primary user (Sparvo + Portal): Settings → Account shows plant administrator and plant primary user with badges (like Admin). The plant administrator can assign or clear the primary role among co-users — only after admin password (Portal:
/settings/members; Sparvo: Account or/settings/members). Optional EMS claim transfer. - Sparvo/Portal EMS: UI Schedule (API field
recipesunchanged). PicoC slots only in Portal EMS connection (/settings/ems) — Sparvo/migration never inventwallbox_*/flex_*. Unbound catalog devices: “EMS pending” chip + Portal link. Roles: Plant administrator / Plant primary user / Co-user. Catalogplant_sparvo_prefsperapi_key_id. Binding is the serve source of truth for the slot (schedules / Flex merge). - Flex Sparvo merge:
flex_pack=0/ I6=0 reads schedules from plant catalogplant_sparvo_prefs(owner prefs fallback). PicoC slot only via Portal EMS; claim preserved on re-bind. - EMS bind overlay: Serve/Flex use only bound PicoC slots (
plant_ems_bindings); conflict losers and unbound catalog rows expose no slot in merge/schedules/Portal UI. - Following-days tile: Planned grid charge line only for planned hours on the tile’s calendar date — not hours listed as skipped cheap hours, and not next-day overnight charge inside the sun→sun window. Without·with grid at morning sun stays the real trajectory.
- Reserve/cover before a later morning sun: The min+10 pp soft-cap applies before every morning sun (cover and reserve, not only the first night). Reserve fills only to the prep floor, not target SoC/100 % — expensive nights such as Mon 03:00/04:00 do not fill the pack when PV + cheaper daytime hours suffice. Survival (min SoC) stays separate.
- PV-hour grid cap (hotfix): Re-assigning the same hour must not exceed remaining
charge power − PV(no fallback to pack headroom → no 14,9 kWh slots on a 1,7 kW port room). After the picker, chronological clip: no further grid charge once Stopp-SOC is 100 % / pack full (e.g. no 0,8 kWh right after 13:00→100 %). - Hours backward-prep (
hours_demand_v1.7): Each gap day looks back within pack carry \(H_{eff}\) (basis.carry_hours) — \(H_{prep}\) is the data window only, not clipped at \(t=0+H_{eff}\). A source day already filled by PV without grid (full PV, no 70% discount) takes no prep kWh — including evening after the fill. Far-cover only in[trough−H_{eff}, trough]and not on fill-days (no backdoor). Cover stretch ends at the last complete PV-day. Slot roleprep→ “reserve hour”. Every successful Decide (ok) runs prep + the slot picker (including when grid charge is off — then energy is zeroed). The battery size & spot cost tile (family B) uses the same sizing. PV hour: grid onlycharge power − PV(1 h: kWh≡kW); skippv_fills_charge_powerwhen PV saturates charge power. Ist/Sinnvoll € may change. Telemetry does not plan.
2026-08-25
- Flex PicoC v0.6.10: I1=−1 clears
lastEin; I1 off→on reparses in the same hour. Hours FetchPlantHours v1.15: 3‑minute pause is same-hour only (:00 after a late previous-hour Decide). ExtractHoursDemand v1.17: age in UTC hours — DST/month wrap no longer latches the 12 h fallback. Sparvo: duration0,25h on list/today; recipe summary without Loxone link; planner kWh in More disabled (recipe 0 wins); live telemetry day kWh one decimal. - Flex PicoC v0.6.9: Window-end stamps
lastHonly when I1=−1 — with I1 on, the hour reparse still runs so overnight does not stay on after winE. Sparvo: recipe 0,25 h stays 0,25; kWh recipes writetarget_kwh; weekly chips do not override recipes; oven and similar presets cannot stayapi_linked. - Flex PicoC v0.6.8: I1=−1 writes the runtime file once at window end; I6=0 wipes leftover hours when the slot turns off (not only at 00:00/06:00); boot/I2/I7 stamp
lastAutoHour(no double fetch in the same hour). Hours FetchPlantHours v1.14: I8 :00/:05 uses the 3‑minute cooldown (no Decide storm after FAIL). ExtractHoursDemand v1.16: month wrap no longer latches the 12 h fallback. PV/Weather debug v1.2: 0→10 hold 200 ms. - Sparvo/Portal: Empty recipe days stay empty after save/edit. Off-ladder recipe hours (e.g. 7 h) stay. AC preset 1,2 kW. Mix chips and week-glance kWh use a comma; plant-outlook kWh one decimal. EN wiring
O3 on/off. - Flex PicoC v0.6.7: I6=0 wipes runtime at 00:00 and 06:00 only if the slot is off — leftover evening hours no longer block the next morning window. Hours FetchPlantHours v1.13:
lastDecHouronly after Decide OK (:05 retries a failed :00); I6 during a fetch queues Decide. - Sparvo/portal: same-clock recipe window shows a 24h hint. Week-glance hours use a comma (not
4.5). - Flex PicoC v0.6.6: I7 (wallbox Vc) re-arms after a fetch when the input falls to 0 — unplug/plug during a poll fetches again. Hours FetchPlantHours v1.12: O1 pause before Extract is 200 ms (Extract loop 100 ms) so Decide is not swallowed.
- Sparvo devices: recipe summary in EN uses Mon–Sun; a
00–00window shows 24h. Duration dropdown and recipe 1 (hours) stay in sync; save copies recipe hours onto device duration. - Flex PicoC v0.6.5: I2 (manual) re-arms after the first pulse when the input falls to 0 — a button fetches again. Runtime counts only while idle. Hours FetchPlantHours v1.11: an I5/I6 edge during a fetch is no longer lost.
- Sparvo device form: Preset duration writes recipe 1 (hours) while that recipe still has the same number.
- Sparvo device form: “Daytime only (08–22)” removed (it fought overnight wallbox/HP). Tap schedule weekdays to deselect; schedules 2–4 can be removed. All days off stays off (edit does not force Mo–So again). PicoC output O1–O8 was chosen in the Sparvo form then (since 26.08. only Portal EMS connection). With EMS on, More hides planner-only fields (values stay). Overlap 400 keeps days/windows. An existing EMS link stays visible without an API key.
- Flex PicoC v0.6.4: I6=0 wipes runtime at 06:00 only if this slot is off (a 05–08 morning window keeps hours). I1=−1 no longer swallows I2/I7 edges or the hour change — returning I1 to 0 reparses or fetches.
- Flex PicoC v0.6.3: I7/I2 edge after a fetch starts again (an edge during the poll is no longer lost). Overnight: do not wipe runtime at midnight (I6=0 resets at 06:00; I6 overnight at window end). I1=−1 also clears error/busy.
- Flex PicoC v0.6.2: On the hour, on goes off if the new hour is not in
slots[](no staleload_nowuntil :04). Runtime file always writes an ASCII dot (1.25), not a locale comma. - Flex PicoC v0.6.1: VO like Hours —
{"slot_id":"3","flex_pack":<v.0>}(slot_idtyped). Five<v.n>in one command is wrong (Loxone = one analog). I1=−1 clears O1–O8 latches. I6=0 sends progress/plug (segments=0); merge still runs. I7 edge fetches again. On the hour PicoC re-reads JSON locally (:04 stays the HTTP fetch). Omittingmode/roledefaults tocheapest/consume. POST body accepts decimal commas. Hours persist is POST-only, ≤70 min. - Flex Sparvo/PicoC v0.6.0: Hours = battery, Flex = loads on/off. Sparvo recipes 1–4 +
slot_idmerge (flex_pack=0omitted). PicoC O1–O8, I6=0 does not force 7, I1=0 means on. Hours O12 ×battery_to_ev_pct(ev_charge_block/ev_charge_limit). Flex wiki rewritten;loxone.mdbudget 7/8 (skip weather). - Flex PicoC v0.5.1: Parse failure keeps LoadNow (
wantLoad); I1=0 also clearsViGridChargeReq; Windows docs say Flex v0.5. - Flex PicoC v0.5: LoadNow from current slot hour (no stale
ems.load_nowon hourly reparse); live I7 each loop; nocontinue; I1=0 resets state; wiki I9+I10 required forhours_completed;flex_packoverrides bodysegments. - Docs UX Flex guide:
/docs/loxone-windows-flexrewritten (short steps,step/examplecallouts, SVG flow, PicoC in appendix). Guide pattern in concept §12. Renderer:step+examplecallouts. - Flex deep-audit v0.4d: ISO window +
deadline_hourwithoutwindow_*infers clock hours fromstart/end(18:00→06:00, deadline 22 drops 02:00). Naivehour≤deadlineremoved. A deadline outside the window does not empty it. Flex wiki walkthrough DE+EN (calloutsstep/example,flow.svg), OpenAPI/concept §8.6 updated. - Flex deep-audit v0.4c:
deadline_hour(Sparvo latest start) filters Flex candidates overnight-aware;windows_querycarriesdeadline_hour; request echowindow_source(flex_pack\|window_hours\|iso);deadline_strict/repeat_dailydocumented as echo/reserved; PicoC I1=0 also clears Demand/Period; concept §15.1 E =flex_pack. - Flex deep-audit v0.4b:
device-schedulesuses the same rolling overnight logic (post-midnight fix);windows_querycarries onlywindow_*hours (no ISO); request echo showsflex_pack/window_*; wiki explains why not multi-VO like Hours; Step 5 without ISO. - Hours/Flex deep audit:
deadline_hour= latest start (overnight-aware, Sparvo). PV-days ignore brief midday dips; an incomplete last PV-day without the next morning sun gets no \(L_D\); prior-day hours only when \(P_{ref}(D)\) is finite.energy_deficit_kwh= placed need (leftover prep excluded). OpenAPI:start/endoptional,flex_pack/window_*documented. PicoC I12 is diagnose-only;repeat_dailyis not applied in v0.4. - Flex PicoC v0.4 + rolling window:
window_start_hour/window_end_houror Loxoneflex_pack(I4|I5<<5|I6<<10|done<<16) in one<v.0>— no daily ISOstart/end. PicoC I4–I6 are the source of truth. O5 always writes the pack, never rawhours_completed. LoadNow only fromems.load_now(notwindow.start).prep_extra_energy_kwh= placed prep kWh. A Flex window beyond the tier isno_slots, notdeadline. - Flex deep audit: live floor (no past hours in flex window); chronological
next_slot_start; soft fallback prefers longest block; afterendDemand/slots=0 + statusdeadline; empty flex exits keepems; PicoC I1=0 clears LoadNow; OverrideEnable documented as reserved.
- Flex PicoC v0.3 + API hardening: Poll accepts
deadline/no_slots; PO Demand = remaining hours (0whensegment_completeor afterend);deadline_reachedwhenevernow ≥ end; live slots only from the current hour; chronological overnight slots; I7 gates LoadNow; I1=0 clears LoadNow;window.hours_requested= remaining need; DA baseline empty when remaining = 0;softtakes the longest block (price is tie-break only);ems.next_slot_start= next hour from now. (Historical: first-wallbox autoslot_idwallbox_1— since 26.08. Portal EMS only.)
- Flex docs completeness: Concept §19.5/§19.6 → POST +
<v.0>; OpenAPI stub GET flex params + POST +device-schedules; PicoC then v0.2; now v0.4. - Windows Flex Loxone VO = Hours pattern:
POST /v1/decision/windowswith JSON body; dynamic values only via<v.0>/<v.2>on the VO Input (v0.4:flex_pack). Named<v.Vi…>dropped. PicoC then v0.2, wiki/docs/loxone-windows-flexupdated. - Hours forward-prep (
hours_demand_v1.6): After cover, every Decide sizes multi-gap prep (layer A \(G_{min}\) + layer B in PV-days \(D-2..D\)) inside \(H_{eff}\) (usable band / load-PV, summer floor0.25·load, no jump to 168). Fieldshours_needed_prep,prep_extra_energy_kwh,basis.forward_prep,basis.prep_horizon_hours. Slot roleprep(“forward-prep hour”). Cheap-hold stays cover-only. Near soft-cap applies to cover and to prep hours before morning sun (no fill above min+10 pp). Storage-size tile uses the same policy; Ist/Sinnvoll € may change. - Docs/Wiki hub: Flex Windows linked from overview DE/EN (
/docs/loxone-windows-flex); Loxone template + integrations README marked live (Wallbox step 1). - Loxone Flex Windows wiki: New step-by-step
/docs/loxone-windows-flex(DE/EN) · PicoCSpotpriceFlexWindows. - Plant Device Schedules (Sparvo Flex 1b):
GET /v1/plant/device-schedulesreturns Sparvoapi_linkeddevices (slot id, segments, window, deadline) for Loxone/EMS. Community+; HTTP 200 withdecision_status. - Loxone FetchPlantHours v1.10: Auto-Decide at hour :00 (I7+I8), :05 fallback only. Fixes relay gap: Extract clears O3/
charge_nowon hour tick; previously Decide waited until :05 so manual “fetch plan” was needed. Re-paste PicoC from/docs/loxone-hours. - Hours stop-SOC follow-on slots: When several charge hours fall on the same day, displayed stop SOC is non-decreasing in time (later hour ≥ earlier). Slot picker refreshes
soc_projected_*from the final charge map (price order previously left stale end SOC). - Hours near/far cover (
hours_demand_v1.5): Survival and hours until first morning sun (cover_window_hours_pv) only take the near need (chrono to PV + cover buffer, soft-cap morning SOC ≤min_soc+ 10 pp). Reserve-stretch kWh (weekend trough) schedule after morning sun — no more front-loading a full charge-power hour today (symptom: 8.3 kWh “minimum charge level” with morning with-grid ~45 %). Cover buffer again visibly affects the near share. Basis:energy_cover_near_kwh. - Decision Windows Flex v1.1 (additive): Optional query params
segments,segment_consecutive(off/soft/required),hours_completed,current_segment_index,repeat_daily,deadline_strict,slot_id. With flex:decision_engine: windows_v1.1,emsblock right afterdecision_status(PO Demand =ems.current_segment_hours, Period =ems.period_hours_todayuntil window end). Without flex params, unchangedwindows_v1. Deploy:deploy.sh api-only. - Explain stop-SOC / balance bars: Survival O4 pins to
min_soc_pctonly when below that floor. Stop SOC on every plan-slot line. Usable battery · now (night) / · At HH:MM (following day = PV-day start). Clock-only stamp in DE: um 08:00. Poll assetshx22.
2026-08-24
- Hours DA replan (production): After day-ahead + inference, a batch replans all active plants (
resolve_hours_demand, snapshot +plant.plan_updated). Poll hook + hourly timer 15:00–23:00 Vienna. Webhook materiality now includes the full plan (not just today’s slots) — EMS should pullhours_demand_urlafter the event. Optionalplan_hashin payload. - Explain cover label: “economical cover hour” instead of “cheapest hour”. Cover slots that are not the day’s minimum get “not the cheapest hour of the day”. The “Cheaper hour skipped: …” line (
cheaper_skipped) is not shown in portal/Sparvo (snapshot/diagnose only). Poll assetshx18. - P1-v2 same-day relocate:
HOURS_CHARGE_DAY_CONSOLIDATE=1(code default OFF) moves cover including survival onto cheaper chargeable hours of the same calendar day — including a single slot; current hour inclusive; elapsed hours stay. 0 €/MWh counts (not missing). Small survival (under 5 kWh) is omitted only when the SoC path still holds the reserve including slack. Slack belowmin_soconly with a price advantage. No new API field, nowait_cheap; O12 unchanged. Shadow loghours_consolidate_shadow.jsonl. Ops diagnose:diagnose-hours-charge-plan.py. - Fix: skipped-hours full list (portal/Sparvo): “Show all N” on
/plant/skipped-hoursor/plant-outlook/skipped-hoursreturned 500 Internal Server Error (Jinja:group.itemsresolved to the dict method, not the key). Full list renders chronologically by day again. - Following days charge effect + ⓘ: Per PV day in portal/Sparvo Explain: line Planned grid charge … kWh · Effect at morning sun: +X % (
pv_day_charge_kwh,soc_delta_pctin snapshotrest_day_balances, UI-only). ⓘ explains reserve floor, direct grid supply below min SoC, and morning-sun stamp (not right after the charge hour). Poll assetshx17. - Week glance day label:
hours_needed_daysmaps onto the calendar grid (D0…D6), not the sparseday_planlist — a day with charge shows 1 h planned again instead of “No charging” with a visible bar (wg6). - Hours API calendar day vs. app PV day: Decide/EMS (
hours_needed_today,period_hours_today,prefix_*,day_plan) stay on calendar days 00:00–24:00 (Period until 23:00). Only Explain Following days in the snapshot (rest_day_balances) uses PV days sun→sun — not in the API response. - Storage tile expand: control rule clarified (discharge throttle only when demand is not covered autonomously from PV+battery); “Without a battery … kWh/day on the grid” line removed. Prefix-view line (“Optimizer period …”) hidden in Explain (EMS field unchanged).
- Grid-charge plan summary copy: Replaced “Next PV yield …” (misleading at midday when PV is producing) with Current period: still X hours until … and In this period … grid-charge hours (portal + Sparvo).
- Following days as PV days (sun→sun): After midnight until morning sun, the night belongs under Until tomorrow morning; the first following day is the current PV day from morning sun to next-day morning sun (not a second night stub). After morning sun / before midnight: following days start at the next morning sun. Bars and without·with grid share that window (portal + Sparvo).
- Hours O12 when the pack is full: cheap-hold (discharge 0) only if there is a cover gap until the next morning sun. Not at SoC ≥99 % and not in a PV-surplus hour — a later reserve trough (e.g. Wednesday) alone must not close O12.
- Hours cover/survival not in PV surplus: the slot picker does not grid-charge an hour whose PV exceeds load (and SoC is above min). Stretch must not pick the cheap solar peak (e.g. 10:00) as “minimum charge level”.
- Hours interim 24 h: “next cheap hour” is only inside the next 24 h (not the cheapest slot in the 168 h horizon). No bridge charge in a PV-surplus hour. Reserve-survival later in the week is kept.
- Hours dawn cover: a 3-hour lookahead alone does not count as “already in PV”.
cover_window_hours_pvends at today’s first good PV hour (not tomorrow 07:00). Survival stop-SOC (O4) is the reserve floor (min_soc_pct) only when below that floor; otherwise trajectory/target (never below live SOC). Interim grid charge only when there is a reale_gap(not a full hour at 0). - Plant outlook expand: plant-specific copy — extra grid kWh + € vs no battery; larger pack kWh + further €/year. Control rule (charge on cover gap; throttle discharge in cheapest hours only when not autonomously covered from PV+battery). Hours JSON unchanged.
- Plant outlook storage tile: Sensible only if year € is cheaper and average ct/kWh is not higher than current — otherwise current / “fits”. Hours JSON unchanged.
2026-08-23
- Hours cheap-hold (v1.4): Only when PV+battery cannot cover forecast load (
load_covered_by_soc_pv=false): O12 = 0 in the cheapest N hours of the calendar day when not charging (cheap_hold) — house from the grid. If cover is already satisfied, discharge stays on (no speculative hold). Full cap whilecharge_now. N =ceil(daily load / charge kW). Annual tile uses the same cover gate. Fieldscheap_take_hours,hour_is_cheap,cheap_hold. - Plant outlook storage tile: Copy is N cheapest hours + winter average (not autonomy days). Sensible only if the year is cheaper than current — otherwise current / “fits”. 24 h daily-load floor only while extra kWh do not raise year cost. Hours JSON still has no annual €.
- Adaptive Hours cap, no default 36: without a request cap the server uses
ceil(usable kWh / kW)+12, max 168.max_hours_today(default 12) is clamped to that cap.basis.storage_horizonomits annual € / size ladder (autonomy, defer, cap only). Annual spot and sensible size live on plant outlook (4 season weeks + 12-month market average). - ExtractHoursDemand v1.15: fallback after 12 h without API (summer 11–15 / winter 01–03, target 35 %). Txt2/Txt3: no-charge /
X Std, Y kWh. Wiki/wiring aligned. Relay latch reset on hour change; year wrap counts as stale. - Plant following-days SoC: Portal/Sparvo show without·with grid per following day at the next morning sun (same as “Until tomorrow morning”), not at midnight. The last day may show a forecast-end stamp.
- PV day navigation D0–D6: Portal plant outlook and Sparvo Plant PV bar chart show all 7 days (today…D+6) again — the UI had capped at 6 while backend/API already delivered 7.
- Grid-charge dispatch unified: Annual storage cost (Familie B) and Hours EMS share
grid_charge_dispatch— cheap hours are always FORWARD over this pack’s adaptive window (no 36 h default). Adaptivemax_hours_horizon: without a request cap, Hours derives it from battery + charge power (need_h+12, max 168). Portal shows planning meta + warning on price heuristic without Hours engine. fc-verification stays a generic archive comparison (slider kWh/kW), not plant SOC simulation.
2026-08-30
- Plant profile annual load (meter): Instead of recency-weighted typical × 365, we now use the arithmetic mean of complete meter days since activation (excluding baseline day and today) × 365, ceiled to 100 kWh — one value for portal, Sparvo, Hours, outlook, savings page. Day / Hours load still uses recency typical.
2026-08-22
- Plant profile annual load: typical meter day × 365 (ceiled to 100 kWh) is written into the profile (portal/Sparvo open + save, telemetry including SOC-only, Hours, outlook, savings page) — not display-only. A stale form value (e.g. 6000) cannot overwrite the estimate.
- Adaptive discharge reserve-protect:
discharge_power_kw(O12) only throttles near min SoC and when no economical recharge is planned (interimpass/ no charge slots). Otherwise full user cap. Charge hours stay at full cap (inverter already takes grid+PV). 0 kW only at/below min SoC with no recharge, or when the user cap is 0. No more spot-hold. Hours +plant.plan_updatedincludedischarge_reason; an O12 cap change is material (webhook). - ExtractHoursDemand v1.11 (Breaking): API fields
prefix_*instead ofloxone_*; newcharge_now,charge_stop_soc_pct,target_soc_pct,capacity_kwh. O3 =charge_now(relay), O4 = stop SOC %, O9 = remaining kWh (SOC delta), O1 = 0 when O9=0. Re-paste PicoC from/docs/loxone-hours. Legacyloxone_*in old JSON readable until re-Decide. - Hours Interim Econ default ON:
HOURS_INTERIM_ECONis on (kill-switch=0).plant.plan_updatedincludes the same EMS scalars (prefix_*,charge_now,charge_stop_soc_pct,discharge_power_kw,adaptive_discharge,min_soc_pct,charge_power_kw,grid_charge_allowed). - Storage horizon is physical: Annual spot € uses PV→battery / battery→load / residual import in the same hour. Cheap hours only fill unused SoC — leftover annual kWh is not repriced onto the cheapest hours (that made 5 kWh look cheaper than 30 kWh).
- UI decimal separator always comma: Portal/Sparvo/Admin display and Loxone
txt_*use,in DE and EN (fmt_num/SpotDecimal). Machine values (data-*, JSON, CSS vars) keep ASCII dots. - Sparvo bar focus: Tap again shows kWh/ct (raw number in
data-price, format display-only). - Plant explain backup reserve: “Without grid” rolls with profile min SoC (backup reserve), not a 0 % floor. Interim
passno longer strips survival slots — reserve charging stays in the plan. Bridge-charge copy +{min} %reserve only when SoC+PV do not already cover the window (otherwise “covered”). Following days: without·with grid always shown; with grid grayed when equal (<1 % diff). O12 kW hint under the plan card removed. - Grid-charge explain plan summary: Portal/Sparvo replace chip/period pill with: next PV yield in X h from HH:MM · grid-charge hours until then · later charge list (date, day kWh, start·kWh). ⓘ only when cover is stretched → minimum charge level. Dump/bridge/off/none keep chip + short copy.
- Charge-hour hint: Each planned hour shows a short reason (
survival→ hold min charge level, cover → cheapest hour, opp → opportunistic fill, bridge → bridge charge).
2026-08-21
- Plant UI V1: Sparvo/Portal order Live → PV (day PV kWh stats only) → Plan/Explain → rest. Plan-span chart load + SoC with/without grid until morning sun; KPI at morning end. Live meter label “since {date}” (
meter_baseline_at/ reset fallback). - Hours Interim Econ (opt-in): Env
HOURS_INTERIM_ECON(default off). When on: if SoC would breach min before the next cheap hour, compare bridge-charge € vs spot pass-through; only charge when strictly cheaper (+0.02 €EPS).period_hours_todaymay shrink (cap 5 h); EMScharge_hours_todaythen has at most one interim slot (later same-day cover stays inday_plan/charge_hours). Additive fields:period_hours_today_default,interim_period_active,interim_needed,interim_decision,interim_period_cap_h,interim_cost_*_eur,interim_slot_start,interim_reason. Extract O2 unchanged. Details: Hours Demand. - Explain: next planned grid charge clarity: Chip/headline for
latershow day kWh + planned HH:MM of the next charge day (not EMS total target and not the night-end stamp). Following-days stamp is a from–to range (night_end→ reserve / last rest charge morning). Both cards list day blocks (kWh + time chips); survival slots marked (survival) with a short tip. 10‑min pollhx11. - Plant explain two horizons: Portal/Sparvo split Until tomorrow morning (load/PV/battery bars only through
cover_window_hours_pv) and Following days (cover remainder when stretched). Battery always With·Without grid. PV week: dual EOD D0…D6 + stats row load·PV·battery With/Without. Skip copy in future tense; 10‑min poll redraws both cards (hx10). - Hours ↔ Loxone grid charge bidirectional: Decide JSON top-level
grid_charge_allowed(EMS front). ExtractHoursDemand v1.17 → O10 = 0/1. Write: partialPOST /v1/plant/telemetry{"grid_charge_allowed":0|1}(VO like SOC/meter). Omit field = profile unchanged. Last-write-wins with Sparvo/portal. - Hours night vs reserve: Explain splits Tonight (
cover_window_hours_pv, first morning sun) and Reserve holds (cover_window_hoursonly whencover_extended_for_reserve). SoC without/with grid at both stamps. Chip stays “Today N h · M h in total”. Today 0 / week > 0 stayscharge_kind=later(with-grid still shown at the reserve stamp). - Master switch
grid_charge_allowed: Default on. Off →hours_needed*=0, empty slots, kindgrid_off/ reasongrid_charge_disabled. Dump has no effect. An explicit telemetry field may set the profile; Decide without the field is read-only. Sparvo + portal location + plant badge. - Midday skip
pv_will_fill_today: Look-ahead (PV×0.70, the slot’s calendar day, fresh SoC) replaces the single-hour PV formula. Leftover headroom instead of a binary skip. Survival candidates before fill. SNAP stays a device layer. - PV card day-end: D0 shows With grid / Without grid (
soc_end_pct_with_grid/soc_end_pct_no_grid; aliassoc_end_pct= with grid). - Portal/Sparvo grid-charge explain: Chip/headline now separate today’s EMS hours from hours until planning-window end (no more “32 kWh in 1 hour”). Skipped cheap hours are chronological with weekday+date; more than 6 rows link to
/plant/skipped-hoursor/plant-outlook/skipped-hours. - Hours cover on a weak weekend: Min SoC applies across D0…D6, not only until the first morning sun. If Saturday/Sunday sit on the reserve without PV carrying the pack, the engine stretches the cover window (
cover_extended_for_reserve,cover_window_hours_pv= first PV recovery) and schedules mandatory grid charge (trough below the reserve, not the week’s net load). Raising min SoC (e.g. 20→40 %) must not merely pin end-of-day levels to the new floor. Portal/Sparvo week glance and the Hours API share this path. - Loxone
d0_soc_eodload shape: End-of-day SoC now rolls the same hourly load/PV shape as plant outlook (meter/profile, D0 from the current hour). Flat 1/24 only as fallback without meter/shape. Meaning stays “midnight today”; values can differ from the old even split. - Portal/Sparvo timeline label (DE): German UI now reads “Zu Planende am {time}” (was “Am Planende um …”). English remains “At window end / at {time}”.
- Plant outlook D0 SoC from now: End-of-day roll starts from the live SoC at the current local hour (no replaying midnight→now). After Hours
day_plan, the SOC path is re-chained with the same charge chips. Loxone display pack uses the same D0-from-now logic. Prevents implausible calendar EOD vs plan-end when no grid charge is planned.
2026-08-20
- Wiki
access.tier: documented values arebasic,community,full_trial,full_active(notprofessional/oemas JSON enum). Rate-limit table uses the same codes; Professional/OEM remain product labels. Telemetry softcaps: Community 2000/day, paid 10000/day. - Webhooks: registration and dispatch require HTTPS to a public host (HTTP, loopback, and RFC1918 rejected; redirects not followed).
GET /v1/statusno longer includescache_path. - Hours cover load with sparse meter telemetry: WD/WE hourly shape no longer treats missing samples (
None) as 0 kWh. Days with <12 observed hours are skipped for the shape (flat 1/24 fallback); never-seen hours get a flat prior. Flat same-day overlay keeps rest-of-today positive (so_far + typical × remaining clock share) so cover evenings are not zeroed. Stops plan-end SoC stuck near 100% when the cover window includes the night (e.g. EMS posts only in the afternoon). - Portal/Sparvo plan-end battery level: “At window end” / Without·With grid projects SoC hour-by-hour over the cover window (no longer a bulk PV−load window sum). Surplus solar while the battery is already full is clipped — overnight drain shows a plausible plan-end level. Basis includes
hourly_load_cover_kwh/hourly_pv_cover_kwh/hourly_grid_charge_cover_kwh. Cover shortfall also uses the same chronological check (maxwith bulk) so “no grid charge” is not claimed when the night would breach min SoC. Plan-end clock = hour floor + cover length (end of the last rolled hour). Rest-of-today load is spread across all remaining today hours (not only hour 0). Dump/Opp after the cover window does not raise plan-end SoC. Engine cover load/PV = sum of the hourly series (no parallel flat cover total). Legacy snapshots missingprojected_soc_pct_with_gridno longer invent With-grid via an energy lump; Opp cap uses chronological cover need. Bulk fallback also respectsallow_untimed_grid_lump.
2026-08-19
- Hours Demand v1.3 — SOC-feasible grid charge: Engine
hours_demand_v1.3picks charge slots only when the battery has headroom and PV does not already fill the hour. Cheap but impossible hours appear inskipped_slots[](always included); shifted charging in later feasible slots.plan_feasibilityreportspartial/storage_undersizedwhen not all kWh can be scheduled. P1 survival before price; hourly load profile (WD/WE shape) in the SOC roll. Portal/Sparvo explain: new reason codesslots_skipped,storage_undersizedplus lines like “16:00 cheap, battery full → next charge 03:00” in the Grid charge block (live resolve). PicoC/Extract unchanged (additive). Details: Hours Demand. - Plant weather for new sites: Daily
spotforecast-plant-weatherscans active API keys with Lat/Lon inuser_plant_locations(discovery without a second timer) and writesdata/plant_weather/{api_key_id}/. Also: portal geo-save and first PV/Weather request (on-demand). Directory ownership*:forecastmode2775. PV Forecast Loxone pack uses the same request-path weather payload for Tmin/Tmax even if a disk write fails once. - Live strip day use after first meter ping: Portal/Sparvo Day use so far stays waiting for meter data (API
day_import_kwh=0) until the first positive delta. The first cumulative total (or a 0 placeholder then the real total) is baseline only — not today’s kWh. No portal Reset meter needed for a new API key.
2026-08-18
- Hours EMS Period until midnight:
period_hours_todayis hours remaining until 23:00 (00:00 → 23, 13:00 → 10, 22:00 → 1). At 23:00 it resets to 24. Demand/charge_hours_todayonly include slots in that window (hour 23 only from 23:00). Extract O2 maps the field (missing/<1→ 24).
- Portal/Sparvo grid-charge blocks: Cover headline and cheapest charge times come from the same live Hours plan as the week glance (
hours_decisioninbuild_plant_outlook). Battery rules, dump, and location saves refresh the explain snapshot. A stale EMS snapshot may still updatecharge_power_kw, but not the hour count without new slots.
- Hours grid-charge power:
charge_power_kwismin(battery.max_charge_kw, grid connection × 0.80 − base load)(20 % reserve, same as Sparvo devices).hours_neededsplits the kWh target across as many cheapest hours as that power allows (20 kWh at a 10 kW connection → 3 h at ~8 kW headroom, not 1 h × 20 kW).
- Hours/profile min SOC default 20 %: When request and plant profile omit
battery.min_soc_pct, the engine default is 20 (was 15). Stored profile values are unchanged. UI: portal/settings/locationand Sparvo Settings → Battery rules (/settings#sv-storage-rules) with sliders for min/target battery level (0–100 %) and grid buffer (0–50 %, default 10 %). - Hours validation:
battery.min_soc_pct/target_soc_pctoutside 0…100 →invalid_input(same pattern as cover buffer 0…50). - Portal/Sparvo plant UI — less duplication: The live strip is the only display for battery level, day import, and meter total (portal
/plant-outlook, Sparvo/plant). Below it: controls only — manual starting battery level (slider, fallback without live telemetry) and Reset meter baseline — no second value grid when live telemetry is active. - Hours explain block: Chip + headline only — the extra net summary under the balance chart is removed (same deficit was shown three times).
- Sparvo
/plant: Order is Live → Hours explain → PV day chart; Planned grid charge lists price/kWh per hour (switch days); need/rationale stays in the explain block above.
2026-08-17
- Hours/portal load profile — same-day overlay: Estimated day use and remaining-today blend typical with meter-so-far using the WD/WE hour shape (not clock × 24). Incomplete today never counts as a full day, including for D+1. Same number in Hours, Sparvo live strip, and outlook D0. Engine tag stays
hours_demand_v1.2. PicoC unchanged (fieldshours_needed*/d0_load_kwhunchanged). - Loxone docs consolidation + PicoC audit: Canonical Loxone overview (one host VO, minute plan, max 8 PicoCs). Weather v1.1 start+:10 (no longer :05 next to Hours Decide). Forecast Debug v5.5 start+:01. Windows v2.2 auto :03. Hours Extract v1.8 reads JSON on start. Re-paste PicoCs from the guides.
- Loxone PV Forecast auto-fetch v1.1 / Extract v2.1: Debug fetches on start (~3 s) and then hourly at :08 (not :05, so Hours Decide stays clear). Extract reads an existing
spotprice_pv.jsonimmediately after Miniserver reboot. Hours users: reuse the same VO parent and add only commandFetchPvForecast. Re-paste both PicoCs from/docs/loxone-pv-forecast. - Loxone PV Forecast Extract v2.0: One GET
/v1/plant/pv-forecastadds ready-madetxt_d0/txt_d1lines (weekday, date, PV, optional load, end-of-day SoC, Tmin–Tmax) plusweek_kwh,d0_load_kwh,d1_load_kwh,d0_soc_eod,d0_tmax,d0_tminnext tod0_kwh…d6_kwh. Missing parts are omitted (no default SoC 30%). End-of-day SoC includes grid charge only with a fresh Hours snapshot; otherwise PV vs load.loxone{}is at the start of the JSON. PicoC Extract v2.0 (O1…O13, Txt1…Txt3); Debug stays v1.0. Guide:/docs/loxone-pv-forecast.
- Hours Demand cover buffer: Default 10 % on a positive cover shortfall (
battery.cover_buffer_pct/ plant profile; portal location + Sparvo Settings → Cover buffer, 0–50 %).energy_charge_kwhand hours include the buffer; a zero shortfall stays 0. Engine tag remainshours_demand_v1.2. - Sparvo cover buffer in Settings: Field under Settings → Cover buffer (
/settings#sv-cover-buffer) and Cheap grid charging (/settings/dump) — not only Plant → location. - Grid-charge copy: Portal/Sparvo plant chip and headline show target kWh in the planned hour (charge only until that amount is in), not “for 1 h”.
- Loxone FetchPlantHours v1.9 + ExtractHoursDemand v1.7: SOC 10% bands up and down → Telemetry+Decide; I7 every 15 min Telemetry, Decide only smart (today still needs charge and SOC rising); I8 :05 fallback; 3 min cooldown. Extract O1/O9 follow the API down as well (grid charge can stop). Re-paste PicoC from
/docs/loxone-hours.
- Hours Demand — slots from now: Charge times and price ranking start at the current local hour (no leftover midnight-window slots).
hours_needed_today=0stays 0 (no fallback to the week total). Portal/Sparvo clear charge chips on days without engineday_plan; D0 drops hours before *now*. energy_charge_kwh: Grid-charge target rounded up to the next whole kWh (1.4 → 2), not max kW × hours. EMS fields sit at the start of the JSON (Loxone Extract ~2.3 KB fread).- Portal/Sparvo grid-charge chip: When today=0 but later days >0, no longer “No grid charging needed” — shows next grid charge (
next_charge_start/next_charge_hours), same semantics as Extract Txt3. Week-glance chip Next …. - Loxone ExtractHoursDemand v1.5: new output O3 =
energy_charge_kwh(optional kWh limiter) plus energy ratchet. Fetch stays v1.8. Re-paste PicoC from/docs/loxone-hours.
2026-08-16
- Docs sync: Loxone overview / Hours / Telemetry / Plant Telemetry bumped to Fetch v1.8 + Extract v1.4; Hours Demand documents cover/dump/bridge; Telemetry guide links to Hours for the PicoC paste (no duplicate embed).
- Loxone FetchPlantHours v1.8 + ExtractHoursDemand v1.4: Downward SOC bands 90→80→…→0 trigger Telemetry+Decide (in addition to hourly :05); Extract ratchets Demand upward within the calendar day (
N_locked), midnight reset. Docs DE/EN Hours + Telemetry + code samples synced. - Hours Demand bridge fix: Bridge charge (
hours_needed_bridge) only when Dump/Opp is on. With Dump off and cover satisfied →hours_needed = 0(no spurious 1h bridge). Engine tag remainshours_demand_v1.2. - Portal/Sparvo grid-charge UI: Cover shortfall vs dump top-up separated (chip/headline/net); dump status + threshold in the section; Sparvo Settings → Cheap grid charging (
/settings/dump).
2026-08-12
- PV/Weather audit fix: Request-path Open-Meteo timeout 8 s (no 60 s worker freeze);
cache_needs_refreshwithout empty-daily refresh loop;wind_speed_unit=kmh; Loxone double-GET coalesce forpv/weather;not_availablebefore quota reserve + middlewarequota_class(access_denied/pv/weather) so basic does not burn decision fair-use. - Plant-weather schema v2 live: Cache includes display fields (humidity, pressure, wind, weather code, daily min/max/sun); timer
spotforecast-plant-weather05:20 Vienna; deploy starts a background refresh for schema heal. - PV Forecast + Weather APIs (public):
GET /v1/plant/pv-forecast(7-day yield kWh, classpv, Community 48/day) andGET /v1/plant/weather(site weather, classweather, 48/day). Docs DE/EN:/docs/pv-forecast,/docs/weather; Loxone guides/docs/loxone-pv-forecast,/docs/loxone-weatherwith PicoC Debug/Extract v1.0 + flow SVGs. - Hours info ⓘ UX: Tips on portal/Sparvo appear next to the tapped ⓘ (fixed) and close on re-tap/click, outside pointer, scroll, resize, or Escape.
- Week-glance copy: Removed “(shared engine)” from visible portal/Sparvo strings (source line + charge hint).
- Live strip UX: Removed method subtitle under “Estimated day total” (meter history / load profile) on portal
/plant-outlookand Sparvo — tile shows the value only. - Portal/Sparvo week glance — audit fixes: missing
strip_ariai18n, demo hint instead of silent hide, explain link only when week glance is present, bar height/CSS, Sparvo glance refresh, Loxone Hours prose O1/O2 → today-slice v1.3, DE Hours aligned to EN. - Portal/Sparvo Hours week glance: Plant outlook and Sparvo Plant show the 7-day plan (
day_plan, default horizon 168 h) from the shared engine — separate from the cover planning window / last EMS decide. Docs DE/EN Hours + Loxone Extract table synced. - Hours Demand v1.2 — dual horizon: Default plan horizon 168 (request overrides). Caps
max_hours_today=12/max_hours_horizon=36. Additive response:hours_needed_today,charge_hours_today,period_hours_today,day_plan. EMS/Loxone Extract v1.3 reads the today slice (optimizer Period stays 24). Portal/Sparvo plant outlook charge chips from shared Hours engine (side_effects=False). Webhookplant.plan_updatedcompact (today + URL). Opp still only below Grenzpreis; cover still PV-recovery.
2026-08-10
- Lead-aware dump scenario:
dump_scenario_ct_kwh/dump_scenario_eur_mwhblend towardprice × pct/100when pct > 0 with lead weights D+1=1.0/ D+2=0.45/ D+3+=0.30(never above the model price). Scope stays midday (midday_11_16) — no evening dump correction.price_ct_kwhunchanged. Clients should use APIdump_scenario_*, not recompute locally.
2026-08-06
- Live strip auto-refresh: Portal + Sparvo Live-Anlage strip refreshes ~every 15 min while the tab is visible (
/plant-outlook/live-telem, Sparvo/api/live-telem) with an updated timestamp — no full page reload. - Estimated day use sooner from meter: Portal (
/plant-outlook), Sparvo live telemetry, and Hours/load profile use meter history after ≥1 full meter day (was ≥2); until then annual÷365. Docs DE/EN + code samples synced.
2026-08-05
- Docs / code-samples sync (Hours v1.1):
/docs/hours-demand+/docs/plant-telemetry(DE/EN) and portal/code-samplesHours explain cover-until-PV, spike damping, and ≥2 full meter days; samples printdecision_engine,cover_window_hours,remaining_today_load_kwh. - Portal live-telemetry hint: estimated day use correctly detects
site_meter_history_*(previously exact matchsite_meter_historyonly → false annual÷365 hint). - Plant Telemetry / Outlook: SOC pings store history (
plant_soc_samples); plant outlook shows hourly load/PV/SOC simulation (soc_model=hourly_v1); day chart: toggle Last/SOC, PV bars always on. - Hours Demand — load-learning spike damping: meter-day typical uses a soft cap (≤2× median of last 4 days) + 25 % robust blend — sustained load jumps still follow; one-off EV/spike days do not overdrive grid-charge planning.
- Hours Demand v1.1 — cover until PV recovery: engine charges only the energy gap until the next good PV block (rest-of-today + D0/D1 window, cap ≈ rest-today+36 h), not the full 7-day load. Load profile: recency-weighted daily mean (half-life ~2 d, WD/WE).
charge_hoursprefer slots inside the cover window. New basis fields:cover_window_hours,remaining_today_load_kwh,horizon_load_kwh; engine taghours_demand_v1.1. - Portal plant-outlook Load/PV/SOC: chart load from meter-delta day buckets (+ Hours estimate for D0…D6); D0 SOC = live; client refresh every 15 min via
/plant-outlook/data; week line = grid charge cost (not household use). - Portal plant-outlook meter total: restored Import meter + day-so-far values in the “Live telemetry (EMS)” card (regression after the live strip).
- Loxone telemetry-only guide: new Community page
/docs/loxone-telemetry(DE/EN) after Overview — Analog Memory wiring, screenshot, FetchPlantHours v1.7 copy-paste; nav + pager +/docs-assets/…. - FetchPlantHours v1.7: ~1 s O4 pulse for Analog Memory Set; Txt points to Memory→VO
<v.2>; removed deadpulseTelpath; meter magnitude for VI/checks (meterAbs). - Portal SOC slider vs live telemetry: Hours/telemetry SOC (
hours_api) overrides manual slider values and locks the slider (~36 h freshness) on/plant-outlookand location settings; without fresh API SOC the slider stays available as fallback. - Telemetry rate limit: softcap for
/v1/plant/telemetryraised to 2000/day (Community) / 10000/day (other) — still does not count against Decision fair-use. - Loxone telemetry
<v.2>+ partial POST: API accepts SOC-only / meter-only bodies; docs/VO guide use official Input placeholders instead of<v.ViName>; FetchPlantHours v1.6. - Live telemetry UI (portal + Sparvo): strip with battery SOC %, day import so far, estimated day total (meter history or load profile), and current meter total — Sparvo home,
/plant,/plant-outlook. - Loxone FetchPlantHours v1.5 + Telemetry
diag: API detects unsubstituted<v.…>tags (hint=loxone_vi_not_substituted) and returns quote-freediagfor Txt2/Txt3; aggressive decimal-comma sanitization; PicoC v1.5 readsdiagfirst. - Loxone FetchPlantHours v1.3: telemetry polls
/user/common/spotprice_telemetry.json(like Decide) — Txt1/Txt2 + O13 show OK/FAIL/timeout instead of only “Telemetry done”. - Plant Telemetry API: empty/invalid body → HTTP 200 with
telemetry_status+hint(no more FastAPI 422); accepts missingContent-Typeand locale comma decimals. - Portal plant outlook — live telemetry:
/plant-outlookshows SOC source/time, import meter total, day import so far, meter timestamp; Reset meter baseline button (next EMS ping = new start value). First meter total (e.g. 3725 kWh) is always baseline (meter.is_baseline). - Telemetry API response: explicit
meter.is_baseline,meter.import_kwh_total,meter.observed_at; docs DE/EN synced. - Rate-limit headers on success: successful
/v1/*responses now includeX-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset, andX-RateLimit-Scope(same headers as on 429;Retry-Afterstill 429-only). - Decision Windows missing params: missing
start/end/mode/rolereturn HTTP 400incomplete_request(aligned with docs; no longer FastAPI 422). - API usage — Loxone double-GET coalesce: Identical
api_key+ path + method within 3 s is logged asquota_class=duplicateand does not increment portal/rate-limit counters (response still returned). Fixes Miniserver Virtual Output analog double-fire. Loxone tip: enable Use as digital output, leave Command OFF empty. DebugSpotpriceFile v5.4 keeps O4 high during fetch. - Loxone Forecast DebugSpotpriceFile v5.4: keeps O4 high during fetch (no mid-pulse
1→0); I1-rising trigger only + cooldown. Docs/samples synced. - Loxone Forecast — double API count: Not a server double-count bug — the Miniserver sent two real
GET /v1/forecastcalls (~2 s apart). Typical cause: Virtual Output Command OFF filled with the same URL as ON (PicoC pulse ON→OFF = 2 GETs). Fix: leave Command OFF + Header OFF empty; DebugSpotpriceFile v5.3 triggers manually only on I1 rising edge (not everynEvents). Docs DE/EN +/code-samplessynced.
2026-08-04
- Landing
#planung-live: removed No dump correction \| Dump price toggle; ranking/sums again always use the model price (same as/fc-verification). Day navigator, Sparvo, and/code-sampleskeep their dump toggle. - Sparvo
/prices+ PDN: negative prices draw bars below the zero line (signed scale); PDN negative bar height fixed (more negative → taller downward). - Loxone Forecast PicoC v5.2 / v17 (live-verified): Debug requires a real forecast payload (
"forecast":+price_ct_kwh+ file length ≥ ~7 KB) — no false OK onforecast_status. Parse v17 reads price now/+1 h viastartneedles without pointer walks (v11–v16 hung on Miniservers after dump-JSON growth). Portal/code-samplesLoxone Forecast ships both programs. Docs DE/EN/docs/loxone+/docs/loxone-forecastsynced. - PDN UI: removed duplicate hour/price label in the chart header on hover/touch (
#pdn-chart-focus); floating tooltip remains the sole detail surface (landing#strompreise,/forecast,/admin/forecast) - API hygiene —
dump_scenario_*aligned to row price: After copying onto Decision /charge_hours/ Windows slots,dump_scenario_ct_kwh/dump_scenario_eur_mwhare always recomputed from that row’s price ×dump_risk_pct(no float drift from EUR÷10). Windows slots strip internal_keys; public ranking stays on the model price. - PDN/Sparvo UI declutter: removed the “Dump risk” chip, help sheet, risk dot, Sparvo hint, home line, and tooltip “Possible dump (illustration)” — No dump correction \| Dump price toggle and midday hatching remain. API
dump_risk*fields unchanged. - Sparvo tap focus: with Dump price active the focus line shows the dump price; with No dump correction it shows the point forecast.
- Portal
/fc-verification: removed the No dump correction \| Dump price toggle; savings simulation and chart again always rank on the model price (dump ranking skewed the savings % for end users). Dump history remains on/fc-verification/detail+ CSV/PDF. Day navigator, Sparvo, and/code-sampleskeep their dump toggle; landing#planung-livealso has no dump toggle (model price). - Forecast API —
dump_scenario_*always usable:dump_scenario_ct_kwh/dump_scenario_eur_mwhhold the dump price whendump_risk_pct> 0, otherwise the point price (nevernullonly because there is no dump). Clients need no null-check; whether a dump is active is shown bydump_risk_pct/dump_risk. Public Windows ranking stays on the model price. Loxone ParseSpotpriceForecast bumped to v14 (O6 = always-usable scenario/point price; show “Dump price” only when pct > 0). Docs + code samples synced. - Forecast API —
dump_risk_pct+dump_scenario_*always on hours: every hour object onGET /v1/forecast(and inherited ondecision[]/forecast[]in Decision, slots in Windows,charge_hours[]in Hours Demand) now always carriesdump_risk_pct(0…100, nevernull) anddump_scenario_ct_kwh/dump_scenario_eur_mwh.dump_risk_pstays 0…1 unchanged (compat).price_ct_kwh/price_eur_mwhare never rewritten. - Portal dump-price toggle (Decision demo,
/code-samples): same Overlay/Dump-price view as the day navigator/Sparvo on the landing planning demo and code samples — display/ranking only, in the portal UI or in the generated sample code. Public endpoints (GET /v1/forecast,GET /v1/decision/windows, …) keep returning the point price unchanged;price_ct_kwhnever changes. - Docs/samples sync — dump risk & field
start: Forecast docs DE/EN fixtimestamp→start; dump risk only on ML hours (forecast_d*), no soft-dump; Loxone ParseSpotpriceForecast v12 (O4 = risk code, O1/O2 unchanged); portal/code-samplesprint optionaldump_risk/dump_risk_p - UI Dump price default: Day navigator (landing/user/admin) + Sparvo default to Dump price; Min/Max/Avg follow displayed dump/point prices
- Sparvo/PDN focus: Tap/touch on dump-risk bars shows the displayed price in the floating tooltip (PDN); Sparvo keeps its tap focus line (toggle: dump vs point)
- Forecast API — Dump risk (additive):
GET /v1/forecastmay includedump_risk,dump_risk_p,dump_risk_scope,dump_risk_versionon midday ML hours (11–16 Vienna). No effect onprice_ct_kwh/price_eur_mwh. Live experiment (ML_DUMP_RISK=live); kill-switch0. Portal bar toggle Overlay \| Dump price.
2026-08-01
- /docs Loxone wiring plan: overview + detailed plans (ASCII + output→input checklist) for Hours, Windows, Forecast; heading fragment anchors
- Loxone FetchPlantHours v1.1: after Decide wait ~3 s then O1 0→10 for Parse (fix: O1 stuck at 10 → no hourly re-parse); Telemetry no longer triggers Parse; diagrams separate Telemetry vs Decide file
- Loxone PicoC harden v1.2: FetchPlantHours polls
decision_status(backup/truncate/timeout restore); Parse Hours/Windows/Forecast trigger only on O1 ≥10 (not fail=9) - Loxone PicoC harden v1.3: Parse keeps last Demand if file missing; Windows/Forecast VO pulse ~200 ms (v2.1/v5.1); Fetch poll requires status plus
hours_needed/charge_hours - Loxone PicoC harden v1.4: commit Demand/slots only on
decision_status=ok/ valid Windows JSON — empty/invalid file no longer zeroes the optimizer - /docs Loxone version table: overview lists current PicoC versions (Fetch v1.2, Parse Hours/Windows v1.4, Debug Windows v2.1, Forecast v5.1/v11); guides re-synced with embedded sources
- /docs beginner rewrite: full plain-language rewrite with everyday examples; Loxone guides step-by-step for users who have never used PicoC
- /docs prev/next: every docs page including the hub has bottom Previous / Next controls (order = sidebar /
PAGE_SLUGS) - /docs mobile UX: mobile-first layout, burger for the docs table of contents (same pattern as portal main nav), and a top-right copy-to-clipboard control on every code block
- Loxone integration guides (new): public docs menu Loxone with overview, Hours & telemetry, Decision Windows, Forecast — step-by-step wiring and PicoC copy-paste at
/docs/loxone - Hours Demand —
charge_hours[]:POST /v1/decision/hours-demandindecidemode now always returns concrete start/end times and prices for every recommended charging hour whendecision_status: ok, not just the count (hours_needed) - Plant Telemetry (new):
POST /v1/plant/telemetry— submit battery SOC% and cumulative import meter readings; own quota classtelemetry, does not count against the Decision fair-use limit - Hours Demand —
mode: "telemetry": the same ingest path as Plant Telemetry is now also reachable directly viaPOST /v1/decision/hours-demandwithmode: "telemetry" - Bridge breakdown in Hours Demand: new fields
hours_needed_cover,hours_needed_opp,hours_needed_bridge, andplant_freshnessin the decide response - Webhook
plant.plan_updated(new): sent to your registered webhook URL wheneverhours_neededorcharge_hours[]change materially — same URL and secret asforecast.updated - Public API documentation (new): a full developer wiki at
/docs, in German and English, crawlable and without login — replaces the previous plain status link in the footer
Earlier
- Decision Windows API (
GET /v1/decision/windows, enginewindows_v1) — contiguous time windows with a full constraint set (min_gap_hours,max_consecutive,allowed_hours,weekdays,allocation) - Decision API (
GET /v1/decision, enginequartile_v1) — quartile-based hourly signalscharge/discharge/hold - Hours Demand Core (
POST /v1/decision/hours-demand, enginehours_demand_v1) — first version withhours_neededderived from a SOC/load/PV balance - Forecast webhook (
forecast.updated) — push notification after every successful forecast update - Forecast API (
GET /v1/forecast) — hourly EPEX AT price series, 48h Basic / 168h full version
Version notes
- API version:
v1(in theapi_versionresponse field) - Engine tags per endpoint:
quartile_v1(Decision),windows_v1(Windows). Hours Demand reportshours_demand_v1.38while any storage-plan flag is on. With every storage flag off and the lead band on, it ishours_demand_v1.37. With the lead band off as well (HOURS_ECON_LEAD_BAND=0), it ishours_demand_v1.36. - Breaking changes are recorded here with date, affected endpoint, and migration window
Next steps
- Downloads — the current OpenAPI specification with every field
- Forecast API · Decision API · Hours Demand API
Register for Community or code samples with your personal key.