-
Notifications
You must be signed in to change notification settings - Fork 0
v1.5.0 — CDR-native engine + 8-retailer parsers + universal wizard #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
68 commits
Select commit
Hold shift + click to select a range
edebcfc
chore: add gstack skill routing rules to CLAUDE.md
Artic0din 9af7fd0
docs(phase-0): ground-truth spec for v1.5.0 CDR evaluator gate
Artic0din 7f465a4
feat(phase-0): Day 1 — plan-pull script + 6 test fixtures
Artic0din 5139ebb
feat(phase-0): Day 2 — evaluator prototype + 7d HA fixture + ZEROHERO…
Artic0din 3e11d8e
feat(phase-0): Day 3 — independent verifier + gate report
Artic0din 053102a
feat(phase-1-entry): legacy TariffEngine parity snapshots + Phase 0 G…
Artic0din 14180a3
fix(phase-1-entry): correct legacy snapshot sub-sampling
Artic0din 48efc06
fix(phase-1-entry): evaluator endTime + credit-GST bugs, parity 0.46%…
Artic0din d43431c
feat(cdr): Phase 1.1 — create cdr/ package + port evaluator
Artic0din de9c7db
chore(release): v1.4.0-beta.2 polish (carry-forward from dev WIP)
Artic0din 98e3adc
feat(cdr): Phase 1.2 — streaming engine + CdrGloBirdProvider
Artic0din 0955dfa
feat(coordinator): Phase 1.3 — feature-flag CDR vs legacy GloBird pro…
Artic0din 67a46a2
chore: gitignore .codex/ + graphify-out/ (local-only artefacts)
Artic0din 01568bc
chore(release): v1.4.0-beta.2 polish pt2 (cache-buster + CHANGELOG)
Artic0din 95dd609
test: track tests/conftest.py (HA module mock infrastructure)
Artic0din 01edd62
test: track tests/test_review_improvements.py (code-review fix coverage)
Artic0din 07b83cb
docs: track AGENTS.md + TODOS.md + assets/DESIGN.claude.md
Artic0din 01f2461
chore(assets): track dashboard v3 design explorations
Artic0din 86235ef
feat(cdr): add async CDR HTTP client for Phase 2 wizard
Artic0din 33e6249
feat(cdr): add retailer registry with jxeeno fallback
Artic0din 140ec4e
feat(wizard): CDR plan picker (Phase 2.2 branch A happy path)
Artic0din beab33e
feat(wizard): CDR retry/error UI (Phase 2.3 branch B)
Artic0din fb979f8
feat(wizard): CDR skip-reason audit field (Phase 2.4 branch C)
Artic0din 067ee23
feat(wizard): CDR override JSON step (Phase 2.5 branch D)
Artic0din e6c4a59
feat(cdr): AGL incentive parser for bonus FIT + Three for Free
Artic0din 39fe8f3
feat(wizard): options-flow CDR re-pick (Phase 2.7)
Artic0din 6dd943f
feat(wizard): pre-filter CDR plans by state + distributor (Phase 2.8)
Artic0din 1db0c4c
feat(wizard): plan confirmation screen (Phase 2.9)
Artic0din 5c90738
fix(wizard): handle real CDR tariffPeriod shape in plan summary
Artic0din 11740fb
fix(wizard): geography-based plan filter + dedupe (Phase 2.10)
Artic0din f8ef761
fix(wizard): handle AGL singleRate dict + per-tariff dailySupplyCharge
Artic0din 4049f54
fix(wizard): TOU FIT summary + show all incentives (Phase 2.10.2)
Artic0din 6ef5355
feat(wizard): controlled-load summary + catalog-pinned shape tests (P…
Artic0din eaf2b26
polish(wizard): strip redundant labels in confirm summary (Phase 2.10.4)
Artic0din 45b6c0f
catalog: v3 incentive shape catalog + 13 catalog v2 tariff regression…
Artic0din d611a30
feat(cdr): tiered FIT incentive parser (Phase 2.11.1)
Artic0din d6d5a03
feat(cdr): wire tiered_fit to Origin/Alinta/EnergyAustralia (Phase 2.…
Artic0din d881a7f
feat(cdr): bonus FIT parser + GloBird Peak FIT wiring (Phase 2.11.3)
Artic0din bea556c
feat(cdr): free / discounted import window parser + 4 retailer wiring…
Artic0din 3f2f02b
feat(coordinator): Amber daily replay + CDR-aware ZEROHERO + supply c…
Artic0din 3199fc9
feat(config_flow): Step-1 cleanup + comparator toggles (Phase 2.12)
Artic0din 495c0a8
feat(cdr): ev_offpeak.py midnight-6am EV rate override (Phase 2.11.6)
Artic0din 9b25b8e
feat(cdr): ovo_interest.py 3%-on-credit-balance parser (Phase 2.11.7)
Artic0din cba796c
feat(cdr): vpp_rebate.py monthly per-battery VPP credit (Phase 2.11.5)
Artic0din 32e0f9f
feat(cdr): Super Export overlap fix + Red weekend-only filter (Phase …
Artic0din ff333e3
feat(cdr): wire opt-in fields through parser dispatch (Phase 2.12.1)
Artic0din 9db2962
docs: Phase 3 multi-plan pivot roadmap
Artic0din 8228770
refactor(providers): CdrGloBirdProvider → CdrPlanProvider (Phase 3.0a)
Artic0din 1b399fd
refactor(coordinator): _globird → _current_plan_provider (Phase 3.0b)
Artic0din c04c37b
refactor(coordinator): rip legacy GloBirdProvider fallback (Phase 3.0c)
Artic0din b138801
refactor(providers): delete legacy GloBirdProvider (Phase 3.0d)
Artic0din 280359c
refactor(coordinator,sensor): rename globird_* data keys → current_pl…
Artic0din 601db40
refactor(config_flow): universal CDR wizard (Phase 3.0f)
Artic0din 8e7b542
test(config_flow): Phase 3.0g — wizard routing helper tests
Artic0din 363ea09
chore(config_flow): drop unused PROVIDER_GLOBIRD import (Phase 3.0g)
Artic0din 5e70750
fix(hassfest): declare recorder dep + strip braces from invalid_json …
Artic0din a31a7da
fix(manifest): sort keys alphabetically after domain+name (hassfest)
Artic0din f4fd690
fix: address Phase 3 reviewer feedback (CodeRabbit + Sourcery)
Artic0din 5c51b4a
fix: Phase 3 second-round reviewer feedback (CodeRabbit PR #28)
Artic0din 101e634
fix(tests): conftest sys.path uses parents[1] (CodeRabbit PR #27)
Artic0din 9debf03
chore(config_flow): delete dead manual-tariff wizard + options steps
Artic0din cddc4fb
fix: round-3 CodeRabbit carry-overs (security + correctness)
Artic0din ed905d7
fix: round-4 CodeRabbit findings + v1.5.0 release scope
Artic0din 4223ff2
chore(coderabbit): add slop_detection + custom finishing-touches recipes
Artic0din 33670fd
fix: UAT-found bugs (Phase 3.0 wizard rollout) (#53)
Artic0din bee52a4
fix: PR #28 CR round-5 inline fixes
Artic0din 8541237
fix(sensor): MetricsWonSensor passthrough — drop dead inline fallback
Artic0din b810240
Phase 3.1 prep — EME refdata2 registry + brand disambiguation (#54)
Artic0din File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| # Phase 3 — Multi-Plan Pivot Roadmap | ||
|
|
||
| Locked 2026-05-15 after Phase 2.12.1 ship + product-direction reset. | ||
|
|
||
| ## Why we pivoted | ||
|
|
||
| Phase 2 architected PriceHawk as **one current retailer + one user-chosen comparison plan**, gated on "current retailer must have a live consumer API". User correction: that's the wrong shape. | ||
|
|
||
| The actual product: **PriceHawk evaluates every CDR plan eligible for the user's geography (state + postcode + distributor) against their real meter data, ranks them, and surfaces the best alternatives.** API providers (Amber, Flow Power, LocalVolts) are optional truth-source overlays for users who happen to have them — they're not a gate. | ||
|
|
||
| Phase 2 incentive parsers (the wedge feature — free-text math nobody else parses) are kept verbatim. Phase 2 orchestration (coordinator wiring, sensor schema, wizard) gets rewritten. | ||
|
|
||
| ## No migration | ||
|
|
||
| Existing entries from Phase 2 are NOT migrated. User must remove + re-add. Justification: migration paths are bug surfaces; this is pre-1.0, expected disruption. | ||
|
|
||
| ## Phase order | ||
|
|
||
| Sequence chosen to land foundation first, then layer features and polish. | ||
|
|
||
| ### Phase 3.0 — Unify under one evaluator (foundation) | ||
|
|
||
| Every cost number flows through `evaluator.evaluate()`. API providers become optional truth-source overlays. | ||
|
|
||
| **Files touched:** | ||
| - `coordinator.py` — rip 4-provider dispatch; introduce `_current_plan_provider` (CdrPlanProvider) + optional `_truth_overlay` | ||
| - `config_flow.py` — wizard: state → distributor → retailer → plan → [optional API connect] → done | ||
| - `const.py` — drop `CONF_CURRENT_PROVIDER` enum semantics; keep PROVIDER_* only for truth-overlay identification | ||
| - `providers/{amber,flow_power,localvolts}.py` — repurpose as truth-source overlays (override computed cost when connected) | ||
| - `sensor.py` — drop per-provider sensor classes; introduce CurrentCostSensor, BestAlternativeSensor (placeholder), WinnerExplanation | ||
| - `__init__.py` — entry setup flow + clean async_migrate_entry returning False | ||
| - New: `cdr/ranking.py` skeleton (3.1 fills it) | ||
|
|
||
| **Commits:** 5-8 small, each independently testable. Expected ~500 LOC delta. | ||
|
|
||
| **Risk:** breaks ~50 existing tests (per-provider sensor tests, single-comparator coord tests). Replace as we go. | ||
|
|
||
| ### Phase 3.1 — Multi-plan ranking engine | ||
|
|
||
| Daily job: filter CDR registry by user geography → cheap-heuristic top-K → deep-evaluate top-K → persist ranked list. | ||
|
|
||
| **Files:** | ||
| - `cdr/ranking.py` — eligibility + cheap-rank + deep-rank | ||
| - `cdr/registry.py` — extend `eligible_plans_for(state, postcode, distributor)` query | ||
| - `coordinator.py` — `async_track_time_change` at 00:30 local → ranking job | ||
| - `__init__.py` — `pricehawk.rank_alternatives` service | ||
|
|
||
| **Commits:** 4-6. ~400 LOC. | ||
|
|
||
| **Heuristic:** rank by `peak_rate * 0.7 + daily_supply * 0.3` (no incentives, no FIT). Top-K=20 default, user-configurable. | ||
|
|
||
| ### Phase 3.2 — Universal HA-history backfill | ||
|
|
||
| At wizard completion, replay HA grid-sensor history through current + top-K plans → populate `daily_cost_history` for full available lookback (HA recorder default: 10 days; longer if user has `purge_keep_days` raised). | ||
|
|
||
| **Files:** | ||
| - Rewrite `backfill.py` — generic replay-through-evaluator | ||
| - New: `cdr/history_replay.py` — multi-plan wrapper | ||
| - `__init__.py` — kick off backfill post-setup; surface `sensor.pricehawk_backfill_status` | ||
|
|
||
| **Commits:** 3-4. ~300 LOC. | ||
|
|
||
| **UX note:** if HA recorder retention is default 10 days, dashboard's "year" rollup will be sparse until 365 days of live data accrues. Surface this in setup. | ||
|
|
||
| ### Phase 3.3 — Period rollup sensors | ||
|
|
||
| Day / week / month / 3-month / 12-month sensors for current + best-alt + savings. | ||
|
|
||
| **Files:** | ||
| - `sensor.py` — new `PeriodRollupSensor` class | ||
| - New: `cdr/rollup.py` — rolling-window aggregate math | ||
|
|
||
| **Sensor names:** | ||
| - `sensor.pricehawk_current_cost_{today, week, month, 3month, year}` | ||
| - `sensor.pricehawk_best_alternative_cost_{today, week, month, 3month, year}` | ||
| - `sensor.pricehawk_savings_{today, week, month, 3month, year}` | ||
|
|
||
| **Commits:** 3-4. ~250 LOC. | ||
|
|
||
| ### Phase 3.4 — Optional named comparator drill-in | ||
|
|
||
| User pins ONE specific CDR plan as primary comparator; gets tick-by-tick computation (vs daily for auto-ranked alternatives). | ||
|
|
||
| **Files:** | ||
| - `config_flow.py` OptionsFlow — "named_comparator" step, skippable | ||
| - `coordinator.py` — extends current_evaluator pattern with parallel `_named_comparator` evaluator running every tick | ||
| - `sensor.py` — `named_comparator_cost_{...}` sensors | ||
|
|
||
| **Commits:** 2-3. ~150 LOC. | ||
|
|
||
| ### Phase 3.5 — Dashboard rewrite | ||
|
|
||
| HA Lovelace cards: current cost + ranked top-N alternatives + drill-in card. | ||
|
|
||
| **Files:** | ||
| - `www/dashboard.html` — rewrite | ||
| - `dashboard_config.py` — entity exposure | ||
| - `assets/DESIGN.claude.md` — design spec update | ||
|
|
||
| **Commits:** 2-3. UI-only. | ||
|
|
||
| ## Cadence | ||
|
|
||
| - 3.0 lands first (foundation; everything else depends on it) | ||
| - 3.1 + 3.2 can develop in parallel after 3.0 | ||
| - 3.3 / 3.4 / 3.5 are independent polish layers, ship in any order | ||
|
|
||
| ## Totals | ||
|
|
||
| | | | | ||
| |---|---| | ||
| | Phases | 6 | | ||
| | Commits | 19-28 | | ||
| | LOC delta | ~1,600 net | | ||
| | Test count | 600 → ~750-800 | | ||
| | Wall-clock | 2-3 weeks focused | | ||
|
|
||
| ## Held by user (not part of Phase 3) | ||
|
|
||
| - "Dynamic wholesale pricing (Amber-style) for CDR plans" — would require CDR plans to publish half-hourly variable rates, which they don't. Defer until AER pushes a CDR amendment, or until we add a wholesale-overlay feature. | ||
|
|
||
| ## v1.5.1+ (post-Phase-3) | ||
|
|
||
| Per TODOS.md: | ||
| - TODO-5 demand charges (~10% AU plans currently silently wrong) | ||
| - TODO-7 Flow Power Happy Hour FiT parser | ||
| - TODO-8 plan-change diff notifications | ||
| - TODO-9 plan-override YAML | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # PriceHawk — Energy Compare HACS Integration | ||
|
|
||
| **Stack:** Python, Home Assistant custom integration (HACS) | ||
|
|
||
| Compare real energy costs between [Amber Electric](https://www.amber.com.au) (wholesale spot pricing) and [GloBird Energy](https://www.globirdenergy.com.au) (time-of-use tariffs) using actual Home Assistant consumption data. | ||
|
|
||
| ## Project Context | ||
|
|
||
| - **Target:** Home Assistant custom integration distributed via HACS | ||
| - **Amber side:** Connects to Amber's public API — straightforward | ||
| - **GloBird side:** No API — users manually configure their tariff rates, time periods, and incentives via a config flow | ||
| - **Users:** Australian solar/battery households comparing energy providers | ||
|
|
||
| ## GloBird Plan Complexity | ||
|
|
||
| Three sample plans in project root (PDFs). Key variations the config flow must handle: | ||
| - **Flat vs TOU** import rates | ||
| - **Stepped pricing** (first X kWh at one rate, remainder at another) | ||
| - **Multiple time windows per period** (e.g., Shoulder = 9pm-12am + 12am-10am + 2pm-4pm) | ||
| - **Separate import and export TOU schedules** | ||
| - **Optional incentives:** ZEROHERO ($1/day credit), Super Export (15c/kWh), Critical Peak, free power windows | ||
| - **Daily supply charge** varies per plan | ||
|
|
||
| ## Integration Structure | ||
|
|
||
| ``` | ||
| custom_components/energy_compare/ | ||
| ├── __init__.py | ||
| ├── manifest.json | ||
| ├── config_flow.py # Amber API key + GloBird tariff builder | ||
| ├── sensor.py # Cost calculation sensors | ||
| ├── const.py | ||
| ├── strings.json | ||
| └── translations/ | ||
| └── en.json | ||
| ``` | ||
|
|
||
| ## Code Conventions | ||
|
|
||
| - Follow Home Assistant integration development guidelines | ||
| - Use `async`/`await` for all I/O operations | ||
| - Config flow must validate Amber API key on entry | ||
| - All sensor calculations use HA's energy sensors as source data | ||
| - Support HACS installation via custom repository | ||
|
|
||
| ## AEGIS-Derived Rules | ||
|
|
||
| _Generated from AEGIS diagnostic audit (2026-04-16). Review invalidation conditions before removing._ | ||
|
|
||
| ### Secrets | ||
|
|
||
| - NEVER hardcode tokens, API keys, or credentials in any file — use HA config entry storage | ||
| - NEVER commit files containing JWTs or Bearer tokens — run `gitleaks detect` before every push | ||
| - The `energy-dashboard.html` at repo root is DELETED — do not recreate | ||
|
|
||
| ### Dashboard | ||
|
|
||
| - The canonical dashboard is `custom_components/pricehawk/www/dashboard.html` — there is no repo-root copy | ||
| - Dashboard entity IDs MUST use the `pricehawk_` prefix matching sensor.py | ||
| - Dashboard MUST use `location.protocol` for WebSocket URL detection, never hardcode ws:// | ||
| - Dashboard MUST read token from URL params or postMessage, never hardcode | ||
|
|
||
| ### CI/CD | ||
|
|
||
| - NEVER interpolate `${{ }}` directly in `run:` blocks — use `env:` intermediate variables | ||
| - NEVER use `permissions: write-all` — specify minimum required permissions per job | ||
|
|
||
| ### Testing | ||
|
|
||
| - Config flow changes require corresponding test updates in test_config_flow.py | ||
| - Tariff rate calculation changes require edge case tests (negative rates, midnight boundaries, empty windows) | ||
|
|
||
| ### State Persistence | ||
|
|
||
| - State restore MUST validate storage version before loading | ||
| - `from_dict()` methods MUST receive an explicit HA-timezone date — no `date.today()` fallback | ||
|
|
||
| ## graphify | ||
|
|
||
| This project has a graphify knowledge graph at graphify-out/. | ||
|
|
||
| Rules: | ||
| - Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure | ||
| - If graphify-out/wiki/index.md exists, navigate it instead of reading raw files | ||
| - After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost) |
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.