Skip to content

fix(app): history tiles draw measured power for grid, PV and battery - #1854

Merged
frahlg merged 3 commits into
masterfrom
fix/app-history-power
Oct 25, 2026
Merged

frahlg merged 3 commits into
masterfrom
fix/app-history-power

Conversation

@frahlg

@frahlg frahlg commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Problem

Fredrik, on TestFlight 0.5.1 against his own box, 25 October 2026: the Solar view's history "hoppar mellan noll och osv". The figure under the finger read 0 W where the curve was higher.

The app's history tiles came from the energy ledger. On his box, the ledger's five-minute buckets for pv_generation held only 0, 1 200 or 2 400 W all day, and grid_import only 0, 1 200, 2 400 or 3 600 W (/api/energy/history?bucket=5m, source hardware_counter). The cause is the Sungrow's energy counters, which move in 0.1 kWh steps. The ledger saves the counter cursor on every reading, so each 100 Wh step lands whole in the second it ticks, and 100 Wh over five minutes reads as 1.2 kW.

The box's own charts (/api/history) draw the measured power rows, and those were right: about 450 W at 08:00 rising to 2 250 W at 09:15, with no zeros. consumer_use is a power balance (source_interval_balance_v1) and was right too.

Change

  • state.LoadPowerBuckets returns the mean measured grid, PV and battery power per fixed bucket of [from, to). It reads the same tiers /api/history unions, now one package constant historyTierUnion, half-open, averaged with the same n weights. A bucket without rows is absent. Each flow's mean is over the rows that measured it. A flow is Missing when more than half of the bucket's rows, by weight, did not measure it, or when none did (maxPowerBucketUnknownShare). Rows roll up after a day into minutes that carry any tick's flag, so a tenth would turn a closed bucket into a hole a day later.
  • appHistory.Window serves grid_w, pv_w and battery_w from it. A missing flow is left out, so it becomes MISSING on the wire. load_w stays the ledger's consumer use, and the ledger is not queried when no load is asked for.
  • FTW Cloud's tiles (cloudSite → appHistory.Window) change with it.

The energy ledger itself is unchanged: kWh totals and costs still come from the counters.

Evidence

  • TestLoadPowerBucketsLaysFixedBuckets (state): buckets laid from from, a row on a boundary counted once, an empty bucket absent, an empty range refused.
  • TestAppHistoryDrawsMeasuredPower (cmd/ftw): measured −400 W PV, 300 W grid and +500 W battery charging reach the tiles with an empty ledger, which on master gives no PV at all. An inverter that falls silent six minutes in leaves a hole in its second bucket, and the grid stays.
  • TestAppHistoryKeepsAFiveMinutesWithOneMissedReading: one stale reading in thirty keeps the bucket at −400 W. It fails under an all-or-nothing rule (checked).
  • TestPowerBucketSurvivesTheMinuteRollup (state): ticks written as Core writes them (every tick flags Load), one stale PV reading, then the day-old roll-up. The bucket keeps −300 W before and after. Under a 10 % limit it becomes a hole after the roll-up (checked).
  • The state test also checks that a row at to stays outside the range.
  • go test ./internal/state ./cmd/ftw ./internal/appproto pass. make verify at the head: vet, test and build clean.
  • Not yet run against the home box. The values above come from its read-only API.

Notes

  • A review agent checked the SQL against the tiers. With aggregation on, which Core always enables, ticks land in history_dashboard or history_dashboard_partial only, so tiers do not overlap. The n weights give the plain tick mean before and after the minute and five-minute roll-ups. Retention covers the 48 h the app and FTW Cloud ask for. Before the dashboard tier (17 September 2026), only 15-minute and daily rows exist, so old 5m tiles are sparse where the ledger had values.

  • contract/registry.yaml still lists '5m': { …, source: energy_ledger }. The registry is edited in ftw-webapp and copied, so that label goes in a paired follow-up.

  • A tile with a stride above 1 still averages the buckets that survive, as it did with the ledger.

  • The app's "so far today" sums the tiles, so a bucket left out lowers that total, as a missing ledger bucket did. A bucket with few rows is not checked for coverage: its mean stands for the five minutes.

  • A site without PV or battery now reads 0 W for that series, where the ledger left it absent. I have not checked how the app draws a series of zeros for a flow the site lacks.

  • fix(history): repair Home use and Solar gaps with audited estimates #1751 changes loadArchiveHistory in hunks that do not overlap this one (source mode at the top, applyHistoricalLoadChart at the end). Its PV repairs reach /api/history but not LoadPowerBuckets; carrying them into the tiles would be a follow-up after it merges.

  • The app side (fix(ios): a step the box is still filling never reads as its mean ftw-app#116) already hides the step still filling. With measured power that step's mean so far is right, so nothing more is needed there.

🤖 Generated with Claude Code

Fredrik's box, 25 October 2026: the app's Solar history jumped between
0 and 1.2 kW while the sun gave about 0.4 kW. The tiles were built from
the energy ledger, and a Sungrow's counters move in 0.1 kWh steps, so
each step landed whole in the five minutes it ticked in (100 Wh over five
minutes is 1.2 kW). The box's own charts draw the power rows and were
right.

appHistory.Window now takes grid, PV and battery from those rows
(state.LoadPowerBuckets: the same tiers /api/history unions, averaged in
fixed buckets from the tile's start, half-open). A flow some row did not
measure is a hole in that bucket. Load stays the ledger's consumer use,
which the source accounting balances from power. FTW Cloud's tiles use the
same window, so they change with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T16:07:47.275203Z 7485919 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

frahlg and others added 2 commits October 25, 2026 17:20
- LoadPowerBuckets means each flow over the rows that measured it and
  marks it missing only past maxSourceDayUnknownShare (10 %) of the
  bucket's rows, as a source-era day's total is. One stale reading no
  longer removes five minutes, and a PV inverter waking at dawn keeps its
  buckets.
- Window skips the ledger query when no ledger series is asked for.
- Comments in appproto and cloud_link no longer say the series come from
  the ledger.
- Tests: a row at `to` stays outside, battery charging is positive, and
  one stale reading keeps its bucket (it fails under an all-or-nothing
  rule).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Second review: Core flags Load on every tick, so all ticks are partial
rows, and the day-old roll-up merges a stale reading's flag into its whole
minute. One stale reading in five minutes grows from 3 % of the rows to
20 %, so under a tenth the closed bucket turned into a hole a day later.
A flow now needs to miss more than half of a bucket's rows to lose its
mean. TestPowerBucketSurvivesTheMinuteRollup writes ticks as Core does,
rolls them up and fails under the old tenth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg merged commit e39e7b8 into master Oct 25, 2026
22 checks passed
frahlg added a commit to srcfl/ftw-webapp that referenced this pull request Oct 25, 2026
srcfl/ftw#1854 serves the app's grid, PV and battery tiles from the
measured power rows /api/history draws, at 5m and 1h alike; load stays
the ledger's consumer use. The registry said energy_ledger and
hourly_rollup.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
frahlg added a commit to srcfl/ftw-app that referenced this pull request Oct 25, 2026
What to Test leads with the stepped solar forecast (#118) and says the
history for solar, grid and battery changes with the next box update
(srcfl/ftw#1854).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
frahlg added a commit that referenced this pull request Oct 25, 2026
Copy of srcfl/ftw-webapp's registry change, with contract_gen.go
regenerated. #1854 serves grid, PV and battery tiles from the measured
power rows at 5m and 1h alike; load stays the ledger's consumer use.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant