Skip to content

fix(home-load): timezone-aware history, additive backfill and retrain feedback (#42)#43

Open
markoceri wants to merge 5 commits into
edge-mining:mainfrom
markoceri:fix/issue-42-home-load-history-tz-backfill
Open

fix(home-load): timezone-aware history, additive backfill and retrain feedback (#42)#43
markoceri wants to merge 5 commits into
edge-mining:mainfrom
markoceri:fix/issue-42-home-load-history-tz-backfill

Conversation

@markoceri

Copy link
Copy Markdown
Collaborator

Closes #42

Summary

Fixes the home load history timezone bug and makes manual collection actually backfill, then adds a user-driven forecast retrain with clear outcome feedback.

Fixed — timezone-aware history datetimes

The optimization loop built its 24h look-back window with a naive datetime.now(), which clashed with the timezone-aware (UTC) timestamps from Home Assistant and the persistence layer, raising "can't compare offset-naive and offset-aware datetimes". The look-back window, the merged-consumption timestamp and the history purge cut-off now use UTC-aware timestamps consistently.

Added — additive backfill on manual collection

A manual per-device collection previously ignored lookback_hours whenever data already existed (it only ingested incrementally from the last stored point), so requesting e.g. 30 days returned just the few most recent points and never filled internal gaps. It now re-fetches the whole requested window from the provider and merges it into the store, de-duplicated by the (device_id, timestamp) primary key, without dropping existing data (bounded by Home Assistant's recorder retention). EnergyLoadHistoryProviderPort.get_power_points gains a force_refresh flag; the scheduled collection stays incremental.

Added — forecast retrain after collection, with outcome feedback

After a manual collection the device history modal prompts the user to retrain that device's forecast model. train_device now returns a LoadTrainingResult value object reporting whether a model was actually trained (best adapter, MAE, samples), skipped (with reason, e.g. insufficient history) or failed, instead of always reporting a generic "completed". The UI shows the outcome in a status toast (teleported above the modal).

Testing

  • Backend: pytest for the training, history and API endpoint suites — all green.
  • Frontend: vue-tsc --noEmit — no type errors.

…manual collection

The load-history pipeline mixed naive and timezone-aware datetimes, raising
"can't compare offset-naive and offset-aware datetimes" while building the
per-device consumption in the optimization loop. The 24h look-back window, the
merged-consumption timestamp and the purge cut-off now use UTC-aware
timestamps consistently.

Manual collection also ignored `lookback_hours` whenever data already existed:
it only fetched incrementally from the last stored point, so requesting e.g.
30 days returned just the few most recent points and never filled internal
gaps. A manual `collect_devices` call now performs an additive full-window
backfill -- `get_power_points` gains a `force_refresh` flag that re-fetches the
whole window from Home Assistant and merges it into the store (de-duplicated by
the (device_id, timestamp) primary key) without dropping existing data. The
scheduled `collect_all` stays incremental.

- optimization_service: UTC-aware look-back window and consumption timestamp
- home_load_history_service: UTC-aware purge cut-off; force_full_window backfill
- history provider port + HA/dummy adapters: force_refresh on get_power_points
After a manual per-device history collection completes, prompt the user to
retrain that device's forecast model with the freshly collected data. The
prompt only appears when the device has a forecast provider configured; on
confirmation it triggers per-device training and refreshes the forecast panel.
A small toast reports progress and surfaces training errors.

Wires a new trainDevice call through the home-loads service and store onto the
existing training-trigger endpoint. Scheduled nightly training is unchanged.
…he UI

train_device now returns a LoadTrainingResult value object describing the
outcome -- trained (with best adapter, MAE and sample count), skipped (with the
reason, e.g. insufficient history) or failed -- instead of returning nothing.
The training endpoint surfaces it, so a real retraining can be told apart from
a silent skip; previously it always reported "completed".

The device history modal shows the outcome in a status toast (success/warning/
error) and only refreshes the forecast when a model was actually (re)trained.
The toast is teleported to <body> with a high z-index so it stays visible above
the open History & Forecast modal instead of behind it.
… feedback

Introduces a top-level CHANGELOG.md (same format as core/CHANGELOG.md) and
records, under Unreleased, the timezone fix, the additive history backfill on
manual collection, the forecast retrain prompt and the training-outcome
reporting.
@markoceri markoceri self-assigned this Jun 28, 2026
@markoceri markoceri added the bug Something isn't working label Jun 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses Home Loads history correctness and UX by standardizing UTC-aware timestamps, making manual history collection perform additive backfill, and adding a user-triggered per-device forecast retrain that reports concrete outcomes to the UI.

Changes:

  • Fix naive-vs-aware datetime comparisons by using UTC-aware timestamps in the optimization loop and history purge cutoff.
  • Add additive backfill behavior for manual device history collection via a force_refresh/force_full_window path.
  • Add per-device model retrain trigger returning a structured outcome (LoadTrainingResult), surfaced through the API and displayed in the device history modal.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/src/core/stores/homeLoadsProfileStore.ts Exposes a trainDevice store action for the UI to trigger retraining.
frontend/src/core/services/homeLoadsProfileService.ts Adds client call to the per-device training trigger endpoint.
frontend/src/components/homeLoads/LoadDeviceHistoryModal.vue Prompts to retrain after manual collection and shows retrain outcome via a toast.
core/tests/unit/adapters/domain/home_load/test_home_load_api_endpoints.py Updates/extends endpoint tests for new training outcome semantics.
core/edge_mining/domain/home_load/value_objects.py Introduces LoadTrainingResult for structured training outcomes.
core/edge_mining/domain/home_load/ports.py Extends history provider port with force_refresh for backfill support.
core/edge_mining/application/services/optimization_service.py Uses UTC-aware timestamps for the 24h lookback window and merged-consumption timestamp.
core/edge_mining/application/services/load_forecast_training_service.py Makes train_device return LoadTrainingResult with trained/skipped/failed outcomes.
core/edge_mining/application/services/home_load_history_service.py Implements additive backfill behavior for manual collection and fixes UTC cutoff in purge.
core/edge_mining/application/interfaces.py Updates interface signatures for collect_devices and train_device to match new behavior/return type.
core/edge_mining/adapters/domain/home_load/history_providers/home_assistant_api_history.py Adds force_refresh path to refetch entire window from Home Assistant for backfill.
core/edge_mining/adapters/domain/home_load/history_providers/dummy.py Updates dummy provider signature/docs to accept force_refresh.
core/edge_mining/adapters/domain/home_load/fast_api/router.py Updates training trigger endpoint to return detailed retrain outcomes.
CHANGELOG.md Documents the fixes/additions (timezone handling, backfill, retrain outcome reporting).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +138 to +142
if force_refresh:
fetched = await self._fetch_from_home_assistant(start, end)
if fetched:
self._history_repo.add_power_points(self.device_id, fetched)
return self._history_repo.get_power_points(self.device_id, start, end)
Comment on lines +929 to +933
if result.status == "trained" and result.best_adapter is not None:
detail = (
f"Model retrained for '{result.device_name}': best={result.best_adapter.value} "
f"MAE={result.best_mae:.1f} ({result.samples_used} samples)."
)
@markoceri markoceri changed the title fix(home-load): timezone-aware history, additive backfill and retrain feedback fix(home-load): timezone-aware history, additive backfill and retrain feedback (#42) Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Home load history: timezone-aware datetimes and additive backfill on manual collection

2 participants