Fix ML predictions missing from report page#36
Closed
pookey wants to merge 92 commits intojohanzander:mainfrom
Closed
Fix ML predictions missing from report page#36pookey wants to merge 92 commits intojohanzander:mainfrom
pookey wants to merge 92 commits intojohanzander:mainfrom
Conversation
Extend the price system to support Octopus Energy alongside Nordpool.
Octopus provides separate import/export rates at 30-minute resolution
via HA event entities, with VAT-inclusive GBP/kWh prices.
Key changes:
- Add period_duration_hours and get_sell_prices_for_date() to PriceSource
- Create OctopusEnergySource for fetching 48 half-hourly import/export rates
- Add price_provider config field ("nordpool", "nordpool_official", "octopus")
- Fix optimization path to use pre-calculated sell prices from price entries
- Pass period_duration_hours through to DP algorithm
- Generalize period count validation for different resolutions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Octopus Energy Agile tariff price support
Add Octopus Energy as a supported price source in README, Installation Guide, and User Guide. Include setup instructions for UK users with entity ID discovery, electricity_price adjustments, and GBP currency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update docs for Octopus Energy support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump version to 6.1.0
Fix get_tomorrow_prices() catching ValueError but not PriceDataUnavailableError, which meant the "return empty list" fallback never fired for Octopus Energy. Relax Octopus rate count validation to accept 46-48 rates instead of exactly 48. Octopus Energy publishes rates incrementally and the last couple of half-hours may arrive slightly later. 46 rates covers through 23:00, sufficient for optimization. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Octopus Energy Agile tariff price support
Promise.all rejected entirely when /api/dashboard returned HTTP 500 (which happens when no optimization schedule exists), discarding all 4 fetch results and leaving every state variable as null. Switched to Promise.allSettled so each fetch succeeds or fails independently. Working endpoints (inverter status, schedule, battery settings) now populate their data even when dashboard fails. Bump version to 6.1.2.
…d-failure Fix Inverter page blank when dashboard endpoint fails
The system uses 15-minute periods internally (96/day) but Octopus Energy returns 48 half-hourly prices. When _gather_optimization_data received period_count=48 from len(prices), it created 48-element arrays, but the current_period index (e.g. 90) is a 15-minute index — causing "list assignment index out of range" and preventing any optimization. Normalize non-quarterly prices to 15-minute resolution in _get_price_data() by repeating each entry to fill the corresponding 15-min slots. The DP algorithm now always runs with period_duration_hours=0.25. Bump version to 6.1.3.
Fix Octopus Energy optimization crash due to period index mismatch
InfluxDB 1.x stores the unit of measurement in _measurement and the entity ID in an entity_id tag, while 2.x stores the entity ID directly in _measurement. Query filters now match on either field. CSV response parsers detect column positions from the header row instead of using hardcoded indices, preventing silent data loss when columns appear in a different order. Bump version to 6.2.0.
Fix InfluxDB queries for dual 1.x/2.x support
…ual rates Two bugs caused incorrect Octopus Energy pricing: 1. PriceManager retained Swedish default parameters (markup=0.08, VAT=1.25, additional=1.03) because update_settings() updated the PriceSettings dataclass but never propagated values to the running PriceManager. Formula: (0.1558 + 0.08) * 1.25 + 1.03 = 1.3248 matched the displayed ~1.33. Fix: propagate all pricing fields and clear the price cache. 2. Octopus 30-min rates (48/day) were not expanded to 15-min quarterly resolution (96/day) at the source level. The previous BSM-level normalization duplicated price-entry dicts with wrong timestamps. Fix: expand in OctopusEnergySource._fetch_rates() so PriceManager generates correct 15-minute timestamps for all 96 periods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix Octopus Energy price display and period resolution
Promise.all rejected entirely when /api/dashboard returned HTTP 500 (which happens when no optimization schedule exists), discarding all 4 fetch results and leaving every state variable as null. Switched to Promise.allSettled so each fetch succeeds or fails independently. Working endpoints (inverter status, schedule, battery settings) now populate their data even when dashboard fails. Bump version to 6.1.2.
The system uses 15-minute periods internally (96/day) but Octopus Energy returns 48 half-hourly prices. When _gather_optimization_data received period_count=48 from len(prices), it created 48-element arrays, but the current_period index (e.g. 90) is a 15-minute index — causing "list assignment index out of range" and preventing any optimization. Normalize non-quarterly prices to 15-minute resolution in _get_price_data() by repeating each entry to fill the corresponding 15-min slots. The DP algorithm now always runs with period_duration_hours=0.25. Bump version to 6.1.3.
InfluxDB 1.x stores the unit of measurement in _measurement and the entity ID in an entity_id tag, while 2.x stores the entity ID directly in _measurement. Query filters now match on either field. CSV response parsers detect column positions from the header row instead of using hardcoded indices, preventing silent data loss when columns appear in a different order. Bump version to 6.2.0.
# Conflicts: # CHANGELOG.md # core/bess/battery_system_manager.py
InfluxDB 1.x stores entity_id without the "sensor." domain prefix (e.g. "urq2eyf00c_..." not "sensor.urq2eyf00c_..."), with the domain in a separate tag. The 6.2.0 filter was querying entity_id with the prefix, which matched zero rows. - Remove "sensor." prefix from entity_id filter in both get_sensor_data and get_sensor_data_batch queries - Update _extract_sensor_name() to normalize unprefixed 1.x entity_id values by prepending "sensor." for downstream consumers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix InfluxDB 1.x entity_id filter returning zero rows
SensorCollector, EnergyFlowCalculator, and HistoricalDataStore were initialized with the default 30 kWh capacity but update_settings() only updated BatterySettings without propagating the configured 10 kWh to these components. SOE was stored at 3x the correct value and then divided by the real capacity at display time, inflating SOC (e.g. 56% became 168%). Capacity is now propagated to all dependent components when battery settings are updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix battery SOC showing >100% due to capacity mismatch
Dashboard energy flow chart and Savings page lost all history on restart because HistoricalDataStore kept records only in memory. Added JSON file persistence to /config/bess_historical_data.json following the same pattern used by ScheduleStore: save after each record_period() call, load on init if data is from today, discard stale data. Deserialization filters init=False fields so EnergyData and EconomicData __post_init__ methods recalculate derived values correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Persist historical data across restarts
HA add-ons don't have /config mounted as persistent storage without a map directive. Both HistoricalDataStore and ScheduleStore were writing to /config which is an ephemeral container layer. Changed to /data which is always available as persistent add-on storage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When tomorrow's Octopus prices aren't yet published, the optimizer falls back to a terminal value to prevent dumping battery charge at end of day. Previously this used the average of all remaining buy prices, which included tonight's expensive peak periods and inflated the terminal value enough to prevent discharge at those same peaks - a circular and self-defeating calculation. Now uses the last known non-zero price in the horizon (typically an overnight off-peak rate), which is a much better proxy for what overnight recharging would cost tomorrow. Co-authored-by: Ian P. Christian <ian.christian@zoopla.co.uk>
XGBoost-based model predicts 24h energy consumption at 15-minute resolution. Integrates with the battery optimizer via `consumption_strategy: ml_prediction` setting, with daily retrain at 23:00 and cached predictions. - ml/ module: CLI tools (train, predict, evaluate, report), feature engineering, InfluxDB data fetching, configurable derived features - Backend: /api/ml-report endpoint serving model metrics, predictions, and feature importance from a training sidecar file - Frontend: ML Report tab with forecast chart, metrics comparison table, and feature importance visualization - Config: ML settings nested under options.ml in config.yaml; model_path is a hardcoded default, not user-configurable - ml_config.yaml removed from tracking (dev-only convenience file) Co-authored-by: Ian P. Christian <ian.christian@zoopla.co.uk> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
XGBoost-based model predicts 24h energy consumption at 15-minute resolution. Integrates with the battery optimizer via `consumption_strategy: ml_prediction` setting, with daily retrain at 23:00 and cached predictions. - ml/ module: CLI tools (train, predict, evaluate, report), feature engineering, InfluxDB data fetching, configurable derived features - Backend: /api/ml-report endpoint serving model metrics, predictions, and feature importance from a training sidecar file - Frontend: ML Report tab with forecast chart, metrics comparison table, and feature importance visualization - Config: ML settings nested under options.ml in config.yaml; model_path is a hardcoded default, not user-configurable - ml_config.yaml removed from tracking (dev-only convenience file) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… image The v7.0.0 release failed to build because the Dockerfile was missing COPY ml/ and the Alpine build dependencies (g++, cmake, make, libgomp) needed to compile xgboost from source on musl. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alpine (musl) has no prebuilt wheels for xgboost, scikit-learn, or pandas, forcing compilation from source on every install (~15+ min). Debian bookworm uses glibc so manylinux wheels install in under a minute. Also fixes the xgboost build failure caused by missing linux/mempolicy.h on Alpine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ml schema section had pv_power, import_power, etc. that belong under sensors, not ml. HA schema validation rejected user configs missing these non-existent ML options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Thread currency from config through the optimization pipeline so economic chain explanations, log messages, and UI displays use the configured currency instead of hardcoded "SEK". Key changes: - Add currency parameter to generate_economic_chain(), create_decision_data(), _calculate_reward(), _run_dynamic_programming(), and optimize_battery_schedule() - battery_system_manager passes home_settings.currency to optimizer - Frontend derives currency from API data instead of hardcoding 'SEK' - Neutralize Swedish-specific comments (öre, överföringsavgift, etc.) - Rename BATTERY_CHARGE_CYCLE_COST_SEK to BATTERY_CHARGE_CYCLE_COST - Widen ElectricitySettings.area type from 'SE1'|'SE2'|'SE3'|'SE4' to string - Clean up config.yaml comments to be locale-neutral Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The power sensor fields (pv_power, local_load_power, import_power, export_power, output_power, self_power, system_power) were in the ml schema section instead of the sensors section. When removed from ml in v7.0.3, they weren't added back to sensors. HA strips options not in the schema, so all power sensor values were None at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…strategy ML Report now shows the weekly average (influxdb_profile) as a third line on the forecast chart alongside ML predictions and yesterday's actual. The report page is also accessible when consumption_strategy is set to influxdb_profile. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the ML model only trained when consumption_strategy was ml_prediction. Now it trains whenever the ml config section is present, so the ML Report page has data even when using influxdb_profile or other strategies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In the HA add-on, InfluxDB credentials live in options.json, not environment variables. The ML config builder now checks env vars first (dev) and falls back to the influxdb section in app_options (production). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ML forecast cache was only populated when consumption_strategy was ml_prediction. The report endpoint now generates predictions on demand when the cache is empty, so the ML line always appears on the chart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: Fill overnight energy data gaps using power (W) sensors when cumulative kWh sensors show zero due to 0.1 kWh reporting resolution. Averages instantaneous power readings per 15-min period and converts to kWh, giving continuous data even at low overnight consumption. Frontend: Fix tomorrow data causing charts to render 3x wider than expected. The API returns tomorrow periods numbered 96-183 but the charts were using these raw numbers for x-axis positioning. Normalize to 0-95 before offsetting by 24h. Also fixes tooltip showing times like "28:00" instead of "04:00" in both EnergyFlowChart and BatteryLevelChart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix overnight chart gaps and tomorrow data rendering
Remove hardcoded SEK currency and Swedish locale references
- Guard missing 'ml' section in load_config() with descriptive KeyError - Validate strategy-config compatibility on startup, fall back to 'fixed' - Generate ML predictions proactively after training (boot + daily 23:00) - Remove on-demand prediction generation from /api/ml-report endpoint - Wrap ML boot block in try/except so training failures don't crash system - Bump version to 7.0.9 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make ML config optional & generate predictions proactively
The ML config builder used HA_TOKEN for the HA API token, but the HA supervisor sets HASSIO_TOKEN. Weather forecast calls failed with 401 auth errors, leaving _ml_forecast_cache as None. Now checks HASSIO_TOKEN first (matching app.py controller pattern), falling back to HA_TOKEN for local development. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The JSON file persistence (_save_to_disk/_load_from_disk) was added as a workaround while InfluxDB 1.x/2.x data-fetching bugs were being diagnosed. Those bugs have since been fixed (header-aware CSV parsing, dual-filter queries, batch parser prefix mismatch). On every startup, _fetch_and_initialize_historical_data() rebuilds all periods from InfluxDB, overwriting any persisted data — making the JSON file redundant. Strategic intent persistence (ScheduleStore) is unaffected. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove redundant disk persistence from HistoricalDataStore
The "last known price" approach was provider-specific — it worked for Octopus Agile by accident (last price before the gap is off-peak) but for Nordpool the last price is hour 23 which could be anything. The median is outlier-resistant, works across all price providers, and correctly handles negative prices (which the p > 0 filter skipped). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use median price for terminal value calculation
…r last period After a restart, the last completed period was collected via live sensors (runtime path) instead of InfluxDB. Live sensor values at e.g. 21:28 include energy from the in-progress period (21:15-21:30), inflating the last completed period and leaving the next period nearly empty on the chart. Fix: when _last_readings cache is None (startup/restart), always use InfluxDB which has correct 15-minute boundary data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Read timezone from Home Assistant instead of hardcoding Europe/Stockholm
HA already knows the timezone via /api/config, so read it at startup
instead of forcing users to configure it or hardcoding a value.
- Add get_ha_config() to HomeAssistantAPIController
- Add set_timezone() to time_utils, called during BESSController init
- Convert all TIMEZONE imports to attribute access (time_utils.TIMEZONE)
so modules see the updated value after set_timezone() runs
- Replace hardcoded ZoneInfo("Europe/Stockholm") in influxdb_helper
- Make docker-compose TZ a per-developer setting via .env
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Bump version to 7.1.0 and update CHANGELOG
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Ian P. Christian <ian.christian@zoopla.co.uk>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The 23:00 cron job generated ML predictions but the 23:55 next-day preparation cleared them without regenerating. Now the 23:00 job only retrains the model, and the 23:55 preparation generates predictions after clearing the cache, so they survive into the new day. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d5f0c69 to
103f05c
Compare
Contributor
Author
|
Fix folded into PR #34 (ml-prediction branch). |
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
Test plan
🤖 Generated with Claude Code