Skip to content

feat: Add PW_SITE_ZERO_THRESHOLD to suppress phantom grid noise#298

Merged
jasonacox merged 8 commits into
jasonacox:mainfrom
jasonacox-sam:feature/site-zero-threshold
May 16, 2026
Merged

feat: Add PW_SITE_ZERO_THRESHOLD to suppress phantom grid noise#298
jasonacox merged 8 commits into
jasonacox:mainfrom
jasonacox-sam:feature/site-zero-threshold

Conversation

@jasonacox-sam
Copy link
Copy Markdown
Collaborator

Summary

Adds PW_SITE_ZERO_THRESHOLD environment variable (default: 0, disabled) to suppress phantom grid import/export readings caused by CT sensor noise.

When set to a positive integer (watts), any site.instant_power value with an absolute value at or below the threshold is clamped to 0W.

Problem

When the Tesla Gateway is physically off-grid (or at night with idle solar), the local API reports a persistent tiny grid import — typically 1–5W. This is a known CT sensor noise artifact, and the official Tesla app already suppresses these readings. However, pypowerwall passes them through, accumulating phantom grid totals in dashboards and historical data.

Implementation

  • Config: PW_SITE_ZERO_THRESHOLD env var, parsed as int, default 0 (disabled — no behavior change)
  • Applied in three locations:
    • /aggregates endpoint — modifies aggregates["site"]["instant_power"] directly
    • /csv and /csv/v2 endpoints — clamps the extracted grid value
    • /json endpoint — clamps the extracted grid value
  • Uses <= threshold — values exactly at the threshold are suppressed
  • Bidirectional — suppresses both import and export noise (matching the request)
  • Follows PW_NEG_SOLAR pattern — same style of env-var-driven data cleanup

Testing

With PW_SITE_ZERO_THRESHOLD=5:

  • Raw site.instant_power of +2W → reported as 0W ✅
  • Raw site.instant_power of -3W → reported as 0W ✅
  • Raw site.instant_power of +50W → passed through as 50W ✅
  • Default (0) → all values passed through unchanged ✅

Closes #295

Adds PW_SITE_ZERO_THRESHOLD env var (default: 0, disabled). When set
to a positive integer (watts), any site/grid instant_power reading with
an absolute value at or below the threshold is clamped to 0W.

This addresses CT sensor noise that reports 1-5W phantom grid draw when
the system is off-grid or solar is idle — consistent with how the
Tesla app already suppresses these readings.

Applied in three locations:
- /aggregates endpoint (raw API data)
- /csv and /csv/v2 endpoints
- /json endpoint

Closes jasonacox#295
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 12, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 26.49%. Comparing base (ae21cd5) to head (093ecac).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #298   +/-   ##
=======================================
  Coverage   26.49%   26.49%           
=======================================
  Files          42       42           
  Lines        7636     7636           
  Branches     1091     1091           
=======================================
  Hits         2023     2023           
  Misses       5469     5469           
  Partials      144      144           
Flag Coverage Δ
py3.10 26.49% <ø> (ø)
py3.11 26.49% <ø> (ø)
py3.12 26.49% <ø> (ø)
py3.13 26.49% <ø> (ø)
py3.9 26.48% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jasonacox
Copy link
Copy Markdown
Owner

Thanks @jasonacox-sam - please also increment the build version to reflect the change. Also update RELEASE.md.

BUILD = "t88"

@jasonacox-sam
Copy link
Copy Markdown
Collaborator Author

Done! Pushed commit 7d87aa4 to the branch:

  • Bumped proxy BUILD from t88 → t89
  • Added RELEASE.md entry for v0.15.7 with PW_SITE_ZERO_THRESHOLD feature description

— Sam ⚡

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an optional proxy-side clamp for near-zero site.instant_power readings to reduce phantom grid import/export noise caused by CT sensor jitter, configured via a new PW_SITE_ZERO_THRESHOLD env var.

Changes:

  • Introduces PW_SITE_ZERO_THRESHOLD configuration and exposes it in /stats config output.
  • Applies threshold clamping to /api/meters/aggregates, /csv (/csv/v2), and /json outputs.
  • Updates release notes and bumps proxy build to t89.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
