diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 680fe56f..f2c422b8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,5 +1,14 @@ # GitHub Copilot Instructions for EOS Connect +⚠️ **CRITICAL WORKFLOW REQUIREMENTS:** + +- **Code changes ALWAYS require documentation updates** — no exceptions +- **"prepare for commit" triggers mandatory documentation checklist** — see Commit Preparation section +- **NEVER auto-commit** — always present changes to user for review and approval +- **GitHub Pages is primary documentation** — README.md is entry point only + +--- + ## Project Guidelines ### Icon Usage @@ -26,19 +35,19 @@ - GitHub Pages documentation is in `/docs` folder - Structure: 4 main sections (what-is, user-guide, advanced, developer) - Use HTML for documentation pages (better styling control than Markdown) -- Keep README.md and CONFIG_README.md concise with links to full docs +- **README.md**: Concise quick-start with links to full GitHub Pages docs (primary entry point) +- **GitHub Pages** (`/docs`): Complete, detailed documentation for all features #### Documentation Update Workflow **When preparing to commit (NEVER stage changed files and commit automatically - always review changes first and ask for confirmation):** -1. **Update README.md** - Minimal info only, focus on quick start + links to GitHub Pages -2. **Update src/CONFIG_README.md** - Essential configuration overview + links to full docs -3. **Update GitHub Pages** (`/docs` folder) - Complete, detailed documentation +1. **Update README.md** - Concise quick-start only (1-3 sentences per feature), links to full docs +2. **Update GitHub Pages** (`/docs` folder) - Complete, detailed documentation - Always write from **user perspective** (except developer section) - Main focus: **"Easy entry for new and existing users"** - Keep all pages current with latest features and changes - - Use clear, practical examples + - Use clear, practical examples with code blocks and screenshots #### Documentation Perspective @@ -75,9 +84,8 @@ When making ANY code changes: - Bug fixes → Update troubleshooting in user-guide if user-facing 2. **Update All Affected Pages**: Changes must be synchronized across: - - `/docs` GitHub Pages (primary documentation) - - `README.md` (if quick start or core features affected) - - `src/CONFIG_README.md` (if configuration parameters changed) - NOTE: This file is being deprecated, integrate changes into README.md instead + - `/docs` GitHub Pages (primary documentation, contains all details) + - `README.md` (if quick start or core features affected, keep concise) 3. **Maintain Accuracy**: Documentation must match actual code behavior - Verify API endpoint responses match code @@ -91,12 +99,59 @@ When making ANY code changes: ### Commit Preparation -- **NEVER commit automatically** - only prepare changes for user review -- When asked to "prepare to commit", ensure documentation is up-to-date: - 1. Update README.md (minimal, with links) - 2. Update src/CONFIG_README.md (essential info, with links) - 3. Update GitHub Pages documentation (complete details) -- Present a summary of changes for user to review before committing +⚠️ **CRITICAL: Triggered by keywords:** "prepare for commit", "ready to commit", "commit prep", "ready to merge", "prepare to commit" + +**MANDATORY CHECKLIST - ALWAYS EXECUTE IN THIS ORDER:** + +**IMPORTANT:** "Prepare for commit" means review **ALL changes since last commit** (not just recent ones). Use `git diff` and `git status` to see complete scope. The commit message should reflect the entire change set, not just the most recent fix. + +1. ✅ **Review ALL changes since last commit** (PRIMARY STEP) + - Run: `git status` (see all modified files) + - Run: `git diff --stat` (see change scope) + - Verify: changes are logically related (if not, break into multiple commits) + - **The most recent change should not dominate the commit message if earlier changes are more significant** + +2. ✅ **Verify code changes are complete and tested** + - All tests pass + - No breaking changes + - All new functionality implemented + +3. ✅ **Update README.md** (ALWAYS REQUIRED) + - Add 1-3 sentence mention of the feature/fix + - Include link to full documentation on GitHub Pages + - Keep concise - no lengthy explanations + - Check: Does it follow existing README style? + +4. ✅ **Update GitHub Pages documentation** (ALWAYS REQUIRED unless bugfix with no user-facing changes) + - Identify all affected doc sections (user-guide, advanced, what-is, etc.) + - Update with complete details, examples, best practices + - Write from **user perspective** - explain "why" and "how", not just "what" + - Verify accuracy: all config names, types, defaults match code + - Add code examples with syntax highlighting where applicable + +5. ✅ **Run schema export** (if config changes) + - Execute: `python scripts/export_config_schema.py` + - Verify: `docs/assets/data/config_schema.json` updated + +6. ✅ **Generate summary and present to user** + - List all files modified (from `git status`) + - List all tests passing + - Link to GitHub Pages sections updated + - **WAIT FOR USER APPROVAL** before any commits + +7. ✅ **NEVER stage or commit automatically** + - Only prepare and present changes for review + - User must explicitly approve before committing + +8. ✅ **Include Conventional Commit message in response** + - **Keep it SHORT**: 50 char max for subject line + - Format: `: ` + - Body (optional): Why this change, not what (code shows what) + - Footer (optional): `Fixes: #123` for issue references + - Make it ready to copy-paste + - **Rule: If you're writing multiple paragraphs, the message is too long. Simplify.** + +**If ANY of these steps are skipped, the preparation is INCOMPLETE.** ### Testing Phase Documentation @@ -344,21 +399,21 @@ Fields grouped by predicted code complexity and user impact. Each group shares i These fields are simple instance attributes that can be set at runtime: -| Group | Fields | Interface | Status | -| --------------- | --------------------------------------------------------------------------------------------- | ----------------------------------- | --------------------------------------------------------------------------- | -| EOS tuning | `eos.timeout`, `eos.dyn_override_discharge_allowed_pv_greater_load`, `eos.pv_battery_charge_control_enabled` | OptimizationInterface | ✅ **IMPLEMENTED** | -| System timing | `refresh_time` | OptimizationScheduler | Next to implement | -| System timing | `eos.time_frame` (900 or 3600) | All interfaces | Requires cache invalidation (see Priority 1.5) | -| Inverter limits | `inverter.max_grid_charge_rate`, `inverter.max_pv_charge_rate` | BaseInverter subclass | Could implement next | -| System | `request_timeout` | All interfaces | Lower priority | +| Group | Fields | Interface | Status | +| --------------- | ------------------------------------------------------------------------------------------------------------ | --------------------- | ---------------------------------------------- | +| EOS tuning | `eos.timeout`, `eos.dyn_override_discharge_allowed_pv_greater_load`, `eos.pv_battery_charge_control_enabled` | OptimizationInterface | ✅ **IMPLEMENTED** | +| System timing | `refresh_time` | OptimizationScheduler | Next to implement | +| System timing | `eos.time_frame` (900 or 3600) | All interfaces | Requires cache invalidation (see Priority 1.5) | +| Inverter limits | `inverter.max_grid_charge_rate`, `inverter.max_pv_charge_rate` | BaseInverter subclass | Could implement next | +| System | `request_timeout` | All interfaces | Lower priority | **Priority 1.5 — Attribute swap + cache clear (medium effort, high user value)** Simple attribute updates but require recalculation or cache invalidation: -| Group | Fields | Interface | Change Required | -| ------------------ | ----------------------------------------- | ---------------------------- | ------------------------------------------------ | -| EOS time slot | `eos.time_frame` | OptimizationInterface + all data providers | Update timeframe on all interfaces + clear forecast caches | +| Group | Fields | Interface | Change Required | +| ------------- | ---------------- | ------------------------------------------ | ---------------------------------------------------------- | +| EOS time slot | `eos.time_frame` | OptimizationInterface + all data providers | Update timeframe on all interfaces + clear forecast caches | **Priority 2 — Requires recalculation or reconnect (medium effort)** @@ -408,6 +463,161 @@ These affect the application infrastructure itself: 4. Add tests in `tests/config_web/test_hot_reload.py` 5. Run `python scripts/export_config_schema.py` +### Interface Creation & Startup Error Handling + +Two new modules work together to provide centralized interface creation with integrated startup validation and user-visible error handling. + +#### InterfaceFactory (src/interface_factory.py) + +**Purpose**: Centralized factory for interface instantiation with integrated startup validation. + +**Responsibilities:** + +- Instantiate all interface types (Load, Battery, Price, PV, MQTT, EVCC, Inverter, Optimization) +- Catch errors during instantiation +- Register errors with `StartupValidator` for visibility in web UI startup panel +- Distinguish critical interfaces (halt startup on failure) from non-critical (use fallbacks) +- Track created interfaces for lifecycle management + +**Usage in eos_connect.py:** + +```python +from interface_factory import InterfaceFactory +from startup_validator import StartupValidator + +# Initialize at startup +validator = StartupValidator() +factory = InterfaceFactory(validator) + +# Create interfaces with automatic error handling +battery_interface = factory.create_battery_interface( + config=config['battery'], + time_zone=time_zone, + critical=True, # Startup halts on failure +) + +load_interface = factory.create_load_interface( + config=config['load'], + time_frame_base=config['refresh_time'], + time_zone=time_zone, + critical=False, # Uses default on failure +) +``` + +**Benefits:** + +- Eliminates boilerplate try/except blocks in main app +- Consistent error categorization across all interface types +- Centralized startup error collection for web UI visibility +- Easy to extend with new interface types + +#### StartupValidator (src/startup_validator.py) + +**Purpose**: Lightweight facade for registering startup errors directly to the logging system. + +**Responsibilities:** + +- Register startup errors with structured metadata +- Write ERROR/WARNING logs captured by `MemoryLogHandler` +- Embed metadata markers in log messages for frontend parsing +- Act as single source of truth for startup errors via `/logs/alerts` endpoint + +**Method signature:** + +```python +validator.add_error( + category="connectivity", # initialization, configuration, connectivity + component="battery_interface", # Component name + severity="error", # error or warning + title="Battery unavailable", # Short, user-friendly title + message="Connection timeout", # Detailed message + action_required=True, # Flag for ACTION REQUIRED badge + config_link="#battery", # Link to config section (e.g., #eos, #battery) +) +``` + +**Frontend Integration:** + +- Errors are fetched via: `GET /logs/alerts?startup_only=1&limit=20` +- Errors with metadata markers are parsed by `main.js` → `parseAlertMeta()` +- Startup panel renders with: + - Component name (extracted from `[component]` prefix) + - Timestamp and occurrence count + - ACTION REQUIRED badge (yellow) if flagged + - "Open Configuration" link pointing to config section + +#### Startup Error Flow + +``` +Startup: + InterfaceFactory.create_*_interface() + ├─ Try to create interface + │ ├─ Success → return interface (silent) + │ └─ Failure → catch exception + │ └─ validator.add_error(...) + │ └─ MemoryLogHandler captures ERROR/WARNING log + │ ├─ Metadata extracted: Config link, ACTION REQUIRED flag + │ └─ Stored in log buffer, visible via /logs/alerts + +Runtime (user views dashboard): + fetch /logs/alerts?startup_only=1 + └─ Frontend renderAlertSection() + ├─ Deduplicates by title, counts occurrences + ├─ Sorts by severity (ACTION REQUIRED first) + ├─ Shows: timestamp, occurrence count, config link button + └─ User clicks → showConfigurationMenu(section) +``` + +#### Log Message Format + +Error messages should include metadata markers for frontend parsing: + +``` +[component] Title: Message | Config: #section | ACTION REQUIRED + +Examples: +[eos_backend] EOS Connection failed: Connection timeout | Config: #eos | ACTION REQUIRED +[battery_interface] Battery SOC error: Authentication failed | Config: #battery | ACTION REQUIRED +[load_interface] Load data unavailable: Request timeout | Config: #load +``` + +**Frontend parsing:** + +- Matches `Config: (#\w+)` for config section link +- Checks for "ACTION REQUIRED" string to show badge +- Extracts component name from `[component]` prefix + +#### Extending with New Interface Types + +To add a new interface creation method: + +1. Add method to `InterfaceFactory` following the pattern of existing methods +2. Specify error category (connectivity, initialization, configuration), component name, config link +3. Mark as critical or non-critical +4. Call from `eos_connect.py` during startup +5. On instantiation failure, `StartupValidator.add_error()` is called automatically +6. Errors appear in startup panel within 1-2 seconds + +**Example:** + +```python +def create_my_new_interface(self, config: Dict[str, Any], critical: bool = True): + return self._create_interface( + component_name="my_new_interface", + category="connectivity", + critical=critical, + title="My New Interface unavailable", + error_message="Failed to initialize", + config_link="#my_section", + creator_func=lambda: self._import_and_create( + "interfaces.my_new_interface", + "MyNewInterface", + config, + request_timeout=10, + ), + ) +``` + ### HA Addon Integration #### Bootstrap Contract with ha_addons Repo @@ -643,3 +853,68 @@ EOS Connect now uses a built-in web UI for all configuration. | CHANGELOG / release notes | Add migration note | Low | > **Important**: Apply identical changes to both `eos_connect/` and `eos_connect_develop/` directories. The only differences between them should be `name`, `version`, `slug`, and `image` fields in `config.yaml`. + +--- + +## Commit Message Guidelines + +All commits follow **Conventional Commits** format for clear, scannable history. + +### Format + +``` +: + +[optional body with details] + +[optional footer with issue references] +``` + +### Types + +- **feat**: New feature (e.g., `feat: add battery forecast smoothing`) +- **fix**: Bug fix (e.g., `fix: prevent startup crash on incomplete config`) +- **docs**: Documentation changes (README.md or GitHub Pages /docs folder) +- **test**: Test additions or fixes (no production code change) +- **refactor**: Code restructuring without feature/fix change +- **perf**: Performance improvement +- **chore**: Build, tooling, dependencies + +### Examples + +**Feature:** + +``` +feat: implement two-tier PV config validation + +- Lenient startup mode to prevent app crash on incomplete config +- Strict hot-reload mode to validate all changes +- Add configuration_state tracking for web UI visibility +``` + +**Bug Fix:** + +``` +fix: prevent startup crash on incomplete config - enable web UI access + +- Implement two-tier validation (lenient startup, strict hot-reload) +- Remove sys.exit(1) calls on validation errors +- Graceful degradation: start in DEGRADED mode instead of crashing + +Fixes: #259 +``` + +**Documentation:** + +``` +docs: update configuration guide with new PV source options +``` + +### Best Practices + +- Keep subject line ≤ 50 characters +- Use imperative mood ("add", "fix", not "added", "fixed") +- Include issue reference in footer: `Fixes: #123` or `Resolves: #123` +- Use body to explain _why_, not _what_ (code shows what) +- Link related issues: `Related: #456, #789` +- If multiple fixes/features, break into separate commits for clarity diff --git a/.github/workflows/docker_develop.yml b/.github/workflows/docker_develop.yml index 633073a5..d152903d 100644 --- a/.github/workflows/docker_develop.yml +++ b/.github/workflows/docker_develop.yml @@ -22,7 +22,7 @@ on: workflow_dispatch: # allows manual triggering of the workflow env: - VERSION_PREFIX: 0.3.34. + VERSION_PREFIX: 0.3.35. VERSION_SUFFIX: -develop jobs: diff --git a/.github/workflows/docker_main.yml b/.github/workflows/docker_main.yml index 71cffbce..eac47c54 100644 --- a/.github/workflows/docker_main.yml +++ b/.github/workflows/docker_main.yml @@ -21,7 +21,7 @@ on: workflow_dispatch: # allows manual triggering of the workflow env: - VERSION_PREFIX: 0.2. + VERSION_PREFIX: 0.3. jobs: pytest: diff --git a/.github/workflows/pylint.yaml b/.github/workflows/pylint.yaml index 2aeff227..f627e9a8 100644 --- a/.github/workflows/pylint.yaml +++ b/.github/workflows/pylint.yaml @@ -20,7 +20,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pylint astroid + pip install pylint astroid pytest pip install -r requirements.txt sudo apt-get update && sudo apt-get install -y bc # Install bc - name: Analysing the code with pylint diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14f2ccf3..27eb67e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,40 @@ Guidelines - Document new config keys / API / MQTT topics - Prefer clarity over cleverness +## Key Architectural Patterns + +### Interface Creation & Startup Error Handling + +When adding new interfaces or modifying interface initialization: + +- **Use `InterfaceFactory`** (`src/interface_factory.py`) for centralized creation with integrated error handling + - Reduces boilerplate error catching in the main app + - Automatically registers errors with `StartupValidator` + - Categorizes critical vs non-critical interfaces + +- **Use `StartupValidator`** (`src/startup_validator.py`) to register startup errors + - Errors appear in the web UI's startup panel within 1-2 seconds + - Include metadata markers: `| Config: #section | ACTION REQUIRED` + - Enables users to quickly fix configuration issues + +**Example:** +```python +# In eos_connect.py +from interface_factory import InterfaceFactory +from startup_validator import StartupValidator + +validator = StartupValidator() +factory = InterfaceFactory(validator) + +battery_interface = factory.create_battery_interface( + config=config['battery'], + time_zone=time_zone, + critical=True, # Halt startup if fails +) +``` + +For detailed architecture and design patterns, see the [Developer Guide → Interface Creation & Startup Error Handling](docs/developer/index.html#architecture). + ## Code Ownership Some components have designated owners who maintain and review changes to those areas. This is documented in the [CODEOWNERS](/.github/CODEOWNERS) file. diff --git a/README.md b/README.md index 29aef265..3a0d642d 100644 --- a/README.md +++ b/README.md @@ -5,40 +5,45 @@ -**For full documentation, guides, and configuration details, visit:** +**For full documentation, guides, and configuration details, visit:** [https://ohAnd.github.io/EOS_connect/](https://ohAnd.github.io/EOS_connect/) --- ## Overview -EOS Connect is an open-source tool for intelligent energy management and optimization. It acts as the orchestration layer between your energy hardware (inverters, batteries, PV forecasts) and external optimization engines. EOS Connect is an integration and control platform—not an optimizer. Optimization calculations are performed by external servers: -- [Akkudoktor EOS](https://github.com/Akkudoktor-EOS/EOS) -- [EVopt](https://github.com/thecem/hassio-evopt) +EOS Connect is a comprehensive energy management and optimization platform. While it remains a flexible orchestration layer between your hardware and various optimization engines, it has evolved from a pure "data gateway" into a full-featured, self-contained optimization solution. -EOS Connect fetches real-time and forecast data, processes it via your chosen optimizer, and controls devices to optimize your energy usage and costs. +EOS Connect now ships with a **built-in MILP optimizer** (`local_evopt`) — providing a complete, high-performance energy management system out of the box. For specialized needs, it maintains its open nature by allowing connections to external backends: +- **Built-in (Recommended):** [local_evopt](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#local-evopt) — A high-performance, local optimizer based on [evcc-io/optimizer](https://github.com/evcc-io/optimizer). +- **External:** [Akkudoktor EOS](https://github.com/Akkudoktor-EOS/EOS) or [EVopt](https://github.com/thecem/hassio-evopt). + +EOS Connect fetches real-time and forecast data (solar, prices), runs the integrated optimization (or delegates it), and automatically controls your devices to maximize self-consumption and minimize grid costs. --- ## Key Features -- **Automated Energy Optimization:** Uses real-time and forecast data to maximize self-consumption and minimize grid costs. -- **Battery and Inverter Management:** Charge/discharge control, grid/PV modes, dynamic charging curves. -- **Integration with Smart Home Platforms:** Home Assistant (MQTT auto discovery), OpenHAB, EVCC, and MQTT for seamless data exchange and automation. -- **Dynamic Web Dashboard:** Live monitoring, manual control, and visualization of your energy system. -- **Cost Optimization:** Aligns energy usage with dynamic electricity prices (Tibber, smartenergy.at, Stromligning.dk) with hourly or quarterly distribution. -- **Smart Price Prediction:** Energyforecast.de integration automatically learns your grid fees and taxes to provide accurate price predictions when your primary source lacks tomorrow's prices. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#energyforecast) -- **Dynamic PV Override:** Automatically allows discharge when solar production exceeds load, preventing unwanted grid input during cloud shadows. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#dyn-override) -- **Flexible Configuration:** Easy to set up and extend for a wide range of energy systems and user needs. +- **All-in-One Optimization Solution:** No external servers required for standard energy optimization. +- **Privacy & Reliability:** With `local_evopt`, all calculations happen on your device, ensuring faster response times and no dependency on external network reachability. +- **Automated Energy Management:** Uses real-time and forecast data into a cohesive control strategy to maximize self-consumption. +- **Battery and Inverter Management:** Precise charge/discharge control, grid/PV modes, and manufacturer-validated dynamic charging curves. +- **Integration with Smart Home Platforms:** Home Assistant (MQTT auto discovery, native inverter control via service calls), OpenHAB, EVCC, and REST APIs. +- **Dynamic Web Dashboard:** Live monitoring, manual overrides, and visualization of the optimization process. +- **Cost Optimization:** Automatic alignment with dynamic electricity prices (Tibber, smartenergy.at, EVCC, timeseries, etc.) with configurable resolution. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#price) +- **Dynamic Feed-In Pricing:** Optimize battery discharge for maximum profit when export prices are favorable. Switch feed-in sources live without restart via hot reload. Supports fixed, Elpris DK, EPEX Spot, and EVCC. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#price) +- **Smart Price Prediction:** Learned grid fees and taxes for accurate planning even when future prices aren't yet available. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#energyforecast) +- **Dynamic PV Override:** Intelligent discharge prevention during high solar production or intermittent clouds. [Learn more →](https://ohAnd.github.io/EOS_connect/user-guide/configuration.html#dyn-override) --- ## How It Works -EOS Connect periodically collects: -- Local energy consumption data -- PV solar forecasts for the next 48 hours -- Upcoming energy prices +EOS Connect acts as the central brain of your energy system: +1. **Data Collection:** Periodically collects local consumption, battery states, and inverter data. +2. **Forecasting:** Fetches PV solar forecasts and upcoming energy prices for the next 48 hours. +3. **Internal Optimization:** The built-in optimizer processes this data locally to generate the most cost-efficient power strategy. +4. **Active Control:** Applies targeted commands to your devices (inverters, batteries, wallboxes) based on the calculated strategy. -It sends this data to the optimizer (EOS or EVopt), which returns a prediction and recommended control strategy. EOS Connect then applies these controls to your devices (inverter, battery, EVCC, etc.). All scheduling and timing is managed by EOS Connect. +All scheduling, logic, and interface management is handled by EOS Connect, providing a unified and reliable energy management experience.
EOS Connect process flow @@ -61,13 +66,14 @@ Supported data sources and integrations: - Home Assistant (latest version recommended) - EOS or EVopt server (can be installed as part of the setup; see below) -2. **Option A: Install EOS Connect Add-on:** +2. **Install EOS Connect Add-on:** - Add the [ohAnd/ha_addons](https://github.com/ohAnd/ha_addons) repository to your Home Assistant add-on store. - Install the **EOS Connect** add-on from the store. - -3. **Option B: Install EOS Connect Add-on:** - - If you want to use EOS as your optimization backend, add the [Duetting/ha_eos_addon](https://github.com/Duetting/ha_eos_addon) or [thecem/ha_eos_addon](https://github.com/thecem/ha_eos_addon) repository to your Home Assistant add-on store and install the EOS add-on, or ensure your EOS server is running and reachable. - - If you prefer the lightweight EVopt backend, install [thecem/hassio-evopt](https://github.com/thecem/hassio-evopt) and make sure it is running. + - The built-in optimizer (`local_evopt`) works out of the box — no additional add-ons required. + +3. **(Optional) External optimization backend:** + - To use Akkudoktor EOS as backend, add the [Duetting/ha_eos_addon](https://github.com/Duetting/ha_eos_addon) or [thecem/ha_eos_addon](https://github.com/thecem/ha_eos_addon) repository and install the EOS add-on. + - To use EVopt, install [thecem/hassio-evopt](https://github.com/thecem/hassio-evopt) and make sure it is running. 4. **Configure:** - On first start, a **Setup Wizard** guides you through initial configuration via the web UI. @@ -93,6 +99,17 @@ If the add-on crashes with a Segmentation Fault on startup, your VM might be usi This allows the add-on to correctly see and use your physical CPU's instructions. +**Note for Proxmox / local_evopt Users:** +If local_evopt fails with "CBC file not found" in Proxmox, force a full container rebuild: +```bash +docker rmi ghcr.io/ohand/ha-addon-eos_connect_develop_amd64:VERSION +# Then restart the addon from Home Assistant UI to pull and rebuild +``` +Cached images may use system Python instead of the venv. + +**Note on SSL Certificate Verification:** +By default, EOS Connect validates SSL certificates when connecting to Home Assistant or OpenHAB. If you use a setup with **self-signed or private CA certificates**, you can disable verification in Settings → Data Source → **SSL Ignore** (expert level, requires restart). Only enable this in **trusted private networks** where you fully control the network path. Currently, EOS Connect does not support supplying custom root CA certificates — this feature is planned for future releases. For production setups, we recommend obtaining a valid certificate through Let's Encrypt (free) or your organization's certificate authority. + --- **Other Installation Options:** @@ -106,18 +123,20 @@ EOS Connect uses a **web-based configuration system**. All settings are managed ### First Start (Setup Wizard) On first launch, a **Setup Wizard** guides you through the essential configuration steps in optimal order: -1. **Optimizer** — Select your optimization backend (EOS Server or EVopt) +1. **Optimizer** — Select your optimization backend (built-in Local EVopt, EOS Server, or external EVopt) 2. **EVCC** (Optional) — Configure if you want to use EVCC for PV forecasts, inverter control gateway, or car charging dependent control. Can be skipped if not using EVCC. 3. **Inverter** — Select your inverter type for battery control (display-only if not using hardware control). Can use EVCC as controller if configured in step 2. 4. **Data Source** — Connect to Home Assistant, OpenHAB, or use default sensors 5. **Battery** — Set capacity and SOC limits 6. **Load** — Connect your load sensor 7. **Price** — Choose your electricity pricing provider -8. **PV Installations** — Configure your solar forecast provider and PV systems +8. **PV Installations** — Configure your solar forecast provider and PV systems (location-based sources only) After the wizard completes, restart EOS Connect to apply the settings. -**Note:** EVCC configuration must come before Inverter so you can select EVCC as your inverter controller type. If EVCC URL is not configured, the option will be greyed out in both the Inverter and PV Source selection fields. +**Note:** +- EVCC configuration must come before Inverter so you can select EVCC as your inverter controller type. If EVCC URL is not configured, the option will be greyed out in both the Inverter and PV Source selection fields. +- PV Installations configuration is only required for location-based forecast sources (Akkudoktor, OpenMeteo, Forecast.Solar). Other sources (Default, Solcast, Victron, EVCC, Timeseries) configure their data elsewhere and do not need PV Installations defined. ### Bootstrap Config (`config.yaml`) Only 3 infrastructure settings live in `config.yaml` — everything else is stored in the database and managed via the web UI: diff --git a/README_OLD.md b/README_OLD.md index e4549a2d..143e5fb7 100644 --- a/README_OLD.md +++ b/README_OLD.md @@ -644,8 +644,8 @@ Override the system mode, duration, and grid charge power. | `Avoid Discharge` | 1 | Prevent battery discharge | | `Discharge Allowed` | 2 | Allow battery discharge | | `Avoid Discharge EVCC FAST` | 3 | Avoid discharge with EVCC fast charge | -| `Avoid Discharge EVCC PV` | 4 | Avoid discharge with EVCC PV mode | -| `Avoid Discharge EVCC MIN+PV` | 5 | Avoid discharge with EVCC MIN+PV mode | +| `Discharge Allowed EVCC PV` | 4 | Discharge allowed with EVCC PV mode | +| `Discharge Allowed EVCC MIN+PV` | 5 | Discharge allowed with EVCC MIN+PV mode | diff --git a/docs/advanced/index.html b/docs/advanced/index.html index 03150239..6afb16ec 100644 --- a/docs/advanced/index.html +++ b/docs/advanced/index.html @@ -19,7 +19,7 @@
- +

EVCC Configuration

- +
Optional Section: Define EVCC URL here to use EVCC as:
    @@ -425,7 +557,7 @@

    evcc.url

- +

Inverter Configuration

Configure inverter control for automated battery management.

@@ -605,23 +737,139 @@

inverter.max_pv_charge_rate

+

Home Assistant Service Call Sequences

+

When using inverter.type: homeassistant, define the HA service calls to execute for each battery control mode. Each field accepts a JSON array of service call objects.

+ +

inverter.charge_from_grid

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterinverter.charge_from_grid
TypeJSON Array
DescriptionService calls to execute when charging from grid
Default[]
AvailabilityOnly visible/required when inverter.type = "homeassistant"
Notes + Use {{ power }} template variable in data_template to receive the optimal wattage from EOS Connect.

+ Example: +
[
+  {"service":"number.set_value","entity_id":"number.charge_power","data_template":{"value":"{{ power }}"}},
+  {"service":"select.select_option","entity_id":"select.mode","data":{"option":"Charge"}}
+]
+
+ +

inverter.avoid_discharge

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterinverter.avoid_discharge
TypeJSON Array
DescriptionService calls to execute when preventing battery discharge (hold mode)
Default[]
AvailabilityOnly visible/required when inverter.type = "homeassistant"
Notes + Use this mode when battery should not discharge but PV charging is allowed. Typically sets the inverter to "hold" or "standby" mode.

+ Example for Sungrow: +
[
+  {"service":"select.select_option","entity_id":"select.sungrow_mode","data":{"option":"Backup"}}
+]
+
+ +

inverter.discharge_allowed

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterinverter.discharge_allowed
TypeJSON Array
DescriptionService calls to execute for normal battery discharge mode
Default[]
AvailabilityOnly visible/required when inverter.type = "homeassistant"
Notes + Use this mode when battery is allowed to discharge normally (providing power to the home and grid).

+ Example: +
[
+  {"service":"select.select_option","entity_id":"select.mode","data":{"option":"Self Use"}}
+]
+
+
Home Assistant Inverter Interface: - The homeassistant inverter type allows controlling any inverter/battery system that is integrated into Home Assistant via configurable service call sequences (e.g., Marstek, Sungrow, Goodwe). -

- For each EOS inverter mode, you define a list of HA service calls under inverter:: +

The homeassistant inverter type allows controlling any inverter/battery system that is integrated into Home Assistant via configurable service call sequences (e.g., Marstek, Sungrow, Goodwe, custom ESPHome integrations).

+ +

Credentials: The inverter automatically uses your data_source credentials (URL and token) — no separate HA authentication needed. This ensures all HA integrations (sensors, inverter control) use the same connection.

+ +

Service Call Sequences: For each battery control mode, define a JSON array of Home Assistant service calls:

    -
  • charge_from_grid – Service calls for grid charging. Supports {{ power }} in data_template for dynamic power values from EOS.
  • -
  • avoid_discharge – Service calls to hold the battery (no discharge, PV charging allowed).
  • +
  • charge_from_grid – Service calls to charge from grid. Supports {{ power }} template variable in data_template for dynamic wattage.
  • +
  • avoid_discharge – Service calls to hold battery (no discharge, PV charging allowed).
  • discharge_allowed – Service calls for normal battery discharge.
- Each step specifies: service (e.g., select.select_option), entity_id, and data (static) or data_template (with {{ power }} variable). - See the Marstek example below for a complete configuration. - For the ESPHome config to connect a Marstek M1 via RS485/Modbus, see this Gist. + +

Service Call Format: Each call in a sequence must specify:

+
    +
  • service – The HA service domain and action (e.g., select.select_option, number.set_value)
  • +
  • entity_id – The target entity (e.g., select.mt1_betriebsmodus)
  • +
  • data or data_template – Parameters for the service call
  • +
+ +

Note: These fields are JSON arrays. Use valid JSON syntax (square brackets, quotes on strings). The web UI provides a JSON editor with syntax validation.

+ +

See the Marstek example below for a complete working configuration.

- +
@@ -629,8 +877,8 @@

Data Source

Define the primary data connection that EOS Connect uses to read sensor values and send control commands.

- Set Once, Used Everywhere: - Once you configure this section, it becomes the single connection point for all sensor references in other sections. + Set Once, Used Everywhere: + Once you configure this section, it becomes the single connection point for all sensor references in other sections. For example:
  • battery.soc_sensor refers to an entity or item from this source
  • @@ -701,6 +949,71 @@

    data_source.access_token

    Find or create an API token in OpenHAB settings + +

    data_source.ssl_ignore

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Parameterdata_source.ssl_ignore
    DescriptionDisable SSL/TLS certificate verification for HTTPS connections
    Valid Values + true – Disable SSL verification (not recommended for production)
    + false – Enable SSL verification (default, secure) +
    Defaultfalse
    Requires RestartYes
    Expert LevelYes — only visible in advanced configuration
    + +
    + Security Warning: + Disabling SSL certificate verification removes protection against man-in-the-middle (MITM) attacks. + Only use this in the following scenarios: +
      +
    • Your Home Assistant or OpenHAB uses a self-signed certificate
    • +
    • Your Home Assistant or OpenHAB uses a private CA certificate
    • +
    • The connection is within a trusted, isolated private network
    • +
    • You understand and accept the security trade-offs
    • +
    +

    If using HTTPS with a valid public certificate, keep this disabled.

    +
    + +

    Troubleshooting Connection Errors

    +

    If you see SSL certificate errors like:

    + + SSLError: Certificate verify failed
    + SSLError: [SSL: SELF_SIGNED_CERT_REJECT] self signed certificate +
    +

    Step 1 (Recommended): Install a valid certificate using Let's Encrypt (free) via your reverse proxy or DNS.

    +

    Step 2: If Step 1 isn't feasible, enable this option in Settings → Data Source, then restart EOS Connect.

    + +

    Example Configuration

    +
    data_source:
    +  type: homeassistant
    +  url: https://homeassistant.local:8123  # Self-signed certificate
    +  access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    +  ssl_ignore: true  # ⚠️ Only because of self-signed cert in private network
    +
    + +
    + Possible Future Enhancement: Support for custom root CA certificates is currently not planned for a future release. Feel free to draft a PR to allow secure HTTPS connections with private CAs without disabling certificate verification. +
@@ -1113,7 +1426,7 @@

Dynamic Battery Pr

Dynamic Battery Price Calculation

EOS Connect can automatically calculate the real cost of energy in your battery by analyzing historical charging events using a Last-In, First-Out (LIFO) inventory model.

- +
How it Works:
  1. Event Detection: The system scans historical data (default 96h) to identify "charging events" where battery power was above the charging_threshold_w.
  2. @@ -1341,7 +1654,7 @@

    battery.battery_price_include_feedin

- +

Load Configuration

@@ -1546,12 +1859,12 @@

load.additional_load_1_consumption

- +

Electricity Price Configuration

Configure dynamic electricity pricing for cost optimization.

- +
Important: All price values must use the same base - either all prices include taxes and fees, or all prices exclude taxes and fees. Mixing different bases will lead to incorrect optimization results.
@@ -1572,7 +1885,9 @@

price.source

tibber - Tibber API
smartenergy_at - Austrian provider
stromligning - Danish provider
+ evcc - EVCC integration
fixed_24h - Custom 24-hour array
+ timeseries - HTTP endpoint returning timeseries data (Home Assistant, custom API, etc.)
default - Akkudoktor API @@ -1582,6 +1897,10 @@

price.source

+
+ Unified Timeseries Source: The timeseries source enables fetching electricity prices from any HTTP endpoint that returns timeseries data. This includes Home Assistant, custom HTTP APIs, and other integration points. See Timeseries Data Source Guide below for detailed configuration and examples. +
+

price.token

@@ -1708,173 +2027,487 @@

price.feed_in_price

- + - + - + - + + + + +
DescriptionCompensation you receive for feeding energy back to the gridFixed compensation for feeding energy back to the grid (used when feed_in_source is "fixed")
UnitEuro per kWh (€/kWh)Cents per kWh (ct/kWh)
Example0.08 (8 cents per kWh)8 (8 cents per kWh)
NotesMust use the same tax/fee basis as your purchase prices. Typical range: 0.05 - 0.12 €/kWhOnly used when feed_in_source: fixed. Must use the same tax/fee basis as your purchase prices. Typical range: 5-12 ct/kWh
Hot-reloadable Yes — changes take effect immediately and trigger a new optimization run. No restart required.
-

price.negative_price_switch

+

price.feed_in_source

- + - + - + + + + + - +
Parameterprice.negative_price_switchprice.feed_in_source
DescriptionHow to handle negative electricity market pricesSource for dynamic feed-in (export) prices
Valid Values - true - Limit feed-in price to €0 when market price is negative
- false - Always use the configured feed_in_price + fixed - Use fixed price from feed_in_price
+ elpris_dk - Elpris DK spot prices (Denmark only)
+ epex_spot - EPEX-Spot prices via Akkudoktor API
+ evcc - EVCC published feed-in tariff (requires EVCC configured)
Defaultfalsefixed
Hot-reloadable Yes — switch sources immediately without restart. New prices are fetched instantly with the new source.
NotesUse true if your feed-in tariff pays €0 during negative market pricesDynamic sources significantly improve battery discharge timing during high-price periods. Changing sources via the web UI triggers immediate price update with new source (no restart needed).
-

Smart Price Prediction (Energyforecast.de)

-
- New Feature: Energyforecast.de integration provides smart price prediction when your primary price source lacks tomorrow's prices. The system automatically learns your grid fees and taxes pattern to provide accurate predictions. -
- -

How It Works

-

Instead of simple price repetition or fixed markups, EOS Connect:

-
    -
  1. Learns the relationship between your primary source (e.g., Tibber) and EPEX spot prices
  2. -
  3. Calculates both multiplicative factor (for VAT) and offset (for grid fees)
  4. -
  5. Applies this learned pattern to future EPEX forecasts
  6. -
- -
- Example: Your Tibber prices: 20-35 ct/kWh, EPEX spot: 8-15 ct/kWh → System learns: customer_price = 2.1 × epex_spot + 9.5 ct/kWh. Tomorrow's EPEX forecast (12 ct/kWh) → Adapted price: 2.1 × 12 + 9.5 = 34.7 ct/kWh -
- -

When It Activates

-
    -
  • Primary Source First: Uses your configured source (Tibber, SmartEnergy, etc.)
  • -
  • Smart Detection: Only triggers when tomorrow's prices aren't available
  • -
  • Seamless: Real prices when available, intelligent predictions when needed
  • -
- -

Configuration Parameters

- -

price.energyforecast_enabled

+

price.feed_in_zone

- + - + - + + + + + - +
Parameterprice.energyforecast_enabledprice.feed_in_zone
DescriptionEnable smart price prediction with energyforecast.dePrice zone for Elpris DK (Denmark)
Valid Values - true - Enable smart price prediction
- false - Disable (use simple price repetition) + DK1 - Western Denmark (Jylland, Fyn)
+ DK2 - Eastern Denmark (Sjælland, Bornholm)
DefaultfalseDK1
Hot-reloadable Yes — zone changes take effect immediately without restart
NotesOnly used when primary source lacks tomorrow's pricesOnly used when feed_in_source: elpris_dk
-

price.energyforecast_token

+

price.data_url

- + - + - - + + - - + + - - + + + + + + + + + +
Parameterprice.energyforecast_tokenprice.data_url
DescriptionAPI token from energyforecast.deFull HTTP endpoint URL for fetching price timeseries data
How to GetRegister at energyforecast.de/api_keysRequiredYes, when source: timeseries
Defaultdemo_tokenFormatMust start with http:// or https://
Notes - Demo token: Limited rate limits, for testing only
- Free tier: 48-hour forecasts (sufficient for most users)
- Paid tier: 96-hour forecasts + higher limits

- EOS Connect automatically validates and warns if using demo_token in production. -
Example (Home Assistant)http://homeassistant.local:8123/api/states/sensor.grid_prices
Example (Custom API)https://api.example.com/v1/prices
Hot-reloadable Yes — changes take effect immediately without restart
-

price.energyforecast_market_zone

+

price.data_path

- + - + - - + + - - + + - - + + + + + + + + + + + + + +
Parameterprice.energyforecast_market_zoneprice.data_path
DescriptionEuropean EPEX spot market zoneJSON path to the timeseries array in the API response (using dot notation)
Valid Values - DE-LU - Germany/Luxembourg
- AT - Austria
- FR - France
- NL - Netherlands
- BE - Belgium
- PL - Poland
- DK1 - Denmark West
- DK2 - Denmark East -
RequiredYes, when source: timeseries
DefaultDE-LUFormatDot notation: object.nested.array
NotesMust match your location for accurate EPEX spot prices. Invalid zones automatically default to DE-LU with a warning.Example (Home Assistant)attributes.data — accesses response["attributes"]["data"]
Example (Nested Array)forecast[0].prices — accesses response["forecast"][0]["prices"]
Data Format ExpectedJSON array: [{start, end, value}, ...]
+ start: ISO8601 timestamp (e.g., "2024-06-12T14:00:00Z")
+ end: ISO8601 timestamp (e.g., "2024-06-12T15:00:00Z")
+ value: Price in EUR/Wh (e.g., 0.25) +
Hot-reloadable Yes — changes take effect immediately without restart
-

Example Configuration

-
price:
-  source: tibber
-  token: "YOUR_TIBBER_TOKEN"
-  feed_in_price: 0.08
-  negative_price_switch: true
-  
-  # Smart price prediction with energyforecast.de
-  energyforecast_enabled: true
+            

price.data_token

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterprice.data_token
DescriptionOptional Bearer token for API authentication (sent in Authorization header)
RequiredNo — only if the API requires authentication
FormatBearer token string (ASCII only)
Example (Home Assistant)eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
HTTP BehaviorIf provided, sends header: Authorization: Bearer <token>
Security NoteToken must contain only ASCII characters (Latin-1 compatible) for HTTP header transmission
Hot-reloadable Yes — changes take effect immediately without restart
+ +

price.feed_in_static_adder

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterprice.feed_in_static_adder
DescriptionFixed adjustment to feed-in price (e.g., transport costs, taxes)
UnitCents per kWh (ct/kWh)
Valid Range-10 to +10 ct/kWh
Default0
Examples + 3.5 - Add 3.5 ct/kWh for transport costs
+ -1.0 - Subtract 1 ct/kWh discount +
Notes Hot-reloadable: Changes take effect immediately and trigger a new optimization run. Applied BEFORE multiplier.
+ Only shown when feed_in_source is elpris_dk or epex_spot — not needed for fixed (just set the price directly).
+ +

price.feed_in_multiplier

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterprice.feed_in_multiplier
DescriptionPercentage multiplier for feed-in prices (e.g., discount strategy)
Valid Range0.5 to 1.5 (0.5 = 50%, 1.0 = no change, 1.5 = 150%)
Default1.0
Examples + 0.95 - Use 95% of market price (conservative)
+ 1.05 - Use 105% of market price (aggressive) +
NotesHot-reloadable: Changes take effect immediately. Expert level setting. Applied AFTER static adder.
+ Only shown when feed_in_source is elpris_dk or epex_spot — not needed for fixed.
+ +

Dynamic Feed-In Pricing Comparison

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SourceAvailabilityUpdate FrequencyTypical RangeBest For
fixedWorldwideManual5-12 ct/kWhSimple tariffs, flat rates
elpris_dkDenmark onlyDaily 13:00 UTC1-80 ct/kWhDanish households, volatile pricing
epex_spotEurope (DE, AT, FR, etc.)Every 15 minutes-50 to +80 ct/kWhProfessional traders, maximum optimization
evccEVCC charger installationsReal-time from EVCC APIDepends on tariffUsers with EVCC charger, real-time feed-in tariffs
+ +

Configuration Examples

+
# Example 1: Fixed German tariff
+price:
+  feed_in_source: fixed
+  feed_in_price: 8  # 8 ct/kWh
+
+# Example 2: Danish dynamic pricing with adjustment
+price:
+  feed_in_source: elpris_dk
+  feed_in_zone: DK1
+  feed_in_static_adder: 2.5  # +2.5 ct/kWh for transport
+  feed_in_multiplier: 0.95    # Conservative: 95% of spot
+
+# Example 3: EPEX-Spot with cost adjustment
+price:
+  feed_in_source: epex_spot
+  feed_in_static_adder: -1.0  # -1 ct/kWh fee
+  feed_in_multiplier: 1.0     # Full spot price
+
+# Example 4: EVCC real-time feed-in tariff
+price:
+  feed_in_source: evcc  # Requires EVCC URL configured
+  # EVCC feed-in prices are used as-is (no adjustments)
+  # The EVCC charger provides real-time tariff data
+ +

price.feed_in_negative_price_switch

+ + + + + + + + + + + + + + + + + + + + + +
Parameterprice.feed_in_negative_price_switch
DescriptionHow to handle negative electricity market prices for feed-in (battery export). When enabled, prevents exporting at a loss when market prices go negative.
Valid Values + true - Set feed-in price to €0 when market price goes negative (don't export)
+ false - Always use the configured feed-in price (including during negative price periods) +
Defaultfalse
NotesThe market price reference varies based on the selected feed-in source:
+ • fixed: Uses Akkudoktor market prices as reference
+ • elpris_dk: Uses Elpris DK spot prices
+ • epex_spot: Uses EPEX Spot prices
+ • evcc: Not applicable (EVCC feed-in prices are used as-is, no negative price handling)
+ Enable when you want to avoid exporting battery power during periods when the market pays to consume.
+ +

Smart Price Prediction (Energyforecast.de)

+
+ Production Ready: Energyforecast.de integration provides smart price prediction when your primary price source lacks tomorrow's prices. The system automatically learns your grid fees and taxes pattern to provide accurate predictions. Well-tested in production for extended period. +
+ +

How It Works

+

Instead of simple price repetition or fixed markups, EOS Connect:

+
    +
  1. Learns the relationship between your primary source (e.g., Tibber) and EPEX spot prices
  2. +
  3. Calculates both multiplicative factor (for VAT) and offset (for grid fees)
  4. +
  5. Applies this learned pattern to future EPEX forecasts
  6. +
+ +
+ Example: Your Tibber prices: 20-35 ct/kWh, EPEX spot: 8-15 ct/kWh → System learns: customer_price = 2.1 × epex_spot + 9.5 ct/kWh. Tomorrow's EPEX forecast (12 ct/kWh) → Adapted price: 2.1 × 12 + 9.5 = 34.7 ct/kWh +
+ +

When It Activates

+
    +
  • Primary Source First: Uses your configured source (Tibber, SmartEnergy, etc.)
  • +
  • Smart Detection: Only triggers when tomorrow's prices aren't available
  • +
  • Seamless: Real prices when available, intelligent predictions when needed
  • +
+ +

Configuration Parameters

+ +

price.energyforecast_enabled

+ + + + + + + + + + + + + + + + + + + + + +
Parameterprice.energyforecast_enabled
DescriptionEnable smart price prediction with energyforecast.de
Valid Values + true - Enable smart price prediction
+ false - Disable (use simple price repetition) +
Defaultfalse
NotesOnly used when primary source lacks tomorrow's prices
+ +

price.energyforecast_token

+ + + + + + + + + + + + + + + + + + + + + +
Parameterprice.energyforecast_token
DescriptionAPI token from energyforecast.de
How to GetRegister at energyforecast.de/api_keys
Defaultdemo_token
Notes + Demo token: Limited rate limits, for testing only
+ Free tier: 48-hour forecasts (sufficient for most users)
+ Paid tier: 96-hour forecasts + higher limits

+ EOS Connect automatically validates and warns if using demo_token in production. +
+ +

price.energyforecast_market_zone

+ + + + + + + + + + + + + + + + + + + + + +
Parameterprice.energyforecast_market_zone
DescriptionEuropean EPEX spot market zone
Valid Values + DE-LU - Germany/Luxembourg
+ AT - Austria
+ FR - France
+ NL - Netherlands
+ BE - Belgium
+ PL - Poland
+ DK1 - Denmark West
+ DK2 - Denmark East +
DefaultDE-LU
NotesMust match your location for accurate EPEX spot prices. Invalid zones automatically default to DE-LU with a warning.
+ +

Example Configuration

+
price:
+  source: tibber
+  token: "YOUR_TIBBER_TOKEN"
+  feed_in_source: fixed
+  feed_in_price: 0.08
+  feed_in_negative_price_switch: true
+
+  # Smart price prediction with energyforecast.de
+  energyforecast_enabled: true
   energyforecast_token: "YOUR_ENERGYFORECAST_TOKEN"
   energyforecast_market_zone: "DE-LU"
@@ -1955,7 +2588,7 @@

Troubleshooting

- +

PV Forecast Configuration

Configure solar generation forecasts from various providers.

@@ -1984,7 +2617,8 @@

pv_forecast_source.source

evcc - EVCC integration
solcast - Solcast
victron - Victron VRM API
- default - Uses akkudoktor + timeseries - HTTP endpoint returning timeseries data (Home Assistant, custom API, etc.)
+ default - Built-in default forecast with fixed values (no external configuration needed) @@ -1993,6 +2627,25 @@

pv_forecast_source.source

+
+ PV Installations Configuration: Only location-based forecast sources require PV Installations configuration: +
    +
  • Required for: akkudoktor, openmeteo, openmeteo_local, forecast_solar — these need latitude, longitude, and panel specifications
  • +
  • Not required for: default, solcast, victron, evcc, timeseries — these configure their data elsewhere: +
      +
    • default: Uses built-in fixed forecast values (no external data source)
    • +
    • solcast, victron: Configure resource IDs in the PV Source section
    • +
    • evcc: Retrieves data from your configured EVCC instance
    • +
    • timeseries: Uses direct data from an HTTP endpoint or Home Assistant
    • +
    +
  • +
+
+ +
+ Unified Timeseries Source: The timeseries source enables fetching PV forecasts from any HTTP endpoint that returns timeseries data. This includes Home Assistant, custom HTTP APIs, and other integration points. See Timeseries Data Source Guide below for detailed configuration and examples. +
+

pv_forecast_source.use_real_data_correction

@@ -2053,6 +2706,114 @@

pv_forecast_source.api_key

+

pv_forecast_source.data_url

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterpv_forecast_source.data_url
DescriptionFull HTTP endpoint URL for fetching PV forecast timeseries data
RequiredYes, when source: timeseries
FormatMust start with http:// or https://
Example (Home Assistant)http://homeassistant.local:8123/api/states/sensor.pv_forecast
Example (Custom API)https://api.example.com/v1/pv/forecast
Hot-reloadable Yes — changes take effect immediately without restart
+ +

pv_forecast_source.data_path

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterpv_forecast_source.data_path
DescriptionJSON path to the timeseries array in the API response (using dot notation)
RequiredYes, when source: timeseries
FormatDot notation: object.nested.array
Example (Home Assistant)attributes.data — accesses response["attributes"]["data"]
Example (Nested Array)forecast[0].values — accesses response["forecast"][0]["values"]
Data Format ExpectedJSON array: [{start, end, value}, ...]
+ start: ISO8601 timestamp (e.g., "2024-06-12T14:00:00Z")
+ end: ISO8601 timestamp (e.g., "2024-06-12T15:00:00Z")
+ value: Power in Wh (e.g., 3600000 for 1 kWh) +
Hot-reloadable Yes — changes take effect immediately without restart
+ +

pv_forecast_source.data_token

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameterpv_forecast_source.data_token
DescriptionOptional Bearer token for API authentication (sent in Authorization header)
RequiredNo — only if the API requires authentication
FormatBearer token string (ASCII only)
Example (Home Assistant)eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
HTTP BehaviorIf provided, sends header: Authorization: Bearer <token>
Security NoteToken must contain only ASCII characters (Latin-1 compatible) for HTTP header transmission
Hot-reloadable Yes — changes take effect immediately without restart
+
Solcast Rate Limits: Free accounts are limited to 10 API calls per day. EOS Connect automatically extends update intervals to 2.5 hours when using Solcast (9.6 calls/day).
@@ -2458,7 +3219,7 @@

How to Determine Your Horizon Values

- +

MQTT Configuration

Configure MQTT broker connection and Home Assistant Auto Discovery.

@@ -2784,7 +3545,7 @@

Two Different Timing Controls

Time Slot Configuration & Optimizer Constraints

The time_frame setting determines the granularity of optimization data:

- + @@ -2826,6 +3587,406 @@

Time Slot Configuration & Optimizer Constraints

+ +
+

EVCC as Price Source (price.source: evcc)

+

Use EVCC (Electric Vehicle Charging Controller) as your price source when you have an EVCC instance managing your electricity pricing or tariff data.

+ +

When to Use EVCC Price Source

+
    +
  • EVCC Integration: You already have EVCC configured with tariff/pricing data
  • +
  • Unified Control: Want to use the same EVCC instance for both EV charging and battery optimization
  • +
  • Automatic Tariff Sync: Tariff rates automatically synchronize between EVCC and EOS Connect
  • +
  • Multiple Tariffs: Need access to both grid consumption prices and feed-in tariffs from EVCC
  • +
+ +

Configuration Steps

+ +
+ Prerequisite: EVCC interface must be configured first (see EVCC Configuration). The price source will use the same evcc.url. +
+ +
    +
  1. Configure EVCC Interface: +
      +
    • Set evcc.enabled: true
    • +
    • Set evcc.url to your EVCC instance (e.g., http://evcc.local:7070)
    • +
    +
  2. +
  3. Enable EVCC Price Source: +
      +
    • Set price.source: evcc
    • +
    • price.token should remain empty (EVCC API doesn't require authentication)
    • +
    +
  4. +
  5. Optional - Add Feed-In Tariff (EVCC 0.132+): +
      +
    • If your EVCC instance publishes /api/tariff/feedin data, it will be used automatically for feed-in pricing
    • +
    • Set price.feed_in_source: evcc to use EVCC's published feed-in tariff
    • +
    +
  6. +
+ +

EVCC Price Data Format

+

EVCC provides electricity prices via the grid tariff API endpoint with 15-minute resolution:

+
+
GET http://evcc.local:7070/api/tariff/grid
+
+Response:
+{
+  "rates": [
+    {
+      "start": "2026-06-15T00:00:00+02:00",
+      "end": "2026-06-15T00:15:00+02:00",
+      "value": 0.3236
+    },
+    {
+      "start": "2026-06-15T00:15:00+02:00",
+      "end": "2026-06-15T00:30:00+02:00",
+      "value": 0.3113
+    }
+  ]
+}
+
+ +
Time Frame
+ + + + + + + + + + + + + + + + + + + + +
FieldDescriptionFormat
startStart timestamp of the price periodISO8601 with timezone (e.g., +02:00)
endEnd timestamp of the price periodISO8601 with timezone
valueElectricity price for that periodEUR/kWh (automatically converted to EUR/Wh internally)
+ +

Price Unit Conversion

+

Important: EVCC provides prices in EUR/kWh. EOS Connect automatically converts these to EUR/Wh for internal calculations:

+
    +
  • EVCC price: 0.120 EUR/kWh
  • +
  • Converted: 0.000120 EUR/Wh (÷ 1000)
  • +
  • Display: Always shown in EUR/kWh in the web UI
  • +
+ +

Automatic Updates

+

EVCC prices are fetched:

+
    +
  • On startup: Initial price load
  • +
  • Every refresh cycle: Default 10 minutes (configurable via refresh_time)
  • +
  • On configuration change: When price.source is changed to/from evcc, prices are fetched immediately (hot-reload)
  • +
  • On EVCC URL change: Automatic reconnection with new URL
  • +
+ +

Troubleshooting EVCC Price Source

+ + + + + + + + + + + + + + + + + + + + + +
IssueSolution
No prices showing +
    +
  • Verify evcc.enabled: true and evcc.url is correct
  • +
  • Check price.source: evcc
  • +
  • Verify EVCC is running and accessible at http://evcc.url:7070/api/tariff/grid
  • +
  • Check application logs for connection errors
  • +
+
EVCC connection timeout +
    +
  • Verify network connectivity to EVCC server
  • +
  • Check firewall rules allowing port 7070
  • +
  • Verify EVCC service is running: systemctl status evcc
  • +
+
Wrong prices after EVCC update +
    +
  • Trigger a manual config save to reload prices (hot-reload)
  • +
  • Check EVCC tariff configuration is correct
  • +
  • Verify price format matches expected EUR/kWh
  • +
+
Price source won't switch +
    +
  • Verify EVCC interface is not disabled or in error state
  • +
  • Check web UI Settings → Price Configuration for any errors
  • +
  • If using hot-reload, wait 5-10 seconds for connection to establish
  • +
+
+ +

Example Configuration (config.yaml)

+
+
evcc:
+  enabled: true
+  url: "http://evcc.local:7070"
+
+price:
+  source: evcc
+  token:                    # Leave empty for EVCC
+  feed_in_source: evcc      # Optional: if EVCC publishes /api/tariff/feedin
+
+ + +
+

Unified Timeseries Data Source Guide

+

EOS Connect supports a flexible timeseries source for both electricity prices and PV forecasts. This unified approach allows you to fetch data from Home Assistant, custom HTTP APIs, or any endpoint that returns timeseries data in a standard format.

+ +

Timeseries Data Format

+

The timeseries source expects JSON data in this standard format:

+
+
{
+  "data": [
+    {
+      "start": "2024-06-12T14:00:00Z",
+      "end": "2024-06-12T15:00:00Z",
+      "value": 0.25
+    },
+    {
+      "start": "2024-06-12T15:00:00Z",
+      "end": "2024-06-12T16:00:00Z",
+      "value": 0.28
+    }
+  ]
+}
+
+ + + + + + + + + + + + + + + + + + + + + + +
FieldDescriptionFormat
startStart timestamp of the timeslotISO8601 (e.g., "2024-06-12T14:00:00Z" or Unix timestamp)
endEnd timestamp of the timeslotISO8601 or Unix timestamp
valueFor Prices: Price in EUR/Wh (e.g., 0.25 for 25 ct/kWh)
+ For PV: Generated power in Wh for the timeslot
Number (float)
+ +
+ Time Resolution: EOS Connect auto-detects whether the timeseries is 15-minute (900s) or hourly (3600s) based on the timestamp differences. It automatically converts 15-minute data to hourly by averaging 4 consecutive values. +
+ +

Home Assistant Integration

+

Home Assistant entity attributes can be accessed using the dot notation in data_path.

+ +

Price Example (Home Assistant)

+ + + + + + + + + + + + + + + + + + + + + +
ConfigurationValue
price.sourcetimeseries
price.data_urlhttp://homeassistant.local:8123/api/states/sensor.grid_prices
price.data_pathattributes.data
price.data_tokenYour HA Long-Lived Access Token (optional if local)
+ +

HA Entity Response Example:

+
+
{
+  "entity_id": "sensor.grid_prices",
+  "attributes": {
+    "data": [
+      {"start": "2024-06-12T14:00:00Z", "end": "2024-06-12T15:00:00Z", "value": 0.25},
+      {"start": "2024-06-12T15:00:00Z", "end": "2024-06-12T16:00:00Z", "value": 0.28}
+    ]
+  }
+}
+
+ +

PV Forecast Example (Home Assistant)

+ + + + + + + + + + + + + + + + + + + + + +
ConfigurationValue
pv_forecast_source.sourcetimeseries
pv_forecast_source.data_urlhttp://homeassistant.local:8123/api/states/sensor.solar_forecast
pv_forecast_source.data_pathattributes.forecast
pv_forecast_source.data_tokenYour HA Long-Lived Access Token (optional if local)
+ +

Custom HTTP API Integration

+

Connect to custom HTTP endpoints that return timeseries data. Supports authentication via Bearer tokens.

+ +

Example: Custom REST API

+ + + + + + + + + + + + + + + + + + + + + +
ConfigurationValue
price.sourcetimeseries
price.data_urlhttps://api.example.com/v1/prices/current
price.data_pathprices (or nested path like result.data)
price.data_tokenYour API token (if required)
+ +

API Response Example:

+
+
{
+  "prices": [
+    {"start": "2024-06-12T14:00:00Z", "end": "2024-06-12T15:00:00Z", "value": 0.25},
+    {"start": "2024-06-12T15:00:00Z", "end": "2024-06-12T16:00:00Z", "value": 0.28}
+  ]
+}
+
+ +

Testing Your Timeseries Configuration

+

Use the built-in connectivity test to validate your timeseries configuration before enabling it.

+ +

HTTP Request:

+
+
POST /api/config/test-timeseries
+Content-Type: application/json
+
+{
+  "source": "price",
+  "data_url": "http://homeassistant.local:8123/api/states/sensor.grid_prices",
+  "data_path": "attributes.data",
+  "data_token": "eyJhbGciOiJIUzI1NiI..."
+}
+
+ +

Response (Success):

+
+
{
+  "success": true,
+  "message": "Successfully fetched 24 price values",
+  "sample_count": 24,
+  "first_entry": {
+    "start": "2024-06-12T14:00:00Z",
+    "end": "2024-06-12T15:00:00Z",
+    "value": 0.25
+  },
+  "last_entry": {
+    "start": "2024-06-13T14:00:00Z",
+    "end": "2024-06-13T15:00:00Z",
+    "value": 0.22
+  }
+}
+
+ +

Error Handling & Retries

+

EOS Connect automatically handles transient API failures:

+
    +
  • Automatic Retries: Up to 3 attempts with exponential backoff (0.5s → 2s → 4s)
  • +
  • Cache Fallback: If API fails, uses the last successfully fetched data
  • +
  • Graceful Degradation: Continues running with cached data if available
  • +
  • Error Logging: Issues are logged with timestamps for troubleshooting
  • +
+ +

Hot-Reload Support

+

All timeseries configuration fields support hot-reload — changes take effect immediately without restarting the application:

+
    +
  • price.data_url, price.data_path, price.data_token
  • +
  • pv_forecast_source.data_url, pv_forecast_source.data_path, pv_forecast_source.data_token
  • +
+

Changes are applied within 1-2 seconds on the next update cycle.

+ +

JSON Path Reference

+

Examples of how to specify data_path for different response structures:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Response Structuredata_pathNotes
{"data": [...]}dataTop-level array
{"attributes": {"data": [...]}}attributes.dataNested object (Home Assistant pattern)
{"result": {"prices": [...]}}result.pricesMultiple levels of nesting
{"forecast": [{"data": [...]}]}forecast[0].dataArray with indexed access
+
+

Configuration Scenarios

@@ -2993,23 +4154,37 @@

Scenario 7: Custom Inverter Control via Home Assistant

Web UI Configuration:
    +
  1. Data Source Section: Set type: homeassistant, url, and access_token. These credentials are automatically used by the inverter.
  2. Inverter Section — Type: Select "homeassistant"
  3. Power Limits: Set max grid charge and PV charge rates in watts
  4. -
  5. Service Call Mappings: For each control state (charge_from_grid, avoid_discharge, discharge_allowed): +
  6. Service Call Sequences: For each control state (charge_from_grid, avoid_discharge, discharge_allowed):
      -
    • Define the sequence of HA service calls
    • +
    • Enter a valid JSON array of HA service call objects
    • Each call specifies: service domain, entity ID, and data
    • Use {{ power }} template variable in data_template for dynamic power from EOS
    • +
    • The web UI provides a JSON editor with real-time syntax validation
-

Example service call sequence for Marstek:

-
    -
  • select.select_option → entity: select.mt1_betriebsmodus → option: "Manuell"
  • -
  • select.select_option → entity: select.mt1_erzwungene_operation → option: "Zwangsladen"
  • -
  • number.set_value → entity: number.mt1_ziel_leistung_manuell → value: {{ power }}
  • -
-

Result: EOS Connect sends optimized battery commands to HA, which executes your custom service call sequence on the inverter.

+

Example JSON for Marstek (charge_from_grid mode):

+
[
+  {
+    "service": "select.select_option",
+    "entity_id": "select.mt1_betriebsmodus",
+    "data": {"option": "Manuell"}
+  },
+  {
+    "service": "select.select_option",
+    "entity_id": "select.mt1_erzwungene_operation",
+    "data": {"option": "Zwangsladen"}
+  },
+  {
+    "service": "number.set_value",
+    "entity_id": "number.mt1_ziel_leistung_manuell",
+    "data_template": {"value": "{{ power }}"}
+  }
+]
+

Result: EOS Connect sends optimized battery commands to Home Assistant, which executes your custom service call sequence on the inverter every control cycle.

For Marstek Setup: See this complete ESPHome integration guide for connecting a Marstek M1 via RS485/Modbus. @@ -3035,10 +4210,10 @@

Complete Parameter Reference

if (!container) return; try { const res = await fetch('../assets/data/config_schema.json'); - if (!res.ok) { + if (!res.ok) { console.error('Failed to load schema:', res.status, res.statusText); - container.innerHTML = '

Parameter reference not available (HTTP ' + res.status + ').

'; - return; + container.innerHTML = '

Parameter reference not available (HTTP ' + res.status + ').

'; + return; } const schemaData = await res.json(); const fields = schemaData.fields || schemaData; diff --git a/docs/user-guide/index.html b/docs/user-guide/index.html index 8db8e0f7..b081dd04 100644 --- a/docs/user-guide/index.html +++ b/docs/user-guide/index.html @@ -19,7 +19,7 @@