Conversation
…urce Implement auxiliary Akkudoktor stock price fetching when using fixed_24h prices with negative_price_switch enabled. This allows feed-in prices to be set to 0 during negative stock price periods, while maintaining user-provided fixed_24h prices for grid cost calculations. Fixes #228 - Fester Stromtarif und Einspeisevergütung=0 bei negativem Strompreis Co-authored-by: Copilot <copilot@github.com>
Files changed: M src/version.py
…inks - Add startup timestamp filtering to /logs/alerts endpoint - Implement alert deduplication and severity sorting in startup panel - Add metadata markers (Config link, ACTION REQUIRED) to error sources - Create InterfaceFactory for centralized interface creation with error handling - Create StartupValidator facade for startup error registration - Document architecture in developer guide and copilot instructions Enables users to see only startup-relevant errors with direct config fix links. Improve Error Handling / Enable User to troubleshoot Fixes #251
Files changed: M src/version.py
- Price interface: add Config metadata to Tibber fetch errors - Battery interface: add Config metadata to state update errors - Properly handle NoneType errors in Tibber price responses Enables users to see data fetch failures in startup panel instead of HA logs. Fixes #251 - improves error visibility for troubleshooting.
Files changed: M src/version.py
Fix incorrect MQTT state display that showed "Avoid Discharge" instead of "Discharge Allowed" for states 4 & 5 (EVCC PV modes). Changes: - mqtt_interface.py: Corrected value_template for states 4 & 5 - State 4: "Avoid Discharge EVCC PV" → "Discharge Allowed EVCC PV" - State 5: "Avoid Discharge EVCC MIN+PV" → "Discharge Allowed EVCC MIN+PV" - mqtt_interface.py: Fixed command_template mapping to enable manual selection - State 4: -2 (Auto) → 4 (actual state) - State 5: -2 (Auto) → 5 (actual state) - mqtt_interface.py: Updated options list to match corrected labels - README_OLD.md: Updated documentation to reflect correct state descriptions Fixes: - HomeAssistant MQTT now displays correct state matching EOS Connect dashboard - Users can now manually select "Discharge Allowed EVCC" modes in HomeAssistant - Documentation is consistent with actual behavior - Internal discharge allowed logic was already correct; only display labels were wrong Verified: - base_control.py correctly identifies states 4 & 5 as discharge_allowed_modes - eos_connect.py correctly sends set_mode_allow_discharge() commands - Fronius inverter control unaffected; already working correctly
Implement dynamic battery export pricing with support for: - Fixed prices (user-configured) - Elpris DK spot prices (Denmark) - EPEX-Spot prices via Akkudoktor API Key Changes: - FeedInPriceInterface: 500-line multi-source manager with background updates - Standardized all prices to ct/kWh for user consistency - Schema: 5 new hot-reload-capable config fields - Web UI: Generalized subsection grouping (reusable for other sections) - Test suite: 26 tests covering all sources Breaking Change: Config key renamed (backward compatible) - Old: static_adder_oere → New: static_adder_ct_kwh Fixes #219
Files changed: M src/version.py
Allow users to disable SSL certificate verification for Home Assistant and OpenHAB connections with self-signed or private CA certificates. Adds config field, passes verify parameter to all HTTP requests, and logs security warnings. - New `data_source.ssl_ignore` config field (expert level) - LoadInterface respects setting in __request_with_retries() - Added tests for SSL verify behavior (all 22 existing tests pass)
Introduces a fully self-contained MILP optimizer (PuLP/CBC) as a new
`local_evopt` optimizer source, eliminating the need for an external
EOS or EVopt server for most home setups.
## New files
- `src/interfaces/optimization_backends/local_evopt/optimizer.py`
Bundled MILP engine derived from evcc-io/optimizer (MIT licence).
Solves the 2-day energy dispatch problem in-process via PuLP/CBC.
Supports charging strategies: charge_before_export,
maximize_self_consumption, attenuate_grid_peaks, discharge_before_import.
Supports discharging strategies: discharge_before_import,
emergency_reserve (end-of-horizon SOC floor).
- `src/interfaces/optimization_backends/optimization_backend_local_evopt.py`
Bridge between EOS request/response format and the MILP engine.
Extends EVOptBackend for format transformation; runs the solver in-process.
Truncates MILP horizon to valid future slots only, preventing stale
wrapped past-data (e.g. today's noon p_N=0) from corrupting results.
- `tests/interfaces/optimization_backends/test_optimization_backend_local_evopt.py`
560-line test suite covering request/response transformation, truncation
fix, strategy routing, infeasible fallback, and edge cases.
## Dependencies
- `requirements.txt`: added `pulp>=2.7.0`
## Config schema (`src/config_web/schema.py`)
- `eos.source`: added `local_evopt` choice; set as new default
- `eos.server` / `eos.port`: hidden unless source is eos_server or evopt
- New `local_evopt`-specific fields (all hot-reloadable or restart_required):
- `local_evopt_charging_strategy` (hot_reload)
- `local_evopt_discharging_strategy` (hot_reload)
- `local_evopt_emergency_reserve_pct` (hot_reload)
- `local_evopt_max_grid_import_w` (restart_required)
- `local_evopt_max_grid_export_w` (restart_required)
- `local_evopt_num_threads` (restart_required)
- `local_evopt_time_limit` (restart_required)
## Hot-reload (`src/config_web/hot_reload.py`)
- `_LOCAL_EVOPT_FIELD_MAP`: maps strategy keys to backend attributes
- `_apply_local_evopt()`: applies strategy changes to the running backend
- `_fire_run_trigger()`: generic helper that calls an optional `on_run_trigger`
callback after strategy changes; reusable for any future hot-reload handler
- `HotReloadAdapter`: new `on_run_trigger` parameter
## Immediate re-run on strategy change (`src/eos_connect.py`)
- `OptimizationScheduler._immediate_run_event`: threading.Event that
interrupts the inter-run sleep when set
- `OptimizationScheduler.request_immediate_run()`: public method, safe to
call from any thread
- `hot_reload_adapter.on_run_trigger = optimization_scheduler.request_immediate_run`
wired after both objects are created
## Bug fixes
- `config.js` / `wizard.js`: `_isDependencyHidden` / `_isDependencyMet` were
missing the plain-string case in the `depends_on` comparison; fields with
`depends_on: {"eos.source": "local_evopt"}` were always shown regardless of
the current source setting
- `maximize_self_consumption` strategy: previous implementation penalised
grid import (already zero during PV surplus); replaced with an export
penalty + charging reward proportional to 15% of the feed-in tariff,
lowering the charging break-even from ~9.8 ct/kWh to ~7.6 ct/kWh
- Startup PV forecast race: poll up to 30 s after init_time for the PV
forecast to populate before the first optimization run
- `max_charge_power` fallback: if battery data has not been fetched yet
(first run), fall back to configured fixed value instead of passing 0 W
- 15-min mode gating: `local_evopt` is now allowed alongside `evopt` for
`time_frame_base=900`; load profile uses `slots_per_hour` scaling
## Docs / schema export
- `docs/assets/data/config_schema.json`: regenerated (99 fields)
- `docs/user-guide/configuration.html`: local_evopt section added
- `docs/what-is/index.html`: updated feature list
- `README.md`: updated optimizer options
## Tests
782 passing (was 774); 8 new tests in `TestHotReloadLocalEVopt`
…e hot-reload When the user toggles 'dyn_override_discharge_allowed_pv_greater_load' via the web UI, the change now fires an immediate optimization run in addition to updating the live attribute — same behaviour as the local_evopt strategy fields. Implementation: - hot_reload.py: add _OPTIMIZER_RUN_TRIGGERS set; _apply_optimizer calls _fire_run_trigger when the key is in that set - Extensible: any future optimizer hot-reload key that should trigger a run just needs to be added to _OPTIMIZER_RUN_TRIGGERS Tests (test_hot_reload.py): - test_dyn_override_fires_run_trigger: toggle fires the trigger - test_timeout_does_not_fire_run_trigger: eos.timeout change does not"
Remove all language describing EOS Connect as a platform that relies on external optimization engines. The built-in local_evopt is now the default and works out of the box — no external server required. what-is/index.html: - Hero subtitle: 'built-in optimizer — and optional external backends' - Intro paragraph: rewritten to lead with built-in MILP optimizer - Removed the orange 'Important Understanding: primary role is integration' warning alert; replaced with a neutral info alert docs/index.html: - Hero subtitle updated - 'Important: bridges your energy system with external engines' alert rewritten to 'what EOS Connect does' with local_evopt as default - Backends list now lists local_evopt first (default, built-in) - Key Features section updated to 3 backends, local_evopt first user-guide/index.html: - Section 'EOS/EVopt Server Setup' → 'External Backend Setup (Optional)' - Opening sentence no longer says a backend is required - Alert rewritten to list all three options with local_evopt as default"
hot_reload.py:
- Add _PRICE_RUN_TRIGGERS = {price.feed_in_price, price.feed_in_static_adder}
- _apply_price: fire run trigger after _recalculate_feedin for feed-in price
- _apply_feed_in_price: fire run trigger after update_prices for static adder
- Add _sync_feed_in_fixed_price(): update FeedInPriceInterface.fixed_price_ct_kwh
and refresh its price array when price.feed_in_price changes hot — this is the
interface the optimizer actually reads (price_interface was legacy-only)
eos_connect.py:
- Pass feed_in_price_interface to HotReloadAdapter (was missing — caused
_sync_feed_in_fixed_price to silently no-op)
feed_in_price_interface.py:
- _fetch_fixed_price: apply static_adder_ct_kwh and multiplier (was silently
ignored for fixed source; dynamic sources already applied them)
schema.py:
- feed_in_static_adder: add depends_on fixed source hidden (only relevant for
elpris_dk / epex_spot — fixed users set price directly)
- feed_in_multiplier: same depends_on, hidden for fixed source
tests (793 pass):
- TestHotReloadFeedInPrice: test_feed_in_price_syncs_fixed_price_ct_kwh
- TestHotReloadPrice: test_feed_in_price_fires_run_trigger,
test_fixed_price_adder_does_not_fire_run_trigger
- TestHotReloadFeedInPrice: static_adder fires run, multiplier does not,
no-interface no-crash
- test_feed_in_price_interface: test_fixed_price_with_static_adder,
test_fixed_price_with_multiplier
docs:
- configuration.html: hot-reload rows + depends_on notes for static_adder and
multiplier; fixed source note added to both fields
- config_schema.json: regenerated (99 fields)
hot_reload.py: - _apply_feed_in_price: narrow 'except Exception' to specific types (W0718) - _sync_feed_in_fixed_price: same narrow exception catch (W0718) - docstring: split long line (C0301) tests/config_web/test_hot_reload.py: - Move 'from zoneinfo import ZoneInfo' before pytest import (C0411) - Remove trailing newline at end of file (C0305) tests/interfaces/test_feed_in_price_interface.py: - Move stdlib imports before third-party imports (C0411)
- Remove false startup warning for inverter.type="default" (display-only mode) - Fix C0301 (line-too-long) violations by restructuring long lines - Fix W0718 (broad-exception-caught) by catching specific exception types Pylint: 10.00/10
- Fix misleading "unlimited" wording for max_grid_import_w and max_grid_export_w - Clarify that 0 means "no additional constraint" (battery/inverter limits still apply) - Update schema descriptions and regenerate config_schema.json
- Fix 22 line length violations (100 char limit) - Add missing docstrings to 4 dataclass definitions - Add type annotation for variables dictionary No functional changes.
Add smart forecast extension to local optimizer that synthetically extends the forecast with morning PV pattern when forecast ends during night hours. This prevents expensive grid charging at end-of-horizon by teaching the optimizer: "After night comes day with PV". The extension activates when: - Forecast ends during nighttime (19:00-05:00) - Last 6 slots contain <100 Wh (confirming nighttime) - Adds 6 hours of conservative morning ramp [10-50%] at 06:00-12:00 PV capacity is extracted from the forecast data and used to scale the morning pattern generation, ensuring realistic expectations aligned with site history. Implemented methods: - _extend_forecast_with_morning_pv(): Detection & array extension logic - _generate_morning_pv_pattern(): Conservative morning ramp generation Test coverage: 4 new tests for extension logic, integration test with optimize() flow, 24 tests validate pattern generation and skip conditions. Also includes code quality improvements: attribute initialization, removed unused imports/parameters, added class docstrings. All 34 tests passing.
Port the negative_price_switch logic from PriceInterface to the new FeedInPriceInterface. When enabled, negative market prices are clamped to 0 instead of being passed through to the optimizer. Fixes: #255
The dashboard chart logic only recognized the "evopt" backend but not "local_evopt", causing incorrect grid bar visualization during charging-only periods. When local_evopt was active, the chart used EOS calculation logic instead of EVopt logic, resulting in unwanted grid bars alongside charge bars. Updated the backend detection to include both "evopt" (remote) and "local_evopt" (in-process) backends, ensuring correct calculation: - EVopt backends: gridValue = Netzbezug - ac_charge (charge bars only) - EOS backends: gridValue = Netzbezug - (response_load - household_load)
…tream penalty_base was accidentally changed to np.min() during the pylint C0301 refactoring (0c8dc0c), capping instead of flooring the value. This could silently zero all soft-constraint penalties when electricity prices are near zero. Restores upstream semantics: penalty_base = np.max([max_import_price, 0.1e-3]) Also expands the modifications docstring to enumerate all 7 actual divergences from evcc-io/optimizer for licence traceability.
- Update README.md and documentation to reflect the shift from a "data gateway" to a self-contained optimization solution. - Highlight the advantages of local_evopt (performance, privacy, reliability). - Refine the "Intelligent Control Cycle" architecture description. - Restored learning links for key features in README.md. - Synchronized version display to v0.3.35 across all doc pages.
Local opt
Files changed: M src/version.py
- Implement two-tier validation: lenient startup (logs warnings), strict hot-reload (logs errors) - Remove sys.exit(1) calls on config validation errors in PvInterface and EOSBackend - Add configuration_state and configuration_valid tracking across all interfaces - Graceful degradation: start in DEGRADED mode instead of crashing - Fix 10 pylint line-too-long violations (100 char limit compliance) - Add 21 comprehensive two-tier validation tests, update 4 existing tests - Resolves web config migration dead-loop: users can now access web UI to fix incomplete config - Pylint: 10.00/10 ✓ | Tests: 60/60 passing ✓ | No regressions Fixes: #259 (and prevents dead-loop from web config migration)
…lls (issue #253) - Remove deprecated inverter.url/token fields from schema - Consolidate HA credentials at data_source level via merger injection - Add three JSON-type fields for mode-sequence service call definitions - Implement cross-field dependencies (inverter depends on data_source) - Add JSON textarea field type with on-blur validation to web UI - Extend CSS with dark-theme JSON field styling - Update InverterHA to parse JSON service call sequences - Fix address logging bug (override schema default with actual HA URL) - Add comprehensive test suite (14 new tests, all 289 passing) - Update documentation with user-facing configuration guide Fixes: #253
Files changed: M src/version.py
Allow users to disable SSL certificate verification for Home Assistant and OpenHAB connections with self-signed or private CA certificates. Adds config field, passes verify parameter to all HTTP requests, and logs security warnings. - New `data_source.ssl_ignore` config field (expert level) - LoadInterface respects setting in __request_with_retries() - Added tests for SSL verify behavior (all 22 existing tests pass)
Extend ssl_ignore feature from LoadInterface to BatteryInterface and InverterHA, enabling self-signed certificate support across all HTTPS connections to Home Assistant and OpenHAB. Implementation: - Add ssl_ignore extraction and injection in merger.py (both _apply_data_source_inheritance and _apply_inverter_data_source_injection) - Read ssl_ignore config and control requests.get/post verify parameter in BatteryInterface and InverterHA - Add comprehensive test coverage (23 new tests) - Add proof-of-concept tests demonstrating verify parameter behavior All 853 tests passing. Documentation already in place (README.md, GitHub Pages). Fixes: #254
Add SSL Certificate Verification Control
Files changed: M src/version.py
Implement flexible timeseries support for both electricity prices and PV forecasts,
enabling integration with Home Assistant sensors and custom HTTP APIs without source-
specific configuration duplication.
**New Features:**
- Add "timeseries" as price source with HTTP endpoint support
- Add "timeseries" as PV forecast source with HTTP endpoint support
- Implement central Home Assistant data source reuse (avoid credential repetition)
- Add pre-flight validation for Home Assistant sensor existence
**Configuration:**
- 10 new schema fields: price.use_ha_central_data_source, price.ha_sensor_name,
price.data_url, price.data_path, price.data_token + PV equivalents
- All timeseries fields support hot-reload (changes apply without restart)
- Standardized JSON format for both sources: [{start, end, value}, ...]
**Hot-Reload Behavior:**
- Immediate price fetch when switching TO timeseries
- Deferred fetch when switching FROM timeseries (prevents incomplete config errors)
- Immediate PV reload for summarized sources (timeseries, evcc)
- Debounced reload for per-installation sources (akkudoktor, openmeteo, etc.)
**Implementation Details:**
- PriceInterface: __retrieve_prices_from_url() with timeseries parsing
- Automatic resolution detection (900s vs 3600s) with conversion
- Value range validation and clamping (-0.5 to 1.0 EUR/Wh)
- Support for both Home Assistant sensors and custom APIs
- Pre-flight validation checks sensor existence before saving
**Documentation:**
- Updated GitHub Pages with timeseries configuration guide
- Added JSON path reference for custom API responses
- Included Home Assistant integration examples
- Updated README.md with timeseries mention
**Testing:**
- 42 new timeseries parsing tests covering format, resolution, averaging, validation
- 8 new hot-reload behavior tests for price/PV sources
- 3 new merger tests for central HA data source injection
- Pre-flight validation test for sensor name changes
Fixes: timeseries support for users with Home Assistant or custom price/PV APIs -> #214
Files changed: M src/version.py
Move resource_id from pv_forecast array entries to pv_forecast_source section and reclassify sources as location-based vs non-location-based. Architecture: - Added LOCATION_BASED_PV_SOURCES constant for code reuse - resource_id now in pv_forecast_source (solcast, victron only) - Simplified depends_on rules via constant references Configuration: - Location-based sources (4): akkudoktor, openmeteo, forecast_solar, openmeteo_local — require PV Installations array - Non-location-based sources (5): default (built-in), solcast (resource ID), victron (resource ID), evcc (from EVCC instance), timeseries (HTTP/HA) UX: - Added source-specific messages in config panel - Each source explains where its data is configured - Updated documentation with requirements matrix
Files changed: M src/version.py
… price switching - Implement EVCC /api/tariff/grid price fetching with 3-tier fallback (energyforecast.de smart prediction → yesterday prices → today repetition) - Fix hot-reload: properly update price_interface.src on config changes - Add cross-field validation: EVCC source requires EVCC URL configured - Add comprehensive test suite (11 new tests, all passing) - Update docs: EVCC configuration guide (164 lines) fix allow EVCC as price source (part 1) Fixes #176
Add EVCC as new feed-in (export) price source option: - Users can now configure EVCC's /api/tariff/feedin for battery discharge optimization - Supports 4 feed-in sources: fixed, elpris_dk, epex_spot, evcc - Proper tariff parsing and EUR/kWh → EUR/Wh conversion - Prices used as-is (no adder/multiplier applied, like grid EVCC) Bonus: Enable hot reload for feed-in source/zone switching: - Users can switch sources live without app restart - Immediate price fetch on source change - Same UX pattern as grid price source switching Changes: - schema.py: Add "evcc" option, enable hot_reload for source/zone - feed_in_price_interface.py: Implement _fetch_evcc_prices(), fix API endpoint - hot_reload.py: Implement _schedule_feedin_reload() for live switching - Configuration docs: Mark fields hot-reloadable, add EVCC setup guide - Tests: 11 EVCC integration + 5 hot reload tests fix allow EVCC as price source (part 2) Fixes #176
- Rename display groups for clearer user intent: - "Grid Price Provider" (source selection) - "Grid Price - Adjustments" (cost modifications) - "Grid Price - Forecast (Advanced)" (smart prediction) - "Feed-In Price" (export pricing, green-themed) - Remove duplicate subsection headers - Preserve green styling for Feed-In Price section - No functional changes; config behavior unchanged
…ental label - Remove "experimental" label from energyforecast fields (enabled, token, market_zone) - Update documentation to reflect production-ready status - Feature has been well-tested over extended period
Files changed: M src/version.py
…nfig - Remove 450+ lines of old full config generation code - config.yaml now manages ONLY 3 bootstrap keys (eos_connect_web_port, time_zone, log_level) - All other settings (111 fields) are managed via web UI + SQLite store - Improves maintainability and aligns with web-based config architecture Includes: - config.yaml creation on fresh install - Fix pv_forecast list format in defaults - Update tests to match bootstrap-only architecture - All 914 tests pass
Files changed: M src/version.py
The dashboard Info menu was missing support for the local_evopt backend and would show "EOS@akkudoktor" regardless of which backend was selected. Now correctly displays: - "Local EVOpt (built-in)" when local_evopt is active - "EVOpt @ EVCC" when evopt is selected - "EOS@akkudoktor" when eos_server is active
Files changed: M src/version.py
Files changed: M src/version.py
…tion and configuration
Files changed: M src/version.py
Files changed: M src/version.py
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.
Release v0.3.35: Built-in Local EVOPT, EVCC Multi-Source Integration, and Production Refinements
Overview
61 new commits post-web-config-UI (PR #241) adding: built-in EVOPT optimizer, extended EVCC data sources, dynamic feed-in pricing, expanded hot-reload, and comprehensive bug fixes.
Key Features
1. Local EVOPT Built-in Optimizer
2. EVCC as Data Source Hub (Expansion)
Extended existing EVCC support from basic charge control to full optimization data:
3. Dynamic Feed-In Pricing
4. Comprehensive Hot-Reload Expansion
21 total fields now live-reloadable (+682 lines improvements):
5. Fully Configurable HA Inverter Service Calls
6. SSL Certificate Ignore Support
7. Startup Error Handling & Diagnostics
8. Bug Fixes
Stats
Technical Changes
New Files:
src/interfaces/optimization_backends/local_evopt/optimizer.py(811 lines)src/interfaces/optimization_backends/optimization_backend_local_evopt.py(482 lines)src/interfaces/feed_in_price_interface.py(680 lines)tests/config_web/test_ha_inverter_config.py(258 lines)tests/interfaces/test_optimization_backend_local_evopt.py(677 lines)Major Updates:
src/config_web/hot_reload.py: +682 linessrc/config_web/schema.py: +688 linessrc/interfaces/price_interface.py: +713 linesdocs/user-guide/configuration.html: +3,411 linesTesting
Issues Closed
#219, #243, #252, #253, #254, #255, #258
Checklist
Production-ready refinements and feature additions to the web-config system. Local EVOPT optimizer as built-in default eliminates external dependencies. EVCC extends to full data source hub. No breaking changes.