fix(dwt): codex P1-5 — reset daily counters on cross-midnight restart#110
Merged
Conversation
Codex full-repo review (2026-05-23, P1-5): the DWT provider's ``from_dict(data, today)`` required ``today`` as a contract argument but then immediately ``del today``'d it and restored daily counters unconditionally. After a restart that crosses midnight, yesterday's ``import_kwh_today`` / ``import_cost_today_c`` / export equivalents were resurrected as today's — corrupting Energy Dashboard cost sensors and the external statistics push for the rest of the day. This is closely related to the codex P0-2 fix (PR #109) but covers a different code path. P0-2 added ``reset_daily()`` to the coordinator's midnight rollover branch — that handles the "HA stays running" midnight case. P1-5 covers the "HA restarts after midnight" case — ``async_restore_state`` runs and pulls the stored daily counters from the JSON store; without a date check those counters land in today's running totals. Fix: - ``to_dict()`` persists ``state_date`` = ``_last_tick.date().isoformat()``, the HA-tz date the daily counters apply to. ``None`` when no tick has run yet (fresh provider, treated as cross-midnight on restore). - ``from_dict()`` parses ``state_date``, compares to the supplied ``today`` HA-tz date. If they match, restore counters (existing roundtrip contract). If not, zero counters and log INFO with the two dates so operators can see why the dashboard reset. - Missing ``state_date`` (pre-fix snapshots) and malformed values are treated as cross-midnight — safe default = zero rather than risk carrying yesterday's totals. Malformed strings additionally emit a WARNING for triage. - ``last_price`` and ``last_tick`` are still restored regardless — they are not daily accumulators and survive midnight cleanly, giving the new day a known rate to start with. 4 regression tests in test_dynamic_wholesale_tariff_provider.py: - cross-midnight restart resets counters (the codex case) - same-day restart preserves counters (existing contract) - missing state_date treated as cross-midnight (back-compat) - malformed state_date treated as cross-midnight 1082 passing (was 1078), ruff clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Artic0din
added a commit
that referenced
this pull request
May 24, 2026
) Bumps manifest to 1.6.0-beta.2 — first HACS-beta tag carrying the full Phase 7-11 work landed since 1.6.0-beta.1. Live UAT against the production HA install on 2026-05-24 confirmed the prior fixes (NEMWeb regex #107, statistic_id sanitization #114/#145, bootstrap block #107, codex P0/P1 work in #109/#110/#111) had not reached users because the last tagged release was v1.4.0-beta.1 from 2026-05-02. Also fixes a UAT-surfaced bug not previously caught: explanation.py — DWT winners no longer produce empty bullets. ``build_explanation`` branched on literal provider IDs ("amber", "globird", "flow_power", "localvolts") but Dynamic Wholesale Tariff providers carry IDs like "dwt_aemo_direct" / "dwt_openelectricity", so every DWT win fell through to an empty bullet list. The Best Provider sensor's winner_explanation attribute showed bullets=[] with margin_aud=0 — no "why" surfaced to the user. Added a ``winner_id.startswith("dwt_")`` branch + ``_dwt_won_bullets`` builder using the standard provider snapshot shape: wholesale spot rate (c/kWh derived from $/MWh), today's import volume + cost, daily supply charge, and a stale-price warning when the wholesale price is over 10 min old. 5 regression tests in ``TestDwtWinnerBullets``. Full test suite: 1125 passing. Tag + GitHub pre-release follow immediately after this merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Codex P1-5: `DynamicWholesaleTariffProvider.from_dict(data, today)` required `today` as a contract argument but immediately `del today`'d it and restored daily counters unconditionally. A restart that crosses midnight resurrected yesterday's daily counters as today's.
Relationship to PR #109
PR #109's P0-2 added `reset_daily()` to the coordinator's midnight rollover branch — that covers the "HA stays running through midnight" case. This PR covers the "HA restarts after midnight" case where `async_restore_state` pulls stored daily counters from the JSON store.
Two different paths, both needed.
Fix
Tests
```
1082 passing (was 1078 — +4)
ruff: All checks passed
```
4 regression tests in `tests/test_dynamic_wholesale_tariff_provider.py`:
Test plan
Codex queue remaining