Stop drawing a stale meter as 0 W balanced - #980
Conversation
A hybrid inverter that is also the site meter going offline left GET /api/status publishing grid_w/load_w as 0. The dashboard and the FTW app treated that as a live idle house, hid solar and battery, and labelled GRID "balanced". Publish JSON null when the meter is not reporting, map that to "no data", and keep an offline-only PV/battery/EV role on the diagram as a placeholder. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
The Flow view already drew placeholders. The Values cards still labelled offline solar as generating and showed 0% SoC for a pack that is not reporting. Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c94a11c. Configure here.
| // Solar only flows one direction (production); the arrow would | ||
| // be redundant. Use the kWh unit instead so the line reads as | ||
| // a standalone total. | ||
| var pvDailyStr = fmtKwhShort(pvKwhTotal) + " kWh"; |
There was a problem hiding this comment.
Live stats still show fake zeros
Medium Severity
The Values tiles now show — / no data when configured solar or battery drivers are offline, but the live stats strip still feeds site-level pv_w, bat_w, and bat_soc (which become 0 once offline drivers are excluded). That strip is meant to mirror the tiles, so it keeps drawing the same idle/empty lie this PR removes elsewhere.
Reviewed by Cursor Bugbot for commit c94a11c. Configure here.
| let selfPoweredPctToday = null; | ||
| if (loadKwhTotal > 0.001 && loadW !== null) { | ||
| selfPoweredPctToday = Math.max(0, Math.min(100, (1 - importKwh / loadKwhTotal) * 100)); | ||
| } |
There was a problem hiding this comment.
Today % gated on live load
Medium Severity
selfPoweredPctToday is computed only from today’s import_wh / load_wh, but the new mapper also requires instantaneous load_w to be non-null. When the site meter goes offline, the hub drops % SELF-POWERED TODAY even though the daily energy totals are still valid.
Reviewed by Cursor Bugbot for commit c94a11c. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c94a11c7c6
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const gIdle = isIdle(gridW, thresholdW); | ||
| planets.push({ | ||
| id: "grid", corner: "bottom-left", title: "GRID", role: "grid", | ||
| kw: Math.abs(gridW) / 1000, toHub: gridW >= 0, |
There was a problem hiding this comment.
Preserve the negative sign for grid export
When grid_w < 0, applying Math.abs makes the grid bubble display a positive power value even though it is labeled “exporting”; the previous mapper and the Values/chart views preserve the negative value. Keep the signed kW value and use toHub only for animation direction so an export is not presented as positive site power.
AGENTS.md reference: AGENTS.md:L32-L33
Useful? React with 👍 / 👎.
| if (loadKwhTotal > 0.001 && loadW !== null) { | ||
| selfPoweredPctToday = Math.max(0, Math.min(100, (1 - importKwh / loadKwhTotal) * 100)); |
There was a problem hiding this comment.
Keep valid daily self-powered history during an outage
If the meter goes offline after accumulating valid readings, /api/status still returns energy.today, but the new loadW !== null condition discards the independently calculated historical percentage. Consequently the component hides “SELF-POWERED TODAY” for the entire outage even though import_wh and load_wh remain available; only the live “now” calculation should depend on the current meter.
Useful? React with 👍 / 👎.
| if (flowEl && typeof window.ftwFlowReadingsFromStatus === "function") { | ||
| lastFlowReadings = window.ftwFlowReadingsFromStatus(data, { |
There was a problem hiding this comment.
Replay status after the mapper module becomes ready
On a cold load where the three /api/* reads finish before the large components/index.js module graph has evaluated, this condition drops the first successful status payload because the readiness callback is nested inside the false branch's body. The flow component then remains in its loading skeleton until the next two-second poll; cache the payload independently and derive/replay it when the module or custom element becomes ready.
Useful? React with 👍 / 👎.
Master moved from srcfl#756 to srcfl#998 under the branch; the only real conflict was firstboot.sh, where srcfl#980-era work made first boot try `docker compose up -d` on locally present images before falling back to the GHCR pull loop. Kept that up-first flow and placed the docker.list restore (srcfl#770) before it, right after cd /opt/ftw. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Claude-Session: https://claude.ai/code/session_012taMkhFbYYNyVQmCTU26sn




Accepted text proposal
Fredrik's NOW screenshot: GRID and the house at 0 W "balanced", solar and battery gone, while the headline already said control is paused because a meter stopped reporting.
What changed
GET /api/statuspublishesgrid_wandload_was JSONnullwhen the site meter is offline. A live 0 W reading is unchanged. Development setups with no configured meter still report 0.pv_w/bat_wso the UI can keep those nodes.web/components/energy-flow-readings.js:grid_w→ GRID "no data", not "balanced"load_w→ house "—"<ftw-energy-flow>treats an explicitload: nullas unknown (does not keep the last 0 W).Why
The meter, PV and battery are often the same hybrid inverter. Skipping it as offline left GRID + EV at 0 W and looked like the house never had solar. Publishing 0 W for a missing meter made the same lie in numbers.
Boundaries and safety
DispatchBlockedByremains the honesty channel on that path. The screenshot was the/api/statuspath (daily kWh on GRID).srcfl/ftw-webapp) already maps nullgrid_wto GRID "no data", but still draws 0 W, coercesload_wto 0, and skips offline inverters. I could not push a companion PR (no write access to that repo). After this box change, apply the same mapper rules there:placeholder: trueon no-data planets, keep offline-only PV/battery,load: null, and re-copyftw-energy-flow.jsfrom this branch so the hub draws "—".Overlapping PRs
web/app.jsinrenderModeCatalog(planner prefs). This change is the status → energy-flow path and the Values tiles.go/internal/api/api.gofor OCPP. This change is isolated tohandleStatus.Verification
go test ./internal/api/ -run TestHandleStatusgo vet ./...andgo build ./...ingo/node --test web/energy-flow-readings.test.mjs web/dashboard-simplification.test.mjs web/javascript-syntax.test.mjsmake verifywas not run to completion here: optimizer venv needspython3-venv, which this environment does not have.Checklist