Skip to content

Feat: Support graceful fallback target (TIMEOUT_FALLBACK_W) - #560

Open
thkrmr wants to merge 2 commits into
tomquist:mainfrom
thkrmr:feature/graceful-fallback
Open

Feat: Support graceful fallback target (TIMEOUT_FALLBACK_W)#560
thkrmr wants to merge 2 commits into
tomquist:mainfrom
thkrmr:feature/graceful-fallback

Conversation

@thkrmr

@thkrmr thkrmr commented Jul 12, 2026

Copy link
Copy Markdown

Feat: Support graceful fallback target (TIMEOUT_FALLBACK_W) on powermeter failure

  • Added TIMEOUT_FALLBACK_W config for CT002 to gracefully step down battery output during meter outages rather than freezing on stale data.
  • Updated ThrottledPowermeter to conditionally bypass its cache on failure when a fallback is configured, allowing the CT002 control loop to activate.
  • Improved test_powermeter to not fail daemon startup if a fallback is configured, enabling AstraMeter to boot during an ongoing outage.
  • Silenced expected traceback spam in fallback mode caused by the custom _AutoExcInfoFilter.
  • Increased Tibber Pulse HTTP timeout from 2s to 5s to avoid false-positive fallback triggers during momentary WiFi slowdowns.

Summary by CodeRabbit

  • New Features
    • Added configurable fallback watt target to keep battery output stable when meter reads fail.
    • Powermeter health reporting now includes whether the last reading was produced via fallback mode.
  • Bug Fixes
    • Improved fallback activation so failures propagate correctly instead of being masked by cached values.
    • Updated device/emulator fallback behavior to send an active control delta toward the fallback target.
    • Made startup meter checks more resilient to transient failures.
  • Chores
    • Increased network timeout tolerance for compatible pulse-based meters.

…eter failure

- Added TIMEOUT_FALLBACK_W config for CT002 to gracefully step down battery output during meter outages rather than freezing on stale data.
- Updated ThrottledPowermeter to conditionally bypass its cache on failure when a fallback is configured, allowing the CT002 control loop to activate.
- Improved test_powermeter to not fail daemon startup if a fallback is configured, enabling AstraMeter to boot during an ongoing outage.
- Silenced expected traceback spam in fallback mode caused by the custom _AutoExcInfoFilter.
- Increased Tibber Pulse HTTP timeout from 2s to 5s to avoid false-positive fallback triggers during momentary WiFi slowdowns.
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d46fd8d-3664-4f8d-a1ef-2e258f678db0

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7c379 and 71810c0.

📒 Files selected for processing (2)
  • src/astrameter/ct002/ct002.py
  • src/astrameter/main.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/astrameter/main.py

Walkthrough

The PR adds configurable CT002 fallback output, propagates powermeter failures through wrappers, allows startup with fallback configured, returns fallback readings during cloud gathering, publishes fallback health state, and lengthens Tibber Pulse connection timeouts.

Changes

Powermeter fallback handling

Layer / File(s) Summary
CT002 fallback control
src/astrameter/ct002/ct002.py
CT002 accepts timeout_fallback_w, reports fallback-specific warnings, and computes per-phase adjustments when powermeter hooks fail.
Startup and wrapper wiring
src/astrameter/main.py, src/astrameter/powermeter/wrappers/throttling.py
Startup clamps and wires the fallback target, allows configured fallback startup, and bypasses cached wrapper values so failures reach fallback logic.
Fallback readings and health reporting
src/astrameter/main.py, src/astrameter/mqtt_insights/service.py
Cloud gathering returns fallback phase values after read failures, and retained MQTT health state includes fallback_active.
Powermeter connection tolerance
src/astrameter/powermeter/tibber_pulse.py
Tibber Pulse uses longer connection timeouts for transient delays.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CloudGather
  participant ThrottledPowermeter
  participant CT002
  participant MQTTHealth
  CloudGather->>ThrottledPowermeter: read raw powermeter watts
  ThrottledPowermeter-->>CloudGather: propagate read failure
  CloudGather->>CT002: use configured fallback target
  CT002-->>CloudGather: provide fallback phase values
  CloudGather->>MQTTHealth: publish fallback_active state
Loading

Possibly related PRs

