Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/validate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ jobs:
- uses: actions/checkout@v4
- name: Hassfest validation
uses: home-assistant/actions/hassfest@master

# Phase 11 PR-17 — HACS validation on every PR.
# Confirms the integration meets HACS's distribution requirements:
# manifest schema (incl. quality_scale: silver from Phase 8 PR-9),
# repository structure, brands entry, version bumps.
validate-hacs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: HACS validation
uses: "hacs/action@main"
with:
category: "integration"
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Fixed

- **Stack-wide regressions caught by `codex review`.** Five functional bugs spanning the Phase 7 / Phase 8 PRs:
- **DWT entry creation failed at first refresh.** The `dashboard_token` entry builder did not copy `CONF_DWT_OE_*` / `CONF_DWT_AEMO_*` / `CONF_DWT_REGION` from the in-progress flow `self._data` into the final entry's `data` + `options`. `_build_dwt_provider()` then raised `ConfigEntryNotReady` (AC-10c) on every new DWT install. (Codex P1, config_flow.py `async_step_dashboard_token`)
- **`static_prd` exposed without a stored static plan.** The Comparators options form rendered all of `ALL_PRICING_MODES` for every comparator, but no flow writes `CONF_AMBER_STATIC_PLAN` / `CONF_LOCALVOLTS_STATIC_PLAN`. Selecting `static_prd` bricked the next reload with `ConfigEntryNotReady`. The form now gates `static_prd` visibility per comparator on whether a static plan is stored. (Codex P2#2, config_flow.py `async_step_comparators`)
- **Reauth dispatcher could not route during startup.** The dispatcher read `entry.runtime_data.coordinator._reauth_provider_id`, but `runtime_data` is only assigned after `async_config_entry_first_refresh()` completes. Auth failures during startup or the first refresh (common after HA restart with an expired Amber / LocalVolts / OpenElectricity key) therefore got `provider_id = None` and aborted with `reauth_provider_unknown`. The dispatcher now falls back to `entry.data[CONF_CURRENT_PROVIDER]` when the coordinator tag is absent. (Codex P2#3, config_flow.py `async_step_reauth`)
- **Reconfigure unreachable for CDR-backed Amber/LV entries.** `_current_plan_provider.id` is `{brand}_{plan_id}` (e.g. `amber_brokerage-xyz`) for CDR users — the install base — never the literal `PROVIDER_AMBER` / `PROVIDER_LOCALVOLTS` slug. Routing on it sent every CDR user to `reconfigure_unsupported`. Dispatcher now reads `entry.data[CONF_CURRENT_PROVIDER]`. (Codex P2#4, config_flow.py `async_step_reconfigure`)
- **Amber schedule endpoint polled every 30s on static/off entries.** `_maybe_poll_amber()` returns early without updating `_last_amber_poll` when Amber mode is static/off, leaving it at `0.0` forever. `_async_update_data()` then re-triggered `_fetch_today_price_schedule()` every coordinator tick on the `_last_amber_poll == 0.0` first-run sentinel — hammering Amber's API with stale or missing credentials for DWT and static-Amber users. Guard now combines the sentinel check with `self._amber_mode == PRICING_MODE_LIVE_API`. (Codex P2#5, coordinator.py `_async_update_data`)

13 regression tests added in `tests/test_codex_regression_fixes.py` (source-level + behavioural where the conftest stubs allow). `tests/test_reconfigure.py` updated for the new dispatcher contract.

### Added

- Hypothesis property-based tests of `tariff_engine` pure functions. Five invariants per v2 research § 7.3: (1) `calc_stepped_cost` is monotonic-non-decreasing in kWh; (2) at threshold it equals `threshold * step1_rate` exactly; (3) above threshold it composes as `step1_cost + (k - threshold) * step2_rate`; (4) `get_stepped_import_rate` returns exactly one of `step1_rate` / `step2_rate`; (5) `get_current_tou_period` returns a known period name or `"unknown"`, with rate matching the period. 9 Hypothesis test classes; ≥200 fuzzed examples per invariant. Final plank toward v3.0 GA. (Phase 11 / PR-18)

- HACS validation job in CI. The existing `Validation` workflow now also runs `hacs/action@main` with `category: integration` on every push + PR. Hassfest job stays as-is — both validators run side-by-side. Catches HACS distribution issues (manifest schema drift, brands gaps, version bump misses) before merge. (Phase 11 / PR-17)

- HA test-harness fixture prototypes (`tests/ha_fixtures.py`). Drop-in mocks for `OpenElectricityPriceSource`, `NEMWebPriceSource`, `async_add_external_statistics`, plus a `mock_config_entry_data` factory for DWT-OE entries. NOT auto-applied — the existing 1028 stub-conftest tests stay HA-free per D-P11-1 (dual-mode test strategy). New tests opt in by importing. `pytest-homeassistant-custom-component>=0.13.0` + `hypothesis>=6.100.0` added to `requirements.txt` for the new harness + Hypothesis fuzzing tests. 10 smoke tests cover the fixture shapes. (Phase 11 / PR-16)

- Blueprints library. Five HA automation blueprints under `custom_components/pricehawk/blueprints/automation/pricehawk/`: `cheapest_plan_alert.yaml` (notify when a retailer would have saved > threshold over 7d), `cheapest_30min_window.yaml` (trigger flexible loads at the lowest-price window), `pause_ev_on_spike.yaml` (suspend EV charger above threshold; hysteresis-aware), `daily_7pm_summary.yaml` (daily cost + savings + best-provider notification), `wholesale_spike_alert.yaml` (early warning when spot price crosses threshold). Users import via the HA "Blueprints" UI with the file URL or by dropping into `<config>/blueprints/automation/pricehawk/`. Ninth and final plank toward v3.0 GA. (Phase 10 / PR-15)

- Lovelace custom card `pricehawk-cost-card`. Compact card showing today's chosen-plan cost + optional savings line. Auto-registered as a Lovelace resource on entry setup — appears in the "Add Card" picker, no manual "Resources" step required. Best-effort: storage-mode Lovelace gets the auto-register; YAML-mode users see a log-line hint with the resource URL. Eighth plank toward v3.0 GA. (Phase 10 / PR-14)

- Lit `panel_custom` foundation for the v2 panel. New sidebar entry "PriceHawk v2" at `/pricehawk` registered via HA's `panel_custom` mechanism — auth flows through the host page's WebSocket session, no LLAT in URL (contract per v2 research § Wave 4). The `pricehawk-panel.js` ESM module imports Lit from the unpkg CDN (no build step). Initial content surfaces the Phase 9 PR-11 `sensor.pricehawk_today_cost` + savings + best provider; full UI port from the legacy iframe dashboard deferred to a dedicated Playwright UAT follow-up. The legacy iframe panel at `/pricehawk-dashboard` continues to work during the migration. Module URL carries a `?v={manifest}.{epoch}` cache buster so HACS upgrades invalidate the browser cache cleanly. Seventh plank toward v3.0 GA. (Phase 10 / PR-13)

- Energy-Dashboard-pickable chosen-plan cost sensor (`sensor.pricehawk_today_cost`). `device_class=MONETARY` + `unit_of_measurement="AUD"` + `state_class=TOTAL` + `last_reset` at midnight together qualify it for HA's Energy Dashboard cost picker. The `unique_id` is provider-INDEPENDENT (`{entry_id}_chosen_plan_today_cost`) so the entity id stays stable across plan swaps — the user's dashboard pick survives migrations between CDR plans and DWT entries. Sixth plank toward v3.0 GA. (Phase 9 / PR-11)

- External statistics dual-write. The coordinator now writes daily provider costs to BOTH the existing JSON Store AND HA's external statistics on every midnight rollover. One-shot backfill on first setup converts the existing `daily_cost_history` into stats entries (one per provider, batched). Each stat carries `unit_of_measurement="AUD"` + `has_sum=True` with a monotonic cumulative sum so the Energy Dashboard can pick it up as a cost source (PR-11 / 09-02). Negative-cost days (export-heavy with high FiT) produce a small dip in the cumulative sum — HA tolerates this for cost-style stats per docs. JSON Store remains the source of truth until PR-12 / 09-03 (stats-only flip; gated on ≥4w + ≥10 tester reports per ROADMAP). Fifth plank toward v3.0 GA. (Phase 9 / PR-10)

- HACS Silver compliance tickbox. `manifest.json` declares `quality_scale: "silver"`. New `quality_scale.yaml` documents every Bronze/Silver/Gold/Platinum rule's status (done / exempt / todo) — honest tickbox. Sensor platform declares `PARALLEL_UPDATES = 0` (CoordinatorEntity-backed → unlimited concurrent reads safe). Service handlers (`analyze_csv`, `backfill_history`, `rank_alternatives`) now raise `HomeAssistantError` on missing coordinator and `ServiceValidationError` on malformed input (was: warn + default-fallback). Version bumped to `1.6.0-beta.1`. Closes Phase 8 (Wave 2). (Phase 8 / PR-9)

- Repairs platform (persistent notifications). PriceHawk now raises HA issue-registry entries when the integration is in a degraded state: `grid_sensor_unavailable` after 10 consecutive None reads (5 min @ 30s coordinator interval) of the configured grid power sensor; `ranking_stale` when the nightly CDR plan ranking job hasn't completed in over 36 hours. Each issue auto-clears on recovery. Multi-entry safe: issue ids prefixed with the entry_id so two PriceHawk entries don't collide on the same issue. Fourth plank of HACS Silver compliance. (Phase 8 / PR-8)

- Diagnostics platform. The "Download diagnostics" button on the integration page now returns a JSON snapshot of entry data + options + selected coordinator runtime state. Every API key (Amber, OpenElectricity, LocalVolts) and HA token field is replaced with `**REDACTED**` via `async_redact_data`. The CDR plan envelope + per-comparator static-PRD envelopes are also redacted — not for secrecy but to keep the diagnostics output to a usable size (~15 KB per plan envelope adds up fast). A `_redaction_count` integer in the output gives reviewers immediate confidence the redaction list is hitting the targets. Third plank of HACS Silver compliance. (Phase 8 / PR-7)

- Per-provider reconfigure flow. HA 2024.10+ "Reconfigure" button now opens a per-provider settings page that lets users adjust Amber network/subscription fees, LocalVolts daily supply / buy ceiling / sell floor guard rails, or DWT (OE/AEMO) daily supply charge — without losing accumulated cost history. Narrow scope: credential rotation stays in PR-5 reauth; region swap is deferred (PriceHawk's entry unique_id is region-derived from Phase 7, swapping would invalidate the unique_id contract). Unsupported entry types (CDR-plan entries) abort cleanly with a clear message pointing at the Configure menu. Second plank of HACS Silver compliance. (Phase 8 / PR-6)
Expand Down
Loading
Loading