Skip to content

fix(web): pause plan, heating, settings, and card polls when hidden - #1177

Open
frahlg wants to merge 5 commits into
masterfrom
fix/hidden-tab-polls
Open

frahlg wants to merge 5 commits into
masterfrom
fix/hidden-tab-polls

Conversation

@frahlg

@frahlg frahlg commented Sep 8, 2026

Copy link
Copy Markdown
Member

Closes #1141.

Background tabs stop Plan, heating, card, loadpoint/twin and Settings EV/System polls; closing Settings stops its status timer. Visible tabs refresh and resume one timer. Energy-flow particles pause while hidden.

The remaining review findings are fixed: Plan waits for its current six-request batch, then runs at most one queued catch-up while visible. Hiding again drops that catch-up. A provider rerender replaces the EV status timer so later responses update the current element.

Validation: 604 web tests and make verify pass on current master including #1170 and #1199. New behavioral tests cover resumed in-flight Plan reads, hiding again before the response, and updates to the new EV status element after a rerender. Both review regressions fail on the original PR code and pass with the fix. Existing tests cover hidden startup, heating concurrency, and Settings close/resume.

Rendered browser smoke check: Overview, Plan, Chargers and System settings, then modal close, using a read-only localhost fixture with synthetic data. This is not a real-site test. The legacy EV tab is no longer in the main Settings navigation; its rerender regression runs against the actual tab script in the test harness.

Pending before merge: AGENTS.md requires a human to inspect the rendered interface. Check return from a background tab, normal Plan/System updates, and stopping Settings polls after close. Local preview: http://127.0.0.1:8189/#plan (synthetic data; no devices). No claim of physical control or release validation.

Existing exclusions remain: Settings HA polling belongs to #1003; models polling and CSS/chart animation work are separate. No settings, stored configuration or control behavior is removed.


Note

Low Risk
Client-side polling and animation lifecycle only; no API or control-path changes, with behavioral tests added for visibility edge cases.

Overview
Background tabs and closed Settings no longer keep hammering the box with timers and animation work. Plan, heating, loadpoints/twins (when advanced UI is visible), and dashboard history, savings, and price cards now listen for visibilitychange: intervals are cleared while document.hidden, and becoming visible triggers one refresh then a single resumed timer.

Plan polling is refactored so fetchAll is single-flight with at most one queued catch-up batch after the current six-request batch finishes; that catch-up is dropped if the tab hides again before it runs. Heating skips overlapping timer ticks and avoids running a queued refresh after hide.

Settings EV and System status polls only while the settings modal is open and the document is visible; closing the modal (via MutationObserver on the modal’s hidden class) clears the timer. EV tab rerenders tear down and reattach polling so updates hit the current status element.

Energy-flow particle requestAnimationFrame loops pause in background tabs and resume on show.

New VM-based tests cover heating/EV/plan visibility and resume behavior.

Reviewed by Cursor Bugbot for commit e955fc3. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 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-09-08T09:44:13.593517Z 4450d42 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.

@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_26a075e4-948f-41e5-8fc8-19f95a08bcd2)

@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: 4450d42710

ℹ️ 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 thread web/settings/tabs/ev.js
Comment thread web/plan.js
Plan, heating, settings, history/savings/price cards, loadpoints and
twins now follow the same visibilitychange rule as status polling:
clear the timer while document.hidden, then one fetch and one timer
on return. Settings EV and System timers also stop when the modal
closes. Energy-flow particles skip rAF while hidden.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg
frahlg force-pushed the fix/hidden-tab-polls branch from 4450d42 to 64dfd25 Compare September 8, 2026 11:20
@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_a6baaade-b01b-4750-8cbe-8bf23bb5402d)

@miravoss26 miravoss26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consistent visibility-pause pattern applied across plan, heating, history/savings/price cards, loadpoints/twins, Settings EV/System, and the energy-flow particle rAF loop: clear timers on visibilitychange → hidden, one fetch + one fresh timer on → visible. Heating's queued-refresh-while-hidden edge case is handled explicitly (waiters resolve without a stale catch-up fetch). Settings EV/System additionally stop via a MutationObserver on the modal's hidden class, which is the right hook since those timers are scoped to the modal being open, not just tab visibility.

Spot-checked ftw-energy-flow.js, ftw-history-card.js, ftw-price-chart.js, ftw-savings-card.js, and heating.js — the _syncPolling/syncHeatingPolling refactors are equivalent to the old _restartPolling plus the new hidden-check, no dropped isConnected guards. hidden-tab-polls.test.mjs (269 new lines) exercises heating and EV-settings with real timer/DOM mocks, not just source-pattern assertions. All CI green, no security surface here (client-side polling/animation only).

Safe to merge from my read.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_322215e8-2ff6-4be8-ba0a-f86fe0c7ee58)

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b147ac1a-f6e8-47d7-b5b3-576c646aea85)

@miravoss26 miravoss26 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the diff (10 files + 2 new test files). Pauses plan/heating/settings/dashboard-card polling and the energy-flow particle animation while the tab is hidden, resuming on visibilitychange.

Correctness:

  • plan.js's new fetchAll/fetchPlanData split adds single-flight + queued-catchup semantics (mirrors the existing heating.js refreshAfterControl pattern the tests already covered) — a caller that arrives mid-fetch marks planRefreshQueued and gets the in-flight promise rather than triggering an overlapping batch; the do/while loop stops once document.hidden, so a hide during the queued catch-up doesn't run an extra fetch after going dark. Test plan-polling.test.mjs exercises exactly this (resume-without-overlap, drop-if-hidden-again-before-response).
  • ftw-energy-flow.js: confirmed the visibility listener is registered in the constructor and only ever removed in disconnectedCallback — checked the file has no connectedCallback, so there's no reconnect path that would leave the listener stripped after a hide/show cycle.
  • The _syncPolling rewrite in ftw-history-card.js/ftw-savings-card.js/ftw-price-chart.js correctly preserves the poll-ms="0" disables-polling behavior (?? not ||) and the price-chart's fed-attribute skip.
  • ev.js/system.js gate on a MutationObserver on the modal's class attribute and visibilitychange, both funneling into the same syncPolling/shouldPoll pair — reasonable given there's no explicit "modal closed" callback to hook. system.js doesn't get its own dedicated behavioral test (only the "file contains visibilitychange" smoke assertion), but it mirrors ev.js's pattern closely enough that I'd call that acceptable, not a blocker.

Security: no new endpoints, no auth changes, no eval, pure client-side timer/animation-frame lifecycle changes. Nothing to flag.

Test coverage is strong for the two most concurrency-sensitive paths (heating single-flight, plan resume/catch-up). Safe to merge from my read.

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.

perf(web): pause plan, heating, settings, and card polls when the tab is hidden

2 participants