Suggested reviewers: tomquist

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding graceful fallback support via TIMEOUT_FALLBACK_W.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/astrameter/ct002/ct002.py`:
- Line 1071: Change the per-request fallback log in the meter polling path to
use debug level instead of warning, while preserving its existing message and
arguments. Leave the rate-limited fallback activation warning in
_call_before_send unchanged.
- Around line 1062-1074: Update the ESPHome CT002 meter-timeout handling in the
relevant control/update method to support timeout_fallback_w like the Python
CT002 implementation: when configured, calculate the delta from the reported
power to the fallback target and apply it across all three phase values;
otherwise preserve the existing zero-adjustment behavior. Keep the fallback
target and reported-power semantics consistent with the Python path.
- Around line 1062-1074: Update the timeout fallback logic in the CT002 handling
block to use reported_phase when constructing values: place the full delta_w in
the battery’s reported phase field and set the other phase fields to zero for
per-phase batteries, while preserving the existing combined (“D”) behavior that
distributes the delta across all three fields. Keep the fallback target and
warning values unchanged.

In `@src/astrameter/main.py`:
- Around line 557-588: The control-loop fallback state is not reaching the MQTT
health publisher when cloud reporting is disabled. Update the state propagation
around the visible powermeter read/failure handling and the MQTT health
publisher so `fallback_active` uses the control-loop’s `chosen.in_fallback_mode`
state, preserving true during CT002 fallback and false after a successful read.
- Around line 694-702: The startup test currently applies one global
has_fallback value to every powermeter. Derive the TIMEOUT_FALLBACK_W setting
for each powermeter’s associated configuration, carry that per-meter fallback
flag through the powermeters iteration, and pass it as allow_failure to
test_powermeter so unrelated meters retain strict failure behavior and accurate
logging.
- Around line 263-280: The bypass_cache_on_error update in run_device must only
affect meter wrappers belonging to the current device, not every entry in the
shared powermeters collection. Filter powermeters to this device’s matching
client or construct its per-device wrapper chain, then set bypass_cache_on_error
only on wrappers in that chain while preserving the existing timeout_fallback_w
condition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4bcc3553-7418-458d-99c0-be181a116d5f

📥 Commits

Reviewing files that changed from the base of the PR and between 94199c2 and 5a7c379.

📒 Files selected for processing (5)
  • src/astrameter/ct002/ct002.py
  • src/astrameter/main.py
  • src/astrameter/mqtt_insights/service.py
  • src/astrameter/powermeter/tibber_pulse.py
  • src/astrameter/powermeter/wrappers/throttling.py

Comment thread src/astrameter/ct002/ct002.py
Comment thread src/astrameter/ct002/ct002.py Outdated
Comment thread src/astrameter/main.py Outdated
Comment thread src/astrameter/main.py
Comment thread src/astrameter/main.py
… delta, improve fallback logging

- Demote per-poll fallback delta log to debug; keep rate-limited
  ENGAGED/DISENGAGED warnings at WARNING level for alerting.
- Fix per-phase battery delta: place full delta_w in the correct
  phase field instead of splitting across all three.
- Scope bypass_cache_on_error lazily in read_ct_powermeter instead
  of eagerly on all wrappers, so unrelated powermeters keep caching.
- Clarify intentional broad scope of has_fallback for startup test.
@tomquist

Copy link
Copy Markdown
Owner

@thkrmr Can you provide more context on what problem you're solving with this change?

@thkrmr

thkrmr commented Jul 13, 2026

Copy link
Copy Markdown
Author

@tomquist My Pulse IR loses connection frequently as of recent (closed distribution box, in a separate room), at times it also fails to deliver a valid SML (I pasted the logs in an issue) but doesn't lose its connection.
What happened to me the day before yesterday: Pulse IR went away for a significant period of time and the battery got stuck draining itself, because the moment when Pulse stopped delivering new data was when AstraMeter just ramped up to 800 watts and thus got stuck maintaining it.
The idea is should Pulse stop delivering reliable data there'd be a threshold configured AstraMeter should maintain regardless of conditions (but not below minimum DC) until the moment Pulse returns with fresh data. Pulse does so on its own. So that we neither get stuck draining the battery OR hovering at 0 because of staleness.

2026-07-13 20:25:24 WARNING:astrameter:CT002 before_send still failing (7 in a row) for ('xxx.xxx.xxx.xx', 10000): Could not decode SML telegram from Tibber Pulse. Fallback active, targeting 80W.
2026-07-13 20:25:56 WARNING:astrameter:CT002 before_send still failing (13 in a row) for ('xxx.xxx.xxx.xx', 10000): Could not decode SML telegram from Tibber Pulse. Fallback active, targeting 80W.
2026-07-13 20:26:29 WARNING:astrameter:CT002 before_send still failing (19 in a row) for ('xxx.xxx.xxx.xx', 10000): Could not decode SML telegram from Tibber Pulse. Fallback active, targeting 80W.
2026-07-13 20:26:58 WARNING:astrameter:CT002: Fallback DISENGAGED for ('xxx.xxx.xxx.xx'', 10000). Powermeter recovered after 23 failure(s).
2026-07-13 20:31:31 WARNING:astrameter:CT002: Fallback ENGAGED for ('xxx.xxx.xxx.xx'', 10000). Powermeter offline (Could not decode SML telegram from Tibber Pulse), targeting 80W.
2026-07-13 20:31:32 WARNING:astrameter:Powermeter TIBBER_PULSE failed (ValueError: Could not decode SML telegram from Tibber Pulse). Using fallback 80W
2026-07-13 20:32:04 WARNING:astrameter:CT002 before_send still failing (7 in a row) for ('xxx.xxx.xxx.xx', 10000): Could not decode SML telegram from Tibber Pulse. Fallback active, targeting 80W.
2026-07-13 20:32:35 WARNING:astrameter:CT002 before_send still failing (13 in a row) for ('xxx.xxx.xxx.xx'', 10000): Could not decode SML telegram from Tibber Pulse. Fallback active, targeting 80W.
2026-07-13 20:32:50 WARNING:astrameter:CT002: Fallback DISENGAGED for ('xxx.xxx.xxx.xx'', 10000). Powermeter recovered after 15 failure(s).

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.

2 participants