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_engine stays hours_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_engine stays hours_demand_v1.38. Kill HOURS_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_eod starts 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 fresh day_plan with energy_kwh > 0. Rollback LOXONE_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_engine stays hours_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_kwh stay on typical. decision_engine stays hours_demand_v1.38. Kill HOURS_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, and reserve_breach stay. It is not raised. Decide, DA replan, and the annual simulation. decision_engine stays hours_demand_v1.38. Kill HOURS_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_needed and hours_needed_days count only hours that buy grid energy. The same row does not set charge_now and 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.txt and OpenAPI name the live Hours engine tag (default hours_demand_v1.38). PV PicoC in llms is DebugPvForecast v1.6 and ExtractPvDaily v2.7. Seven Loxone detail pages are noindex because 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_engine stays hours_demand_v1.38. Kill HOURS_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 datetime made 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_engine stays hours_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_engine stays hours_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_engine stays hours_demand_v1.38. Kill HOURS_WEAK_PV_REDUNDANT_PEEL=0 and HOURS_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_engine stays hours_demand_v1.38. Kill HOURS_CHARGE_SURVIVES_USE=0 and HOURS_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_engine stays hours_demand_v1.38. Kill HOURS_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_engine stays hours_demand_v1.38. Kill HOURS_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_engine stays hours_demand_v1.38. Kill HOURS_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 Jahre from 18 and N- bis M-Jährige are 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-rest are 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 true is not an hour. The chart stays on the calendar day. Survival without lookback, MUST, prefill, spill, interim, prep, and opp stay put. Kill stays HOURS_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/false is 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 weekday true is not hour 1 and not Tuesday. Kill SPARVO_PLAN_BATTERY_COVER=0. All plants/keys.
  • Flex Windows: JSON true/false is not hours already run and not a kWh value in the recipe. A weekday or a window true is not Tuesday and not 01:00. Invalid numbers count as 0. All plants/keys. PicoC unchanged.
  • Display: JSON true/false and 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/false is 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/false is 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 index true is not hour 1. A session counter true is 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 field true is 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/false is 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, true does 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 count true as 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:00 stays 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/windows and /v1/plant/device-schedules are 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_engine stays hours_demand_v1.38. Kill HOURS_COVER_EARLY_PLACE=0. All plants/keys. PicoC unchanged.

