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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [1.6.0-beta.8] - 2026-05-24

Recovery service for the beta.7 cost-math fix. The beta.7 AEMO RRP-parser fix corrected the rate going forward, but the DWT provider's `_import_cost_today_c` accumulator carried the inflated value built up under the bug (the user's screenshot still showed `current_plan_cost_today=$66.78` on a real spend < $2). Adds a manual reset so users don't have to wait until midnight rollover.

### Added

- **`pricehawk.reset_today` service.** Zeros every registered provider's daily accumulators (`import_kwh_today`, `import_cost_today_c`, `export_kwh_today`, `export_earnings_today_c`) on every PriceHawk entry and persists the cleared state to the JSON Store. Use after any cost-math fix lands mid-day. Takes no parameters; resets all entries. Replaces the prior "wait until midnight" workaround. (`__init__.py:264-294`, `services.yaml`)

## [1.6.0-beta.7] - 2026-05-24

🔴 **Critical cost-math bug fix.** AEMO dispatch parser was reading the wrong CSV row type — surfaced by live UAT 2026-05-24 when the user noticed today_cost claiming ~$66 on real consumption of ~12 kWh.
Expand Down
33 changes: 33 additions & 0 deletions custom_components/pricehawk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,44 @@ async def handle_rank_alternatives(call: object) -> None:
len(result),
)

async def handle_reset_today(call: object) -> None:
"""Zero every registered provider's daily accumulators NOW.

Use case: after a code-level cost-math bugfix lands mid-day, the
DWT provider's ``_import_cost_today_c`` still carries inflated
values accumulated under the prior bug. Without this service the
user has to wait until the midnight rollover to see clean data.
Live UAT 2026-05-24 — added after the AEMO RRP-row-type fix
(beta.7) left ``current_plan_cost_today=$66.78`` stuck on a real
spend of <$2.
"""
del call # Service takes no parameters; resets every entry.
for entry in hass.config_entries.async_entries(DOMAIN):
data: PriceHawkData | None = getattr(entry, "runtime_data", None)
if data is None:
continue
coord = data.coordinator
for provider in coord._providers.values():
try:
provider.reset_daily()
except Exception as exc: # noqa: BLE001 — never sink the batch
_LOGGER.warning(
"reset_today: %s.reset_daily failed: %s",
getattr(provider, "id", "?"), exc,
)
await coord.async_persist_state()
_LOGGER.info(
"reset_today: zeroed daily accumulators for %d provider(s) "
"on entry %s",
len(coord._providers), entry.entry_id,
)
Comment on lines +275 to +294

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other service handlers in this integration and to adhere to the 'action-exceptions' rule (part of the Silver quality scale requirements), this handler should raise a HomeAssistantError if no active PriceHawk entries are found. This provides better feedback to the user if the service is called before the integration is fully set up or if it has been unloaded.

        del call  # Service takes no parameters; resets every entry.
        entries = [
            e for e in hass.config_entries.async_entries(DOMAIN)
            if getattr(e, "runtime_data", None) is not None
        ]
        if not entries:
            raise HomeAssistantError(
                "No PriceHawk entry is currently loaded — service call cannot "
                "be routed. Add an entry via Settings → Devices & Services."
            )

        for entry in entries:
            coord = entry.runtime_data.coordinator
            for provider in coord._providers.values():
                try:
                    provider.reset_daily()
                except Exception as exc:  # noqa: BLE001 — never sink the batch
                    _LOGGER.warning(
                        "reset_today: %s.reset_daily failed: %s",
                        getattr(provider, "id", "?"), exc,
                    )
            await coord.async_persist_state()
            _LOGGER.info(
                "reset_today: zeroed daily accumulators for %d provider(s) "
                "on entry %s",
                len(coord._providers), entry.entry_id,
            )
References
  1. Service handlers should follow the 'action-exceptions' discipline as required by the integration's Silver quality scale checklist, raising HomeAssistantError or ServiceValidationError on failure or invalid state.


hass.services.async_register(DOMAIN, "analyze_csv", handle_analyze_csv)
hass.services.async_register(DOMAIN, "backfill_history", handle_backfill)
hass.services.async_register(
DOMAIN, "rank_alternatives", handle_rank_alternatives
)
hass.services.async_register(DOMAIN, "reset_today", handle_reset_today)


async def async_unload_entry(hass: HomeAssistant, entry: PriceHawkConfigEntry) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/pricehawk/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"issue_tracker": "https://github.com/Artic0din/ha-pricehawk/issues",
"quality_scale": "silver",
"requirements": ["openelectricity>=0.10.1,<0.11"],
"version": "1.6.0-beta.7"
"version": "1.6.0-beta.8"
}
8 changes: 8 additions & 0 deletions custom_components/pricehawk/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ backfill_history:
max: 90
mode: slider

reset_today:
name: Reset Today's Cost
description: >-
Zero every provider's daily accumulators (import_kwh_today,
import_cost_today_c, export_*_today, etc.) on every PriceHawk entry,
then persist state. Use after a code-level cost fix lands mid-day to
clear residual values without waiting for midnight rollover.

rank_alternatives:
name: Rank Alternative Plans
description: >-
Expand Down
4 changes: 2 additions & 2 deletions tests/test_silver_checklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ def test_version_bumped(self):
# Track the in-flight beta line. Bump this assertion when cutting
# a new HACS-beta release. Hard-coded so a stray manifest edit
# can't silently break HACS version pinning.
assert m["version"] == "1.6.0-beta.7", (
f"manifest version should be 1.6.0-beta.7, got {m['version']}"
assert m["version"] == "1.6.0-beta.8", (
f"manifest version should be 1.6.0-beta.8, got {m['version']}"
)

def test_codeowner_present(self):
Expand Down
Loading