Skip to content

feat(cloud): report installations, configured capacity and hourly energy - #1693

Merged
frahlg merged 4 commits into
masterfrom
feat/usage-energy-stats
Oct 17, 2026
Merged

frahlg merged 4 commits into
masterfrom
feat/usage-energy-stats

Conversation

@frahlg

@frahlg frahlg commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

FTW's old daily reports count check-ins, so they cannot answer how many installations report, how much flexible power is configured, or how much solar energy FTW records. Add a separate, signed hourly path through Cloud under a new statistics policy. Older consents send none of the new fields.

The owner can read the exact policy at GET /api/cloud/stats-policy and accept it through the existing owner-only API. A policy change rotates the random stats id and starts a new consent window. The sender uploads only later closed hours, rounds to 10 Wh/W, omits unknown values, and excludes Nova partner sites. Configured capacity and power remain distinct from proof of physical control. Read-only batteries add capacity but no flexible power; shared inverter power and charger drivers are counted once. Energy comes from the ledger and can include partial recorded hours.

This is paired with srcfl/ftw-cloud#31, which stores consented hours, deletes them on signed withdrawal/purge, and exposes only cells with at least 20 homes and no home above 30% of a sum. The public dashboard is in srcfl/ftw-webapp#50. Core protocol lands first; Cloud pins this exact commit. No Core release or household consent was changed. Native consent UI still needs the new policy; the signed C8 DPIA remains a deployment requirement in ADR 0008.

Validation: protocol allowlist/document drift and signed golden vectors pass; API tests cover config limits, read-only and shared equipment, unknown readings, partner exclusion and actual ledger energy; cloudlink tests cover old vs new policy, sending and withdrawal. The first full verify hit the existing OCPP listener timeout and SQLite rollback timing test; both packages passed serially. Final GOFLAGS=-p=2 make verify passed: Go and protocol tests, compose/container/release checks, native solver checks, import boundaries, vet and build.

Checked open PRs. This avoids the runtime files removed in #1691; the wire work should stay paired with Cloud. No hardware support or measured dispatch claim is made.

The final rollout has a second gate: Cloud must advertise the exact approved statistics policy in signed hello_ok. Core sends no hourly reports before that signal, even if an owner chose the policy. The owner policy API exposes readiness. This allows a beta and backend rollout with collection off while C8 is reviewed. Pausing collection preserves withdrawal and retention. The owner requested completing Core, Cloud and consent in the task; this includes the existing minor changeset because owners must make a new choice.

The review draft is srcfl/ftw-cloud#31 (privacy/project-statistics-dpia.md). The notice is srcfl/ftw-web#22. Final local GOFLAGS=-p=2 make verify and signed protocol vectors pass, including a service that has not approved the policy. No DPIA signature or household consent is claimed.

@frahlg
frahlg marked this pull request as ready for review October 17, 2026 14:04
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 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-17T14:07:27.510516Z fdd0196 Draft marked ready
ℹ️ 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdd0196088

ℹ️ 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".

Comment on lines +41 to +42
if d.BatteryCapacityWh > 0 {
batteryWh += d.BatteryCapacityWh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude vehicle capacities from the battery total

When an EV or vehicle driver carries the legacy battery_capacity_wh setting, this loop adds the vehicle's capacity to battery_capacity_wh statistics. The existing driverCapacitiesFrom logic in go/cmd/ftw/main.go:3482-3494 explicitly excludes drivers referenced by loadpoints and drivers whose catalog capability is ev or vehicle, because that value is vehicle capacity rather than installed site-battery capacity. Without equivalent filtering here, affected installations permanently publish inflated battery-capacity results under their consented statistics ID.

Useful? React with 👍 / 👎.

Comment on lines +709 to +713
if g.StatsAtMs == 0 || l.grant.StatsPolicy != req.Policy {
g.StatsAtMs = 0
if l.clockSynced() {
g.StatsAtMs = l.opts.Now().UnixMilli()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Start consent when the clock first becomes usable

If the owner accepts statistics before NTP synchronization, the request succeeds but stores StatsAtMs as zero. Nothing later initializes it when the clock synchronizes, while writer.statistics permanently rejects grants with StatsAtMs <= 0; therefore this consent never sends a report unless the owner happens to submit the same policy again. Record a conservative consent start once a synchronized clock becomes available, or reject/defer the original request instead of silently persisting an unusable consent.

Useful? React with 👍 / 👎.

@frahlg
frahlg merged commit 6335dbc into master Oct 17, 2026
22 checks passed
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