2026-10-04

  • Feed-in is the day total: Today's export_feed_kwh is 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_kwh roll 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_kwh is 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_breach row (still survival). 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_engine hours_demand_v1.38. Kills HOURS_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_PRICE each =0 (then v1.37 again 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. Kill HOURS_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_engine hours_demand_v1.37. Point price unchanged; dump scenario is not the control price. A typ-week without forecast starts stays 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). Kill HOURS_ECON_LEAD_BAND=0 restores hours_demand_v1.36 and 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, timer spotforecast-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-out fix_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 exits empty_address/not_enough_input/invalid_coords/invalid_postal/profile errors; Sparvo geocode ignores bad form coordinates). Kill ECONTROL_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 fake cost_dyn (fix×import). Cache/compare ct finite (poison cache → fallback; bad env floats do not crash Hours import). Hours Decide unchanged (basis.storage_horizon has 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=0 ignores leftover dicts (only outcome_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 outcome sinnvoll; collapse/fits/warn/grid/fail). Kill STORAGE_HORIZON_OUTCOME_GUARD default on (=0 → legacy worked example). Soft-link = candidate ∧ edit; citizen fixed-tariff collapse|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_ANNUAL default 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_source meter|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 =1 in account.env → with grid charging on, bridge is the active recommendation (sweet_spot_capacity_kwh). Grid-off: no upsizing. Rollback: =0 + restart account-portal on 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_soc resim, Econ-PP Soft-Cap SoC/stamp maps, Soft-Cap-Spill euro proxy, gate-filter/Econ-redistrib charge maps scrub JSON-bool/NaN→0 (no float(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 (no float(True)=1 Soft-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 (no float(True)=1 Soft-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 (no float(True)=1 Soft-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-bool cover_merged/energy_kwh as 0 (no float(True)=1 floor 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_kwh counts as 0 (no float(True)=1 Soft-Cap floor); coalesce sets charge_kind=must when MUST (no leftover darf/weak_pv); non-cover slot_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_added that 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_role on the sink (Neighborhood / Thin-Cover-Defer stay frozen); blank start rows dropped. Spec HOURS §6.7 + wiki Hours. All plants/keys.
  • Hours Lookback merge survival coalesce: Cover∪Weak same-hour coalesce keeps sibling survival on 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 must onto 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=True only 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 start assumes 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) vs isoformat() cannot leave a second row and inflate Soft-Cap Σ; sink start normalized 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_kwh floor (no bare weak_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_hours coalesces 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_pv on peelable movable; prior-Weak frozen remnant keeps weak_pv (Cover-floor remnant still clears it); split_cover_merged_on_relocate clamps 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 carries cover_merged on drop/relocate and probes softcap_stamp_holds Cover/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_head keeps cover_merged_kwh (Mixed absolute, never bare Weak→Natural); Soft-Cap demote clamps cover_merged≤energy; Lookback-Thin frozen/movable split via split_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_kwh floor (softcap_weak_peelable_kwh) — counting full Mixed energy as Weak stamped Cover→weak_pv and 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 via softcap_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_skip is 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_guard in the Explain JS i18n blob (hydrate dropped SSR chips when why_* was empty); Thin post-move Soft-Cap revert writes note thin_cover_softcap_revert into 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_skip into 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_pv flip). 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 merge coerce_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_now client bool-guard + PicoC spec: Portal/Sparvo/live-telem session poll only when charge_now === 1/"1" (never Number(true)=1); Explain Jinja stop not |int on 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=None and Soft-Cap probe no-op. Spec HOURS §6.7 + wiki Hours. All plants/keys.
  • Hours charge_now bool-guard (UI/live-telem/webhook): Shared coerce_charge_now — JSON true/false never int(True)=1 on 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_pct UI read stop only via coerce_charge_stop_soc_pct (JSON true/false never stop=1); charge_now bool → 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 — JSON true/false never 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, constraints max_hours*/min_hours, snap stop, and PV-array kwp fail-closed like battery/horizon/latch — JSON true/false → invalid_input or field omit (never float(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 — JSON true/false → invalid_input or latch incomplete_hold (never float(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 from v1.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 &lt; min_soc−0.5 pp) — Decide opens with rearm_below_floor (prefix charge_now); above floor unchanged hold until stop−H. Extract v1.23 follows server SoT (charge_now=1 clears local). Spec HOURS §6.7 + wiki Hours/Loxone. Kill HOURS_CHARGE_HYSTERESIS=0 unchanged. All plants/keys.
  • Hours grid-off Speicherschonen evidence: Display strip requires real hold evidence (hold_hours/hold_hours_local/status=hold) — stale hold_hours_n alone does not invent Speicherschonen clocks. Portal explain: live-telem charge_now=0 also 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, or allow_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), synthetic week_status why_code=cold_start. Pause on meter warming (profile stamps/PlantLoadSample) or annual-only &lt;72 h (missing activation stamps / ApiKey lookup fail → fail-closed pause; meter-count DB errors do not invent warming). Warm-up exit clears sticky snap pause_reason (Explain empty_cold_start only while paused). load_covered_by_soc_pv=false + Outlook/empty_reason=cold_start — week glance D0 Warm-up, never Covered; Explain chip/headline chip_cold_start/headline_cold_start (not chip_none). Pause ∩ grid-off → pause wins only while hours_control_paused (top-level/basis; sticky notes alone are not enough): display strip sets charge_kind=none, clears holds, and rewrites week_status why/empty_hint/groups to cold_start (never grid_off/hold/covered). Pause with grid still on: same display mute without inventing grid_off (UI format + Outlook snapshot-first/live-hydrate force hours_needed*/energy_charge_kwh/next_charge_* to 0; live-telem charge_now=0). Grid-off only: half-healed status=charge with hold evidence → Speicherschonen (hold), not autark. Tombstone webhook sets hours_control_paused=false. Kill HOURS_COLD_START_PAUSE=0 (default on). Webhook field hours_control_paused. Portal/Sparvo warm-up banner + Explain empty_cold_start. Spec HOURS §14.5b. All plants/keys.
  • Hours max grid-charge Cap (hours_demand_v1.32): Profile field max_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-PV P_econ ≤ Cap (Cap 0 → 0, no invented C×capacity). Cap/headroom 0 with an uncovered night → UI charge_empty_reason=no_charge_power / week glance “No charge power” (never a false “Covered”). Portal/Sparvo/Guest slider + plant-outlook rules bar; material key includes charge_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_roles charge/energy_charge_kwh/nested day_plan included; resolved_clear from result.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-closed grid_off_clear_check_failed). Non-ok DA under grid-off + charged snap → new tombstone + one webhook. Live-telem / hours_decision_ui_payload and ui_payload_from_resolved force charge_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_kwh despite why=grid_off; hold evidence → status=hold) to status=autark/charge_kwh=0, rebuilds groups, drops stale teaser/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_reason prefers grid_off (warm-up first).
  • Grid charge off → EMS Demand/LoadNow: Server clears plan/charge_now immediately; Portal/Sparvo flash when plant.plan_updated is missing (webhook_not_configured / webhook_failed). Telemetry partial grid_charge_allowed=0 echoes charge_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_bound key; CSS hx30. Spec §6.8.10. All plants/keys. PicoC unchanged.
  • Hours Explain Why-Details deep-audit #4 clean: floor_prefill uses start SoC; Soft-Cap hint without double later; softcap-move before hint; Spec no_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; pin hx29. 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; CSS hx28. 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_legacy parity 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-details arm=kill+href, visibility=available); Stopp-SoC only on prefix stamp (no global fallback); hold nuance from hold_kw→hold_reason_for_cap; client hx54. 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-why whitelist + SSO path; route tests guest/shared-user/owner/kill + snapshot-only (no resolve_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-why narrates the snapshot (use_live_resolve=False); sibling “Details” link beside week teaser; shared-user access like Explain; kill HOURS_WHY_DETAILS (default on); client hx53/hx27. Spec §6.8.10. All plants/keys. PicoC unchanged.
  • Hours Explain deep-audit #4 clean: Jinja fail-closed discharge_power_kw via .get; SSR until_pv_energy_label; wiki Pack→battery/Speicher (+ PV day-pack SoT); client pin hx52. 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↔JS show_rest bars fallback; i18n blob dedupe; Spec §6.8.10 hydrate note. Portal pin hx51; SW hz6/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_autark for until-none); EN Battery conserve in week_status engine + chart JS fallbacks + tip; wiki Pack-Reserve→Speicherreserve; Sparvo base pin hx50, SW pins hz6/v28. Spec §6.8.10. All plants/keys. PicoC unchanged.
  • Hours Explain deep-audit clean: SSR _today_cover/_rest_days set (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_closed only ¬autark∧¬weak, dual_contract only D0=0∧later>0, NB Soft-Cap probe → neighborhood_softcap_dest_skip; UI Pack→battery reserve; Sparvo pins later hx49/hx26. Spec §6.8.10/§14.6 + wiki. All plants/keys. PicoC unchanged.
  • Hours Neighborhood Rebalance default ON (User-Go Dual-Gate): HOURS_NEIGHBORHOOD_REBALANCE default 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; syncs week_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 bare clock. Foot grid_draw_kwh scales 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_kwh after 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_timeline charge 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_energy is remaining SoT — Extract re-baselines SoC every run while charging; energy>0 clears chargeDoneToday before baseline (fix: double-subtract vs session-start SoC → Demand/O3/O9=0 despite charge_now=1). Portal overlay hides when charge_latched or live≥stop−0.5 and strips current-hour charge from plan_timeline. Wiki/samples DE+EN. All plants/keys.
  • ExtractHoursDemand v1.21 (charge_now SoT): Parses charge_latched; when API charge_now=1, clear local session latch (Demand/O3/LoadNow follow open Decide). Else when charge_latched=true, set local latch. Fix: wide H band left Demand/LoadNow=0 while Portal/API showed charge_now=1. Wiki/Samples DE+EN. All plants/keys.
  • ExtractHoursDemand v1.20 (O4 sync): sessionStopSoc tracks API charge_stop_soc_pct on 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_kwh is 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_PLACE default 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) = published charge_stop_soc_pct / O4 (one target; Hold/pv_pack half_up unchanged). Shared apply_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 refreshes soc_projected_* after picker mutations; no live+energy invent (fail-closed None / latch sticky); O4 = floor(prefix-end); plan_roles charge = roll_soc_hour; Explain hx46. 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 pass target_soc; middleware finalizes reserved exceptions as access_denied; finalize keeps duplicate against 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); Interim e_gap not clipped by decide SoC; Lookback delivery cap min(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, plan target, R2 on full port); PV-fill skip only room_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_limit stores burns; reserved demote (4xx and 200 access_denied) restores X-RateLimit-Remaining; auth deferred path rstrip("/"). 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 fake 0 €/MWh; anonymous duplicate coalesce only api_key_id IS NULL; reserved 4xx restores X-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 only api_key_id IS NULL; response quota_class follows 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_prep finite; 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 keeps duplicate (no re-bill after coalesce); check_rate_limit syncs request.state.quota_class; telemetry semantic invalid/incomplete demotes softcap; Spec Force + DE auth. All plants/keys.
  • Hours+Quota fail-closed deep-audit: Incomplete Hours echoes grid_charge_allowed=false when profile/field unknown; Survival/Force-Near/_try_take skip non-finite prices (NaN/±inf) and treat 0.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 demote quota_class; softcap X-RateLimit-Remaining counts reserve. Spec/wiki auth EN + MONITORING. All plants/keys.
  • Portal auto-geocode deep-audit parity: Locality keep preserves nominatim_reverse/nominatim when 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 room upper−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_class on HTTP ≥400 (no default-decision bill); Windows POST gates before body parse; invalid_input/EMS prefix fail-closed grid_charge_allowed=false when 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/windows deferred (access/access_denied before reserve, Hours parity); Loxone twin GET at Fair Use ceiling → free duplicate instead of 429; Force-Near cover rank fail-closed on None/OOB price; incomplete Hours basis echoes grid_charge_allowed. Spec/wiki auth DE + MONITORING. All plants/keys.
  • Hours Lookback Place pack headroom + η clamp (hours_demand_v1.31): Place subtracts already·η from pack headroom (no overfill when stacking on existing dest kWh; LThin/Defer parity); Lookback Place/stress/size_e_bridge + Decide roundtrip_efficiency + SOC slots use shared clamp_charge_eta. Spec §6.2b + wiki. All plants/keys.
  • Hours residual-aware carry + η parity (hours_demand_v1.31): Thin-Cover-Defer / Thin-Now carry uses residual_carry_step (net drain > room above floor → refuse; incomplete horizon fail-closed) instead of vacuous floor clamp; shared clamp_charge_eta for 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/status and 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. under reserve_breach; siblings frozen/movable split; merge lookback_added_kwh freezes absorbed cover and keeps survival against Defer undo; pack headroom+port) when stress-delta conjunction holds (sum≤ ∧ earliest≥); own commit; residual note and lookback_reason_codes only when \(S≠∅\); fields lookback_thin_*; kill HOURS_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 headroom min(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; kill HOURS_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 (hours instead of savings check). SEO sitemap/hreflang/llms; Umami website id 16c2ee7f-… (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 Jinja sameas also for min/target/cover (no |float(True)=1). Engine remains hours_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 via coerce_hysteresis_pp. Engine remains hours_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) uses coerce_hysteresis_pp — bool/Mock → default, never "1" for persist. Engine remains hours_demand_v1.29. All plants/keys.
  • Hours latch H webhook/guest type-guard: plant.plan_updated payload + material key and Sparvo guest profile use coerce_hysteresis_pp (bool/Mock → omit/default). Engine remains hours_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) use coerce_hysteresis_pp / bool reject — no float(True)=1. Family-B Cap-Skip blocked_indices immediately after mix. Engine remains hours_demand_v1.29. All plants/keys.
  • Hours latch H snap type-guard: Snap fallback/tombstone for charge_stop_hysteresis_pp uses the same fail-closed coerce as profile (int/float/str/Decimal; bool/Mock → default/omit). Engine remains hours_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): Lookback blocked_indices + latch H type-guard (Decimal ok, Mock/bool reject); worklist/wiki field table. All plants/keys.
  • Hours Weak Cap-Skip Lookback block (hours_demand_v1.29): Cap-Skip windows in Lookback blocked_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_pp accepts 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_w fail-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; note weak_pv_need_full_pv_cap_skip (Decide basis.notes + Snapshot notes). Kill HOURS_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_kind keep 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_SPILL default 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): Flag HOURS_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); Flag HOURS_LOOKBACK_LATE_BIAS (+ …_EUR default 5) — pre-stress late-bias in price band; Thin-Now does not drop softcap_spill Now; 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 clipped vs euro_cap; Darf-S1 echo on no_grid/autark; DE wiki skip enum + §6.8.14. All plants/keys.
  • Hours Soft-Cap spill R2-continue heal: after €-shrink best>0 but R2 fail → continue cohort (no false euro_cap stop); 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_pack re-estimates a full week when pv_est lacks D0…D6 (no zero-fill); sync same; GET envelope timezone = 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 days only truncates response days/hours/totals (loxone week 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 loxone from aligned view (pack days stay 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_hash FC). GET /v1/plant/pv-forecast / loxone.d0_kwh and Portal/Sparvo apply the same calendar meter blend (additive pv_kind / pv_meter_today_kwh). Meter ticks do not fire plant.plan_updated. Kill HOURS_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_hour NaN 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_blend even 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_pp default 2.0; Sparvo storage_rules_link DE+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 partial invalid_input includes updated.* 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 keeps charge_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 from cover_buffer_pct; latch/EMS charge_stop_hysteresis_pp from profile H; material key includes H; Cover/Bridge/Survival/Lookahead/Lookback/Consolidate/Interim/Spill/Rebalance plan ceiling min(cap,target); Weak/chrono Cap kept. Kill HOURS_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 + profile meter_pv_*; response sibling pv_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-Δ; Portal pv-meter-reset; Loxone VO 1d FetchPlantTelemetryPvMeter (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 to active_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 uses active_charge (latched/grid-off → Hold Cap despite stale charge_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_now no longer bypasses block_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_breach Cover; 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-closed incomplete_hold (prior latched + missing stop/SoC); latch stop from prior snap; Thin-Now cover-only + Hold rebuild fail aborts drop; material key includes charge_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 sticky charge_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 additive charge_latched/charge_stop_hysteresis_pp; O12 latch only suppresses recharge_planned full 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 from cover_buffer_pct (1–20 pp, empty→2) — superseded by Phase-1 split → profile charge_hysteresis_pp; Thin-Now before plan_roles; O12 short-circuit while latched; material key + charge_now/charge_stop_soc_pct; snap additive charge_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. Kill HOURS_CHARGE_HYSTERESIS / HOURS_THIN_NOW default 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_grid pf↔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). Kill HOURS_SOFTCAP_ABANDON_SPILL=0 → fields absent / exact v1.23. Additive softcap_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 shared format_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 segment soc_start_pct (soc_pct stays 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 respects port−already (Cover parity); merge forces slot_role=cover on lookback; Decide skips at charge power 0 like apply_*; wiki duplicate removed + HOURS_LOOKBACK_PASSES in 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 for lookback=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 echoes lookback_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 peel lookback=True; passes default 4 (HOURS_LOOKBACK_PASSES); kill-switch HOURS_LOOKBACK_REBALANCE=0. Additive fields lookback_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, Sparvo hx43): Interim slot picker now searches up to min(breach, next_cheap-1) instead of a fixed 5h pick cap; legacy behavior is available via HOURS_INTERIM_PICK_CAP_H=5. Deferred interim (interim_decision=charge) stays valid with interim_period_active=false; EMS period shrink/today pin only apply when imminent (h < 5). Deferred skip for already scheduled survival energy fail-closes to interim_decision=none, interim_reason=covered_by_scheduled_survival, and scrubs interim_slot_start/interim_cost_*. Snapshot/Explain hints (reason_codes, show_interim_charge_hint, hidePlan) now follow interim_decision=charge. On pass, both econ costs (interim_cost_charge_eur + interim_cost_pass_eur) stay; only none/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). Sparvo hx42. All plants/keys.
  • Hours Folgetage empty after hydrate (hx41): statusChipsHtml was nested under useSummary — Folgetage supply-cost paint threw ReferenceError and the per-day catch wiped every day (card title/hint stayed). Helper hoisted next to displayPrefs; Sparvo cache hx41. All plants/keys.
  • Hours Interim week_status label (hours_demand_v1.21): Interim slots carry interim=True; week_status/rest-day charge_kind=interim (“bridge”) instead of false MUST/reserve. Samples/API tests engine tag v1.21. Spec §6.8.5. All plants/keys.
  • Hours Interim Explain reason gate (hours_demand_v1.21): Snapshot reason_codes emits interim_charge only when interim_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): Weak stage=future day-consolidate receives interim_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, Sparvo hx40): Post-consolidate EMS pin keeps charge_hours_today to one interim slot; r2_rejected clears phantom interim_cost_* and sets interim_decision=none; Explain empty_min hint only for today’s empty_min (not horizon-wide); bridge footnote/hidePlan only when interim_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_headroom take unless Partial Port); charge_kwh aligned; 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 JS hx40. All plants/keys.
  • Snapshot refresh + follow-day chips + Today basis (audit-clean hx38): load_raw_snapshot calls db.refresh against 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/null field, numeric 0 → 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 when issued_at advances during resolve; Portal horizon-html supplies tariff_markup_ct_kwh; lead/explain € without chip wipe; Sparvo SW shell plant-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 profile tariff_markup; tombstone webhook sets charge_now/discharge_power_kw=0; hydrate does not clobber a newer snapshot; spec/wiki DA redispatch on any non-ok Decide. Cache hx37 / plant-hydrate-hz5. All plants/keys.
  • Hours Explain follow-days + O12 + horizon prefs (audit-clean hx36): Folgetage cost paint uses decision d (not undefined data) — otherwise empty follow-days when cost_net_eur lacks day markup; always paintPlantNowDischarge (clear even when available + null kW); storage horizon hydrates lead/basis note/transparency/explain {basis}/{tax} on prefs toggle. Cache hx36. All plants/keys.
  • Hours Explain follow-days (bugfix): displayPrefs top-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/missing discharge_power_kw clears O12 chip (—/is-off); cache hx34 / 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-html injects account display prefs (SSR); after AJAX inject Portal/Sparvo call SpotDisplayCost.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 on not_configured/HTTP fail); DA with incomplete_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; flash error_stale if 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_updated including grid_charge_allowed; snapshot best-effort, webhook best-effort; if snapshot write fails → flash plan=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): /…/data skips 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 includes fetch_timeout / pending without usable Decide; loader cleared when Decide is available. 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=computing keeps polling (no stuck “Computing plan… Retry”); horizon loading skeleton stays (cost tile gap fixed); loader removed when Decide is ready; current discharge from discharge_power_kw even 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_api after received_for (bridge / no live overlay); fresh_api_ts never mints stale; Forecast/Hours wiki: label only after received_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 after received_for / predicate; Hours immediate replan only when received and received_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 with data-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-strompreise share prefs (cross-widget sf-display-prefs-changed); hour table + horizon market/bar ct hydrated; Sparvo no double-apply; estimated_ct includes 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/admin localStorage; admin FC source=admin; fix-vs-dyn net≠gross; PDN disclaimer from i18n data-*. 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. Profile grid_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 profile max_discharge_kw like Decide; SEO meta Peak-Pack→Surplus-Cap; test simulate_week_grid forwards max_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_subset labels Cap>0.2 without reason as hold_share (not cheap_hold); Cap≈HOLD_KW → cheap_hold (O12/Resolve/Snapshot); Cap0 refresh keeps Weak/legacy only; Weak + Family B Peak-Pack + Outlook compute_storage_horizon get profile max_discharge_kw; Hold-Load slot Cap reads start|target_start|time; conserve-SOC copy avoids SOC: — %. Spec §6.4.2 / §6.8.16 / §6.8.17. All plants/keys.
  • Hours Explain pack-conserve kW+SOC: Hold segments in plan_timeline show battery conservation (battery: ~x kW, grid: ~y kW, SOC: ~z %) — mean planned pack take min(net,Cap,s−floor) + house residual (1 dp) + SoC at period end; Cap remains in hold_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 under hold_share); Weak SoC-roll no longer clips Peak Share Caps; Cap-raise-Guard threshold PACK_RESERVE_KW_MAX (0.2); Decide Peak-Pack uses profile max_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; reasons hold_share/pack_reserve/cheap_hold; O12=slot Cap with floor→reserve_empty before Cap; Family B min(net,Cap); webhook material includes Cap vector; telemetry hold-load Decide (Session→Near-Min→Hold-Load). Kill-switches HOURS_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 engine v1.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_min catch-all); markup 0 ct stays 0 (is not None); request battery.grid_surcharge_ct_kwh fail-closed invalid_input; Why {energy} = Decide-horizon X+Y+Z (not Folgetage 1A filter); Sparvo hours_engine mirrors week_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 as planned_direct (calendar-day P33 + spot ≤ p_ersetzt without η/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 surcharge grid_surcharge_ct_kwh (request battery.grid_surcharge_ct_kwh → profile → 1.5 ct/kWh) display-only. Kill-switch HOURS_PLANNED_DIRECT=0. PicoC/day_plan unchanged. 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 poll status=received (catch-up 15–23 Vienna). price_source contract unchanged. All plants/keys. *(Superseded 09.09.: Pass 9 live-tick/bridge + Sofort only received∧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≤0 does not block grid when port>ε; Cover pv_fills skip only when port≈0 (partial port allowed); timeline until-none respects charge_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_gap silent; 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_status API unchanged; plan_timeline UI-only. PicoC unchanged. All plants/keys.
  • Hours Soft-Cap sibling EMS prefix (10): Weak+Cover split same start no 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_pv budget (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_pv on mixed Cover/Weak days; Soft-Cap check includes plan.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 notes charge_fill/nofill mutually exclusive. PicoC unchanged. Spec §6.8.15. All plants/keys.
  • Hours Familie B Soft-Cap calendar-scope (5): Soft-Cap end_soc resim 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 None fallback. 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_before uses planned charge_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 each plan.hi even 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 above max(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 each plan.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_grid at sun→sun end (plan_hi) — Decide §6.8.15. Near-autark: cover_dawn_cap_hours only 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 (anchor max(pv_cover_end, H_{gate}) → hi(next_pv), stamps > H_{gate}); Near stamps ≤ \(H_{gate}\) stay excluded. cover_dawn_cap_hours restored in basis. PicoC unchanged. Spec §6.8.15. All plants/keys.
  • Hours Weak×Near-Autark: autark_ok no 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 final charge_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_overlay only for rentable; Cap0 hold merge separate; Decide parity); Sparvo PV nav always_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); snapshot week_status 1A always_include = wall-clock today (not only issued day); week glance drops duplicate metric_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_status shares 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 = Folgetage date ∪ [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_reserve only; 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 via hold_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 second cover_needed gate (Weak-PV hold vs chrono cover-to-sun). Legacy cheap-clock without hold membership still needs a cover gap. Snapshot no longer restores stale night_hold_hours from 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 sidecar spotprice_pv_soc.txt from 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 heavy txt_d0 stack; key search without escapes; force re-read ~10 s; idle delta d0_kwh+d0_soc_eod; Txt1 OK 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/setlogtext shows OK 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-day pack_reserve must 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_any via hold_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_reserve label even when HOURS_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 at HOURS_HOLD_KW would 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-closed min(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_eod live SoC: A fresh Decide pack previously froze midnight SoC until the next Decide (up to 6 h). GET /v1/plant/pv-forecast and telemetry SoC now recompute loxone.d0_soc_eod / txt_d0 from the current live SoC (PV days/pv_hash unchanged; persist after meter ingest; no clobber of newer Decide packs). Loxone ExtractPvDaily v2.4: file-delta also watches d0_soc_eod (v2.3 only d0_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 with need_full + spot η-gate p/η < 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 pie supply_*/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 from lines (kept in days[]). Heading Thu 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-global opp_off/opp_not_triggered. charge_hold without 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”). localize heals direct_* before Hold-clock backfill; charge status beats stale hold_* why. Chart tip: bare clocks in charge_starts_plan/night/rest only today (dated labels still match by date). PicoC unchanged. All plants/keys.
  • Hours Copy B audit fix 2: localize_week_status heals days[] (why_code/direct_*) to 1-dp like the lines; _line_for_week_day checks charge_hold before Hold. Chart tip: bare charge_starts (14:00) only on today (no false Folgetag Netzladung). Explain tip_p33 + rest-day Hold strings + wiki chip → pack conserve. PicoC unchanged. All plants/keys.
  • Hours Copy B / chart tip audit fix: why_code=hold_direct and direct_hours follow 1-dp display (micro-kWh &lt; 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_status without fake min_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 kWh or no 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), tip PV · load · plan keyword (Charge>Hold from hours_decision), Sparvo payload hourly_load_* + now_hour. Outlook SOC-roll η = profile roundtrip_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 with why_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 / field direct_hours). Legacy without hold_hours_local: clocks from direct_hours. Idle with residual but no hold → no_hold_gap (not false Autark). Covered/autark_ok beats interim_pass hint (same as Explain). Hold-kW copy from HOURS_HOLD_KW. Pack-conserve / residual-import prose only under “This week”; “cheap third” only for darf (§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. Optional hold_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=hold only when night residual-import kWh>0. DP polish only on the existing DA replan (allow_weak_pv_dp, kill HOURS_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_now only). 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 when d0_kwh changes. 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_reason uses recomputed night_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; consolidate near_cover_hours/cover_softcap without 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_take uses full charge power (not P_econ). Relocate syncs energy_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_econ on 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 day pv_kwh from pack days when pack SoT. Decide publishes the same estimate (pv_est / today-past + display_tz). Freshness weather exception fail-closed. Loxone DebugPvForecast v1.5: pendingFetch for 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 and GET /v1/plant/pv-forecast stay 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 when charge_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 test charge_hours_today. All plants/keys.
  • Hours/PV Decide SoT — audit fix: Outlook pack hourly ISO start parsed to datetime (no /plant-outlook crash); overlay charge_starts no longer matches TZ offset +02:00 at hour 02; session-complete charge_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_*); webhook plant.plan_updated includes pv_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 :08 remains backup.
  • Auth — key-owner tier: Wiki note: quotas/access follow 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. Engine hours_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 remains hours_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_kwh follows 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 print cover_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 (min of rooms): overnight + Recovery daytime + Dawn hi(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_hours independent of Recovery. All plants/keys. No PicoC repaste.
  • Hours Dawn-Cap v1.13 (hours_demand_v1.13): Cover/Bridge Soft-Cap and picker_cover clip at hi(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-switch HOURS_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 not storage_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 distinguish already_current (idempotent after first decide) from incomplete_input / incomplete_input_cached and 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-switch HOURS_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_kwh checks 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 field rest_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_time of the day’s last skipped hour (not the last non-empty). Explain preview/JS: same footer including “no further grid charge”. Poll asset hx24.

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; SW sparvo-shell-v26, registration updateViaCache: none.
  • Sparvo — “Savings” menu hidden: Bottom nav, home tiles, and favorites omit /savings; direct URL redirects to /. Off by default — re-enable with env SPARVO_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 in storage_horizon unchanged — 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_compare fallback. /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 on visibilitychange back 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 worker sparvo-shell-v25, assets resume-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_kwh never above v0; optional notes: sized_after_cover. Decide + storage tile family B (simulate_week_grid, same predicates). Cover slots unchanged (non-regression). Kill-switch HOURS_PREP_AFTER_COVER=0. Audit-fix: plan_feasibility refreshed after prep trim — cover shortfall stays partial, prep-only trim does not false storage_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 in simulate_week_grid.
  • Portal/Sparvo — dump-price toggle removed: Day navigator (/live-strompreise, login, admin), Sparvo /prices, /plant, /plant-outlook, and /code-samples now 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=scenario in URLs is ignored. API fields dump_risk* / dump_scenario_* remain optional on GET /v1/forecast (unchanged). FC verification detail + CSV/PDF dump history unchanged. Cleanup: deleted dump-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_SLUGS DE+EN and PicoC `` fences alongside docs-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). Renderer docs-lead unchanged.
  • llms.txt v2 + wiki agent optimization: /llms.txt, /llms-full.txt, and new /docs/llms.txt follow 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.txt with 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_h in autark_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-switch HOURS_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_delta keeps meter-budget norm; plant-outlook chart uses the same cover load as Hours; SOC slice indices clamped to real hours. Extract v1.17: period up to 48, no 40 h reset of lastOkAbs. 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_kwh recomputed 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_decide baselines); outlook patch recomputes pv_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 submit only after sync handlers (setTimeout 0) — cancelled forms (empty admin password / dismissed confirm on 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 (&lt;≈280 ms) stay without overlay. Opt-out: data-sf-no-page-loading on 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/data or /plant-outlook/data (mode=full, include_horizon=False since 2026-09-09 audit-clean); storage horizon via …/horizon-html. Hours resolve capped by HOURS_RESOLVE_MAX_PARALLEL (default 2, non-blocking); busy slots return JSON status=computing + HTTP 200 + Retry-After with client backoff. Soft-busy: status=computing / pending without usable Decide / fetch_timeout (see 2026-09-09 audit-clean). Incomplete/failed shells set pending_hours=false. HTTP 503 (including outlook_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_source warning updates on hydrate. Horizon HTML: empty partial → empty fragment. TTL cache returns deep copies. Empty explain/horizon templates are shell-safe (fmt_num Undefined 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_secondary as 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. /plant short-TTL + PV/forecast/meter dedupe. Hours Decide: basis.storage_horizon annual 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_updated on delta). Sparvo/Portal live; webhook immediately; Loxone Extract on the next Hours Decide. No Decision quota. Kill-switch HOURS_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 only vorrat_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 only vorrat_wuerdig; bridge separate; Family-B Cap clip. Docs: charge_now instead of charge_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) is min(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 without battery.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 recipes unchanged). PicoC slots only in Portal EMS connection (/settings/ems) — Sparvo/migration never invent wallbox_*/flex_*. Unbound catalog devices: “EMS pending” chip + Portal link. Roles: Plant administrator / Plant primary user / Co-user. Catalog plant_sparvo_prefs per api_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 catalog plant_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 role prep → “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 only charge power − PV (1 h: kWh≡kW); skip pv_fills_charge_power when 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: duration 0,25 h 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 lastH only 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 write target_kwh; weekly chips do not override recipes; oven and similar presets cannot stay api_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: lastDecHour only 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–00 window 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 stale load_now until :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_id typed). 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). Omitting mode/role defaults to cheapest/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_id merge (flex_pack=0 omitted). 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.md budget 7/8 (skip weather).
  • Flex PicoC v0.5.1: Parse failure keeps LoadNow (wantLoad); I1=0 also clears ViGridChargeReq; Windows docs say Flex v0.5.
  • Flex PicoC v0.5: LoadNow from current slot hour (no stale ems.load_now on hourly reparse); live I7 each loop; no continue; I1=0 resets state; wiki I9+I10 required for hours_completed; flex_pack overrides body segments.
  • Docs UX Flex guide: /docs/loxone-windows-flex rewritten (short steps, step/example callouts, SVG flow, PicoC in appendix). Guide pattern in concept §12. Renderer: step + example callouts.
  • Flex deep-audit v0.4d: ISO window + deadline_hour without window_* infers clock hours from start/end (18:00→06:00, deadline 22 drops 02:00). Naive hour≤deadline removed. A deadline outside the window does not empty it. Flex wiki walkthrough DE+EN (callouts step/example, flow.svg), OpenAPI/concept §8.6 updated.
  • Flex deep-audit v0.4c: deadline_hour (Sparvo latest start) filters Flex candidates overnight-aware; windows_query carries deadline_hour; request echo window_source (flex_pack\|window_hours\|iso); deadline_strict/repeat_daily documented as echo/reserved; PicoC I1=0 also clears Demand/Period; concept §15.1 E = flex_pack.
  • Flex deep-audit v0.4b: device-schedules uses the same rolling overnight logic (post-midnight fix); windows_query carries only window_* hours (no ISO); request echo shows flex_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/end optional, flex_pack / window_* documented. PicoC I12 is diagnose-only; repeat_daily is not applied in v0.4.
  • Flex PicoC v0.4 + rolling window: window_start_hour/window_end_hour or Loxone flex_pack (I4|I5<<5|I6<<10|done<<16) in one <v.0> — no daily ISO start/end. PicoC I4–I6 are the source of truth. O5 always writes the pack, never raw hours_completed. LoadNow only from ems.load_now (not window.start). prep_extra_energy_kwh = placed prep kWh. A Flex window beyond the tier is no_slots, not deadline.
  • Flex deep audit: live floor (no past hours in flex window); chronological next_slot_start; soft fallback prefers longest block; after end Demand/slots=0 + status deadline; empty flex exits keep ems; PicoC I1=0 clears LoadNow; OverrideEnable documented as reserved.
  • Flex PicoC v0.3 + API hardening: Poll accepts deadline/no_slots; PO Demand = remaining hours (0 when segment_complete or after end); deadline_reached whenever now ≥ 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; soft takes the longest block (price is tie-break only); ems.next_slot_start = next hour from now. (Historical: first-wallbox auto slot_id wallbox_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/windows with 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-flex updated.
  • 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 floor 0.25·load, no jump to 168). Fields hours_needed_prep, prep_extra_energy_kwh, basis.forward_prep, basis.prep_horizon_hours. Slot role prep (“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) · PicoC SpotpriceFlexWindows.
  • Plant Device Schedules (Sparvo Flex 1b): GET /v1/plant/device-schedules returns Sparvo api_linked devices (slot id, segments, window, deadline) for Loxone/EMS. Community+; HTTP 200 with decision_status.
  • Loxone FetchPlantHours v1.10: Auto-Decide at hour :00 (I7+I8), :05 fallback only. Fixes relay gap: Extract clears O3/charge_now on 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, ems block right after decision_status (PO Demand = ems.current_segment_hours, Period = ems.period_hours_today until window end). Without flex params, unchanged windows_v1. Deploy: deploy.sh api-only.
  • Explain stop-SOC / balance bars: Survival O4 pins to min_soc_pct only 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 assets hx22.

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 pull hours_demand_url after the event. Optional plan_hash in 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 assets hx18.
  • 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 below min_soc only with a price advantage. No new API field, no wait_cheap; O12 unchanged. Shadow log hours_consolidate_shadow.jsonl. Ops diagnose: diagnose-hours-charge-plan.py.
  • Fix: skipped-hours full list (portal/Sparvo): “Show all N” on /plant/skipped-hours or /plant-outlook/skipped-hours returned 500 Internal Server Error (Jinja: group.items resolved 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_pct in snapshot rest_day_balances, UI-only). ⓘ explains reserve floor, direct grid supply below min SoC, and morning-sun stamp (not right after the charge hour). Poll assets hx17.
  • Week glance day label: hours_needed_days maps onto the calendar grid (D0…D6), not the sparse day_plan list — 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_pv ends 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 real e_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 while charge_now. N = ceil(daily load / charge kW). Annual tile uses the same cover gate. Fields cheap_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_horizon omits 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). Adaptive max_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 (interim pass / 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_updated include discharge_reason; an O12 cap change is material (webhook).
  • ExtractHoursDemand v1.11 (Breaking): API fields prefix_* instead of loxone_*; new charge_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. Legacy loxone_* in old JSON readable until re-Decide.
  • Hours Interim Econ default ON: HOURS_INTERIM_ECON is on (kill-switch =0). plant.plan_updated includes 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 pass no 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 (&lt;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_today may shrink (cap 5 h); EMS charge_hours_today then has at most one interim slot (later same-day cover stays in day_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 later show 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 poll hx11.
  • 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: partial POST /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_hours only when cover_extended_for_reserve). SoC without/with grid at both stamps. Chip stays “Today N h · M h in total”. Today 0 / week > 0 stays charge_kind=later (with-grid still shown at the reserve stamp).
  • Master switch grid_charge_allowed: Default on. Off → hours_needed*=0, empty slots, kind grid_off / reason grid_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; alias soc_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-hours or /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_eod load 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 are basic, community, full_trial, full_active (not professional/oem as 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/status no longer includes cache_path.
  • Hours cover load with sparse meter telemetry: WD/WE hourly shape no longer treats missing samples (None) as 0 kWh. Days with &lt;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 (max with 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 missing projected_soc_pct_with_grid no longer invent With-grid via an energy lump; Opp cap uses chronological cover need. Bulk fallback also respects allow_untimed_grid_lump.

2026-08-19

  • Hours Demand v1.3 — SOC-feasible grid charge: Engine hours_demand_v1.3 picks charge slots only when the battery has headroom and PV does not already fill the hour. Cheap but impossible hours appear in skipped_slots[] (always included); shifted charging in later feasible slots. plan_feasibility reports partial / storage_undersized when 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 codes slots_skipped, storage_undersized plus 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-weather scans active API keys with Lat/Lon in user_plant_locations (discovery without a second timer) and writes data/plant_weather/{api_key_id}/. Also: portal geo-save and first PV/Weather request (on-demand). Directory ownership *:forecast mode 2775. 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_today is hours remaining until 23:00 (00:00 → 23, 13:00 → 10, 22:00 → 1). At 23:00 it resets to 24. Demand/charge_hours_today only 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_decision in build_plant_outlook). Battery rules, dump, and location saves refresh the explain snapshot. A stale EMS snapshot may still update charge_power_kw, but not the hour count without new slots.
  • Hours grid-charge power: charge_power_kw is min(battery.max_charge_kw, grid connection × 0.80 − base load) (20 % reserve, same as Sparvo devices). hours_needed splits 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/location and 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_pct outside 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 (fields hours_needed* / d0_load_kwh unchanged).
  • 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.json immediately after Miniserver reboot. Hours users: reuse the same VO parent and add only command FetchPvForecast. Re-paste both PicoCs from /docs/loxone-pv-forecast.
  • Loxone PV Forecast Extract v2.0: One GET /v1/plant/pv-forecast adds ready-made txt_d0/txt_d1 lines (weekday, date, PV, optional load, end-of-day SoC, Tmin–Tmax) plus week_kwh, d0_load_kwh, d1_load_kwh, d0_soc_eod, d0_tmax, d0_tmin next to d0_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_kwh and hours include the buffer; a zero shortfall stays 0. Engine tag remains hours_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=0 stays 0 (no fallback to the week total). Portal/Sparvo clear charge chips on days without engine day_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 remains hours_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_refresh without empty-daily refresh loop; wind_speed_unit=kmh; Loxone double-GET coalesce for pv/weather; not_available before quota reserve + middleware quota_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-weather 05:20 Vienna; deploy starts a background refresh for schema heal.
  • PV Forecast + Weather APIs (public): GET /v1/plant/pv-forecast (7-day yield kWh, class pv, Community 48/day) and GET /v1/plant/weather (site weather, class weather, 48/day). Docs DE/EN: /docs/pv-forecast, /docs/weather; Loxone guides /docs/loxone-pv-forecast, /docs/loxone-weather with 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-outlook and Sparvo — tile shows the value only.
  • Portal/Sparvo week glance — audit fixes: missing strip_aria i18n, 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). Webhook plant.plan_updated compact (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_mwh blend toward price × pct/100 when 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_kwh unchanged. Clients should use API dump_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-samples Hours explain cover-until-PV, spike damping, and ≥2 full meter days; samples print decision_engine, cover_window_hours, remaining_today_load_kwh.
  • Portal live-telemetry hint: estimated day use correctly detects site_meter_history_* (previously exact match site_meter_history only → 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_hours prefer slots inside the cover window. New basis fields: cover_window_hours, remaining_today_load_kwh, horizon_load_kwh; engine tag hours_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 dead pulseTel path; 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-outlook and location settings; without fresh API SOC the slider stays available as fallback.
  • Telemetry rate limit: softcap for /v1/plant/telemetry raised 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-free diag for Txt2/Txt3; aggressive decimal-comma sanitization; PicoC v1.5 reads diag first.
  • 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 missing Content-Type and locale comma decimals.
  • Portal plant outlook — live telemetry: /plant-outlook shows 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 include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and X-RateLimit-Scope (same headers as on 429; Retry-After still 429-only).
  • Decision Windows missing params: missing start/end/mode/role return HTTP 400 incomplete_request (aligned with docs; no longer FastAPI 422).
  • API usage — Loxone double-GET coalesce: Identical api_key + path + method within 3 s is logged as quota_class=duplicate and 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/forecast calls (~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 every nEvents). Docs DE/EN + /code-samples synced.

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-samples keep 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 on forecast_status. Parse v17 reads price now/+1 h via start needles without pointer walks (v11–v16 hung on Miniservers after dump-JSON growth). Portal /code-samples Loxone Forecast ships both programs. Docs DE/EN /docs/loxone + /docs/loxone-forecast synced.
  • 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_mwh are 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-samples keep their dump toggle; landing #planung-live also has no dump toggle (model price).
  • Forecast API — dump_scenario_* always usable: dump_scenario_ct_kwh / dump_scenario_eur_mwh hold the dump price when dump_risk_pct > 0, otherwise the point price (never null only because there is no dump). Clients need no null-check; whether a dump is active is shown by dump_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 on GET /v1/forecast (and inherited on decision[]/forecast[] in Decision, slots in Windows, charge_hours[] in Hours Demand) now always carries dump_risk_pct (0…100, never null) and dump_scenario_ct_kwh / dump_scenario_eur_mwh. dump_risk_p stays 0…1 unchanged (compat). price_ct_kwh / price_eur_mwh are 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_kwh never changes.
  • Docs/samples sync — dump risk & field start: Forecast docs DE/EN fix timestamp → start; dump risk only on ML hours (forecast_d*), no soft-dump; Loxone ParseSpotpriceForecast v12 (O4 = risk code, O1/O2 unchanged); portal /code-samples print optional dump_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/forecast may include dump_risk, dump_risk_p, dump_risk_scope, dump_risk_version on midday ML hours (11–16 Vienna). No effect on price_ct_kwh / price_eur_mwh. Live experiment (ML_DUMP_RISK=live); kill-switch 0. 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-demand in decide mode now always returns concrete start/end times and prices for every recommended charging hour when decision_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 class telemetry, 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 via POST /v1/decision/hours-demand with mode: "telemetry"
  • Bridge breakdown in Hours Demand: new fields hours_needed_cover, hours_needed_opp, hours_needed_bridge, and plant_freshness in the decide response
  • Webhook plant.plan_updated (new): sent to your registered webhook URL whenever hours_needed or charge_hours[] change materially — same URL and secret as forecast.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, engine windows_v1) — contiguous time windows with a full constraint set (min_gap_hours, max_consecutive, allowed_hours, weekdays, allocation)
  • Decision API (GET /v1/decision, engine quartile_v1) — quartile-based hourly signals charge/discharge/hold
  • Hours Demand Core (POST /v1/decision/hours-demand, engine hours_demand_v1) — first version with hours_needed derived 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 the api_version response field)
  • Engine tags per endpoint: quartile_v1 (Decision), windows_v1 (Windows). Hours Demand reports hours_demand_v1.38 while any storage-plan flag is on. With every storage flag off and the lead band on, it is hours_demand_v1.37. With the lead band off as well (HOURS_ECON_LEAD_BAND=0), it is hours_demand_v1.36.
  • Breaking changes are recorded here with date, affected endpoint, and migration window

Next steps

Start with SpotpriceAPI

Register for Community or code samples with your personal key.

Live API status