RELEASE.md Documents the new env var feature and the proxy build bump.
proxy/server.py Parses PW_SITE_ZERO_THRESHOLD, adds clamping logic to multiple proxy endpoints, bumps BUILD.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread proxy/server.py
Comment on lines 191 to 193
neg_solar = os.getenv("PW_NEG_SOLAR", "yes").lower() == "yes"
site_zero_threshold = int(os.getenv("PW_SITE_ZERO_THRESHOLD", "0"))
api_base_url = os.getenv(
Comment thread proxy/server.py
Comment thread proxy/server.py
Comment on lines +1263 to +1265
# Apply site zero threshold - suppress phantom grid noise
if site_zero_threshold > 0 and abs(grid) <= site_zero_threshold:
grid = 0
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@jasonacox-sam need to pass through None

Comment thread proxy/server.py Outdated


# Apply site zero threshold - suppress phantom grid noise
if site_zero_threshold > 0 and abs(grid) <= site_zero_threshold:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@jasonacox-sam need to pass through None

Comment thread RELEASE.md
Comment thread proxy/server.py
Comment on lines +1263 to +1265
# Apply site zero threshold - suppress phantom grid noise
if site_zero_threshold > 0 and abs(grid) <= site_zero_threshold:
grid = 0
@jasonacox
Copy link
Copy Markdown
Owner

@jasonacox-sam Pleae address copilot reviews. Also as mentioned above, we need to pass through None values, not force them 0, as that indicates a gap in data we need to honor by passing None.

…fix RELEASE.md

- Add try/except around PW_SITE_ZERO_THRESHOLD env var parsing
- Guard all three clamp sites against None values (pass through as data gap)
- Remove incorrect /vitals mention from RELEASE.md
@jasonacox-sam
Copy link
Copy Markdown
Collaborator Author

Pushed commit 252cd06 addressing all review feedback:

Copilot — Defensive parsing: ✅ Added try/except around int(os.getenv("PW_SITE_ZERO_THRESHOLD", "0")) with a warning log on invalid input.

Jason — Pass through None values: ✅ All three clamp sites now check is not None before applying the threshold. If the upstream value is None (data gap), it passes through unchanged — not forced to 0.

Copilot — RELEASE.md accuracy: ✅ Removed the incorrect /vitals mention. Updated to reflect actual scope: /api/meters/aggregates, /csv//csv/v2, and /json endpoints.


— Sam ⚡ (pypowerwall agent)

When aggregates data is unavailable (None), CSV and JSON endpoints now
return None to indicate a data gap rather than artificially setting
grid/solar/battery/home to 0. This preserves the distinction between
'no reading' and 'zero power' for downstream consumers like Grafana.

Addresses Jason's review feedback on PR jasonacox#298.
@jasonacox-sam
Copy link
Copy Markdown
Collaborator Author

Good catch — you're right. When the aggregates fetch fails, setting grid = solar = battery = home = 0 was masking data gaps as real readings. Downstream consumers (Grafana, InfluxDB) need to see None/null to distinguish "no data" from "zero power."

Changes pushed:

  • /csv and /json endpoints: else branch now sets values to None instead of 0
  • Added early return None for CSV when data fetch fails (so CSV skips the row entirely rather than writing bogus zeros)
  • JSON naturally serializes None as null
  • Negative solar correction guarded with is not None check so it doesn't error on None values

— Sam 🌊

jasonacox-sam and others added 3 commits May 16, 2026 03:19
When aggregates data is unavailable (timeout/error), internal values are
set to None to distinguish data gaps from actual zeros. This None must
be converted back to 0 at the output boundary for backward compatibility.

Fixes:
- test_json_null_aggregates: grid was None instead of 0
- test_csv_with_null_values: returned TIMEOUT! instead of zero CSV
@jasonacox jasonacox merged commit dacab2e into jasonacox:main May 16, 2026
16 checks passed
@jasonacox
Copy link
Copy Markdown
Owner

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.

Feature Request: PW_SITE_IMPORT_ZERO_THRESHOLD to filter phantom grid draw (off-grid / night-time sensor noise)

4 participants