Repository navigation
feat(cloud): report installations, configured capacity and hourly energy - #1693
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 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".
| if d.BatteryCapacityWh > 0 { | ||
| batteryWh += d.BatteryCapacityWh |
There was a problem hiding this comment.
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 👍 / 👎.
| if g.StatsAtMs == 0 || l.grant.StatsPolicy != req.Policy { | ||
| g.StatsAtMs = 0 | ||
| if l.clockSynced() { | ||
| g.StatsAtMs = l.opts.Now().UnixMilli() | ||
| } |
There was a problem hiding this comment.
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 👍 / 👎.
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-policyand 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 verifypassed: 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 localGOFLAGS=-p=2 make verifyand signed protocol vectors pass, including a service that has not approved the policy. No DPIA signature or household consent is claimed.