feat(config_flow): per-provider reauth flows (Phase 8 PR-5)#90
Merged
Conversation
PR-5 — When Amber, LocalVolts, or OpenElectricity (DWT-OE) rejects an
API key (HTTP 401/403), PriceHawk now raises ConfigEntryAuthFailed and
HA prompts the user to re-enter credentials via the integration UI.
First plank of HACS Silver compliance.
- coordinator.py: Amber _fetch_amber_with_retry raises on 401/403 (was
silently returning None). LocalVolts _maybe_poll_localvolts translates
LocalVoltsAPIError("auth failed") to ConfigEntryAuthFailed at the
coordinator boundary; non-auth LocalVoltsAPIError re-raises as-is.
DWT-OE _refresh_dwt_price tags the failed provider before re-raising
the existing ConfigEntryAuthFailed from PR-2.
- New _reauth_provider_id instance attribute on the coordinator —
single source of truth for "which provider failed last." Set BEFORE
raising; read by the ConfigFlow dispatcher.
- config_flow.py: new async_step_reauth(entry_data) dispatcher reads
the tag via entry.runtime_data.coordinator and routes to one of:
async_step_reauth_amber, async_step_reauth_localvolts, or
async_step_reauth_dwt_oe. Unknown tag aborts cleanly with
reason=reauth_provider_unknown.
- Each sub-step pre-fills the existing entry's field(s), validates the
new credential against a live probe (Amber /sites, LocalVolts
fetch_recent_intervals, OpenElectricity SDK), and calls
async_update_reload_and_abort on success. Entry id, options, and
runtime_data accumulators (daily_cost_history, _saving_month_aud,
_daily_wins) are preserved through the reauth.
- API keys NEVER appear in log messages, UI errors, or exception
strings. Extends the PR-2 OpenElectricity scrubber pattern.
- strings + translations: new reauth_amber / reauth_localvolts /
reauth_dwt_oe step descriptions + invalid_credentials error +
reauth_provider_unknown / reauth_successful abort reasons.
strings.json byte-identical to translations/en.json.
- 26 new tests covering raise sites, key redaction (with caplog),
dispatcher source contracts, sub-step source contracts, strings
parity, and history-preservation guard.
- 910 total pass (884 baseline + 26 new).
Decisions: DECISIONS.md > D-P8-1 (dispatcher-pattern reauth chosen over
ConfigEntryAuthFailed subclassing — HA strips the exception before
invoking reauth, only entry id survives the round-trip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
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
PR-5 — First plank of HACS Silver. When Amber, LocalVolts, or OpenElectricity (DWT-OE) rejects an API key, PriceHawk raises
ConfigEntryAuthFailedand HA prompts the user to re-enter credentials via the integration UI. Closes the loop that today produces silent WARNING spam (Amber / LocalVolts) or a stuck setup (DWT-OE raises but no reauth handler picks it up).coordinator.py: Amber_fetch_amber_with_retryraises on 401/403 (was silently returning None); LocalVolts boundary translatesLocalVoltsAPIError("auth failed")toConfigEntryAuthFailedwhile preserving non-authLocalVoltsAPIErrorsemantics for other consumers; DWT-OE tags the failed provider before re-raising the existing PR-2ConfigEntryAuthFailed._reauth_provider_idinstance attribute on the coordinator — single source of truth for "which provider failed last." Set BEFORE raising; read by the ConfigFlow dispatcher viaentry.runtime_data.coordinator(Phase 7 PR-1 typed runtime data path).config_flow.py: newasync_step_reauth(entry_data)dispatcher routes to one of three per-provider sub-steps:async_step_reauth_amber,async_step_reauth_localvolts,async_step_reauth_dwt_oe. Unknown tag aborts cleanly withreason="reauth_provider_unknown"./v1/sites, LocalVoltsfetch_recent_intervals, OpenElectricity SDKfetch_current_price), and callsasync_update_reload_and_aborton success. Entry id, options, and runtime_data accumulators (daily_cost_history,_saving_month_aud,_daily_wins) are preserved.test_amber_auth_failed_message_does_not_contain_keyusescaplogto verify.strings.json+translations/en.jsonbyte-identical mirror with three new reauth step descriptions,invalid_credentialserror, andreauth_provider_unknown/reauth_successfulabort reasons.Stacked on PR #89 (Phase 7 PR-4). GitHub auto-retargets up the chain (PR #86 → #87 → #88 → #89 → this) as Phase 7 merges.
Test plan
pytest --tb=short -q→ 910 passed (884 baseline + 26 new)ruff check coordinator.py config_flow.py tests/test_reauth.py→ cleandiff strings.json translations/en.json→ byte-identicalhomeassistant.local:8123:sensor.pricehawk_today_costcontinues from yesterday's accumulated value (NOT reset to 0)invalid_api_keyerror (skip if no waitlisted OE key available)Decisions:
DECISIONS.md> D-P8-1.Closes: Phase 8 / PR-5. Foundation for PR-6 reconfigure (consumes same dispatcher pattern).