From 58d70e417bc2bff6d268314c9c2d57584b6ab3b3 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Tue, 30 Dec 2025 22:08:21 +0100 Subject: [PATCH] feat: add Home Assistant and NodeRED adapters (#764) Adapters for Home Assistant and NodeRED integration are added. Akkudoktor-EOS can now be run as Home Assistant add-on and standalone. As Home Assistant add-on EOS uses ingress to fully integrate the EOSdash dashboard in Home Assistant. The fix includes several bug fixes that are not directly related to the adapter implementation but are necessary to keep EOS running properly and to test and document the changes. * fix: development version scheme The development versioning scheme is adaptet to fit to docker and home assistant expectations. The new scheme is x.y.z and x.y.z.dev. Hash is only digits as expected by home assistant. Development version is appended by .dev as expected by docker. * fix: use mean value in interval on resampling for array When downsampling data use the mean value of all values within the new sampling interval. * fix: default battery ev soc and appliance wh Make the genetic simulation return default values for the battery SoC, electric vehicle SoC and appliance load if these assets are not used. * fix: import json string Strip outer quotes from JSON strings on import to be compliant to json.loads() expectation. * fix: default interval definition for import data Default interval must be defined in lowercase human definition to be accepted by pendulum. * fix: clearoutside schema change * feat: add adapters for integrations Adapters for Home Assistant and NodeRED integration are added. Akkudoktor-EOS can now be run as Home Assistant add-on and standalone. As Home Assistant add-on EOS uses ingress to fully integrate the EOSdash dashboard in Home Assistant. * feat: allow eos to be started with root permissions and drop priviledges Home assistant starts all add-ons with root permissions. Eos now drops root permissions if an applicable user is defined by paramter --run_as_user. The docker image defines the user eos to be used. * feat: make eos supervise and monitor EOSdash Eos now not only starts EOSdash but also monitors EOSdash during runtime and restarts EOSdash on fault. EOSdash logging is captured by EOS and forwarded to the EOS log to provide better visibility. * feat: add duration to string conversion Make to_duration to also return the duration as string on request. * chore: Use info logging to report missing optimization parameters In parameter preparation for automatic optimization an error was logged for missing paramters. Log is now down using the info level. * chore: make EOSdash use the EOS data directory for file import/ export EOSdash use the EOS data directory for file import/ export by default. This allows to use the configuration import/ export function also within docker images. * chore: improve EOSdash config tab display Improve display of JSON code and add more forms for config value update. * chore: make docker image file system layout similar to home assistant Only use /data directory for persistent data. This is handled as a docker volume. The /data volume is mapped to ~/.local/share/net.akkudoktor.eos if using docker compose. * chore: add home assistant add-on development environment Add VSCode devcontainer and task definition for home assistant add-on development. * chore: improve documentation --- .devcontainer/devcontainer.json | 39 + .env | 22 +- .github/workflows/bump-version.yml | 4 +- .vscode/tasks.json | 58 ++ CHANGELOG.md | 98 +++ DOCS.md | 28 + Dockerfile | 86 ++- Makefile | 60 +- README.md | 45 ++ build.yaml | 14 + config.yaml | 57 ++ docker-compose.ps1 | 37 + docker-compose.yaml | 14 +- docs/_generated/config.md | 1 + docs/_generated/configadapter.md | 223 ++++++ docs/_generated/configdevices.md | 92 ++- docs/_generated/configelecprice.md | 31 +- docs/_generated/configexample.md | 50 +- docs/_generated/configfeedintariff.md | 25 +- docs/_generated/configgeneral.md | 37 +- docs/_generated/configload.md | 30 +- docs/_generated/configlogging.md | 2 +- docs/_generated/configmeasurement.md | 2 +- docs/_generated/configoptimization.md | 28 +- docs/_generated/configprediction.md | 14 - docs/_generated/configpvforecast.md | 18 +- docs/_generated/configweather.md | 25 +- docs/_generated/openapi.md | 2 +- docs/_static/azimuth.gif | Bin 0 -> 28721 bytes docs/_static/horizon_eyefish_en.png | Bin 0 -> 416016 bytes docs/_static/slope.gif | Bin 0 -> 25022 bytes docs/akkudoktoreos/adapter.md | 23 + .../adapter/adapterhomeassistant.md | 126 +++ docs/akkudoktoreos/adapter/adapternodered.md | 4 + docs/akkudoktoreos/configuration.md | 2 +- docs/akkudoktoreos/optimauto.md | 3 +- docs/akkudoktoreos/prediction.md | 14 +- docs/akkudoktoreos/resource.md | 2 +- docs/develop/develop.md | 104 +++ docs/develop/install.md | 116 ++- docs/develop/release.md | 6 +- docs/develop/update.md | 109 +++ docs/index.md | 1 + openapi.json | 728 +++++++++++++++++- pyproject.toml | 3 + repository.yaml | 9 + requirements-dev.txt | 4 +- scripts/bump_dev_version.py | 19 +- scripts/update_version.py | 28 +- src/akkudoktoreos/adapter/adapter.py | 94 +++ src/akkudoktoreos/adapter/adapterabc.py | 160 ++++ src/akkudoktoreos/adapter/homeassistant.py | 524 +++++++++++++ src/akkudoktoreos/adapter/nodered.py | 128 +++ src/akkudoktoreos/config/config.py | 184 +++-- src/akkudoktoreos/config/configmigrate.py | 5 + src/akkudoktoreos/core/cachesettings.py | 5 +- src/akkudoktoreos/core/coreabc.py | 41 + src/akkudoktoreos/core/dataabc.py | 99 ++- src/akkudoktoreos/core/decorators.py | 4 +- src/akkudoktoreos/core/emplan.py | 58 +- src/akkudoktoreos/core/ems.py | 71 +- src/akkudoktoreos/core/logging.py | 2 +- src/akkudoktoreos/core/pydantic.py | 80 +- src/akkudoktoreos/core/version.py | 23 +- src/akkudoktoreos/devices/devices.py | 9 +- src/akkudoktoreos/devices/genetic/battery.py | 2 +- .../optimization/genetic/genetic.py | 20 +- .../optimization/genetic/geneticparams.py | 67 +- .../optimization/genetic/geneticsolution.py | 68 +- .../optimization/optimization.py | 18 +- src/akkudoktoreos/prediction/elecprice.py | 8 +- src/akkudoktoreos/prediction/feedintariff.py | 8 +- src/akkudoktoreos/prediction/load.py | 8 +- src/akkudoktoreos/prediction/loadimport.py | 2 +- src/akkudoktoreos/prediction/loadvrm.py | 2 +- src/akkudoktoreos/prediction/prediction.py | 17 +- src/akkudoktoreos/prediction/pvforecast.py | 6 + .../prediction/pvforecastakkudoktor.py | 14 - src/akkudoktoreos/prediction/pvforecastvrm.py | 2 +- src/akkudoktoreos/prediction/weather.py | 8 +- .../prediction/weatherclearoutside.py | 12 +- src/akkudoktoreos/server/dash/about.py | 3 + src/akkudoktoreos/server/dash/admin.py | 67 +- src/akkudoktoreos/server/dash/bokeh.py | 30 +- src/akkudoktoreos/server/dash/components.py | 416 +++++++++- .../server/dash/configuration.py | 315 +++++--- src/akkudoktoreos/server/dash/context.py | 169 ++++ src/akkudoktoreos/server/dash/footer.py | 6 +- src/akkudoktoreos/server/dash/markdown.py | 160 ++-- src/akkudoktoreos/server/dash/plan.py | 13 +- src/akkudoktoreos/server/eos.py | 183 +---- src/akkudoktoreos/server/eosdash.py | 230 ++++-- src/akkudoktoreos/server/rest/starteosdash.py | 269 +++++++ src/akkudoktoreos/server/rest/tasks.py | 4 +- src/akkudoktoreos/server/server.py | 176 +++++ src/akkudoktoreos/utils/datetimeutil.py | 250 ++++-- tests/conftest.py | 42 +- tests/test_adapter.py | 79 ++ tests/test_adapternodered.py | 127 +++ tests/test_config.py | 8 +- tests/test_dataabc.py | 27 + tests/test_datetimeutil.py | 221 ++++-- tests/test_emplan.py | 212 ++++- tests/test_eosdashconfig.py | 26 +- tests/test_eosdashserver.py | 162 ++++ tests/test_homeassistant.py | 343 +++++++++ tests/test_logging.py | 6 +- tests/test_server.py | 140 ++-- tests/test_version.py | 56 +- tests/testdata/optimize_result_1.json | 76 +- tests/testdata/optimize_result_2.json | 6 +- 111 files changed, 6815 insertions(+), 1199 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .vscode/tasks.json create mode 100644 DOCS.md create mode 100644 build.yaml create mode 100644 config.yaml create mode 100644 docker-compose.ps1 create mode 100644 docs/_generated/configadapter.md create mode 100644 docs/_static/azimuth.gif create mode 100644 docs/_static/horizon_eyefish_en.png create mode 100644 docs/_static/slope.gif create mode 100644 docs/akkudoktoreos/adapter.md create mode 100644 docs/akkudoktoreos/adapter/adapterhomeassistant.md create mode 100644 docs/akkudoktoreos/adapter/adapternodered.md create mode 100644 docs/develop/update.md create mode 100644 repository.yaml create mode 100644 src/akkudoktoreos/adapter/adapter.py create mode 100644 src/akkudoktoreos/adapter/adapterabc.py create mode 100644 src/akkudoktoreos/adapter/homeassistant.py create mode 100644 src/akkudoktoreos/adapter/nodered.py create mode 100644 src/akkudoktoreos/server/dash/context.py create mode 100644 src/akkudoktoreos/server/rest/starteosdash.py create mode 100644 tests/test_adapter.py create mode 100644 tests/test_adapternodered.py create mode 100644 tests/test_homeassistant.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c391ce2 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,39 @@ +{ + "name": "Devcontainer for Akkudoktor-EOS add-on repository", + "image": "ghcr.io/home-assistant/devcontainer:2-addons", + "appPort": ["7123:8123", "7357:4357", "8503:8503", "8504:8504"], + "remoteUser": "root", + "postStartCommand": "bash -c 'echo \"127.0.0.1 $(hostname)\" >> /etc/hosts' && bash devcontainer_bootstrap", + "runArgs": [ + "-e", + "GIT_EDITOR=code --wait", + "--privileged", + "--hostname=homeassistant" + ], + "containerEnv": { + "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}" + }, + "workspaceFolder": "/mnt/supervisor/addons/local/${localWorkspaceFolderBasename}", + "workspaceMount": "source=${localWorkspaceFolder},target=${containerWorkspaceFolder},type=bind,consistency=cached", + "customizations": { + "vscode": { + "extensions": ["timonwong.shellcheck", "esbenp.prettier-vscode"], + "settings": { + "terminal.integrated.profiles.linux": { + "zsh": { + "path": "/usr/bin/zsh" + } + }, + "terminal.integrated.defaultProfile.linux": "zsh", + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true + } + } + }, + "mounts": [ + "type=volume,target=/var/lib/docker", + "type=volume,target=/mnt/supervisor" + ] +} diff --git a/.env b/.env index c58592f..2062437 100644 --- a/.env +++ b/.env @@ -1,5 +1,21 @@ -EOS_VERSION=main +# Default environment settings + +# ----------------------------------------------------------------------------- +# Docker Compose defaults +# ----------------------------------------------------------------------------- + +# Host data directory for EOS (Linux / macOS) +# Can be overridden by environment variables (e.g. PowerShell on Windows) +DOCKER_COMPOSE_DATA_DIR=${HOME}/.local/share/net.akkudoktor.eos + +# ----------------------------------------------------------------------------- +# Image / build +# ----------------------------------------------------------------------------- +VERSION=0.2.0.dev70048701 +PYTHON_VERSION=3.13.9 + +# ----------------------------------------------------------------------------- +# Ports +# ----------------------------------------------------------------------------- EOS_SERVER__PORT=8503 EOS_SERVER__EOSDASH_PORT=8504 - -PYTHON_VERSION=3.12.6 diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index f460a84..42ba75b 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -47,7 +47,7 @@ jobs: - name: Update files and commit run: | # Define files to update - UPDATE_FILES="haaddon/config.yaml" + UPDATE_FILES="config.yaml .env" # Call general Python version replacement script python scripts/update_version.py "${{ steps.calc.outputs.version }}" $UPDATE_FILES @@ -62,6 +62,7 @@ jobs: else git commit -m "chore: bump version to ${{ steps.calc.outputs.version }}" git push + fi # --- Step 6: Create release tag --- - name: Create release tag if it does not exist @@ -97,3 +98,4 @@ jobs: else git commit -m "chore: bump dev version to ${VERSION_BASE}" git push + fi diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6acc3aa --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,58 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Home Assistant", + "type": "shell", + "command": "supervisor_run", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Start Addon", + "type": "shell", + "command": "ha addons stop \"local_${input:addonName}\"; ha addons start \"local_${input:addonName}\"; docker logs --follow \"addon_local_${input:addonName}\"", + "group": { + "kind": "test", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [], + "runOptions": { + "reevaluateOnRerun": false + } + }, + { + "label": "Rebuild and Start Addon", + "type": "shell", + "command": "ha addons rebuild \"local_${input:addonName}\"; ha addons start \"local_${input:addonName}\"; docker logs --follow \"addon_local_${input:addonName}\"", + "group": { + "kind": "test", + "isDefault": false + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "addonName", + "type": "pickString", + "description": "Name of addon (to add your addon to this list, please edit .vscode/tasks.json)", + "options": ["eos"] + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ade6d..e8da6ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,104 @@ All notable changes to the akkudoktoreos project will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.0 (2025-12-??) + +Adapters for Home Assistant and NodeRed integration are added. These adapters +provide a simplified interface to these HEMS besides the standard REST interface. +Akkudoktor-EOS can now be run as Home Assistant add-on and standalone. +As Home Assistant add-on EOS uses ingress to fully integrate the EOSdash dashboard +in Home Assistant. + +In addition, bugs were fixed and new features were added. + +### Feat + +- add adapters for integrations + + Adapters for Home Assistant and NodeRED integration are added. + Akkudoktor-EOS can now be run as Home Assistant add-on and standalone. + + As Home Assistant add-on EOS uses ingress to fully integrate the EOSdash dashboard + in Home Assistant. + +- allow eos to be started with root permissions and drop priviledges + + Home assistant starts all add-ons with root permissions. Eos now drops + root permissions if an applicable user is defined by paramter --run_as_user. + The docker image defines the user eos to be used. + +- make eos supervise and monitor EOSdash + + Eos now not only starts EOSdash but also monitors EOSdash during runtime + and restarts EOSdash on fault. EOSdash logging is captured by EOS + and forwarded to the EOS log to provide better visibility. + +- add duration to string conversion + + Make to_duration to also return the duration as string on request. + +### Fixed + +- development version scheme + + The development versioning scheme is adaptet to fit to docker and + home assistant expectations. The new scheme is x.y.z and x.y.z.dev. + Hash is only digits as expected by home assistant. Development version + is appended by .dev as expected by docker. + +- use mean value in interval on resampling for array + + When downsampling data use the mean value of all values within the new + sampling interval. + +- default battery ev soc and appliance wh + + Make the genetic simulation return default values for the + battery SoC, electric vehicle SoC and appliance load if these + assets are not used. + +- import json string + + Strip outer quotes from JSON strings on import to be compliant to json.loads() + expectation. + +- default interval definition for import data + + Default interval must be defined in lowercase human definition to + be accepted by pendulum. + +- clearoutside schema change + +### Chore + +- Use info logging to report missing optimization parameters + + In parameter preparation for automatic optimization an error was logged for missing paramters. + Log is now down using the info level. + +- make EOSdash use the EOS data directory for file import/ export + + EOSdash use the EOS data directory for file import/ export by default. + This allows to use the configuration import/ export function also + within docker images. + +- improve EOSdash config tab display + + Improve display of JSON code and add more forms for config value update. + +- make docker image file system layout similar to home assistant + + Only use /data directory for persistent data. This is handled as a + docker volume. The /data volume is mapped to ~/.local/share/net.akkudoktor.eos + if using docker compose. + +- add home assistant add-on development environment + + Add VSCode devcontainer and task definition for home assistant add-on + development. + +- improve documentation + ## 0.2.0 (2025-11-09) The most important new feature is **automatic optimization**. diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 0000000..09cf843 --- /dev/null +++ b/DOCS.md @@ -0,0 +1,28 @@ + + +# Akkudoktor-EOS – Home Assistant Add-on Documentation + +## Overview + +**Akkudoktor-EOS** is a Home Assistant add-on that optimizes household energy flows such as battery storage, photovoltaic (PV) generation, grid usage, and controllable loads. + +The add-on is designed to be *practical and user-focused*: once configured, it runs automatically in the background and integrates seamlessly with Home Assistant. Advanced optimization logic and simulations are handled internally — no programming required. + +EOS is especially suited for users who: + +* Have a home battery and/or PV system +* Want to use forecasts to make smarter energy decisions +* Prefer a transparent, configurable, and locally running solution + + +## Features + +* šŸ”‹ Battery and storage optimization +* ā˜€ļø PV forecast integration +* ⚔ Load and power profile optimization +* 🧠 Forecast-based optimization +* 🧩 Modular simulation and controller architecture +* šŸ  Native Home Assistant add-on integration +* šŸ“Š Designed for extensibility with custom energy devices diff --git a/Dockerfile b/Dockerfile index 5368978..a0e530c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,29 @@ # syntax=docker/dockerfile:1.7 # Dockerfile -# Set base image first +# Support both Home Assistant builds and standalone builds +# Only Debian based images are supported (no Alpine) +ARG BUILD_FROM ARG PYTHON_VERSION=3.13.9 -FROM python:${PYTHON_VERSION}-slim -LABEL source="https://github.com/Akkudoktor-EOS/EOS" +# If BUILD_FROM is set (Home Assistant), use it; otherwise use python-slim +FROM ${BUILD_FROM:-python:${PYTHON_VERSION}-slim} + +LABEL \ + io.hass.version="VERSION" \ + io.hass.type="addon" \ + io.hass.arch="aarch64|amd64" \ + source="https://github.com/Akkudoktor-EOS/EOS" -ENV MPLCONFIGDIR="/tmp/mplconfigdir" ENV EOS_DIR="/opt/eos" -ENV EOS_CACHE_DIR="${EOS_DIR}/cache" -ENV EOS_OUTPUT_DIR="${EOS_DIR}/output" -ENV EOS_CONFIG_DIR="${EOS_DIR}/config" +# Create persistent data directory similar to home assistant add-on +# - EOS_DATA_DIR: Persistent data directory +# - MPLCONFIGDIR: user customizations to Mathplotlib +ENV EOS_DATA_DIR="/data" +ENV EOS_CACHE_DIR="${EOS_DATA_DIR}/cache" +ENV EOS_OUTPUT_DIR="${EOS_DATA_DIR}/output" +ENV EOS_CONFIG_DIR="${EOS_DATA_DIR}/config" +ENV MPLCONFIGDIR="${EOS_DATA_DIR}/mplconfigdir" # Overwrite when starting the container in a production environment ENV EOS_SERVER__EOSDASH_SESSKEY=s3cr3t @@ -23,45 +35,65 @@ ENV MKL_NUM_THREADS=1 ENV PIP_PROGRESS_BAR=off ENV PIP_NO_COLOR=1 +# Generic environment +ENV LANG=C.UTF-8 +ENV VENV_PATH=/opt/venv +# - Use .venv for python commands +ENV PATH="$VENV_PATH/bin:$PATH" + WORKDIR ${EOS_DIR} -RUN adduser --system --group --no-create-home eos \ - && mkdir -p "${MPLCONFIGDIR}" \ - && chown eos "${MPLCONFIGDIR}" \ - && mkdir -p "${EOS_CACHE_DIR}" \ - && chown eos "${EOS_CACHE_DIR}" \ - && mkdir -p "${EOS_OUTPUT_DIR}" \ - && chown eos "${EOS_OUTPUT_DIR}" \ - && mkdir -p "${EOS_CONFIG_DIR}" \ - && chown eos "${EOS_CONFIG_DIR}" +# Create eos user and data directories with eos user permissions +RUN apt-get update && apt-get install -y --no-install-recommends adduser \ + && adduser --system --group --no-create-home eos \ + && mkdir -p "${EOS_DATA_DIR}" \ + && chown -R eos:eos "${EOS_DATA_DIR}" \ + && mkdir -p "${EOS_CACHE_DIR}" "${EOS_OUTPUT_DIR}" "${EOS_CONFIG_DIR}" "${MPLCONFIGDIR}" \ + && chown -R eos:eos "${EOS_CACHE_DIR}" "${EOS_OUTPUT_DIR}" "${EOS_CONFIG_DIR}" "${MPLCONFIGDIR}" -# Install requirements +# Install build dependencies (Debian) +# - System deps +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3-venv \ + gcc g++ gfortran \ + libopenblas-dev liblapack-dev \ + && rm -rf /var/lib/apt/lists/* + +# - Create venv +RUN python3 -m venv ${VENV_PATH} + +# - Upgrade pip inside venv +RUN pip install --upgrade pip setuptools wheel + +# - Install deps COPY requirements.txt . -RUN --mount=type=cache,target=/root/.cache/pip \ - pip install --no-cache-dir -r requirements.txt +RUN pip install --no-cache-dir -r requirements.txt -# Copy source +# Install EOS/ EOSdash +# - Copy source COPY src/ ./src COPY pyproject.toml . -# Create version information +# - Create version information COPY scripts/get_version.py ./scripts/get_version.py RUN python scripts/get_version.py > ./version.txt RUN rm ./scripts/get_version.py RUN echo "Building Akkudoktor-EOS with Python $PYTHON_VERSION" -# Install akkudoktoreos package in editable form (-e) -# pyproject-toml will read the version from version.txt +# - Install akkudoktoreos package in editable form (-e) +# - pyproject-toml will read the version from version.txt RUN pip install --no-cache-dir -e . -USER eos ENTRYPOINT [] -EXPOSE 8503 EXPOSE 8504 +EXPOSE 8503 # Ensure EOS and EOSdash bind to 0.0.0.0 -CMD ["python", "-m", "akkudoktoreos.server.eos", "--host", "0.0.0.0"] +# EOS is started with root provileges. EOS will drop root proviledges and switch to user eos. +CMD ["python", "-m", "akkudoktoreos.server.eos", "--host", "0.0.0.0", "--run_as_user", "eos"] -VOLUME ["${MPLCONFIGDIR}", "${EOS_CACHE_DIR}", "${EOS_OUTPUT_DIR}", "${EOS_CONFIG_DIR}"] +# Persistent data +# (Not recognized by home assistant add-on management, but there we have /data anyway) +VOLUME ["${EOS_DATA_DIR}"] diff --git a/Makefile b/Makefile index 1bb18c8..c0d33b7 100644 --- a/Makefile +++ b/Makefile @@ -10,30 +10,30 @@ all: help # Target to display help information help: @echo "Available targets:" - @echo " venv - Set up a Python 3 virtual environment." - @echo " pip - Install dependencies from requirements.txt." - @echo " pip-dev - Install dependencies from requirements-dev.txt." - @echo " format - Format source code." - @echo " gitlint - Lint last commit message." - @echo " mypy - Run mypy." - @echo " install - Install EOS in editable form (development mode) into virtual environment." - @echo " docker-run - Run entire setup on docker" - @echo " docker-build - Rebuild docker image" - @echo " docs - Generate HTML documentation (in build/docs/html/)." - @echo " read-docs - Read HTML documentation in your browser." - @echo " gen-docs - Generate openapi.json and docs/_generated/*." - @echo " clean-docs - Remove generated documentation." - @echo " run - Run EOS production server in virtual environment." - @echo " run-dev - Run EOS development server in virtual environment (automatically reloads)." - @echo " run-dash - Run EOSdash production server in virtual environment." - @echo " run-dash-dev - Run EOSdash development server in virtual environment (automatically reloads)." - @echo " test - Run tests." - @echo " test-full - Run all tests (e.g. to finalize a commit)." - @echo " test-system - Run tests with system tests enabled." - @echo " test-ci - Run tests as CI does. No user config file allowed." - @echo " test-profile - Run single test optimization with profiling." - @echo " dist - Create distribution (in dist/)." - @echo " clean - Remove generated documentation, distribution and virtual environment." + @echo " venv - Set up a Python 3 virtual environment." + @echo " pip - Install dependencies from requirements.txt." + @echo " pip-dev - Install dependencies from requirements-dev.txt." + @echo " format - Format source code." + @echo " gitlint - Lint last commit message." + @echo " mypy - Run mypy." + @echo " install - Install EOS in editable form (development mode) into virtual environment." + @echo " docker-run - Run entire setup on docker" + @echo " docker-build - Rebuild docker image" + @echo " docs - Generate HTML documentation (in build/docs/html/)." + @echo " read-docs - Read HTML documentation in your browser." + @echo " gen-docs - Generate openapi.json and docs/_generated/*." + @echo " clean-docs - Remove generated documentation." + @echo " run - Run EOS production server in virtual environment." + @echo " run-dev - Run EOS development server in virtual environment (automatically reloads)." + @echo " run-dash - Run EOSdash production server in virtual environment." + @echo " run-dash-dev - Run EOSdash development server in virtual environment (automatically reloads)." + @echo " test - Run tests." + @echo " test-finalize - Run all tests (e.g. to finalize a commit)." + @echo " test-system - Run tests with system tests enabled." + @echo " test-ci - Run tests as CI does. No user config file allowed." + @echo " test-profile - Run single test optimization with profiling." + @echo " dist - Create distribution (in dist/)." + @echo " clean - Remove generated documentation, distribution and virtual environment." @echo " prepare-version - Prepare a version defined in setup.py." # Target to set up a Python 3 virtual environment @@ -79,13 +79,13 @@ gen-docs: pip-dev version-txt # Target to build HTML documentation docs: pip-dev - .venv/bin/pytest --full-run tests/test_docsphinx.py + .venv/bin/pytest --finalize tests/test_docsphinx.py @echo "Documentation build to build/docs/html/." # Target to read the HTML documentation read-docs: @echo "Read the documentation in your browser" - .venv/bin/pytest --full-run tests/test_docsphinx.py + .venv/bin/pytest --finalize tests/test_docsphinx.py .venv/bin/python -m webbrowser build/docs/html/index.html # Clean Python bytecode @@ -108,7 +108,7 @@ clean: clean-docs run: @echo "Starting EOS production server, please wait..." - .venv/bin/python -m akkudoktoreos.server.eos + .venv/bin/python -m akkudoktoreos.server.eos --startup_eosdash true run-dev: @echo "Starting EOS development server, please wait..." @@ -142,7 +142,7 @@ test-system: .venv/bin/pytest --system-test -vs --cov src --cov-report term-missing # Target to run all tests. -test-full: +test-finalize: @echo "Running all tests..." .venv/bin/pytest --finalize @@ -165,10 +165,14 @@ mypy: # Run entire setup on docker docker-run: + @echo "Build and run EOS docker container locally." + @echo "Persistent data (and config) in ${HOME}/.local/share/net.akkudoktor.eos" @docker pull python:3.13.9-slim @docker compose up --remove-orphans docker-build: + @echo "Build EOS docker container locally." + @echo "Persistent data (and config) in ${HOME}/.local/share/net.akkudoktor.eos" @docker pull python:3.13.9-slim @docker compose build diff --git a/README.md b/README.md index e8cdc1c..961be20 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,51 @@ docker run -d \ ## Installation +### Home Assistant add-on + +![Supports aarch64 Architecture][aarch64-shield] +![Supports amd64 Architecture][amd64-shield] + +[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg +[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg + +To install the **Akkudoktor-EOS** add-on in Home Assistant: + +[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FAkkudoktor-EOS%2FEOS) + +1. **Add the repository URL**: + + In Home Assistant, go to: + + ``` + Settings → Add-ons → Add-on Store → ā‹® (top-right menu) → Repositories + ``` + + and enter the URL of this Git repository: + + ``` + https://github.com/Akkudoktor-EOS/EOS + ``` + +2. **Install the add-on**: + + After adding the repository, the add-on will appear in the Add-on Store. Click **Install**. + +3. **Start the add-on**: + + Once installed, click **Start** in the add-on panel. + +4. **Access the dashboard**: + + Click **Open Web UI** in the add-on panel. + +5. **Configure EOS** (optional): + In the dashboard, go to: + + ``` + Config + ``` + ### Docker (Recommended) ```bash diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..f4be9d8 --- /dev/null +++ b/build.yaml @@ -0,0 +1,14 @@ +# Home Assistant - Add-on Build Configuration +# ------------------------------------------- +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-extended-build +# +# The Akkudoktor-EOS add-on repo is special because there is only one add-on and it is in +# the root directory (no add-on folder as usual). + +image: "addon-eos" +build_from: + # Debian based images only + amd64: "ghcr.io/home-assistant/amd64-base-debian:trixie" + aarch64: "ghcr.io/home-assistant/aarch64-base-debian:trixie" +args: + PYTHON_VERSION: "3.13" diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..de5ac70 --- /dev/null +++ b/config.yaml @@ -0,0 +1,57 @@ +# Home Assistant - Add-on Configuration +# ------------------------------------- +# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-configuration +# +# The Akkudoktor-EOS add-on repo is special because there is only one add-on and it is in +# the root directory (no add-on folder as usual). + +name: "Akkudoktor-EOS" +version: "0.2.0.dev70048701" +slug: "eos" +description: "Akkudoktor-EOS add-on" +url: "https://github.com/Akkudoktor-EOS/EOS" + +arch: + - aarch64 + - amd64 + +startup: "application" +init: false +boot: "auto" + +# Map home assistant persistent folders +# /data is automatically mapped - do not list here +map: + - share:rw + - config:rw + +# API access +homeassistant: true +homeassistant_api: true + +# Ports exposed by the add-on +ports: + 8503/tcp: 8503 +# 8504/tcp: 8504 + +ports_description: + 8503/tcp: "EOS REST server" +# 8504/tcp: "EOSdash dashboard server" + +# EOSdash interface (if not ingress) +# webui: "http://[HOST]:[PORT:8504]" + +# EOSdash by ingress +ingress: true +ingress_port: 8504 +ingress_stream: true +panel_icon: "mdi:web" + +# EOS uses several directories under /data - config, cache, output +backup_exclude: + - /data/cache + - /data/output + +# We do not use options +options: {} +schema: {} diff --git a/docker-compose.ps1 b/docker-compose.ps1 new file mode 100644 index 0000000..fb15411 --- /dev/null +++ b/docker-compose.ps1 @@ -0,0 +1,37 @@ +# docker-compose.ps1 +# EOS Docker Compose launcher for Windows + +$ErrorActionPreference = "Stop" + +function Is-WSL2 { + try { + docker info --format '{{.OperatingSystem}}' 2>$null | Select-String -Pattern "WSL2" + } catch { + return $false + } +} + +if (Is-WSL2) { + Write-Host "Detected Docker running on WSL2" + + # Linux path inside WSL + $User = $env:USERNAME.ToLower() + $DockerComposeDataDir = "/home/$User/.local/share/net.akkudoktor.eos" +} +else { + Write-Host "Detected native Windows Docker" + + $HomeDir = [Environment]::GetFolderPath("UserProfile") + $DockerComposeDataDir = Join-Path $HomeDir "AppData\Local\net.akkudoktor.eos" + $DockerComposeDataDir = $DockerComposeDataDir.Replace("\", "/") + + if (-not (Test-Path $DockerComposeDataDir)) { + New-Item -ItemType Directory -Path $DockerComposeDataDir -Force | Out-Null + } +} + +$env:DOCKER_COMPOSE_DATA_DIR = $DockerComposeDataDir + +Write-Host "EOS data dir: '$env:DOCKER_COMPOSE_DATA_DIR'" + +docker compose -f docker-compose.yml up -d diff --git a/docker-compose.yaml b/docker-compose.yaml index 959f6e9..4afe5ae 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,11 +1,10 @@ --- networks: default: - external: true name: "eos" services: eos: - image: "akkudoktor/eos:${EOS_VERSION}" + image: "akkudoktor/eos:${VERSION}" container_name: "akkudoktoreos" read_only: true build: @@ -21,12 +20,12 @@ services: - MKL_NUM_THREADS=1 - PIP_PROGRESS_BAR=off - PIP_NO_COLOR=1 - - EOS_CONFIG_DIR=config - EOS_SERVER__EOSDASH_SESSKEY=s3cr3t - EOS_SERVER__HOST=0.0.0.0 - EOS_SERVER__PORT=8503 - EOS_SERVER__EOSDASH_HOST=0.0.0.0 - EOS_SERVER__EOSDASH_PORT=8504 + - DOCKER_COMPOSE_DATA_DIR=${HOME}/.local/share/net.akkudoktor.eos ulimits: nproc: 65535 nofile: 65535 @@ -38,9 +37,6 @@ services: - "${EOS_SERVER__PORT}:8503" - "${EOS_SERVER__EOSDASH_PORT}:8504" - # Volume mount configuration (optional) - # Example volume mounts (uncomment to use): - # volumes: - # - ./config:/opt/eos/config # Mount local config directory - # - ./cache:/opt/eos/cache # Mount local cache directory - # - ./output:/opt/eos/output # Mount local output directory + # Volume mount configuration + volumes: + - ${DOCKER_COMPOSE_DATA_DIR}:/data:rw diff --git a/docs/_generated/config.md b/docs/_generated/config.md index d31d3df..42a8a61 100644 --- a/docs/_generated/config.md +++ b/docs/_generated/config.md @@ -2,6 +2,7 @@ :maxdepth: 1 :caption: Configuration Table +../_generated/configadapter.md ../_generated/configcache.md ../_generated/configdevices.md ../_generated/configelecprice.md diff --git a/docs/_generated/configadapter.md b/docs/_generated/configadapter.md new file mode 100644 index 0000000..18e4a86 --- /dev/null +++ b/docs/_generated/configadapter.md @@ -0,0 +1,223 @@ +## Adapter Configuration + + +:::{table} adapter +:widths: 10 20 10 5 5 30 +:align: left + +| Name | Environment Variable | Type | Read-Only | Default | Description | +| ---- | -------------------- | ---- | --------- | ------- | ----------- | +| homeassistant | `EOS_ADAPTER__HOMEASSISTANT` | `HomeAssistantAdapterCommonSettings` | `rw` | `required` | Home Assistant adapter settings. | +| nodered | `EOS_ADAPTER__NODERED` | `NodeREDAdapterCommonSettings` | `rw` | `required` | NodeRED adapter settings. | +| provider | `EOS_ADAPTER__PROVIDER` | `Optional[list[str]]` | `rw` | `None` | List of adapter provider id(s) of provider(s) to be used. | +| providers | | `list[str]` | `ro` | `N/A` | Available electricity price provider ids. | +::: + + + +**Example Input** + + + +```json + { + "adapter": { + "provider": [ + "HomeAssistant" + ], + "homeassistant": { + "config_entity_ids": null, + "load_emr_entity_ids": null, + "pv_production_emr_entity_ids": null, + "device_measurement_entity_ids": null, + "device_instruction_entity_ids": null, + "solution_entity_ids": null, + "homeassistant_entity_ids": [], + "eos_solution_entity_ids": [], + "eos_device_instruction_entity_ids": [] + }, + "nodered": { + "host": "127.0.0.1", + "port": 1880 + } + } + } +``` + + + +**Example Output** + + + +```json + { + "adapter": { + "provider": [ + "HomeAssistant" + ], + "homeassistant": { + "config_entity_ids": null, + "load_emr_entity_ids": null, + "pv_production_emr_entity_ids": null, + "device_measurement_entity_ids": null, + "device_instruction_entity_ids": null, + "solution_entity_ids": null, + "homeassistant_entity_ids": [], + "eos_solution_entity_ids": [], + "eos_device_instruction_entity_ids": [] + }, + "nodered": { + "host": "127.0.0.1", + "port": 1880 + }, + "providers": [ + "HomeAssistant", + "NodeRED" + ] + } + } +``` + + +### Common settings for the NodeRED adapter + +The Node-RED adapter sends to HTTP IN nodes. + +This is the example flow: + +[HTTP In \\] -> [Function (parse payload)] -> [Debug] -> [HTTP Response] + +There are two URLs that are used: + +- GET /eos/data_aquisition + The GET is issued before the optimization. +- POST /eos/control_dispatch + The POST is issued after the optimization. + + +:::{table} adapter::nodered +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| host | `Optional[str]` | `rw` | `127.0.0.1` | Node-RED server IP address. Defaults to 127.0.0.1. | +| port | `Optional[int]` | `rw` | `1880` | Node-RED server IP port number. Defaults to 1880. | +::: + + + +**Example Input/Output** + + + +```json + { + "adapter": { + "nodered": { + "host": "127.0.0.1", + "port": 1880 + } + } + } +``` + + +### Common settings for the home assistant adapter + + +:::{table} adapter::homeassistant +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| config_entity_ids | `Optional[dict[str, str]]` | `rw` | `None` | Mapping of EOS config keys to Home Assistant entity IDs. +The config key has to be given by a ā€˜/’-separated path +e.g. devices/batteries/0/capacity_wh | +| device_instruction_entity_ids | `Optional[list[str]]` | `rw` | `None` | Entity IDs for device (resource) instructions to be updated by EOS. +The device ids (resource ids) have to be prepended by 'sensor.eos_' to build the entity_id. +E.g. The instruction for device id 'battery1' becomes the entity_id 'sensor.eos_battery1'. | +| device_measurement_entity_ids | `Optional[dict[str, str]]` | `rw` | `None` | Mapping of EOS measurement keys used by device (resource) simulations to Home Assistant entity IDs. | +| eos_device_instruction_entity_ids | `list[str]` | `ro` | `N/A` | Entity IDs for energy management instructions available at EOS. | +| eos_solution_entity_ids | `list[str]` | `ro` | `N/A` | Entity IDs for optimization solution available at EOS. | +| homeassistant_entity_ids | `list[str]` | `ro` | `N/A` | Entity IDs available at Home Assistant. | +| load_emr_entity_ids | `Optional[list[str]]` | `rw` | `None` | Entity ID(s) of load energy meter reading [kWh] | +| pv_production_emr_entity_ids | `Optional[list[str]]` | `rw` | `None` | Entity ID(s) of PV production energy meter reading [kWh] | +| solution_entity_ids | `Optional[list[str]]` | `rw` | `None` | Entity IDs for optimization solution keys to be updated by EOS. +The solution keys have to be prepended by 'sensor.eos_' to build the entity_id. +E.g. solution key 'battery1_idle_op_mode' becomes the entity_id 'sensor.eos_battery1_idle_op_mode'. | +::: + + + +**Example Input** + + + +```json + { + "adapter": { + "homeassistant": { + "config_entity_ids": { + "devices/batteries/0/capacity_wh": "sensor.battery1_capacity" + }, + "load_emr_entity_ids": [ + "sensor.load_energy_total_kwh" + ], + "pv_production_emr_entity_ids": [ + "sensor.pv_energy_total_kwh" + ], + "device_measurement_entity_ids": { + "ev11_soc_factor": "sensor.ev11_soc_factor", + "battery1_soc_factor": "sensor.battery1_soc_factor" + }, + "device_instruction_entity_ids": [ + "sensor.eos_battery1" + ], + "solution_entity_ids": [ + "sensor.eos_battery1_idle_mode_mode" + ] + } + } + } +``` + + + +**Example Output** + + + +```json + { + "adapter": { + "homeassistant": { + "config_entity_ids": { + "devices/batteries/0/capacity_wh": "sensor.battery1_capacity" + }, + "load_emr_entity_ids": [ + "sensor.load_energy_total_kwh" + ], + "pv_production_emr_entity_ids": [ + "sensor.pv_energy_total_kwh" + ], + "device_measurement_entity_ids": { + "ev11_soc_factor": "sensor.ev11_soc_factor", + "battery1_soc_factor": "sensor.battery1_soc_factor" + }, + "device_instruction_entity_ids": [ + "sensor.eos_battery1" + ], + "solution_entity_ids": [ + "sensor.eos_battery1_idle_mode_mode" + ], + "homeassistant_entity_ids": [], + "eos_solution_entity_ids": [], + "eos_device_instruction_entity_ids": [] + } + } + } +``` + diff --git a/docs/_generated/configdevices.md b/docs/_generated/configdevices.md index 446de16..2560feb 100644 --- a/docs/_generated/configdevices.md +++ b/docs/_generated/configdevices.md @@ -15,7 +15,7 @@ | max_electric_vehicles | `EOS_DEVICES__MAX_ELECTRIC_VEHICLES` | `Optional[int]` | `rw` | `None` | Maximum number of electric vehicles that can be set | | max_home_appliances | `EOS_DEVICES__MAX_HOME_APPLIANCES` | `Optional[int]` | `rw` | `None` | Maximum number of home_appliances that can be set | | max_inverters | `EOS_DEVICES__MAX_INVERTERS` | `Optional[int]` | `rw` | `None` | Maximum number of inverters that can be set | -| measurement_keys | | `Optional[list[str]]` | `ro` | `N/A` | None | +| measurement_keys | | `Optional[list[str]]` | `ro` | `N/A` | Return the measurement keys for the resource/ device stati that are measurements. | ::: @@ -36,7 +36,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -63,7 +75,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -107,7 +131,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -134,7 +170,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -185,7 +233,7 @@ | battery_id | `Optional[str]` | `rw` | `None` | ID of battery controlled by this inverter. | | device_id | `str` | `rw` | `` | ID of device | | max_power_w | `Optional[float]` | `rw` | `None` | Maximum power [W]. | -| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | None | +| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | Measurement keys for the inverter stati that are measurements. | ::: @@ -242,7 +290,7 @@ | consumption_wh | `int` | `rw` | `required` | Energy consumption [Wh]. | | device_id | `str` | `rw` | `` | ID of device | | duration_h | `int` | `rw` | `required` | Usage duration in hours [0 ... 24]. | -| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | None | +| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | Measurement keys for the home appliance stati that are measurements. | | time_windows | `Optional[akkudoktoreos.utils.datetimeutil.TimeWindowSequence]` | `rw` | `None` | Sequence of allowed time windows. Defaults to optimization general time window. | ::: @@ -320,19 +368,19 @@ | Name | Type | Read-Only | Default | Description | | ---- | ---- | --------- | ------- | ----------- | | capacity_wh | `int` | `rw` | `8000` | Capacity [Wh]. | -| charge_rates | `Optional[numpydantic.vendor.npbase_meta_classes.NDArray]` | `rw` | `[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]` | Charge rates as factor of maximum charging power [0.00 ... 1.00]. None triggers fallback to default charge-rates. | +| charge_rates | `Optional[list[float]]` | `rw` | `[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]` | Charge rates as factor of maximum charging power [0.00 ... 1.00]. None triggers fallback to default charge-rates. | | charging_efficiency | `float` | `rw` | `0.88` | Charging efficiency [0.01 ... 1.00]. | | device_id | `str` | `rw` | `` | ID of device | | discharging_efficiency | `float` | `rw` | `0.88` | Discharge efficiency [0.01 ... 1.00]. | | levelized_cost_of_storage_kwh | `float` | `rw` | `0.0` | Levelized cost of storage (LCOS), the average lifetime cost of delivering one kWh [€/kWh]. | | max_charge_power_w | `Optional[float]` | `rw` | `5000` | Maximum charging power [W]. | | max_soc_percentage | `int` | `rw` | `100` | Maximum state of charge (SOC) as percentage of capacity [%]. | -| measurement_key_power_3_phase_sym_w | `str` | `ro` | `N/A` | None | -| measurement_key_power_l1_w | `str` | `ro` | `N/A` | None | -| measurement_key_power_l2_w | `str` | `ro` | `N/A` | None | -| measurement_key_power_l3_w | `str` | `ro` | `N/A` | None | -| measurement_key_soc_factor | `str` | `ro` | `N/A` | None | -| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | None | +| measurement_key_power_3_phase_sym_w | `str` | `ro` | `N/A` | Measurement key for the symmetric 3 phase power the battery is charged or discharged with [W]. | +| measurement_key_power_l1_w | `str` | `ro` | `N/A` | Measurement key for the L1 power the battery is charged or discharged with [W]. | +| measurement_key_power_l2_w | `str` | `ro` | `N/A` | Measurement key for the L2 power the battery is charged or discharged with [W]. | +| measurement_key_power_l3_w | `str` | `ro` | `N/A` | Measurement key for the L3 power the battery is charged or discharged with [W]. | +| measurement_key_soc_factor | `str` | `ro` | `N/A` | Measurement key for the battery state of charge (SoC) as factor of total capacity [0.0 ... 1.0]. | +| measurement_keys | `Optional[list[str]]` | `ro` | `N/A` | Measurement keys for the battery stati that are measurements. | | min_charge_power_w | `Optional[float]` | `rw` | `50` | Minimum charging power [W]. | | min_soc_percentage | `int` | `rw` | `0` | Minimum state of charge (SOC) as percentage of capacity [%]. This is the target SoC for charging | ::: @@ -355,7 +403,13 @@ "levelized_cost_of_storage_kwh": 0.12, "max_charge_power_w": 5000.0, "min_charge_power_w": 50.0, - "charge_rates": "[0. 0.25 0.5 0.75 1. ]", + "charge_rates": [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0 + ], "min_soc_percentage": 10, "max_soc_percentage": 100 } @@ -382,7 +436,13 @@ "levelized_cost_of_storage_kwh": 0.12, "max_charge_power_w": 5000.0, "min_charge_power_w": 50.0, - "charge_rates": "[0. 0.25 0.5 0.75 1. ]", + "charge_rates": [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0 + ], "min_soc_percentage": 10, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", diff --git a/docs/_generated/configelecprice.md b/docs/_generated/configelecprice.md index 9110848..01879cc 100644 --- a/docs/_generated/configelecprice.md +++ b/docs/_generated/configelecprice.md @@ -11,12 +11,13 @@ | elecpriceimport | `EOS_ELECPRICE__ELECPRICEIMPORT` | `ElecPriceImportCommonSettings` | `rw` | `required` | Import provider settings. | | energycharts | `EOS_ELECPRICE__ENERGYCHARTS` | `ElecPriceEnergyChartsCommonSettings` | `rw` | `required` | Energy Charts provider settings. | | provider | `EOS_ELECPRICE__PROVIDER` | `Optional[str]` | `rw` | `None` | Electricity price provider id of provider to be used. | +| providers | | `list[str]` | `ro` | `N/A` | Available electricity price provider ids. | | vat_rate | `EOS_ELECPRICE__VAT_RATE` | `Optional[float]` | `rw` | `1.19` | VAT rate factor applied to electricity price when charges are used. | ::: -**Example Input/Output** +**Example Input** @@ -38,6 +39,34 @@ ``` + +**Example Output** + + + +```json + { + "elecprice": { + "provider": "ElecPriceAkkudoktor", + "charges_kwh": 0.21, + "vat_rate": 1.19, + "elecpriceimport": { + "import_file_path": null, + "import_json": null + }, + "energycharts": { + "bidding_zone": "DE-LU" + }, + "providers": [ + "ElecPriceAkkudoktor", + "ElecPriceEnergyCharts", + "ElecPriceImport" + ] + } + } +``` + + ### Common settings for Energy Charts electricity price provider diff --git a/docs/_generated/configexample.md b/docs/_generated/configexample.md index 19e2089..374b97b 100644 --- a/docs/_generated/configexample.md +++ b/docs/_generated/configexample.md @@ -3,6 +3,26 @@ ```json { + "adapter": { + "provider": [ + "HomeAssistant" + ], + "homeassistant": { + "config_entity_ids": null, + "load_emr_entity_ids": null, + "pv_production_emr_entity_ids": null, + "device_measurement_entity_ids": null, + "device_instruction_entity_ids": null, + "solution_entity_ids": null, + "homeassistant_entity_ids": [], + "eos_solution_entity_ids": [], + "eos_device_instruction_entity_ids": [] + }, + "nodered": { + "host": "127.0.0.1", + "port": 1880 + } + }, "cache": { "subpath": "cache", "cleanup_interval": 300.0 @@ -17,7 +37,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -44,7 +76,19 @@ "levelized_cost_of_storage_kwh": 0.0, "max_charge_power_w": 5000, "min_charge_power_w": 50, - "charge_rates": "[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]", + "charge_rates": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "min_soc_percentage": 0, "max_soc_percentage": 100, "measurement_key_soc_factor": "battery1-soc-factor", @@ -92,7 +136,7 @@ } }, "general": { - "version": "0.2.0+dev.4dbc2d", + "version": "0.2.0.dev70048701", "data_folder_path": null, "data_output_subpath": "output", "latitude": 52.52, diff --git a/docs/_generated/configfeedintariff.md b/docs/_generated/configfeedintariff.md index 70c9349..8e882a1 100644 --- a/docs/_generated/configfeedintariff.md +++ b/docs/_generated/configfeedintariff.md @@ -9,11 +9,12 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | provider | `EOS_FEEDINTARIFF__PROVIDER` | `Optional[str]` | `rw` | `None` | Feed in tariff provider id of provider to be used. | | provider_settings | `EOS_FEEDINTARIFF__PROVIDER_SETTINGS` | `FeedInTariffCommonProviderSettings` | `rw` | `required` | Provider settings | +| providers | | `list[str]` | `ro` | `N/A` | Available feed in tariff provider ids. | ::: -**Example Input/Output** +**Example Input** @@ -30,6 +31,28 @@ ``` + +**Example Output** + + + +```json + { + "feedintariff": { + "provider": "FeedInTariffFixed", + "provider_settings": { + "FeedInTariffFixed": null, + "FeedInTariffImport": null + }, + "providers": [ + "FeedInTariffFixed", + "FeedInTariffImport" + ] + } + } +``` + + ### Common settings for feed in tariff data import from file or JSON string diff --git a/docs/_generated/configgeneral.md b/docs/_generated/configgeneral.md index 22d23e1..6ae8eaf 100644 --- a/docs/_generated/configgeneral.md +++ b/docs/_generated/configgeneral.md @@ -1,17 +1,4 @@ -## Settings for common configuration - -General configuration to set directories of cache and output files and system location (latitude -and longitude). -Validators ensure each parameter is within a specified range. A computed property, `timezone`, -determines the time zone based on latitude and longitude. - -Attributes: - latitude (Optional[float]): Latitude in degrees, must be between -90 and 90. - longitude (Optional[float]): Longitude in degrees, must be between -180 and 180. - -Properties: - timezone (Optional[str]): Computed time zone string based on the specified latitude - and longitude. +## General settings :::{table} general @@ -20,15 +7,16 @@ Properties: | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | -| config_file_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | None | -| config_folder_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | None | +| config_file_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Path to EOS configuration file. | +| config_folder_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Path to EOS configuration directory. | | data_folder_path | `EOS_GENERAL__DATA_FOLDER_PATH` | `Optional[pathlib.Path]` | `rw` | `None` | Path to EOS data directory. | -| data_output_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | None | +| data_output_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Computed data_output_path based on data_folder_path. | | data_output_subpath | `EOS_GENERAL__DATA_OUTPUT_SUBPATH` | `Optional[pathlib.Path]` | `rw` | `output` | Sub-path for the EOS output data directory. | -| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°) | -| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees, within -180 to 180 (°) | -| timezone | | `Optional[str]` | `ro` | `N/A` | None | -| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0+dev.4dbc2d` | Configuration file version. Used to check compatibility. | +| home_assistant_addon | | `bool` | `ro` | `N/A` | EOS is running as home assistant add-on. | +| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (°) | +| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees within -180 to 180 (°) | +| timezone | | `Optional[str]` | `ro` | `N/A` | Computed timezone based on latitude and longitude. | +| version | `EOS_GENERAL__VERSION` | `str` | `rw` | `0.2.0.dev70048701` | Configuration file version. Used to check compatibility. | ::: @@ -40,7 +28,7 @@ Properties: ```json { "general": { - "version": "0.2.0+dev.4dbc2d", + "version": "0.2.0.dev70048701", "data_folder_path": null, "data_output_subpath": "output", "latitude": 52.52, @@ -58,7 +46,7 @@ Properties: ```json { "general": { - "version": "0.2.0+dev.4dbc2d", + "version": "0.2.0.dev70048701", "data_folder_path": null, "data_output_subpath": "output", "latitude": 52.52, @@ -66,7 +54,8 @@ Properties: "timezone": "Europe/Berlin", "data_output_path": null, "config_folder_path": "/home/user/.config/net.akkudoktoreos.net", - "config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json" + "config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json", + "home_assistant_addon": false } } ``` diff --git a/docs/_generated/configload.md b/docs/_generated/configload.md index 11ccf3a..1be86c4 100644 --- a/docs/_generated/configload.md +++ b/docs/_generated/configload.md @@ -9,11 +9,12 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | provider | `EOS_LOAD__PROVIDER` | `Optional[str]` | `rw` | `None` | Load provider id of provider to be used. | | provider_settings | `EOS_LOAD__PROVIDER_SETTINGS` | `LoadCommonProviderSettings` | `rw` | `required` | Provider settings | +| providers | | `list[str]` | `ro` | `N/A` | Available load provider ids. | ::: -**Example Input/Output** +**Example Input** @@ -31,6 +32,31 @@ ``` + +**Example Output** + + + +```json + { + "load": { + "provider": "LoadAkkudoktor", + "provider_settings": { + "LoadAkkudoktor": null, + "LoadVrm": null, + "LoadImport": null + }, + "providers": [ + "LoadAkkudoktor", + "LoadAkkudoktorAdjusted", + "LoadVrm", + "LoadImport" + ] + } + } +``` + + ### Common settings for load data import from file or JSON string @@ -64,7 +90,7 @@ ``` -### Common settings for VRM API +### Common settings for load forecast VRM API :::{table} load::provider_settings::LoadVrm diff --git a/docs/_generated/configlogging.md b/docs/_generated/configlogging.md index 53a7ca5..04ad1f9 100644 --- a/docs/_generated/configlogging.md +++ b/docs/_generated/configlogging.md @@ -9,7 +9,7 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | console_level | `EOS_LOGGING__CONSOLE_LEVEL` | `Optional[str]` | `rw` | `None` | Logging level when logging to console. | | file_level | `EOS_LOGGING__FILE_LEVEL` | `Optional[str]` | `rw` | `None` | Logging level when logging to file. | -| file_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | None | +| file_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Computed log file path based on data output path. | ::: diff --git a/docs/_generated/configmeasurement.md b/docs/_generated/configmeasurement.md index 13267d7..158df67 100644 --- a/docs/_generated/configmeasurement.md +++ b/docs/_generated/configmeasurement.md @@ -9,7 +9,7 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | grid_export_emr_keys | `EOS_MEASUREMENT__GRID_EXPORT_EMR_KEYS` | `Optional[list[str]]` | `rw` | `None` | The keys of the measurements that are energy meter readings of energy export to grid [kWh]. | | grid_import_emr_keys | `EOS_MEASUREMENT__GRID_IMPORT_EMR_KEYS` | `Optional[list[str]]` | `rw` | `None` | The keys of the measurements that are energy meter readings of energy import from grid [kWh]. | -| keys | | `list[str]` | `ro` | `N/A` | None | +| keys | | `list[str]` | `ro` | `N/A` | The keys of the measurements that can be stored. | | load_emr_keys | `EOS_MEASUREMENT__LOAD_EMR_KEYS` | `Optional[list[str]]` | `rw` | `None` | The keys of the measurements that are energy meter readings of a load [kWh]. | | pv_production_emr_keys | `EOS_MEASUREMENT__PV_PRODUCTION_EMR_KEYS` | `Optional[list[str]]` | `rw` | `None` | The keys of the measurements that are PV production energy meter readings [kWh]. | ::: diff --git a/docs/_generated/configoptimization.md b/docs/_generated/configoptimization.md index faebe45..d70f144 100644 --- a/docs/_generated/configoptimization.md +++ b/docs/_generated/configoptimization.md @@ -11,11 +11,12 @@ | genetic | `EOS_OPTIMIZATION__GENETIC` | `Optional[akkudoktoreos.optimization.optimization.GeneticCommonSettings]` | `rw` | `None` | Genetic optimization algorithm configuration. | | horizon_hours | `EOS_OPTIMIZATION__HORIZON_HOURS` | `Optional[int]` | `rw` | `24` | The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours. | | interval | `EOS_OPTIMIZATION__INTERVAL` | `Optional[int]` | `rw` | `3600` | The optimization interval [sec]. | +| keys | | `list[str]` | `ro` | `N/A` | The keys of the solution. | ::: -**Example Input/Output** +**Example Input** @@ -38,6 +39,31 @@ ``` + +**Example Output** + + + +```json + { + "optimization": { + "horizon_hours": 24, + "interval": 3600, + "algorithm": "GENETIC", + "genetic": { + "individuals": 400, + "generations": 400, + "seed": null, + "penalties": { + "ev_soc_miss": 10 + } + }, + "keys": [] + } + } +``` + + ### General Genetic Optimization Algorithm Configuration diff --git a/docs/_generated/configprediction.md b/docs/_generated/configprediction.md index 5d4ba17..d5b4d79 100644 --- a/docs/_generated/configprediction.md +++ b/docs/_generated/configprediction.md @@ -1,19 +1,5 @@ ## General Prediction Configuration -This class provides configuration for prediction settings, allowing users to specify -parameters such as the forecast duration (in hours). -Validators ensure each parameter is within a specified range. - -Attributes: - hours (Optional[int]): Number of hours into the future for predictions. - Must be non-negative. - historic_hours (Optional[int]): Number of hours into the past for historical data. - Must be non-negative. - -Validators: - validate_hours (int): Ensures `hours` is a non-negative integer. - validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer. - :::{table} prediction :widths: 10 20 10 5 5 30 diff --git a/docs/_generated/configpvforecast.md b/docs/_generated/configpvforecast.md index 12b455c..ea78dbd 100644 --- a/docs/_generated/configpvforecast.md +++ b/docs/_generated/configpvforecast.md @@ -9,13 +9,14 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | max_planes | `EOS_PVFORECAST__MAX_PLANES` | `Optional[int]` | `rw` | `0` | Maximum number of planes that can be set | | planes | `EOS_PVFORECAST__PLANES` | `Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]]` | `rw` | `None` | Plane configuration. | -| planes_azimuth | | `List[float]` | `ro` | `N/A` | None | -| planes_inverter_paco | | `Any` | `ro` | `N/A` | None | -| planes_peakpower | | `List[float]` | `ro` | `N/A` | None | -| planes_tilt | | `List[float]` | `ro` | `N/A` | None | -| planes_userhorizon | | `Any` | `ro` | `N/A` | None | +| planes_azimuth | | `List[float]` | `ro` | `N/A` | Compute a list of the azimuths per active planes. | +| planes_inverter_paco | | `Any` | `ro` | `N/A` | Compute a list of the maximum power rating of the inverter per active planes. | +| planes_peakpower | | `List[float]` | `ro` | `N/A` | Compute a list of the peak power per active planes. | +| planes_tilt | | `List[float]` | `ro` | `N/A` | Compute a list of the tilts per active planes. | +| planes_userhorizon | | `Any` | `ro` | `N/A` | Compute a list of the user horizon per active planes. | | provider | `EOS_PVFORECAST__PROVIDER` | `Optional[str]` | `rw` | `None` | PVForecast provider id of provider to be used. | | provider_settings | `EOS_PVFORECAST__PROVIDER_SETTINGS` | `PVForecastCommonProviderSettings` | `rw` | `required` | Provider settings | +| providers | | `list[str]` | `ro` | `N/A` | Available PVForecast provider ids. | ::: @@ -144,6 +145,11 @@ } ], "max_planes": 1, + "providers": [ + "PVForecastAkkudoktor", + "PVForecastVrm", + "PVForecastImport" + ], "planes_peakpower": [ 5.0, 3.5 @@ -177,7 +183,7 @@ ``` -### Common settings for VRM API +### Common settings for PV forecast VRM API :::{table} pvforecast::provider_settings::PVForecastVrm diff --git a/docs/_generated/configweather.md b/docs/_generated/configweather.md index f7d1778..6d83c9f 100644 --- a/docs/_generated/configweather.md +++ b/docs/_generated/configweather.md @@ -9,11 +9,12 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | provider | `EOS_WEATHER__PROVIDER` | `Optional[str]` | `rw` | `None` | Weather provider id of provider to be used. | | provider_settings | `EOS_WEATHER__PROVIDER_SETTINGS` | `WeatherCommonProviderSettings` | `rw` | `required` | Provider settings | +| providers | | `list[str]` | `ro` | `N/A` | Available weather provider ids. | ::: -**Example Input/Output** +**Example Input** @@ -29,6 +30,28 @@ ``` + +**Example Output** + + + +```json + { + "weather": { + "provider": "WeatherImport", + "provider_settings": { + "WeatherImport": null + }, + "providers": [ + "BrightSky", + "ClearOutside", + "WeatherImport" + ] + } + } +``` + + ### Common settings for weather data import from file or JSON string diff --git a/docs/_generated/openapi.md b/docs/_generated/openapi.md index 6a74d4f..0dde373 100644 --- a/docs/_generated/openapi.md +++ b/docs/_generated/openapi.md @@ -1,6 +1,6 @@ # Akkudoktor-EOS -**Version**: `v0.2.0+dev.4dbc2d` +**Version**: `v0.2.0.dev70048701` **Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period. diff --git a/docs/_static/azimuth.gif b/docs/_static/azimuth.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f1f14be235480905770a6ebc4a1c8a0eac0501e GIT binary patch literal 28721 zcmbTdXHXMw{I9*qCL#2INH0OUqDJXGp*Jz~Duf~(41zQfLhl%gfYi`C2BivWC?ZXa ziWEhSbPzQtA~t@wB$=X($WZUBJv3F)GO zqOqwNHy5|HjkWg9c5Pklh{%Y!*US3{`$G?hEUhf(W@l2;Qk2gt*Vonu2L|tc-kljA zGc-0_d$&9`KGxma9Ty+>=jhMe{G8%R#fq{DA1|Lj2m50Whum!JW?szgZmcb@tQFfd zxVstZY7dQ!*;!fUq@`$|S8i{tj|dH3TUj3J?M{i0Tb`d&KB-t=RT1p#Gd41`yR*Ch zb$4cVru%MpTx`VhyJbsr!=mCMH+Q$Sf42_)3_Tp1o1Lp~tPc(i-u=2eHa;^qzg$sO z;p6*n&wtz;>~=T)?Qo{MvwQjV^4yELKfnGc%1RsR8`{~}?yZ4L_E30ek8=G6(J0EvHeg4AS`}%GF`@xUHpTB

t*_dg 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

VYi=C$u$D% zc<_UM^NZ0S8Ae6sFRB+E62lQJjF31DD192&e7724fHGeT4Sq-ks{=~OL&faO+lEnB zdeJyXw&7G87|aB@qrg54m=dF6O&g*`ZXc*=KSFLFe%`LdY#+VfeuUb3s@fMRZyMAc zZ}Z!-sL zU|t*n(1D6{umv6L3^u?&Lf>r5sD2^ytN3qpXZ%oy=ld$2YG2f?7uz}|4O`U1ZB>?S zRfW~mtDDWCPF38=F9#r?>iz%+PN*0sNF(prNekYNM3rYZc@Q{Y=S?ujTORL9km;Vf zg-o}>u)3jhw^3@hVQu$0zitcXZtLxC)57la&ON4nJq~`|rkEaUi*5r&fENkG-ceum z>-9?Q^{MUEA%X73_E-)>tz^1onP3%cuWxE!_$`nmQ{9oP2gC&5DC~==?LC7AI&k%w z;z8~f{b{Ma@x$sw78&M_hIuex$|&$*ihA%t%kt*HL$1MM#lZ*8Ktn%}lV6X4#l=U% zgLM{z7O6dz&fOU`0`-1FROdmv?H+SKP}8kS6mqyrarl+^aKa{#BtAr|?M^!AJ-_Z2bVXzW?3-)axpkYULzL3)g_ESscg!HvG2^--)2bT z#sunK3;B-`0E`2!vBNvB1yBq=36KC5cWacwV2qimFMK1IQ{3%iJdoPHu9Dd6Wm2(!nY@QxZmjBe(SI> z0UUV^!a!hqKu0R{6b7b(dnmpz=A1U^yEEapFj=`g`CIV~Yj_NrCNKJeQQk1_Unj5f zVobbljI=Xx#F7EmoBBIBd97|bX=E%hZ7eQrG9zsgm@s+s&$v~ceD3YHenn%T+l)B9 zw->o*C=xTge`Yv}Cdro5LY7k^MU#?sV~74QXt&40B<3X2=B|%u@KHy%x90#)U^k9t zZ&5DbIe75^9a`|{;DUDToB7rD`B9~X)r0xrk@+`&=Et}f*jU1worS3v3!|0`%XO^z zgT#f=KiYF63(TVVrJcnEy+xMy=$Da>t=p1I;5g+jx?bRZZ5bi@Jpu#2OAARqOZdqoRx^%kXBOCx`lj$18H z*1c1B@D3EYD9`h5R*55Wuq* z@Bk{hSIR>JiPM2vG=K_YHE?(B4$pd&73s<9V{IBlg8_Lrv=%+Op2pf;&sfCyk{~J= z01vxSIVVc2c|mOHtfSig<5{v;Ui31$oF{8rle9N>=v`*v&!t9+KR z{@ku*;+esf*MdfufKo8fjWsm6n;yK_QmVrcmIqZ;SYZV)Bu)UcKq=G4?M7`@<&|2{>@HU=Qof7 z6@1$DhZpKYg38Yw}(;JJ-y(f>dDk){|FAKYDrp zjJ@07nf&2-1U%gN;g!mtxz~ScUA}6fSU<|{418l9ECjID{?6gCCX!4rhWT5k6YxEq z^~L%xNz!y_6sYy?lQI=L>iYLDFZ}-n+vXWxKq7fLdE~UtD}#l6*+;7XSJ*brMG#4} z@z6fRse0A%jCJ?_0ox`WbHzy>7r*MR<*%$|hFJMe*fz%XnjF^Vm450ee?P@AsgDo; z1GdeQHiI{L80RCj9xR1*As_uOY!J?vaMGKp3{f z%HqDrsnApE|0CN9=2AfdP$9owxM&mhb=|*ZTcx)UqAph7{i(xC5omeaf5WyfqCRaF zW8YA;pBHlfH*9;>=Fp~bmk8&4*Bga@!?qW5NRReVG4Lx)sLa1%+aD=7V`YB$k+~cn z54VEtf5Eo(L!4KoRs0%KmOxO$<$uAp5}EA5dMT4cuOI2-NU8Y;w!O8Yh^Cw{8&wR> z7h#V41KYALW6b4hp3r49zlhl2+}`~Qwl(I%*@_5lHD=j|ar_Imz2*@Qa@`@v+|lm2 z_%GO2nx(uSj-KL_lz5*mqHcs){ztZzIqD=H_{z@gD~FANJ4O8;+4i)%kZ5-Z_+qu* zRkDZxge(3Qr8?WxEYOLHavz_!K+E%SU{ z%=-@#Sq^^xz_zk~F?>wnzW6Wg6M*qRVs_U0ehHa$;&=JW>jLptzvG#j>kCvD7{!E$NGC3D+A zhIU`hd)%+(5%B9ck`3G5sjRV4*GSFdw(uMKGG=xJ zZc>3%R)~){4F{({#l@*aWGm^MTscX;8t@Bu2#tqGZ+eO^K!pPry-H#@K zAn%7O>dO|fR}Q`~E(%16)*>%`h=9*9LDOS5KE616j7()>H@CgP2v5}BpI_TkPfd6o z6_~FC&h?VN2u3zP{>z#dkiveBs{($X*H?-7^pOmI-y_zJZmoMk4MMx6ixBS7k&ZNB zfc9ATVTHUaGO{hBD(Q=H7-DX8oG4q=@ke#n)H?ii!88_qKBz6rGq9U7Ys)0Ve{BL&A}Y?f zevcJ-M$}upZoM(v`a1f90wlQ`7qqzYj3aNHiqe=ClC8*&J2N72L@Nd;Gk787#FF*B z?E-H1y~6YYbyo!Go(;T2Ojj+5dOVG_K^s1$%*m9EMsB28+(0T{&NYcdd0X|(GG!i! z2|7ApVnoMRqGST8Wj8XTRWi)qIz|D3Ce`EATo179>eFic!X?N}ytu~jF&D!ofbYve z+e=GcK|*pz9Bct`P0zm7=YM{8_VzkeER0zb#$9_qnbbXQ52*7;#R%yho$lS;s20pa z=*zAOB%S`=;0k&rJb}$m#|3g3_tbhnFXe*{$LcLS=@L<-jg7Slne?>*uez+#C=+DP z(A#y_qCBk{5rb$g`z}x;HGV=9>mB(fdc>%ImYn9 zkLPC;qF)|_923yG!R3K9V2N}MEpAGuxY5R6g2p-DxV#qvb6zrl$>CxZX|h0LT(qzn zGw%1ZA9P`0w8iKi=>8J^c}-n_5}gv4ZGz_%?I(-M-R~{8w2t5qSNqXHy`8um%^j*65Gz20zp5IhepoID~ROvn6WrSl9_X|w~RPn zzXkaQUjTvfq5w|QOX-hD&mxOr^5hI1d>|!9yyFd?DTaC9oodg3TmmA6L@sd^(4$V3 z+O_4hd_w2iM#BZVI0Ixsiv($5?&`In^0=3IQRM<$8o$JemDs2m3!h_8mFtfzVGqrc zW0V_(_#GGx!mMmsoU}mF(~p~tc~3ORw7#)^BkCd7(N)M` zl|HxD6|%F;a&IWq5T1;ODR`2Bv6)KMN6G}$YK{v`nUpl;=+xUv@y$uGqGNAZUh_{( zQJX#S3IwBVs1|lji(tQth4w==ZgmYhq+IXi|6}d0tlf{zMNs+*pJ9Y=PHrZph^13M z1nD?q`rC5V{Mhf%3HD9CdjU>m^)vd1xIU;&O+u-6q3(a$Gn8B@6A}%uA4!n z@|79Us8Muic=U7EzzuCm%}Cg`dI2+iG;dPaI`qkJ9VX$!OX10_@1sLkUI+F45N>A# zo)zvIbOF5NYaxABDU5ty_3p{|@#Awcx1yl2oDI*MLgK{}yI@-Mmx8uRA7W1yYz0;6 zGT`amcL)}(dL5a%f(t?0?J10g%h797?Xa?fVz{rpj?^SAE-iTe^}Zw~Duv8aycqP< z#tuAjq3Frh;4I6{%Jxg=3vcN=SHXT1kmnvKHbEj5rd}6q1^2qW`RQV+Cc#Wy)|{p4 zPHrG$`utPN1-_dAbenHKzbd}w3A&62xnl9IDCluJUqRaSwbF=wlGR$bgXYViSwb-A zNyyMWZ_2$}gxsLCP*O1y3}ryQFr=XhXM#yc$~_|NhBvg&U&{+5BO8Q811{V7`+Siz z$PEI~qa^b@@z@KlZear@k^s$k9&d8Wlze<>NOdnUqtm`MD*riyv$uQuW ze{6Y}hsbTlC7`O7yCTiiTo8z$sfWlMfBP&V*^b~UEUdkse9{eF_j6R}pK(6jO+Bdojo61o)Kyr0k(*(_ws--kkSw$mt3-af2@-o z3Huadv7QkBBWPnf+39JDC>=zgh5D`@PN`(mdT08`-cMk`&$JHt^o5Qk;t4otA2W>@ z#slEEP2ljoy)RhV;>|amfONR^SC&MmfL+!k6l%Cb${_+|KmmJjX+kk>gzDkXO?{66 zO;OY`-Q^r8+fYL+Mb|D*)vLnUtTgkjkdNwq2;A&8j_wd$9=&!wnQSL?r|8=|Ktar!P$;u%uF8*=&~f)atgu{%}G}w^vJ_rkl|Cr`^FGwRL(Iu*#jFW0UybZr6q8wf#U_>y3S`l&o9k_DoJE{f(E(<7M)e#cwc z-w_yrP3;#75EqUtIKkyhzv_~i3E?1kJ|nr1y~+k$@F*YzS42l#hwMTBf@6~_(r-+A zi|s=ROi1%a&JsQ6xkp%PZrs_)R2&6ZDq8eTurMBg=R)6ezTuz6;eKEPr3Z;(0W^5>aQ3zmszRbIMVdGfl5k0~aPk;rRIgHAk(SkOAN5 zC+{&F^yHYo;l>?92cY&8uxI0ut33qGb}RQil+3eH)lt$yKvs`01P2Y8Aw#uG&mIGc z63cR@??Y%e1O$tjJ|S%A+Z6!fPT&?KsNL<)@;H%Owp0k*EFTL~G(}&(-XFFlcZiq= z+_nShRde=zhHfBY3)j8>l%%8S)ObgVAOTkQRKu{ExR#eJCv%4%4G?8ky`p*Q0YG=w z$v$$SFu6ml*&kJB#DL1>WdM%wci)qn<%C<4x%=ExcI}JLdB=YzJZPH@x2D0QafI~a zB<-KxPvjoBYgavGpN-Fkkk|cHj^CmDs(pw&{Tfp1w;zL>y<~)gno@v7Q%o1Y@Z3@Y zsQJP5n`!cl@(;u^BlmdGq!`cqOiepr57G5SKCot!^5(}y=jIS20LYYC`*$-cN88&3 zo^>`arY$eDld9lKzWl1KF!(1?5(hM90EK@)_62}+WMj(V!3nTpBU-f35P}cSDQILxMc`5(Xb!5J&7Uxlsv=s)FQHaSC8gPhAe-xLJ`;ya!=I49_>J=O4Kq z&PvhSNbggvVc{+SW?RKhc?p+gNBc{k04;4RM zoaq%WoJfn%ZjKiNa?zo-iQbFxaRQqGl9=?h_s}cn9@pbcjthJC-+TDFS;Lvpl8=RfzjvS!i*a`{Ng+qA~uwD zy*0)|BukS*yU&n2V_(zP99O{cV$G?<4w}=L7(SaAXGDP3(V*rihY-2?Vb)BN=1ZtC zCFA$=PUlt_2va>cNU7QKaAhDxv598t;ZyKTksvEY$}>K{GCLuZNM6j8$ZceDYmYrb z0tH=rf7Mi^STMigF5%^Izp|NDhg1Dw@Ai{ZPBiCWBh7IE+0W^f$<>9IpXiDAe98sz zQFM!aZMS?<&Kg5p(LrjnPBY$NPX+>tNlyv%S2oTi9P?QMZE1=emd4@Ww)=ca>lx}P zQM=XQwm67)VJ|oGPD1DluY!klT0WU{&yOXKJ!;wnNxhNVeaFxs4!^FNJo?pEVoZ51 z1rzYI%X?kDHGu$Ht?A4-s6~L~09?hpVOGmR2rLeI{7_0nO(Sc9qo?fFP&5sec`RAQ z%Uf{3A+7y_=7-x&`u?fN40BQBv(%xo+98{%fH>K1%M*YX-WlSia0IQOQKvrDHoUeR zhGp~(`<3+>M8wZMeNbCvi~vau^tHAtMCJ9I>u>yIpRtlUbb(T4c&?hgN7S2>cgA;| zhjmZ6`)>x46&-uk0xf^s8shci5<~?DMR`APXu8bkNroG`(lBRX_xt8w+6*=XHNzgS z-EQ7^%E2_Nz9LKLbd*F9;nj4ZNPUf2;yIt##`S30#?%j4IG);(7@8ij zpCJ|wS#FlO!AJZ&^0b_@^qyBiq=a zMbb$C<~p^1A`)D&~Ou4LtWeT%43 zld-m^%8zGqvh0ht)Og0*mXu|u2fa(p&yaqlOrMt2n_#Xh@TZ(~-}Ir~m$A&0+8eb2 zn+t;H9qhtg2KS}h`>=I8l_j3C?pY|RzGwM>74I(F=| zwZVWoL2juqt;v^v*TN#s?hh-1Me!B=%*|x$IR{*qfo@yo zyLkd7s4;Z+o=T4dMd0c>FlYK_RGq?a;m*>|8FN%hUCH~ZfC<80QTEcW$L|=zK}s3X zTTQQr`FQ#yaof-D4B74_7?Hq_jvNdh{4^Ne>VG|KjE!-oej0gPb8Ppj5qjoLK>d;? z;#kn5#D{-A5UccP+gU};$h&Wucj}Wyp?Z5h7AtF(Ir|&dSx1fhB}tIXRv0VJ@5EK2 zi&rw435{pz|NmrLN8-)og6g)xS>#5@3UDG-D~<#P}@E{ zzkR?Zp`>Sd#s79%kp!YQ=)7W$ki{hn-5QkDt$dDIb*CIYo37jXdG&u` z+ld;d&UC%!-*@JpNBreE)4u=d{m}ih8E0Pn`nEmy{0{Hgj)R|Hx8^!C&UUi?9Q^+M zmlsb4@tEPGV3LXdyS0V8#qwL3xy1>&B)Y|mUF&!IAK2E+JxTUKqIM8iUWxN literal 0 HcmV?d00001 diff --git a/docs/_static/horizon_eyefish_en.png b/docs/_static/horizon_eyefish_en.png new file mode 100644 index 0000000000000000000000000000000000000000..ce926d06f7df60a477272ffe08ba6da96e025812 GIT binary patch literal 416016 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±Px+8~y&ki2KN(Kp&uy!tqq_R) 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 z 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≪G0Bgz= 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
F^_mC& z;Tvdw9ui5--HPQeGyO^@CMM>+oaME3+#eJ;Jo_(%O7`1f)1eGJF684!#xnC}7CwCp z2#}pQ`p;441lt&bhWi}T|9B^a{LAb-N?xk$U523yQbQG_!xS3!m@Ae}D3-oxJrOtm zcHU>U2eB5;6tr}CLDDE=y0ZDF^-2Fdn%#-Pc(23t7}j`kG#7!cs<4$BhQt7ci+{G z!O5=M?fRlxQEWcuIA%~=#JWSVBfKp{lt&U~jM}?(j5;+MrNPgYB$AmF(Wh{Dz}p!) z>^F4Xr{=U~b!PJqV)7*h#=Znb_w$-xXLUbNN$b~jwa!oL8}m5T_05<`?Fdm%g(g{to5s;y|rg)?@3zTkP9*>-4*TAC9Z z+2e06H4(G3(hZikYyPmM#Bc=1`!|vF2qbh^2}o1jz1vq{us7pJ3LSeY#jGr=uTXjG zKM9TSd83FfQ<@trQCF!j>n*We3HVRK4>LPvUO>!5BE9oHy1XjJsd{(=SOZ+x%%xV? zpYY!fzj`yY!5sHGgFd&mnPhs`BDR6=k00rC6x^TnE+c;cRnyGXh_0!G44h#%y_$q` ztfY9R!KKirtHp}t-<0pAh5V`8QvQAQP=@NQBk5?jR&nRNOV<6l$oq+_6ocuEP)T

Cf0$;!~WSb5D3_mUY|3qtg-=5yCau)3 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 E0RWSUNdN!< literal 0 HcmV?d00001 diff --git a/docs/_static/slope.gif b/docs/_static/slope.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef1d5ef83b545dcf19fa2025a3e75bced2f088a2 GIT binary patch literal 25022 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 tbKHjG#^9Vt +```yaml +template: + - sensor: + - name: "Battery1 SoC Factor" + unique_id: "battery1_soc_factor" + state: > + {% set bat_charge_soc = states('sensor.battery1_soc_percent') | float(100) -%} + {{ bat_charge_soc / 100.0 }} + state_class: measurement +``` + diff --git a/docs/akkudoktoreos/adapter/adapternodered.md b/docs/akkudoktoreos/adapter/adapternodered.md new file mode 100644 index 0000000..edf4d96 --- /dev/null +++ b/docs/akkudoktoreos/adapter/adapternodered.md @@ -0,0 +1,4 @@ +% SPDX-License-Identifier: Apache-2.0 +(adapter-nodered-page)= + +# NodeRED Adapter diff --git a/docs/akkudoktoreos/configuration.md b/docs/akkudoktoreos/configuration.md index dd858aa..a14ca95 100644 --- a/docs/akkudoktoreos/configuration.md +++ b/docs/akkudoktoreos/configuration.md @@ -1,7 +1,7 @@ % SPDX-License-Identifier: Apache-2.0 (configuration-page)= -# Configuration Guideline +# Configuration Guide The configuration controls all aspects of EOS: optimization, prediction, measurement, and energy management. diff --git a/docs/akkudoktoreos/optimauto.md b/docs/akkudoktoreos/optimauto.md index 8a14bd8..73ecea3 100644 --- a/docs/akkudoktoreos/optimauto.md +++ b/docs/akkudoktoreos/optimauto.md @@ -36,7 +36,8 @@ Through an iterative process of selection, crossover, and mutation, the algorith more effective solutions. The final result is an optimized control strategy that balances multiple system goals within the constraints of the input data and configuration. -:::{note} +:::{admonition} Note +:class: note You don’t need to understand the internal workings of the genetic algorithm to benefit from automatic optimization. EOS handles everything behind the scenes based on your configuration. However, advanced users can fine-tune the optimization behavior using additional settings like diff --git a/docs/akkudoktoreos/prediction.md b/docs/akkudoktoreos/prediction.md index d40aedc..a310df1 100644 --- a/docs/akkudoktoreos/prediction.md +++ b/docs/akkudoktoreos/prediction.md @@ -51,13 +51,16 @@ A dictionary with the following structure: ```json { "start_datetime": "2024-01-01 00:00:00", - "interval": "1 Hour", + "interval": "1 hour", "": [value, value, ...], "": [value, value, ...], ... } ``` +If `start_datetime` is not provided EOS defaults to the `start_datetime` of the current energy +management run. If `interval` is not provided EOS defaults to one hour. + #### 2. DateTimeDataFrame A JSON string created from a [pandas](https://pandas.pydata.org/docs/index.html) dataframe with a @@ -402,10 +405,11 @@ represent equal angular distance around the horizon. For instance, if you have 3 point is due north, the next is 10 degrees east of north, and so on, until the last point, 10 degrees west of north. ---- +![Userhorizon PVGIS](../_static/horizon_eyefish_en.png) Most of the configuration options are in line with the -[PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition for PVGIS data. +[PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition +for PVGIS data. Detailed definitions from **PVLib** for PVGIS data. @@ -413,12 +417,14 @@ Detailed definitions from **PVLib** for PVGIS data. Tilt angle from horizontal plane. +![Tilt PVGIS](../_static/slope.gif) + - `surface_azimuth` Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). This is offset 180 degrees from the convention used by PVGIS. ---- +![Azimuth PVGIS](../_static/azimuth.gif) ### PVForecastAkkudoktor Provider diff --git a/docs/akkudoktoreos/resource.md b/docs/akkudoktoreos/resource.md index 2e063fb..5d0982b 100644 --- a/docs/akkudoktoreos/resource.md +++ b/docs/akkudoktoreos/resource.md @@ -253,6 +253,6 @@ the home appliance can be operated in two operation modes: |-----------------------|-------------------------------------------------------------------------| | **RUN** | The home appliance is started and runs until the end of it's power | | | sequence. | -| **IDLE** | The home appliance does not run. | +| **OFF** | The home appliance does not run. | The **operation mode factor** (0.0–1.0) is ignored. diff --git a/docs/develop/develop.md b/docs/develop/develop.md index a0ae2fc..7a15be2 100644 --- a/docs/develop/develop.md +++ b/docs/develop/develop.md @@ -328,8 +328,11 @@ For detailed Docker instructions, refer to [Installation Guideline](install-page #### Step 4.1 - Create a development branch +Create a local development branch and make it know on your GitHub repo. + ```bash git checkout -b +git push --set-upstream origin ``` Replace `` with the development branch name. The branch name shall be of the @@ -591,6 +594,10 @@ Ensure your changes do not break existing functionality: Keep your code consistent with existing style and conventions. +#### Keep Python Docstrings RST Compatible + +The docstrings will be parsed by Sphinx in automatic documentation generation. + ### Use Issues for Discussion Before making major changes, open an issue or discuss with maintainers. @@ -598,3 +605,100 @@ Before making major changes, open an issue or discuss with maintainers. ### Document Changes Update docstrings, comments, and any relevant documentation. + +### Start or Reopen the Home Assistant Dev Container in VS Code + +### 1. Open Visual Studio Code + +Start Visual Studio Code. + +### 2. Open the Command Palette + +Open the Command Palette: + +- **Windows / Linux:** `Ctrl + Shift + P` +- **macOS:** `Cmd + Shift + P` + +### 3. Reopen the Workspace in the Dev Container + +In the Command Palette, select: + +```text +Dev Containers: Reopen in Container +``` + +VS Code will: + +- Build the dev container (if required) +- Start the container +- Reopen the workspace inside the container + +### 4. Start Home Assistant + +Open the Command Palette again and select: + +```text +Dev Terminal: Run Task... → Start Home Assistant +``` + +:::{admonition} Note +:class: note +Startup may take several minutes while the Home Assistant Supervisor initializes. +::: + +If startup fails you may retry with container rebuild before: + +```text +Dev Containers: Rebuild Container without Cache +``` + +### 5. Open Home Assistant + +Once startup is complete, open your browser and navigate to: + +```text +http://localhost:7123/ +``` + +If this is your first start, complete the standard Home Assistant onboarding process. + +### 6. Install the Local Akkudoktor-EOS Add-on + +#### 6.1 Open the Add-on Store + +In Home Assistant, navigate to: + +```text +Settings → Add-ons → Add-on Store +``` + +Open the top-right menu (ā‹®), then select: + +```text +Repositories → Local add-ons +``` + +Choose **Akkudoktor-EOS**. + +#### 6.2 Install the Add-on + +The Akkudoktor-EOS add-on is automatically available. +Click **Install** to begin installation. + +#### 6.3 Start the Add-on + +After installation completes, click **Start** in the add-on panel. + +#### 6.4 Open the EOS Web Interface + +In the add-on panel, click **Open Web UI** to access the EOS dashboard. + +#### 6.5 Configure EOS (Optional) + +In the EOS dashboard, navigate to: + +```text +Config +``` + +to adjust configuration settings as needed. diff --git a/docs/develop/install.md b/docs/develop/install.md index 80c3df1..3df6660 100644 --- a/docs/develop/install.md +++ b/docs/develop/install.md @@ -3,12 +3,13 @@ # Installation Guide -This guide provides different methods to install AkkudoktorEOS: +This guide provides different methods to install Akkudoktor-EOS: - Installation from Source (GitHub) (M1) - Installation from Release Package (GitHub) (M2) - Installation with Docker (DockerHub) (M3) - Installation with Docker (docker-compose) (M4) +- Installation in Home Assistant (M5) Choose the method that best suits your needs. @@ -22,20 +23,34 @@ release see the [Revert Guideline](revert-page). Before installing, ensure you have the following: -### For Source / Release Installation +### For Source / Release Installation (M1/M2) - Python 3.10 or higher - pip - Git (only for source) - Tar/Zip (for release package) -### For Docker Installation +### For Docker Installation (M3/M4) - Docker Engine 20.10 or higher - Docker Compose (optional, recommended) +:::{admonition} Tip +:class: Note See [Install Docker Engine](https://docs.docker.com/engine/install/) on how to install docker on your Linux distro. +::: + +### For Installation in Home Assistant (M5) + +- [Home Assistant Operating System](https://www.home-assistant.io/installation/) + +:::{admonition} Warning +:class: Warning +Akkudoktor-EOS is a [Home Assistant add-on](https://www.home-assistant.io/addons/). +[Home Assistant Container](https://www.home-assistant.io/installation/) installations don’t +have access to add-ons. +::: ## Installation from Source (GitHub) (M1) @@ -214,7 +229,12 @@ should be available at [http://localhost:8504](http://localhost:8504). ### 4) Configure EOS (M3) -Use EOSdash at [http://localhost:8504](http://localhost:8504) to configure EOS. +Use EOSdash at [http://localhost:8504](http://localhost:8504) to configure EOS. In the dashboard, +go to: + +```bash +Config +``` ## Installation with Docker (docker-compose) (M4) @@ -251,37 +271,85 @@ docker logs akkudoktoreos EOS should now be accessible at [http://localhost:8503/docs](http://localhost:8503/docs) and EOSdash should be available at [http://localhost:8504](http://localhost:8504). -### 4) Configure EOS +The configuration file is in `${HOME}/.local/share/net.akkudoktor.eos/config/EOS.config.json`. -Use EOSdash at [http://localhost:8504](http://localhost:8504) to configure EOS. +### 4) Configure EOS (M4) + +Use EOSdash at [http://localhost:8504](http://localhost:8504) to configure EOS. In the dashboard, +go to: + +```bash +Config +``` + +You may edit the configuration file directly at +`${HOME}/.local/share/net.akkudoktor.eos/config/EOS.config.json`. + +## Installation in Home Assistant (M5) + +[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FAkkudoktor-EOS%2FEOS) + +### 1) Add the repository URL (M5) + +In Home Assistant, go to: + +```bash +Settings → Add-ons → Add-on Store → ā‹® (top-right menu) → Repositories +``` + +and enter the URL of this Git repository: + +```bash +https://github.com/Akkudoktor-EOS/EOS +``` + +### 2) Install the add-on (M5) + +After adding the repository, the add-on will appear in the Add-on Store. Click `Install`. + +### 3) Start the add-on (M5) + +Once installed, click `Start` in the add-on panel. + +### 4) Access the dashboard (M5) + +Click `Open Web UI` in the add-on panel. + +### 5) Configure EOS (M5) + +In the dashboard, go to: + +```bash +Config +``` ## Helpful Docker Commands -**View logs:** +### View logs ```bash docker logs -f akkudoktoreos ``` -**Stop the container:** +### Stop the container ```bash docker stop akkudoktoreos ``` -**Start the container:** +### Start the container ```bash docker start akkudoktoreos ``` -**Remove the container:** +### Remove the container ```bash docker rm -f akkudoktoreos ``` -**Update to latest version:** +### Update to latest version ```bash docker pull Akkudoktor-EOS/EOS:latest @@ -289,3 +357,29 @@ docker stop akkudoktoreos docker rm akkudoktoreos # Then run the container again with the run command ``` + +### Solve docker DNS not working + +Switch Docker to use the real resolv.conf, not the stub. + +1ļøāƒ£ Replace /etc/resolv.conf symlink + +```bash +sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf +``` + +This file contains the actual upstream DNS servers (e.g. your Fritz!Box). + +2ļøāƒ£ Restart Docker + +```bash +sudo systemctl restart docker +``` + +3ļøāƒ£ Verify + +```bash +docker run --rm busybox nslookup registry-1.docker.io +``` + +You should now see a valid IP address. diff --git a/docs/develop/release.md b/docs/develop/release.md index 8436886..8ab4955 100644 --- a/docs/develop/release.md +++ b/docs/develop/release.md @@ -10,7 +10,7 @@ and how to set a **development version** after the release. | Step | Actor | Action | |------|-------------|--------| -| 1 | Contributor | Prepare a release branch **in your fork** using Commitizen | +| 1 | Contributor | Prepare a release branch **in your fork** | | 2 | Contributor | Open a **Pull Request to upstream** (`Akkudoktor-EOS/EOS`) | | 3 | Maintainer | Review and **merge the release PR** | | 4 | CI | Create the **GitHub Release and tag** | @@ -48,7 +48,7 @@ __version__ = 0.3.0 Prepare version by updating versioned files, e.g.: -- haaddon/config.yaml +- config.yaml and the generated documentation: @@ -132,7 +132,7 @@ See `.github/workflwows/bump-version.yml`for details. ### 5ļøāƒ£ CI: Prepare the Development Version Marker -The development version marker will automatically be set by the GitHub CI action. +The development version marker `.dev` will automatically be set by the GitHub CI action. See `.github/workflwows/bump-version.yml`for details. diff --git a/docs/develop/update.md b/docs/develop/update.md new file mode 100644 index 0000000..541b23c --- /dev/null +++ b/docs/develop/update.md @@ -0,0 +1,109 @@ +% SPDX-License-Identifier: Apache-2.0 +(update-page)= + +# Update Guide + +This guide explains how to update AkkudoktorEOS to a newer version. + +- Updating from Source (M1) +- Updating from Release Package (M2) +- Updating Docker Installation (M3) +- Updating Docker Compose Installation (M4) +- Updating Home Assistant Add-on Installation (M5) + +Choose the section based on how you originally [installed EOS](install-page). + +:::{admonition} Tip +:class: Note +If you need to revert instead, see the see the [Revert Guideline](revert-page). +::: + +## Updating from Source (M1) + +```{eval-rst} +.. tabs:: + + .. tab:: Windows + + .. code-block:: powershell + + git pull origin main + .venv\Scripts\pip install -r requirements.txt --upgrade + + .. tab:: Linux + + .. code-block:: bash + + git pull origin main + .venv/bin/pip install -r requirements.txt --upgrade +``` + +Restart EOS normally. + +## Updating from Release Package (M2) + +1. Download new release +2. Extract to a new directory +3. Recreate virtual environment & reinstall dependencies +4. Optionally remove previous directory + +Follow steps from [Installation from Release Package (GitHub) (M2)](install-page). + +## Updating Docker Installation (M3) + +```bash +docker pull akkudoktor/eos:latest +docker stop akkudoktoreos +docker rm akkudoktoreos +``` + +Then start the container again using your normal `docker run` command. + +## Updating Docker Compose Installation (M4) + +1. Stop & remove existing container + + ```bash + docker stop akkudoktoreos + docker rm akkudoktoreos + ``` + +2. Update source (if using source checkout) — see M1 or M2 +3. Rebuild & start + + ```bash + docker compose up --build + ``` + +## Verify Docker Update (M3/M4) + +Check logs: + +```bash +docker logs akkudoktoreos +``` + +Then visit: + +- API: [http://localhost:8503/docs](http://localhost:8503/docs) +- UI: [http://localhost:8504](http://localhost:8504) + +## Updating Home Assistant Add-on Installation (M5) + +1. Open 'Home Assistant' and navigate to 'Settings → Add-ons'. +2. Select the 'Akkudoktor-EOS' add-on from your installed add-ons. +3. If an update is available, click 'Update'. +4. Wait for the update process to finish, then restart the add-on if prompted. + +If you installed Akkudoktor-EOS from a custom repository and no update appears, open the Add-on +Store, click the 'ā‹®' menu in the top right, and choose 'Reload' to refresh the repository. + +## Backup Recommendation + +Before updating, back up your config: + +```bash +EOS.config.json +``` + +EOS also maintains internal configuration backups. diff --git a/docs/index.md b/docs/index.md index 23bf586..ddc9ad6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,6 +28,7 @@ develop/getting_started.md develop/CONTRIBUTING.md develop/install.md +akkudoktoreos/configuration.md develop/update.md develop/revert.md diff --git a/openapi.json b/openapi.json index f04fa05..5f76e22 100644 --- a/openapi.json +++ b/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Akkudoktor-EOS", "description": "This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.", - "version": "v0.2.0+dev.4dbc2d" + "version": "v0.2.0.dev70048701" }, "paths": { "/v1/admin/cache/clear": { @@ -176,6 +176,9 @@ }, "/v1/health": { "get": { + "tags": [ + "health" + ], "summary": "Fastapi Health Get", "description": "Health check endpoint to verify that the EOS server is alive.", "operationId": "fastapi_health_get_v1_health_get", @@ -2020,6 +2023,96 @@ }, "components": { "schemas": { + "AdapterCommonSettings-Input": { + "properties": { + "provider": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provider", + "description": "List of adapter provider id(s) of provider(s) to be used.", + "examples": [ + [ + "HomeAssistant" + ], + [ + "HomeAssistant", + "NodeRED" + ] + ] + }, + "homeassistant": { + "$ref": "#/components/schemas/HomeAssistantAdapterCommonSettings-Input", + "description": "Home Assistant adapter settings." + }, + "nodered": { + "$ref": "#/components/schemas/NodeREDAdapterCommonSettings", + "description": "NodeRED adapter settings." + } + }, + "type": "object", + "title": "AdapterCommonSettings", + "description": "Adapter Configuration." + }, + "AdapterCommonSettings-Output": { + "properties": { + "provider": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Provider", + "description": "List of adapter provider id(s) of provider(s) to be used.", + "examples": [ + [ + "HomeAssistant" + ], + [ + "HomeAssistant", + "NodeRED" + ] + ] + }, + "homeassistant": { + "$ref": "#/components/schemas/HomeAssistantAdapterCommonSettings-Output", + "description": "Home Assistant adapter settings." + }, + "nodered": { + "$ref": "#/components/schemas/NodeREDAdapterCommonSettings", + "description": "NodeRED adapter settings." + }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available electricity price provider ids.", + "readOnly": true + } + }, + "type": "object", + "required": [ + "providers" + ], + "title": "AdapterCommonSettings", + "description": "Adapter Configuration." + }, "BatteriesCommonSettings-Input": { "properties": { "device_id": { @@ -2123,6 +2216,19 @@ ], "title": "Charge Rates", "description": "Charge rates as factor of maximum charging power [0.00 ... 1.00]. None triggers fallback to default charge-rates.", + "default": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "examples": [ [ 0.0, @@ -2264,6 +2370,19 @@ ], "title": "Charge Rates", "description": "Charge rates as factor of maximum charging power [0.00 ... 1.00]. None triggers fallback to default charge-rates.", + "default": [ + 0.0, + 0.1, + 0.2, + 0.3, + 0.4, + 0.5, + 0.6, + 0.7, + 0.8, + 0.9, + 1.0 + ], "examples": [ [ 0.0, @@ -2340,7 +2459,7 @@ } ], "title": "Measurement Keys", - "description": "Measurement keys for the battery stati that are measurements.\n\nBattery SoC, power.", + "description": "Measurement keys for the battery stati that are measurements.", "readOnly": true } }, @@ -2406,13 +2525,14 @@ "general": { "$ref": "#/components/schemas/GeneralSettings-Output", "default": { - "version": "0.2.0+dev.4dbc2d", + "version": "0.2.0.dev70048701", "data_output_subpath": "output", "latitude": 52.52, "longitude": 13.405, "timezone": "Europe/Berlin", "config_folder_path": "/home/user/.config/net.akkudoktoreos.net", - "config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json" + "config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json", + "home_assistant_addon": false } }, "cache": { @@ -2447,7 +2567,7 @@ } }, "optimization": { - "$ref": "#/components/schemas/OptimizationCommonSettings", + "$ref": "#/components/schemas/OptimizationCommonSettings-Output", "default": { "horizon_hours": 24, "interval": 3600, @@ -2455,7 +2575,8 @@ "genetic": { "generations": 400, "individuals": 300 - } + }, + "keys": [] } }, "prediction": { @@ -2472,19 +2593,34 @@ "elecpriceimport": {}, "energycharts": { "bidding_zone": "DE-LU" - } + }, + "providers": [ + "ElecPriceAkkudoktor", + "ElecPriceEnergyCharts", + "ElecPriceImport" + ] } }, "feedintariff": { "$ref": "#/components/schemas/FeedInTariffCommonSettings-Output", "default": { - "provider_settings": {} + "provider_settings": {}, + "providers": [ + "FeedInTariffFixed", + "FeedInTariffImport" + ] } }, "load": { "$ref": "#/components/schemas/LoadCommonSettings-Output", "default": { - "provider_settings": {} + "provider_settings": {}, + "providers": [ + "LoadAkkudoktor", + "LoadAkkudoktorAdjusted", + "LoadVrm", + "LoadImport" + ] } }, "pvforecast": { @@ -2492,6 +2628,11 @@ "default": { "provider_settings": {}, "max_planes": 0, + "providers": [ + "PVForecastAkkudoktor", + "PVForecastVrm", + "PVForecastImport" + ], "planes_peakpower": [], "planes_azimuth": [], "planes_tilt": [], @@ -2502,7 +2643,12 @@ "weather": { "$ref": "#/components/schemas/WeatherCommonSettings-Output", "default": { - "provider_settings": {} + "provider_settings": {}, + "providers": [ + "BrightSky", + "ClearOutside", + "WeatherImport" + ] } }, "server": { @@ -2517,6 +2663,24 @@ "utils": { "$ref": "#/components/schemas/UtilsCommonSettings", "default": {} + }, + "adapter": { + "$ref": "#/components/schemas/AdapterCommonSettings-Output", + "default": { + "homeassistant": { + "eos_device_instruction_entity_ids": [], + "eos_solution_entity_ids": [], + "homeassistant_entity_ids": [] + }, + "nodered": { + "host": "127.0.0.1", + "port": 1880 + }, + "providers": [ + "HomeAssistant", + "NodeRED" + ] + } } }, "additionalProperties": false, @@ -3098,9 +3262,21 @@ "energycharts": { "$ref": "#/components/schemas/ElecPriceEnergyChartsCommonSettings", "description": "Energy Charts provider settings." + }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available electricity price provider ids.", + "readOnly": true } }, "type": "object", + "required": [ + "providers" + ], "title": "ElecPriceCommonSettings", "description": "Electricity Price Prediction Configuration." }, @@ -3976,9 +4152,21 @@ "examples": [ {} ] + }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available feed in tariff provider ids.", + "readOnly": true } }, "type": "object", + "required": [ + "providers" + ], "title": "FeedInTariffCommonSettings", "description": "Feed In Tariff Prediction Configuration." }, @@ -4084,7 +4272,7 @@ "type": "string", "title": "Version", "description": "Configuration file version. Used to check compatibility.", - "default": "0.2.0+dev.4dbc2d" + "default": "0.2.0.dev70048701" }, "data_folder_path": { "anyOf": [ @@ -4129,7 +4317,7 @@ } ], "title": "Latitude", - "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)", + "description": "Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (\u00b0)", "default": 52.52 }, "longitude": { @@ -4144,13 +4332,13 @@ } ], "title": "Longitude", - "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)", + "description": "Longitude in decimal degrees within -180 to 180 (\u00b0)", "default": 13.405 } }, "type": "object", "title": "GeneralSettings", - "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude." + "description": "General settings." }, "GeneralSettings-Output": { "properties": { @@ -4158,7 +4346,7 @@ "type": "string", "title": "Version", "description": "Configuration file version. Used to check compatibility.", - "default": "0.2.0+dev.4dbc2d" + "default": "0.2.0.dev70048701" }, "data_folder_path": { "anyOf": [ @@ -4203,7 +4391,7 @@ } ], "title": "Latitude", - "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)", + "description": "Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (\u00b0)", "default": 52.52 }, "longitude": { @@ -4218,7 +4406,7 @@ } ], "title": "Longitude", - "description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)", + "description": "Longitude in decimal degrees within -180 to 180 (\u00b0)", "default": 13.405 }, "timezone": { @@ -4231,7 +4419,7 @@ } ], "title": "Timezone", - "description": "Compute timezone based on latitude and longitude.", + "description": "Computed timezone based on latitude and longitude.", "readOnly": true }, "data_output_path": { @@ -4245,7 +4433,7 @@ } ], "title": "Data Output Path", - "description": "Compute data_output_path based on data_folder_path.", + "description": "Computed data_output_path based on data_folder_path.", "readOnly": true }, "config_folder_path": { @@ -4275,6 +4463,12 @@ "title": "Config File Path", "description": "Path to EOS configuration file.", "readOnly": true + }, + "home_assistant_addon": { + "type": "boolean", + "title": "Home Assistant Addon", + "description": "EOS is running as home assistant add-on.", + "readOnly": true } }, "type": "object", @@ -4282,10 +4476,11 @@ "timezone", "data_output_path", "config_folder_path", - "config_file_path" + "config_file_path", + "home_assistant_addon" ], "title": "GeneralSettings", - "description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude." + "description": "General settings." }, "GeneticCommonSettings": { "properties": { @@ -5010,6 +5205,310 @@ "title": "HomeApplianceParameters", "description": "Home Appliance Device Simulation Configuration." }, + "HomeAssistantAdapterCommonSettings-Input": { + "properties": { + "config_entity_ids": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Config Entity Ids", + "description": "Mapping of EOS config keys to Home Assistant entity IDs.\nThe config key has to be given by a \u2018/\u2019-separated path\ne.g. devices/batteries/0/capacity_wh", + "examples": [ + { + "devices/batteries/0/capacity_wh": "sensor.battery1_capacity" + } + ] + }, + "load_emr_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Load Emr Entity Ids", + "description": "Entity ID(s) of load energy meter reading [kWh]", + "examples": [ + [ + "sensor.load_energy_total_kwh" + ], + [ + "sensor.load_emr1_kwh", + "sensor.load_emr2_kwh" + ] + ] + }, + "pv_production_emr_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pv Production Emr Entity Ids", + "description": "Entity ID(s) of PV production energy meter reading [kWh]", + "examples": [ + [ + "sensor.pv_energy_total_kwh" + ], + [ + "sensor.pv_emr1_kwh", + "sensor.pv_emr2_kwh" + ] + ] + }, + "device_measurement_entity_ids": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Device Measurement Entity Ids", + "description": "Mapping of EOS measurement keys used by device (resource) simulations to Home Assistant entity IDs.", + "examples": [ + { + "battery1_soc_factor": "sensor.battery1_soc_factor", + "ev11_soc_factor": "sensor.ev11_soc_factor" + } + ] + }, + "device_instruction_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Device Instruction Entity Ids", + "description": "Entity IDs for device (resource) instructions to be updated by EOS.\nThe device ids (resource ids) have to be prepended by 'sensor.eos_' to build the entity_id.\nE.g. The instruction for device id 'battery1' becomes the entity_id 'sensor.eos_battery1'.", + "examples": [ + [ + "sensor.eos_battery1" + ] + ] + }, + "solution_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Solution Entity Ids", + "description": "Entity IDs for optimization solution keys to be updated by EOS.\nThe solution keys have to be prepended by 'sensor.eos_' to build the entity_id.\nE.g. solution key 'battery1_idle_op_mode' becomes the entity_id 'sensor.eos_battery1_idle_op_mode'.", + "examples": [ + [ + "sensor.eos_battery1_idle_mode_mode" + ] + ] + } + }, + "type": "object", + "title": "HomeAssistantAdapterCommonSettings", + "description": "Common settings for the home assistant adapter." + }, + "HomeAssistantAdapterCommonSettings-Output": { + "properties": { + "config_entity_ids": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Config Entity Ids", + "description": "Mapping of EOS config keys to Home Assistant entity IDs.\nThe config key has to be given by a \u2018/\u2019-separated path\ne.g. devices/batteries/0/capacity_wh", + "examples": [ + { + "devices/batteries/0/capacity_wh": "sensor.battery1_capacity" + } + ] + }, + "load_emr_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Load Emr Entity Ids", + "description": "Entity ID(s) of load energy meter reading [kWh]", + "examples": [ + [ + "sensor.load_energy_total_kwh" + ], + [ + "sensor.load_emr1_kwh", + "sensor.load_emr2_kwh" + ] + ] + }, + "pv_production_emr_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Pv Production Emr Entity Ids", + "description": "Entity ID(s) of PV production energy meter reading [kWh]", + "examples": [ + [ + "sensor.pv_energy_total_kwh" + ], + [ + "sensor.pv_emr1_kwh", + "sensor.pv_emr2_kwh" + ] + ] + }, + "device_measurement_entity_ids": { + "anyOf": [ + { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Device Measurement Entity Ids", + "description": "Mapping of EOS measurement keys used by device (resource) simulations to Home Assistant entity IDs.", + "examples": [ + { + "battery1_soc_factor": "sensor.battery1_soc_factor", + "ev11_soc_factor": "sensor.ev11_soc_factor" + } + ] + }, + "device_instruction_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Device Instruction Entity Ids", + "description": "Entity IDs for device (resource) instructions to be updated by EOS.\nThe device ids (resource ids) have to be prepended by 'sensor.eos_' to build the entity_id.\nE.g. The instruction for device id 'battery1' becomes the entity_id 'sensor.eos_battery1'.", + "examples": [ + [ + "sensor.eos_battery1" + ] + ] + }, + "solution_entity_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Solution Entity Ids", + "description": "Entity IDs for optimization solution keys to be updated by EOS.\nThe solution keys have to be prepended by 'sensor.eos_' to build the entity_id.\nE.g. solution key 'battery1_idle_op_mode' becomes the entity_id 'sensor.eos_battery1_idle_op_mode'.", + "examples": [ + [ + "sensor.eos_battery1_idle_mode_mode" + ] + ] + }, + "homeassistant_entity_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Homeassistant Entity Ids", + "description": "Entity IDs available at Home Assistant.", + "readOnly": true + }, + "eos_solution_entity_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Eos Solution Entity Ids", + "description": "Entity IDs for optimization solution available at EOS.", + "readOnly": true + }, + "eos_device_instruction_entity_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Eos Device Instruction Entity Ids", + "description": "Entity IDs for energy management instructions available at EOS.", + "readOnly": true + } + }, + "type": "object", + "required": [ + "homeassistant_entity_ids", + "eos_solution_entity_ids", + "eos_device_instruction_entity_ids" + ], + "title": "HomeAssistantAdapterCommonSettings", + "description": "Common settings for the home assistant adapter." + }, "InverterCommonSettings-Input": { "properties": { "device_id": { @@ -5314,9 +5813,21 @@ "examples": [ {} ] + }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available load provider ids.", + "readOnly": true } }, "type": "object", + "required": [ + "providers" + ], "title": "LoadCommonSettings", "description": "Load Prediction Configuration." }, @@ -5385,7 +5896,7 @@ }, "type": "object", "title": "LoadVrmCommonSettings", - "description": "Common settings for VRM API." + "description": "Common settings for load forecast VRM API." }, "LoggingCommonSettings-Input": { "properties": { @@ -5684,6 +6195,46 @@ "title": "MeasurementCommonSettings", "description": "Measurement Configuration." }, + "NodeREDAdapterCommonSettings": { + "properties": { + "host": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Host", + "description": "Node-RED server IP address. Defaults to 127.0.0.1.", + "default": "127.0.0.1", + "examples": [ + "127.0.0.1", + "localhost" + ] + }, + "port": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Port", + "description": "Node-RED server IP port number. Defaults to 1880.", + "default": 1880, + "examples": [ + 1880 + ] + } + }, + "type": "object", + "title": "NodeREDAdapterCommonSettings", + "description": "Common settings for the NodeRED adapter.\n\nThe Node-RED adapter sends to HTTP IN nodes.\n\nThis is the example flow:\n\n[HTTP In \\\\] -> [Function (parse payload)] -> [Debug] -> [HTTP Response]\n\nThere are two URLs that are used:\n\n- GET /eos/data_aquisition\n The GET is issued before the optimization.\n- POST /eos/control_dispatch\n The POST is issued after the optimization." + }, "OMBCInstruction": { "properties": { "id": { @@ -5799,7 +6350,7 @@ "title": "OMBCStatus", "description": "Reports the current operational status of an Operation Mode Based Control system.\n\nThis model provides real-time status information about an OMBC-controlled device,\nincluding which operation mode is currently active, how it is configured,\nand information about recent mode transitions. It enables monitoring of the\ndevice's operational state and tracking mode transition history." }, - "OptimizationCommonSettings": { + "OptimizationCommonSettings-Input": { "properties": { "horizon_hours": { "anyOf": [ @@ -5877,6 +6428,96 @@ "title": "OptimizationCommonSettings", "description": "General Optimization Configuration." }, + "OptimizationCommonSettings-Output": { + "properties": { + "horizon_hours": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Horizon Hours", + "description": "The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours.", + "default": 24, + "examples": [ + 24 + ] + }, + "interval": { + "anyOf": [ + { + "type": "integer", + "maximum": 3600.0, + "minimum": 900.0 + }, + { + "type": "null" + } + ], + "title": "Interval", + "description": "The optimization interval [sec].", + "default": 3600, + "examples": [ + 3600, + 900 + ] + }, + "algorithm": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Algorithm", + "description": "The optimization algorithm.", + "default": "GENETIC", + "examples": [ + "GENETIC" + ] + }, + "genetic": { + "anyOf": [ + { + "$ref": "#/components/schemas/GeneticCommonSettings" + }, + { + "type": "null" + } + ], + "description": "Genetic optimization algorithm configuration.", + "examples": [ + { + "individuals": 400, + "penalties": { + "ev_soc_miss": 10 + } + } + ] + }, + "keys": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Keys", + "description": "The keys of the solution.", + "readOnly": true + } + }, + "type": "object", + "required": [ + "keys" + ], + "title": "OptimizationCommonSettings", + "description": "General Optimization Configuration." + }, "OptimizationSolution": { "properties": { "id": { @@ -6641,6 +7282,15 @@ 2 ] }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available PVForecast provider ids.", + "readOnly": true + }, "planes_peakpower": { "items": { "type": "number" @@ -6681,6 +7331,7 @@ }, "type": "object", "required": [ + "providers", "planes_peakpower", "planes_azimuth", "planes_tilt", @@ -7027,7 +7678,7 @@ }, "type": "object", "title": "PVForecastVrmCommonSettings", - "description": "Common settings for VRM API." + "description": "Common settings for PV forecast VRM API." }, "PowerMeasurement-Input": { "properties": { @@ -7144,7 +7795,7 @@ }, "type": "object", "title": "PredictionCommonSettings", - "description": "General Prediction Configuration.\n\nThis class provides configuration for prediction settings, allowing users to specify\nparameters such as the forecast duration (in hours).\nValidators ensure each parameter is within a specified range.\n\nAttributes:\n hours (Optional[int]): Number of hours into the future for predictions.\n Must be non-negative.\n historic_hours (Optional[int]): Number of hours into the past for historical data.\n Must be non-negative.\n\nValidators:\n validate_hours (int): Ensures `hours` is a non-negative integer.\n validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer." + "description": "General Prediction Configuration." }, "PydanticDateTimeData": { "additionalProperties": { @@ -7175,7 +7826,7 @@ }, "type": "object", "title": "PydanticDateTimeData", - "description": "Pydantic model for time series data with consistent value lengths.\n\nThis model validates a dictionary where:\n- Keys are strings representing data series names\n- Values are lists of numeric or string values\n- Special keys 'start_datetime' and 'interval' can contain string values\nfor time series indexing\n- All value lists must have the same length\n\nExample:\n .. code-block:: python\n\n {\n \"start_datetime\": \"2024-01-01 00:00:00\", # optional\n \"interval\": \"1 Hour\", # optional\n \"loadforecast_power_w\": [20.5, 21.0, 22.1],\n \"load_min\": [18.5, 19.0, 20.1]\n }" + "description": "Pydantic model for time series data with consistent value lengths.\n\nThis model validates a dictionary where:\n- Keys are strings representing data series names\n- Values are lists of numeric or string values\n- Special keys 'start_datetime' and 'interval' can contain string values\nfor time series indexing\n- All value lists must have the same length\n\nExample:\n .. code-block:: python\n\n {\n \"start_datetime\": \"2024-01-01 00:00:00\", # optional\n \"interval\": \"1 hour\", # optional\n \"loadforecast_power_w\": [20.5, 21.0, 22.1],\n \"load_min\": [18.5, 19.0, 20.1]\n }" }, "PydanticDateTimeDataFrame": { "properties": { @@ -7421,7 +8072,7 @@ "optimization": { "anyOf": [ { - "$ref": "#/components/schemas/OptimizationCommonSettings" + "$ref": "#/components/schemas/OptimizationCommonSettings-Input" }, { "type": "null" @@ -7516,6 +8167,17 @@ } ], "description": "Utilities Settings" + }, + "adapter": { + "anyOf": [ + { + "$ref": "#/components/schemas/AdapterCommonSettings-Input" + }, + { + "type": "null" + } + ], + "description": "Adapter Settings" } }, "additionalProperties": false, @@ -7931,9 +8593,21 @@ "examples": [ {} ] + }, + "providers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Providers", + "description": "Available weather provider ids.", + "readOnly": true } }, "type": "object", + "required": [ + "providers" + ], "title": "WeatherCommonSettings", "description": "Weather Forecast Configuration." }, diff --git a/pyproject.toml b/pyproject.toml index 01ea2b5..6135199 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,9 @@ convention = "google" minversion = "8.3.3" pythonpath = [ "src", ] testpaths = [ "tests", ] +markers = [ + "docker: marks tests that require a local Docker engine" +] [tool.mypy] files = ["src", "tests"] diff --git a/repository.yaml b/repository.yaml new file mode 100644 index 0000000..e63fecb --- /dev/null +++ b/repository.yaml @@ -0,0 +1,9 @@ +# Home Assistant - Add-on Repository Configuration +# ------------------------------------------------ +# https://developers.home-assistant.io/docs/add-ons/repository#repository-configuration +# +# The Akkudoktor-EOS add-on repo is special because there is only one add-on and it is in +# the root directory (no add-on folder as usual). +name: Akkudoktor-EOS +url: http://github.com/Akkudoktor-EOS/EOS +maintainer: Akkudoktor-EOS Team diff --git a/requirements-dev.txt b/requirements-dev.txt index 025ddf4..3e5d493 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,9 +25,11 @@ sphinx_rtd_theme==3.0.2 sphinx-tabs==3.4.7 GitPython==3.1.45 myst-parser==4.0.1 +docutils==0.21.2 # Pytest pytest==9.0.2 +pytest-asyncio==1.3.0 pytest-cov==7.0.0 -coverage==7.13.0 pytest-xprocess==1.0.2 +coverage==7.13.0 diff --git a/scripts/bump_dev_version.py b/scripts/bump_dev_version.py index 9077fe0..b6af450 100644 --- a/scripts/bump_dev_version.py +++ b/scripts/bump_dev_version.py @@ -1,17 +1,16 @@ #!/usr/bin/env python3 -""" -Update VERSION_BASE in version.py after a release tag. +"""Update VERSION_BASE in version.py after a release tag. Behavior: - Read VERSION_BASE from version.py -- Strip ANY existing "+dev" suffix -- Append exactly one "+dev" +- Strip ANY existing ".dev" suffix +- Append exactly one ".dev" - Write back the updated file This ensures: - 0.2.0 --> 0.2.0+dev - 0.2.0+dev --> 0.2.0+dev - 0.2.0+dev+dev -> 0.2.0+dev + 0.2.0 --> 0.2.0.dev + 0.2.0.dev --> 0.2.0.dev + 0.2.0.dev.dev -> 0.2.0.dev """ import re @@ -33,11 +32,11 @@ def bump_dev_version_file(file: Path) -> str: base_version = m.group(1) - # Remove trailing +dev if present → ensure idempotency - cleaned = re.sub(r'(\+dev)+$', '', base_version) + # Remove trailing .dev if present → ensure idempotency + cleaned = re.sub(r'(\.dev)+$', '', base_version) # Append +dev - new_version = f"{cleaned}+dev" + new_version = f"{cleaned}.dev" # Replace inside file content new_text = re.sub( diff --git a/scripts/update_version.py b/scripts/update_version.py index eb6c1bd..f915bfc 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -15,43 +15,49 @@ from typing import List VERSION_PATTERNS = [ # Python: __version__ = "1.2.3" re.compile( - r'(?\d+\.\d+\.\d+(?:\+[0-9A-Za-z\.]+)?)' + r'(?\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' r'(")' ), # Python: version = "1.2.3" re.compile( - r'(?\d+\.\d+\.\d+(?:\+[0-9A-Za-z\.]+)?)' + r'(?\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' r'(")' ), # JSON: "version": "1.2.3" re.compile( - r'(?\d+\.\d+\.\d+(?:\+[0-9A-Za-z\.]+)?)' + r'(?\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' r'(")' ), # Makefile-style: VERSION ?= 1.2.3 re.compile( - r'(?\d+\.\d+\.\d+(?:\+[0-9A-Za-z\.]+)?)' + r'(?\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' + ), + + # Environment-style: VERSION = 1.2.3 + re.compile( + r'(?\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' ), # YAML: version: "1.2.3" re.compile( r'(?m)^(version\s*:\s*["\']?)' - r'(?P\d+\.\d+\.\d+(?:\+[0-9A-Za-z\.]+)?)' + r'(?P\d+\.\d+\.\d+(?:[\.\+\-][0-9A-Za-z]+)?)' r'(["\']?)\s*$' ), ] def update_version_in_file(file_path: Path, new_version: str) -> bool: - """ - Replace version strings in a file based on VERSION_PATTERNS. + """Replace version strings in a file based on VERSION_PATTERNS. + Returns True if the file was updated. """ content = file_path.read_text() diff --git a/src/akkudoktoreos/adapter/adapter.py b/src/akkudoktoreos/adapter/adapter.py new file mode 100644 index 0000000..3aa07bd --- /dev/null +++ b/src/akkudoktoreos/adapter/adapter.py @@ -0,0 +1,94 @@ +from typing import TYPE_CHECKING, Optional, Union + +from pydantic import Field, computed_field, field_validator + +from akkudoktoreos.adapter.adapterabc import AdapterContainer +from akkudoktoreos.adapter.homeassistant import ( + HomeAssistantAdapter, + HomeAssistantAdapterCommonSettings, +) +from akkudoktoreos.adapter.nodered import NodeREDAdapter, NodeREDAdapterCommonSettings +from akkudoktoreos.config.configabc import SettingsBaseModel + +if TYPE_CHECKING: + adapter_providers: list[str] + + +class AdapterCommonSettings(SettingsBaseModel): + """Adapter Configuration.""" + + provider: Optional[list[str]] = Field( + default=None, + json_schema_extra={ + "description": ("List of adapter provider id(s) of provider(s) to be used."), + "examples": [["HomeAssistant"], ["HomeAssistant", "NodeRED"]], + }, + ) + + homeassistant: HomeAssistantAdapterCommonSettings = Field( + default_factory=HomeAssistantAdapterCommonSettings, + json_schema_extra={"description": "Home Assistant adapter settings."}, + ) + + nodered: NodeREDAdapterCommonSettings = Field( + default_factory=NodeREDAdapterCommonSettings, + json_schema_extra={"description": "NodeRED adapter settings."}, + ) + + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available electricity price provider ids.""" + return adapter_providers + + # Validators + @field_validator("provider", mode="after") + @classmethod + def validate_provider(cls, value: Optional[list[str]]) -> Optional[list[str]]: + if value is None: + return value + for provider_id in value: + if provider_id not in adapter_providers: + raise ValueError( + f"Provider '{value}' is not a valid adapter provider: {adapter_providers}." + ) + return value + + +class Adapter(AdapterContainer): + """Adapter container to manage multiple adapter providers. + + Attributes: + providers (List[Union[PVForecastAkkudoktor, WeatherBrightSky, WeatherClearOutside]]): + List of forecast provider instances, in the order they should be updated. + Providers may depend on updates from others. + """ + + providers: list[ + Union[ + HomeAssistantAdapter, + NodeREDAdapter, + ] + ] = Field(default_factory=list, json_schema_extra={"description": "List of adapter providers"}) + + +# Initialize adapter providers, all are singletons. +homeassistant_adapter = HomeAssistantAdapter() +nodered_adapter = NodeREDAdapter() + + +def get_adapter() -> Adapter: + """Gets the EOS adapter data.""" + # Initialize Adapter instance with providers in the required order + # Care for provider sequence as providers may rely on others to be updated before. + adapter = Adapter( + providers=[ + homeassistant_adapter, + nodered_adapter, + ] + ) + return adapter + + +# Valid adapter providers +adapter_providers = [provider.provider_id() for provider in get_adapter().providers] diff --git a/src/akkudoktoreos/adapter/adapterabc.py b/src/akkudoktoreos/adapter/adapterabc.py new file mode 100644 index 0000000..819b01d --- /dev/null +++ b/src/akkudoktoreos/adapter/adapterabc.py @@ -0,0 +1,160 @@ +"""Abstract and base classes for adapters.""" + +from abc import abstractmethod +from typing import Any, Optional + +from loguru import logger +from pydantic import ( + Field, + field_validator, +) + +from akkudoktoreos.core.coreabc import ( + ConfigMixin, + MeasurementMixin, + SingletonMixin, + StartMixin, +) +from akkudoktoreos.core.pydantic import PydanticBaseModel +from akkudoktoreos.utils.datetimeutil import ( + DateTime, +) + + +class AdapterProvider(SingletonMixin, ConfigMixin, MeasurementMixin, StartMixin, PydanticBaseModel): + """Abstract base class for adapter providers with singleton thread-safety and configurable data parameters. + + Note: + Derived classes have to provide their own _update_data method. + """ + + update_datetime: Optional[DateTime] = Field( + None, json_schema_extra={"description": "Latest update datetime for adapter data"} + ) + + @abstractmethod + def provider_id(self) -> str: + """Return the unique identifier for the adapter provider. + + To be implemented by derived classes. + """ + return "AdapterProvider" + + def enabled(self) -> bool: + """Return True if the provider is enabled according to configuration. + + Can be overwritten by derived classes. + """ + if self.config.adapter is None: + return False + if isinstance(self.config.adapter.provider, str): + return self.provider_id() == self.config.adapter.provider + if isinstance(self.config.adapter.provider, list): + return self.provider_id() in self.config.adapter.provider + return False + + @abstractmethod + def _update_data(self) -> None: + """Abstract method for custom adapter data update logic, to be implemented by derived classes. + + Data update may be requested at different stages of energy management. The stage can be + detected by self.ems.stage(). + """ + pass + + def __init__(self, *args: Any, **kwargs: Any) -> None: + if hasattr(self, "_initialized"): + return + super().__init__(*args, **kwargs) + + def update_data( + self, + force_enable: Optional[bool] = False, + ) -> None: + """Calls the custom update function if enabled or forced. + + Args: + force_enable (bool, optional): If True, forces the update even if the provider is disabled. + """ + # Check after configuration is updated. + if not force_enable and not self.enabled(): + return + + # Call the custom update logic + logger.debug(f"Update adapter provider: {self.provider_id()}") + self._update_data() + + +class AdapterContainer(SingletonMixin, ConfigMixin, PydanticBaseModel): + """A container for managing multiple adapter provider instances. + + This class enables to control multiple adapter providers + """ + + providers: list[AdapterProvider] = Field( + default_factory=list, json_schema_extra={"description": "List of adapter providers"} + ) + + @field_validator("providers") + def check_providers(cls, value: list[AdapterProvider]) -> list[AdapterProvider]: + # Check each item in the list + for item in value: + if not isinstance(item, AdapterProvider): + raise TypeError( + f"Each item in the adapter providers list must be an AdapterProvider, got {type(item).__name__}" + ) + return value + + @property + def enabled_providers(self) -> list[Any]: + """List of providers that are currently enabled.""" + enab = [] + for provider in self.providers: + if provider.enabled(): + enab.append(provider) + return enab + + def __init__(self, *args: Any, **kwargs: Any) -> None: + if hasattr(self, "_initialized"): + return + super().__init__(*args, **kwargs) + + def provider_by_id(self, provider_id: str) -> AdapterProvider: + """Retrieves an adapter provider by its unique identifier. + + This method searches through the list of all available providers and + returns the first provider whose `provider_id` matches the given + `provider_id`. If no matching provider is found, the method returns `None`. + + Args: + provider_id (str): The unique identifier of the desired data provider. + + Returns: + DataProvider: The data provider matching the given `provider_id`. + + Raises: + ValueError if provider id is unknown. + + Example: + provider = data.provider_by_id("WeatherImport") + """ + providers = {provider.provider_id(): provider for provider in self.providers} + if provider_id not in providers: + error_msg = f"Unknown provider id: '{provider_id}' of '{providers.keys()}'." + logger.error(error_msg) + raise ValueError(error_msg) + return providers[provider_id] + + def update_data( + self, + force_enable: Optional[bool] = False, + ) -> None: + """Calls the custom update function of all adapters if enabled or forced. + + Args: + force_enable (bool, optional): If True, forces the update even if the provider is disabled. + """ + # Call the custom update logic + if len(self.providers) > 0: + for provider in self.providers: + provider.update_data(force_enable=force_enable) diff --git a/src/akkudoktoreos/adapter/homeassistant.py b/src/akkudoktoreos/adapter/homeassistant.py new file mode 100644 index 0000000..f33c668 --- /dev/null +++ b/src/akkudoktoreos/adapter/homeassistant.py @@ -0,0 +1,524 @@ +"""Home Assistant adapter.""" + +import os +from typing import Optional, Union + +import pandas as pd +import requests +from loguru import logger +from pydantic import Field, computed_field, field_validator + +from akkudoktoreos.adapter.adapterabc import AdapterProvider +from akkudoktoreos.config.configabc import SettingsBaseModel +from akkudoktoreos.core.emplan import ( + DDBCInstruction, + FRBCInstruction, +) +from akkudoktoreos.core.ems import EnergyManagementStage +from akkudoktoreos.devices.devices import get_resource_registry +from akkudoktoreos.utils.datetimeutil import to_datetime + +# Supervisor API endpoint and token (injected automatically in add-on container) +CORE_API = "http://supervisor/core/api" +TOKEN = os.environ.get("SUPERVISOR_TOKEN") + +HEADERS = { + "Authorization": f"Bearer {TOKEN}", + "Content-Type": "application/json", +} + +HOMEASSISTANT_ENTITY_ID_PREFIX = "sensor.eos_" + +resources_eos = get_resource_registry() + + +class HomeAssistantAdapterCommonSettings(SettingsBaseModel): + """Common settings for the home assistant adapter.""" + + config_entity_ids: Optional[dict[str, str]] = Field( + default=None, + json_schema_extra={ + "description": ( + "Mapping of EOS config keys to Home Assistant entity IDs.\n" + "The config key has to be given by a ā€˜/’-separated path\n" + "e.g. devices/batteries/0/capacity_wh" + ), + "examples": [ + { + "devices/batteries/0/capacity_wh": "sensor.battery1_capacity", + } + ], + }, + ) + + load_emr_entity_ids: Optional[list[str]] = Field( + default=None, + json_schema_extra={ + "description": "Entity ID(s) of load energy meter reading [kWh]", + "examples": [ + ["sensor.load_energy_total_kwh"], + ["sensor.load_emr1_kwh", "sensor.load_emr2_kwh"], + ], + }, + ) + + pv_production_emr_entity_ids: Optional[list[str]] = Field( + default=None, + json_schema_extra={ + "description": "Entity ID(s) of PV production energy meter reading [kWh]", + "examples": [ + ["sensor.pv_energy_total_kwh"], + ["sensor.pv_emr1_kwh", "sensor.pv_emr2_kwh"], + ], + }, + ) + + device_measurement_entity_ids: Optional[dict[str, str]] = Field( + default=None, + json_schema_extra={ + "description": "Mapping of EOS measurement keys used by device (resource) simulations to Home Assistant entity IDs.", + "examples": [ + { + "ev11_soc_factor": "sensor.ev11_soc_factor", + "battery1_soc_factor": "sensor.battery1_soc_factor", + } + ], + }, + ) + + device_instruction_entity_ids: Optional[list[str]] = Field( + default=None, + json_schema_extra={ + "description": ( + "Entity IDs for device (resource) instructions to be updated by EOS.\n" + f"The device ids (resource ids) have to be prepended by '{HOMEASSISTANT_ENTITY_ID_PREFIX}' to build the entity_id.\n" + f"E.g. The instruction for device id 'battery1' becomes the entity_id " + f"'{HOMEASSISTANT_ENTITY_ID_PREFIX}battery1'." + ), + "examples": [ + [ + f"{HOMEASSISTANT_ENTITY_ID_PREFIX}battery1", + ] + ], + }, + ) + + solution_entity_ids: Optional[list[str]] = Field( + default=None, + json_schema_extra={ + "description": ( + "Entity IDs for optimization solution keys to be updated by EOS.\n" + f"The solution keys have to be prepended by '{HOMEASSISTANT_ENTITY_ID_PREFIX}' to build the entity_id.\n" + f"E.g. solution key 'battery1_idle_op_mode' becomes the entity_id " + f"'{HOMEASSISTANT_ENTITY_ID_PREFIX}battery1_idle_op_mode'." + ), + "examples": [ + [ + f"{HOMEASSISTANT_ENTITY_ID_PREFIX}battery1_idle_mode_mode", + ] + ], + }, + ) + + # Computed fields + @computed_field # type: ignore[prop-decorator] + @property + def homeassistant_entity_ids(self) -> list[str]: + """Entity IDs available at Home Assistant.""" + try: + from akkudoktoreos.adapter.adapter import get_adapter + + adapter_eos = get_adapter() + result = adapter_eos.provider_by_id("HomeAssistant").get_homeassistant_entity_ids() + except: + return [] + return result + + @computed_field # type: ignore[prop-decorator] + @property + def eos_solution_entity_ids(self) -> list[str]: + """Entity IDs for optimization solution available at EOS.""" + try: + from akkudoktoreos.adapter.adapter import get_adapter + + adapter_eos = get_adapter() + result = adapter_eos.provider_by_id("HomeAssistant").get_eos_solution_entity_ids() + except: + return [] + return result + + @computed_field # type: ignore[prop-decorator] + @property + def eos_device_instruction_entity_ids(self) -> list[str]: + """Entity IDs for energy management instructions available at EOS.""" + try: + from akkudoktoreos.adapter.adapter import get_adapter + + adapter_eos = get_adapter() + result = adapter_eos.provider_by_id( + "HomeAssistant" + ).get_eos_device_instruction_entity_ids() + except: + return [] + return result + + # Validators + @field_validator("solution_entity_ids", mode="after") + @classmethod + def validate_solution_entity_ids(cls, value: Optional[list[str]]) -> Optional[list[str]]: + if value is None: + return None + for entity_id in value: + if not entity_id.startswith(HOMEASSISTANT_ENTITY_ID_PREFIX): + raise ValueError( + f"Invalid optimization solution entity id '{entity_id}': prefix '{HOMEASSISTANT_ENTITY_ID_PREFIX}' expected." + ) + return value + + @field_validator("device_instruction_entity_ids", mode="after") + @classmethod + def validate_device_instruction_entity_ids( + cls, value: Optional[list[str]] + ) -> Optional[list[str]]: + if value is None: + return None + for entity_id in value: + if not entity_id.startswith(HOMEASSISTANT_ENTITY_ID_PREFIX): + raise ValueError( + f"Invalid instruction entity id '{entity_id}': prefix '{HOMEASSISTANT_ENTITY_ID_PREFIX}' expected." + ) + return value + + +class HomeAssistantAdapter(AdapterProvider): + @classmethod + def provider_id(cls) -> str: + """Return the unique identifier for the adapter provider.""" + return "HomeAssistant" + + def get_homeassistant_entity_ids(self) -> list[str]: + """Retrieve the available entity IDs from Home Assistant. + + Returns: + list[str]: The available entity IDs, or []. + + Example: + >>> entity_ids = get_homeassistant_entity_ids() + >>> print(entity_ids) + ["sensor.pv_all", "sensor.battery1_soc"] + """ + if not TOKEN: + raise RuntimeError("Missing SUPERVISOR_TOKEN environment variable.") + + entity_ids = [] + + url = f"{CORE_API}/states" + resp = requests.get(url, headers=HEADERS, timeout=10) + if resp.ok: + data = resp.json() + entity_ids = [ + entity["entity_id"] + for entity in data + if not entity["entity_id"].startswith(HOMEASSISTANT_ENTITY_ID_PREFIX) + ] + debug_msg = f"homeassistant_entity_ids: {entity_ids}" + logger.debug(debug_msg) + else: + error_msg = f"Failed to read entity states: {resp.text}" + logger.error(error_msg) + raise ValueError(error_msg) + + return sorted(entity_ids) + + def _entity_id_from_solution_key(self, key: str) -> str: + return HOMEASSISTANT_ENTITY_ID_PREFIX + key + + def get_eos_solution_entity_ids(self) -> list[str]: + """Retrieve the available entity IDs for the EOS optimization solution. + + Returns: + list[str]: The available entity IDs, or []. + """ + solution_entity_ids = [] + try: + optimization_solution_keys = self.config.optimization.keys + for key in sorted(optimization_solution_keys): + solution_entity_ids.append(self._entity_id_from_solution_key(key)) + except: + solution_entity_ids = [] + return solution_entity_ids + + def _entity_id_from_resource_id(self, resource_id: str) -> str: + return HOMEASSISTANT_ENTITY_ID_PREFIX + resource_id + + def get_eos_device_instruction_entity_ids(self) -> list[str]: + """Retrieve the available entity IDs for the EOS energy management plan instructions. + + Returns: + list[str]: The available entity IDs, or []. + """ + instruction_entity_ids = [] + plan = self.ems.plan() + if plan: + resource_ids = plan.get_resources() + for resource_id in resource_ids: + instruction_entity_ids.append(self._entity_id_from_resource_id(resource_id)) + return sorted(instruction_entity_ids) + + def set_entity_state( + self, entity_id: str, state_value: str, attributes: dict | None = None + ) -> None: + """Post or update a Home Assistant entity state. + + Args: + entity_id (str): The Home Assistant entity ID to update. + state_value (str): The new state value for the entity. + attributes (dict | None): Optional dictionary of additional attributes. + + Raises: + requests.RequestException: If the HTTP request to Home Assistant fails. + + Example: + >>> set_entity_state("sensor.energy_optimizer_status", "running") + """ + if not TOKEN: + raise RuntimeError("Missing SUPERVISOR_TOKEN environment variable.") + + url = f"{CORE_API}/states/{entity_id}" + data = {"state": state_value, "attributes": attributes or {}} + resp = requests.post(url, headers=HEADERS, json=data, timeout=10) + if resp.status_code not in (200, 201): + error_msg = f"Failed to update {entity_id}: {resp.text}" + logger.error(error_msg) + raise ValueError(error_msg) + else: + debug_msg = f"Updated {entity_id} = {state_value}" + logger.debug(debug_msg) + + def get_entity_state(self, entity_id: str) -> str: + """Retrieve the current state of an entity from Home Assistant. + + Args: + entity_id (str): The Home Assistant entity ID to query. + + Returns: + str: The current state of the entity. + + Example: + >>> state = get_entity_state("switch.living_room_lamp") + >>> print(state) + "on" + """ + if not TOKEN: + raise RuntimeError("Missing SUPERVISOR_TOKEN environment variable.") + + url = f"{CORE_API}/states/{entity_id}" + resp = requests.get(url, headers=HEADERS, timeout=10) + if resp.ok: + data = resp.json() + debug_msg = f"{entity_id}: {data['state']}" + logger.debug(debug_msg) + return data["state"] + else: + error_msg = f"Failed to read {entity_id}: {resp.text}" + logger.error(error_msg) + raise ValueError(error_msg) + + def _convert_entity_state(self, state: str) -> Union[bool, float, str, None]: + """Convert a Home Assistant entity state to a Python value. + + This method converts the raw ``state`` string of a Home Assistant entity + into an appropriate Python type, following Home Assistant's global + state model and commonly used domain semantics. + + Conversion rules: + + **Availability states** + - ``"unavailable"``, ``"unknown"``, ``"none"`` → ``None`` + + **Binary / boolean states** + Used by binary sensors and many device domains: + - ``"on"``, ``"true"``, ``"yes"``, ``"open"``, ``"opening"``, + ``"locked"``, ``"home"``, ``"detected"``, ``"active"`` → ``True`` + - ``"off"``, ``"false"``, ``"no"``, ``"closed"``, ``"closing"``, + ``"unlocked"``, ``"not_home"``, ``"clear"``, ``"idle"`` → ``False`` + + **Numeric states** + - Values that can be parsed as numbers are converted to ``float``. + This covers most sensor entities (temperature, power, energy, etc.). + + **Other states** + - Any remaining states (e.g. ``"playing"``, ``"paused"``, + ``"cooling"``, ``"heating"``, ``"standby"``, ``"jammed"``) are + returned as their original string value. + + The input state is normalized using ``strip()`` and ``lower()`` before + conversion. If numeric conversion fails, the original unmodified + state string is returned. + + Args: + state: Raw entity state as provided by Home Assistant. + + Returns: + The converted entity state as one of: + ``None``, ``bool``, ``float``, or ``str``. + """ + raw_state = state + value = state.strip().lower() + + # Availability / unknown states + if value in {"unavailable", "unknown", "none"}: + return None + + # States that semantically represent True + if value in { + "on", + "true", + "yes", + "y", + "open", + "opening", + "locked", + "home", + "detected", + "active", + }: + return True + + # States that semantically represent False + if value in { + "off", + "false", + "no", + "n", + "closed", + "closing", + "unlocked", + "not_home", + "clear", + "idle", + }: + return False + + # Numeric states (sensors, counters, percentages, etc.) + try: + return float(value) + except ValueError: + # Preserve original state for enums and free-text states + return raw_state + + def _update_data(self) -> None: + stage = self.ems.stage() + if stage == EnergyManagementStage.DATA_ACQUISITION: + # Sync configuration + entity_ids = self.config.adapter.homeassistant.config_entity_ids + if entity_ids: + for ( + config_key, + entity_id, + ) in entity_ids.items(): + try: + state = self.get_entity_state(entity_id) + logger.debug(f"Entity {entity_id}: {state}") + value = self._convert_entity_state(state) + if value: + self.config.set_nested_value(config_key, value) + except Exception as e: + logger.error(f"{e}") + + # Retrieve measurements necessary for device simulations + entity_ids = self.config.adapter.homeassistant.device_measurement_entity_ids + if entity_ids: + for ( + measurement_key, + entity_id, + ) in entity_ids.items(): + if entity_id: + try: + state = self.get_entity_state(entity_id) + logger.debug(f"Entity {entity_id}: {state}") + if state: + measurement_value = float(state) + self.measurement.update_value( + self.ems_start_datetime, measurement_key, measurement_value + ) + except Exception as e: + logger.error(f"{e}") + + # Retrieve measurements for load prediction + entity_ids = self.config.adapter.homeassistant.load_emr_entity_ids + if entity_ids: + measurement_keys = self.config.measurement.load_emr_keys + if measurement_keys is None: + measurement_keys = [] + for entity_id in entity_ids: + measurement_key = entity_id + if measurement_key not in measurement_keys: + measurement_keys.append(measurement_key) + self.comfig.measurement.load_emr_keys = measurement_keys + try: + state = self.get_entity_state(entity_id) + logger.debug(f"Entity {entity_id}: {state}") + if state: + measurement_value = float(state) + self.measurement.update_value( + self.ems_start_datetime, measurement_key, measurement_value + ) + except Exception as e: + logger.error(f"{e}") + + # Retrieve measurements for PV prediction + entity_ids = self.config.adapter.homeassistant.pv_production_emr_entity_ids + if entity_ids: + measurement_keys = self.config.measurement.pv_production_emr_keys + if measurement_keys is None: + measurement_keys = [] + for entity_id in entity_ids: + measurement_key = entity_id + if measurement_key not in measurement_keys: + measurement_keys.append(measurement_key) + self.comfig.measurement.pv_production_emr_keys = measurement_keys + try: + state = self.get_entity_state(entity_id) + logger.debug(f"Entity {entity_id}: {state}") + if state: + measurement_value = float(state) + self.measurement.update_value( + self.ems_start_datetime, measurement_key, measurement_value + ) + except Exception as e: + logger.error(f"{e}") + + # We got data - mark the update time + self.update_datetime = to_datetime() + + if stage == EnergyManagementStage.CONTROL_DISPATCH: + # Currently active optimization solution + optimization_solution = self.ems.optimization_solution() + entity_ids = self.config.adapter.homeassistant.solution_entity_ids + if optimization_solution and entity_ids: + df = optimization_solution.solution.to_dataframe() + now = pd.Timestamp.now(tz=df.index.tz) + row = df.loc[:now].iloc[-1] # Last known value before now + for entity_id in entity_ids: + solution_key = entity_id[len(HOMEASSISTANT_ENTITY_ID_PREFIX) :] + try: + self.set_entity_state(entity_id, row[solution_key]) + except Exception as e: + logger.error(f"{e}") + # Currently active instructions + instructions = self.ems.plan().get_active_instructions() + entity_ids = self.config.adapter.homeassistant.device_instruction_entity_ids + if instructions and entity_ids: + for instruction in instructions: + entity_id = self._entity_id_from_resource_id(instruction.resource_id) + if entity_id in entity_ids: + if isinstance(instruction, (DDBCInstruction, FRBCInstruction)): + state = instruction.operation_mode_id.lower() + attributes = { + "operation_mode_factor": instruction.operation_mode_factor, + } + try: + self.set_entity_state(entity_id, state, attributes) + except Exception as e: + logger.error(f"{e}") diff --git a/src/akkudoktoreos/adapter/nodered.py b/src/akkudoktoreos/adapter/nodered.py new file mode 100644 index 0000000..b7e94eb --- /dev/null +++ b/src/akkudoktoreos/adapter/nodered.py @@ -0,0 +1,128 @@ +"""Nod-RED adapter.""" + +from typing import Optional, Union + +import requests +from loguru import logger +from pydantic import Field, field_validator + +from akkudoktoreos.adapter.adapterabc import AdapterProvider +from akkudoktoreos.config.configabc import SettingsBaseModel +from akkudoktoreos.core.emplan import DDBCInstruction, FRBCInstruction +from akkudoktoreos.core.ems import EnergyManagementStage +from akkudoktoreos.server.server import get_default_host, validate_ip_or_hostname +from akkudoktoreos.utils.datetimeutil import to_datetime + + +class NodeREDAdapterCommonSettings(SettingsBaseModel): + r"""Common settings for the NodeRED adapter. + + The Node-RED adapter sends to HTTP IN nodes. + + This is the example flow: + + [HTTP In \\] -> [Function (parse payload)] -> [Debug] -> [HTTP Response] + + There are two URLs that are used: + + - GET /eos/data_aquisition + The GET is issued before the optimization. + - POST /eos/control_dispatch + The POST is issued after the optimization. + """ + + host: Optional[str] = Field( + default=get_default_host(), + json_schema_extra={ + "description": "Node-RED server IP address. Defaults to 127.0.0.1.", + "examples": ["127.0.0.1", "localhost"], + }, + ) + port: Optional[int] = Field( + default=1880, + json_schema_extra={ + "description": "Node-RED server IP port number. Defaults to 1880.", + "examples": [ + 1880, + ], + }, + ) + + @field_validator("host", mode="before") + def validate_server_host(cls, value: Optional[str]) -> Optional[str]: + if isinstance(value, str): + value = validate_ip_or_hostname(value) + return value + + @field_validator("port") + def validate_server_port(cls, value: Optional[int]) -> Optional[int]: + if value is not None and not (1024 <= value <= 49151): + raise ValueError("Server port number must be between 1024 and 49151.") + return value + + +class NodeREDAdapter(AdapterProvider): + def provider_id(self) -> str: + """Return the unique identifier for the adapter provider.""" + return "NodeRED" + + def _update_data(self) -> None: + """Custom adapter data update logic. + + Data update may be requested at different stages of energy management. The stage can be + detected by self.ems.stage(). + """ + server = f"http://{self.config.adapter.nodered.host}:{self.config.adapter.nodered.port}" + + data: Optional[dict[str, Union[str, float]]] = None + stage = self.ems.stage() + if stage == EnergyManagementStage.CONTROL_DISPATCH: + data = {} + # currently active instructions + instructions = self.ems.plan().get_active_instructions() + for instruction in instructions: + idx = instruction.id.find("@") + resource_id = instruction.id[:idx] if idx != -1 else instruction.id + operation_mode_id = "" + operation_mode_factor = 0.0 + if isinstance(instruction, (DDBCInstruction, FRBCInstruction)): + operation_mode_id = instruction.operation_mode_id + operation_mode_factor = instruction.operation_mode_factor + data[f"{resource_id}_op_mode"] = operation_mode_id + data[f"{resource_id}_op_factor"] = operation_mode_factor + elif stage == EnergyManagementStage.DATA_ACQUISITION: + data = {} + + if data is None: + return + + logger.info(f"NodeRED {str(stage).lower()} at {server}: {data}") + + try: + error_msg = None + if stage == EnergyManagementStage.CONTROL_DISPATCH: + response = requests.post(f"{server}/eos/{str(stage).lower()}", json=data, timeout=5) + elif stage == EnergyManagementStage.DATA_ACQUISITION: + response = requests.get(f"{server}/eos/{str(stage).lower()}", json=data, timeout=5) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + try: + # Try to get 'detail' from the JSON response + detail = response.json().get( + "detail", f"No error details for data '{data}' '{response.text}'" + ) + except ValueError: + # Response is not JSON + detail = f"No error details for data '{data}' '{response.text}'" + error_msg = f"NodeRED `{str(stage).lower()}` fails at `{server}`: {detail}" + except Exception as e: + error_msg = f"NodeRED `{str(stage).lower()}` fails at `{server}`: {e}" + if error_msg: + logger.error(error_msg) + raise RuntimeError(error_msg) + + if stage == EnergyManagementStage.DATA_ACQUISITION: + data = response.json() + + # We got data - mark the update time + self.update_datetime = to_datetime() diff --git a/src/akkudoktoreos/config/config.py b/src/akkudoktoreos/config/config.py index c6aa963..a9c8a76 100644 --- a/src/akkudoktoreos/config/config.py +++ b/src/akkudoktoreos/config/config.py @@ -13,7 +13,7 @@ import json import os import tempfile from pathlib import Path -from typing import Any, ClassVar, Optional, Type +from typing import Any, ClassVar, Optional, Type, Union import pydantic_settings from loguru import logger @@ -21,6 +21,7 @@ from platformdirs import user_config_dir, user_data_dir from pydantic import Field, computed_field, field_validator # settings +from akkudoktoreos.adapter.adapter import AdapterCommonSettings from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.config.configmigrate import migrate_config_data, migrate_config_file from akkudoktoreos.core.cachesettings import CacheCommonSettings @@ -65,25 +66,17 @@ def get_absolute_path( class GeneralSettings(SettingsBaseModel): - """Settings for common configuration. - - General configuration to set directories of cache and output files and system location (latitude - and longitude). - Validators ensure each parameter is within a specified range. A computed property, `timezone`, - determines the time zone based on latitude and longitude. - - Attributes: - latitude (Optional[float]): Latitude in degrees, must be between -90 and 90. - longitude (Optional[float]): Longitude in degrees, must be between -180 and 180. - - Properties: - timezone (Optional[str]): Computed time zone string based on the specified latitude - and longitude. - """ + """General settings.""" _config_folder_path: ClassVar[Optional[Path]] = None _config_file_path: ClassVar[Optional[Path]] = None + # Detect Home Assistant add-on environment + # Home Assistant sets this environment variable automatically + _home_assistant_addon: ClassVar[bool] = ( + "HASSIO_TOKEN" in os.environ or "SUPERVISOR_TOKEN" in os.environ + ) + version: str = Field( default=__version__, json_schema_extra={ @@ -109,21 +102,21 @@ class GeneralSettings(SettingsBaseModel): ge=-90.0, le=90.0, json_schema_extra={ - "description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)" + "description": "Latitude in decimal degrees between -90 and 90. North is positive (ISO 19115) (°)" }, ) longitude: Optional[float] = Field( default=13.405, ge=-180.0, le=180.0, - json_schema_extra={"description": "Longitude in decimal degrees, within -180 to 180 (°)"}, + json_schema_extra={"description": "Longitude in decimal degrees within -180 to 180 (°)"}, ) # Computed fields @computed_field # type: ignore[prop-decorator] @property def timezone(self) -> Optional[str]: - """Compute timezone based on latitude and longitude.""" + """Computed timezone based on latitude and longitude.""" if self.latitude and self.longitude: return to_timezone(location=(self.latitude, self.longitude), as_string=True) return None @@ -131,7 +124,10 @@ class GeneralSettings(SettingsBaseModel): @computed_field # type: ignore[prop-decorator] @property def data_output_path(self) -> Optional[Path]: - """Compute data_output_path based on data_folder_path.""" + """Computed data_output_path based on data_folder_path.""" + if self.home_assistant_addon: + # Only /data is persistent for home assistant add-on + return Path("/data/output") return get_absolute_path(self.data_folder_path, self.data_output_subpath) @computed_field # type: ignore[prop-decorator] @@ -146,6 +142,12 @@ class GeneralSettings(SettingsBaseModel): """Path to EOS configuration file.""" return self._config_file_path + @computed_field # type: ignore[prop-decorator] + @property + def home_assistant_addon(self) -> bool: + """EOS is running as home assistant add-on.""" + return self._home_assistant_addon + compatible_versions: ClassVar[list[str]] = [__version__] @field_validator("version") @@ -160,6 +162,22 @@ class GeneralSettings(SettingsBaseModel): raise ValueError(error) return v + @field_validator("data_folder_path", mode="after") + @classmethod + def validate_data_folder_path(cls, value: Optional[Union[str, Path]]) -> Optional[Path]: + """Ensure dir is available.""" + if cls._home_assistant_addon: + # Force to home assistant add-on /data directory + return Path("/data") + if value is None: + return None + if isinstance(value, str): + value = Path(value) + value.resolve() + if not value.is_dir(): + raise ValueError(f"Data folder path '{value}' is not a directory.") + return value + class SettingsEOS(pydantic_settings.BaseSettings, PydanticModelNestedValueMixin): """Settings for all EOS. @@ -212,6 +230,9 @@ class SettingsEOS(pydantic_settings.BaseSettings, PydanticModelNestedValueMixin) utils: Optional[UtilsCommonSettings] = Field( default=None, json_schema_extra={"description": "Utilities Settings"} ) + adapter: Optional[AdapterCommonSettings] = Field( + default=None, json_schema_extra={"description": "Adapter Settings"} + ) model_config = pydantic_settings.SettingsConfigDict( env_nested_delimiter="__", @@ -242,6 +263,7 @@ class SettingsEOSDefaults(SettingsEOS): weather: WeatherCommonSettings = WeatherCommonSettings() server: ServerCommonSettings = ServerCommonSettings() utils: UtilsCommonSettings = UtilsCommonSettings() + adapter: AdapterCommonSettings = AdapterCommonSettings() def __hash__(self) -> int: # Just for usage in configmigrate, finally overwritten when used by ConfigEOS. @@ -297,6 +319,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): APP_NAME: ClassVar[str] = "net.akkudoktor.eos" # reverse order APP_AUTHOR: ClassVar[str] = "akkudoktor" EOS_DIR: ClassVar[str] = "EOS_DIR" + EOS_DATA_DIR: ClassVar[str] = "EOS_DATA_DIR" EOS_CONFIG_DIR: ClassVar[str] = "EOS_CONFIG_DIR" ENCODING: ClassVar[str] = "UTF-8" CONFIG_FILE_NAME: ClassVar[str] = "EOS.config.json" @@ -355,27 +378,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): - It ensures that a fallback to a default configuration file is always possible. """ # Ensure we know and have the config folder path and the config file - config_file, exists = cls._get_config_file_path() - config_dir = config_file.parent - if not exists: - config_dir.mkdir(parents=True, exist_ok=True) - # Create minimum config file - config_minimum_content = '{ "general": { "version": "' + __version__ + '" } }' - try: - config_file.write_text(config_minimum_content, encoding="utf-8") - except Exception as exc: - # Create minimum config in temporary config directory as last resort - error_msg = f"Could not create minimum config file in {config_dir}: {exc}" - logger.error(error_msg) - temp_dir = Path(tempfile.mkdtemp()) - info_msg = f"Using temporary config directory {temp_dir}" - logger.info(info_msg) - config_dir = temp_dir - config_file = temp_dir / config_file.name - config_file.write_text(config_minimum_content, encoding="utf-8") - # Remember config_dir and config file - GeneralSettings._config_folder_path = config_dir - GeneralSettings._config_file_path = config_file + config_file = cls._setup_config_file() # All the settings sources in priority sequence setting_sources = [ @@ -384,7 +387,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): dotenv_settings, ] - # Apend file settings to sources + # Append file settings to sources file_settings: Optional[pydantic_settings.JsonConfigSettingsSource] = None try: backup_file = config_file.with_suffix(f".{to_datetime(as_string='YYYYMMDDHHmmss')}") @@ -426,7 +429,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): # (Re-)load settings - call base class init SettingsEOSDefaults.__init__(self, *args, **kwargs) # Init config file and data folder pathes - self._create_initial_config_file() + self._setup_config_file() self._update_data_folder_path() self._initialized = True logger.debug("Config setup:\n{}", self) @@ -559,17 +562,6 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): return result - def _create_initial_config_file(self) -> None: - if self.general.config_file_path and not self.general.config_file_path.exists(): - self.general.config_file_path.parent.mkdir(parents=True, exist_ok=True) - try: - with self.general.config_file_path.open("w", encoding="utf-8", newline="\n") as f: - f.write(self.model_dump_json(indent=4)) - except Exception as e: - logger.error( - f"Could not write configuration file '{self.general.config_file_path}': {e}" - ) - def _update_data_folder_path(self) -> None: """Updates path to the data directory.""" # From Settings @@ -579,7 +571,16 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): self.general.data_folder_path = data_dir return except Exception as e: - logger.warning(f"Could not setup data dir: {e}") + logger.warning(f"Could not setup data dir {data_dir}: {e}") + # From EOS_DATA_DIR env + if env_dir := os.getenv(self.EOS_DATA_DIR): + try: + data_dir = Path(env_dir).resolve() + data_dir.mkdir(parents=True, exist_ok=True) + self.general.data_folder_path = data_dir + return + except Exception as e: + logger.warning(f"Could not setup data dir {data_dir}: {e}") # From EOS_DIR env if env_dir := os.getenv(self.EOS_DIR): try: @@ -588,7 +589,7 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): self.general.data_folder_path = data_dir return except Exception as e: - logger.warning(f"Could not setup data dir: {e}") + logger.warning(f"Could not setup data dir {data_dir}: {e}") # From platform specific default path try: data_dir = Path(user_data_dir(self.APP_NAME, self.APP_AUTHOR)) @@ -597,9 +598,10 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): self.general.data_folder_path = data_dir return except Exception as e: - logger.warning(f"Could not setup data dir: {e}") + logger.warning(f"Could not setup data dir {data_dir}: {e}") # Current working directory data_dir = Path.cwd() + logger.warning(f"Using data dir {data_dir}") self.general.data_folder_path = data_dir @classmethod @@ -611,16 +613,28 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): 2. user configuration directory 3. current working directory + If running as Home Assistat add-on returns /data/config/EOS.config.json. + Returns: tuple[Path, bool]: The path to the configuration file and if there is already a config file there """ + if GeneralSettings._home_assistant_addon: + # Only /data is persistent for home assistant add-on + cfile = Path("/data/config") / cls.CONFIG_FILE_NAME + logger.debug(f"Config file forced to: '{cfile}'") + return cfile, cfile.exists() + config_dirs = [] - env_base_dir = os.getenv(cls.EOS_DIR) - env_config_dir = os.getenv(cls.EOS_CONFIG_DIR) - env_dir = get_absolute_path(env_base_dir, env_config_dir) - logger.debug(f"Environment config dir: '{env_dir}'") - if env_dir is not None: - config_dirs.append(env_dir.resolve()) + env_eos_dir = os.getenv(cls.EOS_DIR) + logger.debug(f"Environment EOS_DIR: '{env_eos_dir}'") + + env_eos_config_dir = os.getenv(cls.EOS_CONFIG_DIR) + logger.debug(f"Environment EOS_CONFIG_DIR: '{env_eos_config_dir}'") + env_config_dir = get_absolute_path(env_eos_dir, env_eos_config_dir) + logger.debug(f"Resulting environment config dir: '{env_config_dir}'") + + if env_config_dir is not None: + config_dirs.append(env_config_dir.resolve()) config_dirs.append(Path(user_config_dir(cls.APP_NAME, cls.APP_AUTHOR))) config_dirs.append(Path.cwd()) for cdir in config_dirs: @@ -628,8 +642,52 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults): if cfile.exists(): logger.debug(f"Found config file: '{cfile}'") return cfile, True + return config_dirs[0].joinpath(cls.CONFIG_FILE_NAME), False + @classmethod + def _setup_config_file(cls) -> Path: + """Setup config file. + + Creates an initial config file if it does not exist. + + Returns: + config_file_path (Path): Path to config file + """ + config_file_path, exists = cls._get_config_file_path() + if ( + GeneralSettings._config_file_path + and GeneralSettings._config_file_path != config_file_path + ): + debug_msg = ( + f"Config file changed from '{GeneralSettings._config_file_path}' to " + f"'{config_file_path}'" + ) + logger.debug(debug_msg) + if not exists: + # Create minimum config file + config_minimum_content = '{ "general": { "version": "' + __version__ + '" } }' + try: + config_file_path.parent.mkdir(parents=True, exist_ok=True) + config_file_path.write_text(config_minimum_content, encoding="utf-8") + except Exception as exc: + # Create minimum config in temporary config directory as last resort + error_msg = ( + f"Could not create minimum config file in {config_file_path.parent}: {exc}" + ) + logger.error(error_msg) + temp_dir = Path(tempfile.mkdtemp()) + info_msg = f"Using temporary config directory {temp_dir}" + logger.info(info_msg) + config_file_path = temp_dir / config_file_path.name + config_file_path.write_text(config_minimum_content, encoding="utf-8") + + # Remember config_dir and config file + GeneralSettings._config_folder_path = config_file_path.parent + GeneralSettings._config_file_path = config_file_path + + return config_file_path + def to_config_file(self) -> None: """Saves the current configuration to the configuration file. diff --git a/src/akkudoktoreos/config/configmigrate.py b/src/akkudoktoreos/config/configmigrate.py index e4a3d6b..9ad075f 100644 --- a/src/akkudoktoreos/config/configmigrate.py +++ b/src/akkudoktoreos/config/configmigrate.py @@ -21,6 +21,11 @@ if TYPE_CHECKING: # - tuple[str, Callable[[Any], Any]] (new path + transform) # - None (drop) MIGRATION_MAP: Dict[str, Union[str, Tuple[str, Callable[[Any], Any]], None]] = { + # 0.2.0.dev -> 0.2.0.dev + "adapter/homeassistant/optimization_solution_entity_ids": ( + "adapter/homeassistant/solution_entity_ids", + lambda v: v if isinstance(v, list) else None, + ), # 0.2.0 -> 0.2.0+dev "elecprice/provider_settings/ElecPriceImport/import_file_path": "elecprice/elecpriceimport/import_file_path", "elecprice/provider_settings/ElecPriceImport/import_json": "elecprice/elecpriceimport/import_json", diff --git a/src/akkudoktoreos/core/cachesettings.py b/src/akkudoktoreos/core/cachesettings.py index e3581eb..56bc5c3 100644 --- a/src/akkudoktoreos/core/cachesettings.py +++ b/src/akkudoktoreos/core/cachesettings.py @@ -27,7 +27,10 @@ class CacheCommonSettings(SettingsBaseModel): # Do not make this a pydantic computed field. The pydantic model must be fully initialized # to have access to config.general, which may not be the case if it is a computed field. def path(self) -> Optional[Path]: - """Compute cache path based on general.data_folder_path.""" + """Computed cache path based on general.data_folder_path.""" + if self.config.general.home_assistant_addon: + # Only /data is persistent for home assistant add-on + return Path("/data/cache") data_cache_path = self.config.general.data_folder_path if data_cache_path is None or self.subpath is None: return None diff --git a/src/akkudoktoreos/core/coreabc.py b/src/akkudoktoreos/core/coreabc.py index 48e5373..31b4b45 100644 --- a/src/akkudoktoreos/core/coreabc.py +++ b/src/akkudoktoreos/core/coreabc.py @@ -18,12 +18,53 @@ from loguru import logger from akkudoktoreos.core.decorators import classproperty from akkudoktoreos.utils.datetimeutil import DateTime +adapter_eos: Any = None config_eos: Any = None measurement_eos: Any = None prediction_eos: Any = None ems_eos: Any = None +class AdapterMixin: + """Mixin class for managing EOS adapter. + + This class serves as a foundational component for EOS-related classes requiring access + to the global EOS adapters. It provides a `adapter` property that dynamically retrieves + the adapter instance. + + Usage: + Subclass this base class to gain access to the `adapter` attribute, which retrieves the + global adapter instance lazily to avoid import-time circular dependencies. + + Attributes: + adapter (Adapter): Property to access the global EOS adapter. + + Example: + .. code-block:: python + + class MyEOSClass(AdapterMixin): + def my_method(self): + self.adapter.update_date() + + """ + + @classproperty + def adapter(cls) -> Any: + """Convenience class method/ attribute to retrieve the EOS adapters. + + Returns: + Adapter: The adapters. + """ + # avoid circular dependency at import time + global adapter_eos + if adapter_eos is None: + from akkudoktoreos.adapter.adapter import get_adapter + + adapter_eos = get_adapter() + + return adapter_eos + + class ConfigMixin: """Mixin class for managing EOS configuration data. diff --git a/src/akkudoktoreos/core/dataabc.py b/src/akkudoktoreos/core/dataabc.py index 9428e98..f4217b0 100644 --- a/src/akkudoktoreos/core/dataabc.py +++ b/src/akkudoktoreos/core/dataabc.py @@ -1018,7 +1018,7 @@ class DataSequence(DataBase, MutableSequence): end_datetime: Optional[DateTime] = None, interval: Optional[Duration] = None, fill_method: Optional[str] = None, - dropna: Optional[bool] = None, + dropna: Optional[bool] = True, ) -> NDArray[Shape["*"], Any]: """Extract an array indexed by fixed time intervals from data records within an optional date range. @@ -1032,17 +1032,19 @@ class DataSequence(DataBase, MutableSequence): - 'ffill': Forward fill missing values. - 'bfill': Backward fill missing values. - 'none': Defaults to 'linear' for numeric values, otherwise 'ffill'. - dropna: (bool, optional): Whether to drop NAN/ None values before processing. Defaults to True. + dropna: (bool, optional): Whether to drop NAN/ None values before processing. + Defaults to True. Returns: - np.ndarray: A NumPy Array of the values extracted from the specified key. + np.ndarray: A NumPy Array of the values at the chosen frequency extracted from the + specified key. Raises: KeyError: If the specified key is not found in any of the DataRecords. """ self._validate_key(key) - # General check on fill_method + # Validate fill method if fill_method not in ("ffill", "bfill", "linear", "none", None): raise ValueError(f"Unsupported fill method: {fill_method}") @@ -1050,13 +1052,17 @@ class DataSequence(DataBase, MutableSequence): start_datetime = to_datetime(start_datetime, to_maxtime=False) if start_datetime else None end_datetime = to_datetime(end_datetime, to_maxtime=False) if end_datetime else None - resampled = None if interval is None: interval = to_duration("1 hour") + resample_freq = "1h" + else: + resample_freq = to_duration(interval, as_string="pandas") + # Load raw lists (already sorted & filtered) dates, values = self.key_to_lists(key=key, dropna=dropna) values_len = len(values) + # Bring lists into shape if values_len < 1: # No values, assume at least one value set to None if start_datetime is not None: @@ -1092,40 +1098,40 @@ class DataSequence(DataBase, MutableSequence): dates.append(end_datetime) values.append(values[-1]) - series = pd.Series(data=values, index=pd.DatetimeIndex(dates), name=key) - if not series.index.inferred_type == "datetime64": + # Construct series + series = pd.Series(values, index=pd.DatetimeIndex(dates), name=key) + if series.index.inferred_type != "datetime64": raise TypeError( f"Expected DatetimeIndex, but got {type(series.index)} " f"infered to {series.index.inferred_type}: {series}" ) - # Handle missing values - if series.dtype in [np.float64, np.float32, np.int64, np.int32]: - # Numeric types - if fill_method is None: + # Determine default fill method depending on dtype + if fill_method is None: + if pd.api.types.is_numeric_dtype(series): fill_method = "linear" - # Resample the series to the specified interval - resampled = series.resample(interval, origin=resample_origin).first() - if fill_method == "linear": - resampled = resampled.interpolate(method="linear") - elif fill_method == "ffill": - resampled = resampled.ffill() - elif fill_method == "bfill": - resampled = resampled.bfill() - elif fill_method != "none": - raise ValueError(f"Unsupported fill method: {fill_method}") - else: - # Non-numeric types - if fill_method is None: + else: fill_method = "ffill" - # Resample the series to the specified interval + + # Perform the resampling + if pd.api.types.is_numeric_dtype(series): + # numeric → use mean + resampled = series.resample(interval, origin=resample_origin).mean() + else: + # non-numeric → fallback (first, last, mode, or ffill) resampled = series.resample(interval, origin=resample_origin).first() - if fill_method == "ffill": - resampled = resampled.ffill() - elif fill_method == "bfill": - resampled = resampled.bfill() - elif fill_method != "none": - raise ValueError(f"Unsupported fill method for non-numeric data: {fill_method}") + + # Handle missing values after resampling + if fill_method == "linear" and pd.api.types.is_numeric_dtype(series): + resampled = resampled.interpolate("linear") + elif fill_method == "ffill": + resampled = resampled.ffill() + elif fill_method == "bfill": + resampled = resampled.bfill() + elif fill_method == "none": + pass + else: + raise ValueError(f"Unsupported fill method: {fill_method}") logger.debug( "Resampled for '{}' with length {}: {}...{}", @@ -1141,6 +1147,16 @@ class DataSequence(DataBase, MutableSequence): if end_datetime is not None and len(resampled) > 0: resampled = resampled.truncate(after=end_datetime.subtract(seconds=1)) array = resampled.values + + # Convert NaN to None if there are actually NaNs + if ( + isinstance(array, np.ndarray) + and np.issubdtype(array.dtype.type, np.floating) + and pd.isna(array).any() + ): + array = array.astype(object) + array[pd.isna(array)] = None + logger.debug( "Array for '{}' with length {}: {}...{}", key, len(array), array[:10], array[-10:] ) @@ -1691,6 +1707,14 @@ class DataImportMixin: } """ + # Strip quotes if provided - does not effect unquoted string + json_str = json_str.strip() # strip white space at start and end + if (json_str.startswith("'") and json_str.endswith("'")) or ( + json_str.startswith('"') and json_str.endswith('"') + ): + json_str = json_str[1:-1] # strip outer quotes + json_str = json_str.strip() # strip remaining white space at start and end + # Try pandas dataframe with orient="split" try: import_data = PydanticDateTimeDataFrame.model_validate_json(json_str) @@ -1720,10 +1744,15 @@ class DataImportMixin: logger.debug(f"PydanticDateTimeData import: {error_msg}") # Use simple dict format - import_data = json.loads(json_str) - self.import_from_dict( - import_data, key_prefix=key_prefix, start_datetime=start_datetime, interval=interval - ) + try: + import_data = json.loads(json_str) + self.import_from_dict( + import_data, key_prefix=key_prefix, start_datetime=start_datetime, interval=interval + ) + except Exception as e: + error_msg = f"Invalid JSON string '{json_str}': {e}" + logger.debug(error_msg) + raise ValueError(error_msg) from e def import_from_file( self, diff --git a/src/akkudoktoreos/core/decorators.py b/src/akkudoktoreos/core/decorators.py index 95b5528..ee9976c 100644 --- a/src/akkudoktoreos/core/decorators.py +++ b/src/akkudoktoreos/core/decorators.py @@ -25,11 +25,11 @@ class classproperty: Methods: __get__: Retrieves the value of the class property by calling the - decorated method on the class. + decorated method on the class. Parameters: fget (Callable[[Any], Any]): A method that takes the class as an - argument and returns a value. + argument and returns a value. Raises: RuntimeError: If `fget` is not defined when `__get__` is called. diff --git a/src/akkudoktoreos/core/emplan.py b/src/akkudoktoreos/core/emplan.py index 2704d2d..3d1e20f 100644 --- a/src/akkudoktoreos/core/emplan.py +++ b/src/akkudoktoreos/core/emplan.py @@ -10,9 +10,11 @@ Demand Driven Based Control. import uuid from abc import ABC, abstractmethod +from collections import defaultdict from enum import Enum from typing import Annotated, Literal, Optional, Union +from loguru import logger from pydantic import Field, computed_field, model_validator from akkudoktoreos.core.pydantic import PydanticBaseModel @@ -2257,20 +2259,60 @@ class EnergyManagementPlan(PydanticBaseModel): self.valid_from = to_datetime() self.valid_until = None + def get_resources(self) -> list[str]: + """Retrieves the resource_ids for the resources the plan currently holds instructions for. + + Returns a list of resource ids. + """ + resource_ids = [] + for instr in self.instructions: + resource_id = instr.resource_id + if resource_id not in resource_ids: + resource_ids.append(resource_id) + return resource_ids + def get_active_instructions( self, now: Optional[DateTime] = None - ) -> list[EnergyManagementInstruction]: - """Retrieves all currently active instructions at the specified time.""" + ) -> list["EnergyManagementInstruction"]: + """Retrieves the currently active instruction for each resource at the specified time. + + Semantics: + - For each resource, consider only instructions with execution_time <= now. + - Choose the instruction with the latest execution_time (the most recent). + - If that instruction has a duration (timedelta), it's active only if now < execution_time + duration. + - If that instruction has no duration (None), treat it as open-ended (active until superseded). + + Returns a list with at most one instruction per resource (the active one). + """ now = now or to_datetime() - active = [] + # Group instructions by resource_id + by_resource: dict[str, list["EnergyManagementInstruction"]] = defaultdict(list) for instr in self.instructions: - instr_duration = instr.duration() + # skip instructions scheduled in the future + if instr.execution_time <= now: + by_resource[instr.resource_id].append(instr) + + active: list["EnergyManagementInstruction"] = [] + + for resource_id, instrs in by_resource.items(): + # pick latest instruction by execution_time + latest = max(instrs, key=lambda i: i.execution_time) + + if len(instrs) == 0: + # No instructions, ther shall be at least one + error_msg = f"No instructions for {resource_id}" + logger.error(error_msg) + raise ValueError(error_msg) + + instr_duration = latest.duration() # expected: Duration| None if instr_duration is None: - if instr.execution_time <= now: - active.append(instr) + # open-ended (active until replaced) -> active because we selected latest <= now + active.append(latest) else: - if instr.execution_time <= now < instr.execution_time + instr_duration: - active.append(instr) + # active only if now is strictly before execution_time + duration + if latest.execution_time + instr_duration > now: + active.append(latest) + return active def get_next_instruction( diff --git a/src/akkudoktoreos/core/ems.py b/src/akkudoktoreos/core/ems.py index 6ceb39d..82563b0 100644 --- a/src/akkudoktoreos/core/ems.py +++ b/src/akkudoktoreos/core/ems.py @@ -1,6 +1,7 @@ import traceback from asyncio import Lock, get_running_loop from concurrent.futures import ThreadPoolExecutor +from enum import Enum from functools import partial from typing import ClassVar, Optional @@ -8,7 +9,12 @@ from loguru import logger from pydantic import computed_field from akkudoktoreos.core.cache import CacheEnergyManagementStore -from akkudoktoreos.core.coreabc import ConfigMixin, PredictionMixin, SingletonMixin +from akkudoktoreos.core.coreabc import ( + AdapterMixin, + ConfigMixin, + PredictionMixin, + SingletonMixin, +) from akkudoktoreos.core.emplan import EnergyManagementPlan from akkudoktoreos.core.emsettings import EnergyManagementMode from akkudoktoreos.core.pydantic import PydanticBaseModel @@ -24,7 +30,23 @@ from akkudoktoreos.utils.datetimeutil import DateTime, compare_datetimes, to_dat executor = ThreadPoolExecutor(max_workers=1) -class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBaseModel): +class EnergyManagementStage(Enum): + """Enumeration of the main stages in the energy management lifecycle.""" + + IDLE = "IDLE" + DATA_ACQUISITION = "DATA_AQUISITION" + FORECAST_RETRIEVAL = "FORECAST_RETRIEVAL" + OPTIMIZATION = "OPTIMIZATION" + CONTROL_DISPATCH = "CONTROL_DISPATCH" + + def __str__(self) -> str: + """Return the string representation of the stage.""" + return self.value + + +class EnergyManagement( + SingletonMixin, ConfigMixin, PredictionMixin, AdapterMixin, PydanticBaseModel +): """Energy management.""" # Start datetime. @@ -33,6 +55,9 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas # last run datetime. Used by energy management task _last_run_datetime: ClassVar[Optional[DateTime]] = None + # Current energy management stage + _stage: ClassVar[EnergyManagementStage] = EnergyManagementStage.IDLE + # energy management plan of latest energy management run with optimization _plan: ClassVar[Optional[EnergyManagementPlan]] = None @@ -81,6 +106,15 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas cls._start_datetime = start_datetime.set(minute=0, second=0, microsecond=0) return cls._start_datetime + @classmethod + def stage(cls) -> EnergyManagementStage: + """Get the the stage of the energy management. + + Returns: + EnergyManagementStage: The current stage of energy management. + """ + return cls._stage + @classmethod def plan(cls) -> Optional[EnergyManagementPlan]: """Get the latest energy management plan. @@ -122,6 +156,7 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas """Run the energy management. This method initializes the energy management run by setting its + start datetime, updating predictions, and optionally starting optimization depending on the selected mode or configuration. @@ -157,6 +192,8 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas logger.info("Starting energy management run.") + cls._stage = EnergyManagementStage.DATA_ACQUISITION + # Remember/ set the start datetime of this energy management run. # None leads cls.set_start_datetime(start_datetime) @@ -164,12 +201,23 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas # Throw away any memory cached results of the last energy management run. CacheEnergyManagementStore().clear() + # Do data aquisition by adapters + try: + cls.adapter.update_data(force_enable) + except Exception as e: + trace = "".join(traceback.TracebackException.from_exception(e).format()) + error_msg = f"Adapter update failed - phase {cls._stage}: {e}\n{trace}" + logger.error(error_msg) + + cls._stage = EnergyManagementStage.FORECAST_RETRIEVAL + if mode is None: mode = cls.config.ems.mode if mode is None or mode == "PREDICTION": # Update the predictions cls.prediction.update_data(force_enable=force_enable, force_update=force_update) logger.info("Energy management run done (predictions updated)") + cls._stage = EnergyManagementStage.IDLE return # Prepare optimization parameters @@ -184,8 +232,12 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas logger.error( "Energy management run canceled. Could not prepare optimisation parameters." ) + cls._stage = EnergyManagementStage.IDLE return + cls._stage = EnergyManagementStage.OPTIMIZATION + logger.info("Starting energy management optimization.") + # Take values from config if not given if genetic_individuals is None: genetic_individuals = cls.config.optimization.genetic.individuals @@ -195,7 +247,6 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas if cls._start_datetime is None: # Make mypy happy - already set by us raise RuntimeError("Start datetime not set.") - logger.info("Starting energy management optimization.") try: optimization = GeneticOptimization( verbose=bool(cls.config.server.verbose), @@ -208,8 +259,11 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas ) except: logger.exception("Energy management optimization failed.") + cls._stage = EnergyManagementStage.IDLE return + cls._stage = EnergyManagementStage.CONTROL_DISPATCH + # Make genetic solution public cls._genetic_solution = solution @@ -224,6 +278,17 @@ class EnergyManagement(SingletonMixin, ConfigMixin, PredictionMixin, PydanticBas logger.debug("Energy management plan:\n{}", cls._plan) logger.info("Energy management run done (optimization updated)") + # Do control dispatch by adapters + try: + cls.adapter.update_data(force_enable) + except Exception as e: + trace = "".join(traceback.TracebackException.from_exception(e).format()) + error_msg = f"Adapter update failed - phase {cls._stage}: {e}\n{trace}" + logger.error(error_msg) + + # energy management run finished + cls._stage = EnergyManagementStage.IDLE + async def run( self, start_datetime: Optional[DateTime] = None, diff --git a/src/akkudoktoreos/core/logging.py b/src/akkudoktoreos/core/logging.py index 1ae0df5..d9a48bd 100644 --- a/src/akkudoktoreos/core/logging.py +++ b/src/akkudoktoreos/core/logging.py @@ -65,7 +65,7 @@ console_handler_id = None file_handler_id = None -def track_logging_config(config_eos: Any, path: str, old_value: Any, value: Any) -> None: +def logging_track_config(config_eos: Any, path: str, old_value: Any, value: Any) -> None: """Track logging config changes.""" global console_handler_id, file_handler_id diff --git a/src/akkudoktoreos/core/pydantic.py b/src/akkudoktoreos/core/pydantic.py index f15701d..781b4b6 100644 --- a/src/akkudoktoreos/core/pydantic.py +++ b/src/akkudoktoreos/core/pydantic.py @@ -400,7 +400,21 @@ class PydanticModelNestedValueMixin: # Get next value next_value = None - if isinstance(model, BaseModel): + if isinstance(model, RootModel): + # If this is the final key, set the value + if is_final_key: + try: + model.validate_and_set(key, value) + except Exception as e: + raise ValueError(f"Error updating model: {e}") from e + return + + next_value = model.root + + elif isinstance(model, BaseModel): + logger.debug( + f"Detected base model {model.__class__.__name__} of type {type(model)}" + ) # Track parent and key for possible assignment later parent = model parent_key = [ @@ -432,6 +446,7 @@ class PydanticModelNestedValueMixin: next_value = getattr(model, key, None) elif isinstance(model, list): + logger.debug(f"Detected list of type {type(model)}") # Handle lists (ensure index exists and modify safely) try: idx = int(key) @@ -468,6 +483,7 @@ class PydanticModelNestedValueMixin: return elif isinstance(model, dict): + logger.debug(f"Detected dict of type {type(model)}") # Handle dictionaries (auto-create missing keys) # Get next type from parent key type information @@ -795,29 +811,61 @@ class PydanticBaseModel(PydanticModelNestedValueMixin, BaseModel): @classmethod def field_description(cls, field_name: str) -> Optional[str]: - """Return the description metadata of a model field, if available. + """Return a human-readable description for a model field. - This method retrieves the `Field` specification from the model's - `model_fields` registry and extracts its description from the field's - `json_schema_extra` / `extra` metadata (as provided by - `_field_extra_dict`). If the field does not exist or no description is - present, ``None`` is returned. + Looks up descriptions for both regular and computed fields. + Resolution order: + + Normal fields: + 1) json_schema_extra["description"] + 2) field.description + + Computed fields: + 1) ComputedFieldInfo.description + 2) function docstring (func.__doc__) + 3) json_schema_extra["description"] + + If a field exists but no description is found, returns "-". + If the field does not exist, returns None. Args: - field_name (str): - Name of the field whose description should be returned. + field_name: Field name. Returns: - Optional[str]: - The textual description if present, otherwise ``None``. + Description string, "-" if missing, or None if not a field. """ - field = cls.model_fields.get(field_name) - if not field: + # 1) Regular declared fields + field: FieldInfo | None = cls.model_fields.get(field_name) + if field is not None: + extra = cls._field_extra_dict(field) + if "description" in extra: + return str(extra["description"]) + # some FieldInfo may also have .description directly + if getattr(field, "description", None): + return str(field.description) + return None - extra = cls._field_extra_dict(field) + + # 2) Computed fields live in a separate mapping + cfield: ComputedFieldInfo | None = cls.model_computed_fields.get(field_name) + if cfield is None: + return None + + # 2a) ComputedFieldInfo may have a description attribute + if getattr(cfield, "description", None): + return str(cfield.description) + + # 2b) fallback to wrapped property's docstring + func = getattr(cfield, "func", None) + if func and func.__doc__: + return func.__doc__.strip() + + # 2c) last resort: json_schema_extra if you use it for computed fields + extra = cls._field_extra_dict(cfield) if "description" in extra: return str(extra["description"]) - return None + + return "-" @classmethod def field_deprecated(cls, field_name: str) -> Optional[str]: @@ -887,7 +935,7 @@ class PydanticDateTimeData(RootModel): { "start_datetime": "2024-01-01 00:00:00", # optional - "interval": "1 Hour", # optional + "interval": "1 hour", # optional "loadforecast_power_w": [20.5, 21.0, 22.1], "load_min": [18.5, 19.0, 20.1] } diff --git a/src/akkudoktoreos/core/version.py b/src/akkudoktoreos/core/version.py index 9521a63..bd445ce 100644 --- a/src/akkudoktoreos/core/version.py +++ b/src/akkudoktoreos/core/version.py @@ -6,13 +6,15 @@ from fnmatch import fnmatch from pathlib import Path from typing import Optional -# For development add `+dev` to previous release -# For release omit `+dev`. -VERSION_BASE = "0.2.0+dev" +# For development add `.dev` to previous release +# For release omit `.dev`. +VERSION_BASE = "0.2.0.dev" # Project hash of relevant files HASH_EOS = "" +# Number of digits to append to .dev to identify a development version +VERSION_DEV_PRECISION = 8 # ------------------------------ # Helpers for version generation @@ -91,8 +93,11 @@ def _version_calculate() -> str: """Compute version.""" global HASH_EOS HASH_EOS = _version_hash() - if VERSION_BASE.endswith("+dev"): - return f"{VERSION_BASE}.{HASH_EOS[:6]}" + if VERSION_BASE.endswith("dev"): + # After dev only digits are allowed - convert hexdigest to digits + hash_value = int(HASH_EOS, 16) + hash_digits = str(hash_value % (10**VERSION_DEV_PRECISION)).zfill(VERSION_DEV_PRECISION) + return f"{VERSION_BASE}{hash_digits}" else: return VERSION_BASE @@ -114,10 +119,10 @@ __version__ = _version_calculate() VERSION_RE = re.compile( r""" ^(?P\d+\.\d+\.\d+) # x.y.z - (?:\+ # +dev.hash starts here + (?:[\.\+\-] # .dev starts here (?: (?Pdev) # literal 'dev' - (?:\.(?P[A-Za-z0-9]+))? # optional .hash + (?:(?P[A-Za-z0-9]+))? # optional ) )? $ @@ -131,8 +136,8 @@ def version() -> dict[str, Optional[str]]: The version string shall be of the form: x.y.z - x.y.z+dev - x.y.z+dev.HASH + x.y.z.dev + x.y.z.dev Returns: .. code-block:: python diff --git a/src/akkudoktoreos/devices/devices.py b/src/akkudoktoreos/devices/devices.py index 43cdd64..b7a27c4 100644 --- a/src/akkudoktoreos/devices/devices.py +++ b/src/akkudoktoreos/devices/devices.py @@ -18,7 +18,7 @@ from akkudoktoreos.devices.devicesabc import DevicesBaseSettings from akkudoktoreos.utils.datetimeutil import DateTime, TimeWindowSequence, to_datetime # Default charge rates for battery -BATTERY_DEFAULT_CHARGE_RATES = np.linspace(0.0, 1.0, 11) # 0.0, 0.1, ..., 1.0 +BATTERY_DEFAULT_CHARGE_RATES: list[float] = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] class BatteriesCommonSettings(DevicesBaseSettings): @@ -68,7 +68,7 @@ class BatteriesCommonSettings(DevicesBaseSettings): json_schema_extra={"description": "Minimum charging power [W].", "examples": [50]}, ) - charge_rates: Optional[NDArray[Shape["*"], float]] = Field( + charge_rates: Optional[list[float]] = Field( default=BATTERY_DEFAULT_CHARGE_RATES, json_schema_extra={ "description": ( @@ -165,10 +165,7 @@ class BatteriesCommonSettings(DevicesBaseSettings): @computed_field # type: ignore[prop-decorator] @property def measurement_keys(self) -> Optional[list[str]]: - """Measurement keys for the battery stati that are measurements. - - Battery SoC, power. - """ + """Measurement keys for the battery stati that are measurements.""" keys: list[str] = [ self.measurement_key_soc_factor, self.measurement_key_power_l1_w, diff --git a/src/akkudoktoreos/devices/genetic/battery.py b/src/akkudoktoreos/devices/genetic/battery.py index c615a92..c4cd7fa 100644 --- a/src/akkudoktoreos/devices/genetic/battery.py +++ b/src/akkudoktoreos/devices/genetic/battery.py @@ -25,7 +25,7 @@ class Battery: self.discharging_efficiency = self.parameters.discharging_efficiency # Charge rates, in case of None use default - self.charge_rates = BATTERY_DEFAULT_CHARGE_RATES + self.charge_rates = np.array(BATTERY_DEFAULT_CHARGE_RATES, dtype=float) if self.parameters.charge_rates: charge_rates = np.array(self.parameters.charge_rates, dtype=float) charge_rates = np.unique(charge_rates) diff --git a/src/akkudoktoreos/optimization/genetic/genetic.py b/src/akkudoktoreos/optimization/genetic/genetic.py index b854626..47f4065 100644 --- a/src/akkudoktoreos/optimization/genetic/genetic.py +++ b/src/akkudoktoreos/optimization/genetic/genetic.py @@ -234,14 +234,14 @@ class GeneticSimulation(PydanticBaseModel): consumption_energy_per_hour = np.full((total_hours), np.nan) costs_per_hour = np.full((total_hours), np.nan) revenue_per_hour = np.full((total_hours), np.nan) - soc_per_hour = np.full((total_hours), np.nan) - soc_ev_per_hour = np.full((total_hours), np.nan) losses_wh_per_hour = np.full((total_hours), np.nan) - home_appliance_wh_per_hour = np.full((total_hours), np.nan) electricity_price_per_hour = np.full((total_hours), np.nan) # Set initial state if battery_fast: + # Pre-allocate arrays for the results, optimized for speed + soc_per_hour = np.full((total_hours), np.nan) + soc_per_hour[0] = battery_fast.current_soc_percentage() # Fill the charge array of the battery dc_charge_hours_fast[0:start_hour] = 0 @@ -255,8 +255,14 @@ class GeneticSimulation(PydanticBaseModel): bat_discharge_hours_fast[0:start_hour] = 0 bat_discharge_hours_fast[end_hour:] = 0 battery_fast.discharge_array = bat_discharge_hours_fast + else: + # Default return if no battery is available + soc_per_hour = np.full((total_hours), 0) if ev_fast: + # Pre-allocate arrays for the results, optimized for speed + soc_ev_per_hour = np.full((total_hours), np.nan) + soc_ev_per_hour[0] = ev_fast.current_soc_percentage() # Fill the charge array of the ev ev_charge_hours_fast[0:start_hour] = 0 @@ -266,14 +272,22 @@ class GeneticSimulation(PydanticBaseModel): ev_discharge_hours_fast[0:start_hour] = 0 ev_discharge_hours_fast[end_hour:] = 0 ev_fast.discharge_array = ev_discharge_hours_fast + else: + # Default return if no electric vehicle is available + soc_ev_per_hour = np.full((total_hours), 0) if home_appliance_fast and self.home_appliance_start_hour: home_appliance_enabled = True + # Pre-allocate arrays for the results, optimized for speed + home_appliance_wh_per_hour = np.full((total_hours), np.nan) + self.home_appliance_start_hour = home_appliance_fast.set_starting_time( self.home_appliance_start_hour, start_hour ) else: home_appliance_enabled = False + # Default return if no home appliance is available + home_appliance_wh_per_hour = np.full((total_hours), 0) for hour in range(start_hour, end_hour): hour_idx = hour - start_hour diff --git a/src/akkudoktoreos/optimization/genetic/geneticparams.py b/src/akkudoktoreos/optimization/genetic/geneticparams.py index b1da350..5c7ff50 100644 --- a/src/akkudoktoreos/optimization/genetic/geneticparams.py +++ b/src/akkudoktoreos/optimization/genetic/geneticparams.py @@ -177,33 +177,33 @@ class GeneticOptimizationParameters( # Check for general predictions conditions if cls.config.general.latitude is None: default_latitude = 52.52 - logger.error(f"Latitude unknown - defaulting to {default_latitude}.") + logger.info(f"Latitude unknown - defaulting to {default_latitude}.") cls.config.general.latitude = default_latitude if cls.config.general.longitude is None: default_longitude = 13.405 - logger.error(f"Longitude unknown - defaulting to {default_longitude}.") + logger.info(f"Longitude unknown - defaulting to {default_longitude}.") cls.config.general.longitude = default_longitude if cls.config.prediction.hours is None: - logger.error("Prediction hours unknown - defaulting to 48 hours.") + logger.info("Prediction hours unknown - defaulting to 48 hours.") cls.config.prediction.hours = 48 if cls.config.prediction.historic_hours is None: - logger.error("Prediction historic hours unknown - defaulting to 24 hours.") + logger.info("Prediction historic hours unknown - defaulting to 24 hours.") cls.config.prediction.historic_hours = 24 # Check optimization definitions if cls.config.optimization.horizon_hours is None: - logger.error("Optimization horizon unknown - defaulting to 24 hours.") + logger.info("Optimization horizon unknown - defaulting to 24 hours.") cls.config.optimization.horizon_hours = 24 if cls.config.optimization.interval is None: - logger.error("Optimization interval unknown - defaulting to 3600 seconds.") + logger.info("Optimization interval unknown - defaulting to 3600 seconds.") cls.config.optimization.interval = 3600 if cls.config.optimization.interval != 3600: - logger.error( + logger.info( "Optimization interval '{}' seconds not supported - forced to 3600 seconds." ) cls.config.optimization.interval = 3600 # Check genetic algorithm definitions if cls.config.optimization.genetic is None: - logger.error( + logger.info( "Genetic optimization configuration not configured - defaulting to demo config." ) cls.config.optimization.genetic = { @@ -215,16 +215,16 @@ class GeneticOptimizationParameters( }, } if cls.config.optimization.genetic.individuals is None: - logger.error("Genetic individuals unknown - defaulting to 300.") + logger.info("Genetic individuals unknown - defaulting to 300.") cls.config.optimization.genetic.individuals = 300 if cls.config.optimization.genetic.generations is None: - logger.error("Genetic generations unknown - defaulting to 400.") + logger.info("Genetic generations unknown - defaulting to 400.") cls.config.optimization.genetic.generations = 400 if cls.config.optimization.genetic.penalties is None: - logger.error("Genetic penalties unknown - defaulting to demo config.") + logger.info("Genetic penalties unknown - defaulting to demo config.") cls.config.optimization.genetic.penalties = {"ev_soc_miss": 10} if "ev_soc_miss" not in cls.config.optimization.genetic.penalties: - logger.error("ev_soc_miss penalty function parameter unknown - defaulting to 100.") + logger.info("ev_soc_miss penalty function parameter unknown - defaulting to 10.") cls.config.optimization.genetic.penalties["ev_soc_miss"] = 10 # Get start solution from last run @@ -262,7 +262,7 @@ class GeneticOptimizationParameters( * power_to_energy_per_interval_factor ).tolist() except: - logger.exception( + logger.info( "No PV forecast data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -270,6 +270,7 @@ class GeneticOptimizationParameters( { "pvforecast": { "provider": "PVForecastAkkudoktor", + "max_planes": 4, "planes": [ { "peakpower": 5.0, @@ -314,7 +315,7 @@ class GeneticOptimizationParameters( fill_method="ffill", ).tolist() except: - logger.exception( + logger.info( "No Electricity Marketprice forecast data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -330,7 +331,7 @@ class GeneticOptimizationParameters( fill_method="ffill", ).tolist() except: - logger.exception( + logger.info( "No Load forecast data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -357,7 +358,7 @@ class GeneticOptimizationParameters( fill_method="ffill", ).tolist() except: - logger.exception( + logger.info( "No feed in tariff forecast data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -384,7 +385,7 @@ class GeneticOptimizationParameters( fill_method="ffill", ).tolist() except: - logger.exception( + logger.info( "No weather forecast data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -397,14 +398,14 @@ class GeneticOptimizationParameters( # Batteries # --------- if cls.config.devices.max_batteries is None: - logger.error("Number of battery devices not configured - defaulting to 1.") + logger.info("Number of battery devices not configured - defaulting to 1.") cls.config.devices.max_batteries = 1 if cls.config.devices.max_batteries == 0: battery_params = None battery_lcos_kwh = 0 else: if cls.config.devices.batteries is None: - logger.error("No battery device data available - defaulting to demo data.") + logger.info("No battery device data available - defaulting to demo data.") cls.config.devices.batteries = [{"device_id": "battery1", "capacity_wh": 8000}] try: battery_config = cls.config.devices.batteries[0] @@ -418,7 +419,7 @@ class GeneticOptimizationParameters( max_soc_percentage=battery_config.max_soc_percentage, ) except: - logger.exception( + logger.info( "No battery device data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -427,7 +428,7 @@ class GeneticOptimizationParameters( continue # Levelized cost of ownership if battery_config.levelized_cost_of_storage_kwh is None: - logger.error( + logger.info( "No battery device LCOS data available - defaulting to 0 €/kWh. Parameter preparation attempt {}.", attempt, ) @@ -449,7 +450,7 @@ class GeneticOptimizationParameters( except: initial_soc_percentage = None if initial_soc_percentage is None: - logger.error( + logger.info( f"No battery device SoC data (measurement key = '{battery_config.measurement_key_soc_factor}') available - defaulting to 0." ) initial_soc_percentage = 0 @@ -458,13 +459,13 @@ class GeneticOptimizationParameters( # Electric Vehicles # ----------------- if cls.config.devices.max_electric_vehicles is None: - logger.error("Number of electric_vehicle devices not configured - defaulting to 1.") + logger.info("Number of electric_vehicle devices not configured - defaulting to 1.") cls.config.devices.max_electric_vehicles = 1 if cls.config.devices.max_electric_vehicles == 0: electric_vehicle_params = None else: if cls.config.devices.electric_vehicles is None: - logger.error( + logger.info( "No electric vehicle device data available - defaulting to demo data." ) cls.config.devices.max_electric_vehicles = 1 @@ -489,7 +490,7 @@ class GeneticOptimizationParameters( max_soc_percentage=electric_vehicle_config.max_soc_percentage, ) except: - logger.exception( + logger.info( "No electric_vehicle device data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -520,7 +521,7 @@ class GeneticOptimizationParameters( except: initial_soc_percentage = None if initial_soc_percentage is None: - logger.error( + logger.info( f"No electric vehicle device SoC data (measurement key = '{electric_vehicle_config.measurement_key_soc_factor}') available - defaulting to 0." ) initial_soc_percentage = 0 @@ -529,13 +530,13 @@ class GeneticOptimizationParameters( # Inverters # --------- if cls.config.devices.max_inverters is None: - logger.error("Number of inverter devices not configured - defaulting to 1.") + logger.info("Number of inverter devices not configured - defaulting to 1.") cls.config.devices.max_inverters = 1 if cls.config.devices.max_inverters == 0: inverter_params = None else: if cls.config.devices.inverters is None: - logger.error("No inverter device data available - defaulting to demo data.") + logger.info("No inverter device data available - defaulting to demo data.") cls.config.devices.inverters = [ { "device_id": "inverter1", @@ -551,7 +552,7 @@ class GeneticOptimizationParameters( battery_id=inverter_config.battery_id, ) except: - logger.exception( + logger.info( "No inverter device data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -568,14 +569,14 @@ class GeneticOptimizationParameters( # Home Appliances # --------------- if cls.config.devices.max_home_appliances is None: - logger.error("Number of home appliance devices not configured - defaulting to 1.") + logger.info("Number of home appliance devices not configured - defaulting to 1.") cls.config.devices.max_home_appliances = 1 if cls.config.devices.max_home_appliances == 0: home_appliance_params = None else: home_appliance_params = None if cls.config.devices.home_appliances is None: - logger.error( + logger.info( "No home appliance device data available - defaulting to demo data." ) cls.config.devices.home_appliances = [ @@ -606,7 +607,7 @@ class GeneticOptimizationParameters( time_windows=home_appliance_config.time_windows, ) except: - logger.exception( + logger.info( "No home appliance device data available - defaulting to demo data. Parameter preparation attempt {}.", attempt, ) @@ -639,7 +640,7 @@ class GeneticOptimizationParameters( start_solution=start_solution, ) except: - logger.exception( + logger.info( "Can not prepare optimization parameters - will retry. Parameter preparation attempt {}.", attempt, ) diff --git a/src/akkudoktoreos/optimization/genetic/geneticsolution.py b/src/akkudoktoreos/optimization/genetic/geneticsolution.py index 806116d..c558719 100644 --- a/src/akkudoktoreos/optimization/genetic/geneticsolution.py +++ b/src/akkudoktoreos/optimization/genetic/geneticsolution.py @@ -416,9 +416,35 @@ class GeneticSolution(ConfigMixin, GeneticParametersBaseModel): solution[key] = operation[key] # Add home appliance data - if self.washingstart: + if self.config.devices.max_home_appliances and self.config.devices.max_home_appliances > 0: + # Use config and not self.washingstart as washingstart may be None (no start) + # even if configured to be started. + # result starts at start_day_hour solution["homeappliance1_energy_wh"] = self.result.Home_appliance_wh_per_hour[:n_points] + operation = { + "homeappliance1_run_op_mode": [], + "homeappliance1_run_op_factor": [], + "homeappliance1_off_op_mode": [], + "homeappliance1_off_op_factor": [], + } + for hour_idx, energy in enumerate(solution["homeappliance1_energy_wh"]): + if energy > 0.0: + operation["homeappliance1_run_op_mode"].append(1.0) + operation["homeappliance1_run_op_factor"].append(1.0) + operation["homeappliance1_off_op_mode"].append(0.0) + operation["homeappliance1_off_op_factor"].append(0.0) + else: + operation["homeappliance1_run_op_mode"].append(0.0) + operation["homeappliance1_run_op_factor"].append(0.0) + operation["homeappliance1_off_op_mode"].append(1.0) + operation["homeappliance1_off_op_factor"].append(1.0) + for key in operation.keys(): + if len(operation[key]) != n_points: + error_msg = f"instruction {key} has invalid length {len(operation[key])} - expected {n_points}" + logger.error(error_msg) + raise ValueError(error_msg) + solution[key] = operation[key] # Fill prediction into dataframe with correct column names # - pvforecast_ac_energy_wh_energy_wh: PV energy prediction (positive) in wh @@ -633,19 +659,33 @@ class GeneticSolution(ConfigMixin, GeneticParametersBaseModel): ) # Add home appliance instructions (demand driven based control) - if self.washingstart: + if self.config.devices.max_home_appliances and self.config.devices.max_home_appliances > 0: + # Use config and not self.washingstart as washingstart may be None (no start) + # even if configured to be started. resource_id = "homeappliance1" - operation_mode = ApplianceOperationMode.RUN # type: ignore[assignment] - operation_mode_factor = 1.0 - execution_time = start_datetime.add(hours=self.washingstart - start_day_hour) - plan.add_instruction( - DDBCInstruction( - resource_id=resource_id, - execution_time=execution_time, - actuator_id=resource_id, - operation_mode_id=operation_mode, - operation_mode_factor=operation_mode_factor, - ) - ) + last_energy: Optional[float] = None + for hours, energy in enumerate(self.result.Home_appliance_wh_per_hour): + # hours starts at start_datetime with 0 + if energy is None: + raise ValueError( + f"Unexpected value {energy} in {self.result.Home_appliance_wh_per_hour}" + ) + if last_energy is None or energy != last_energy: + if energy > 0.0: + operation_mode = ApplianceOperationMode.RUN # type: ignore[assignment] + else: + operation_mode = ApplianceOperationMode.OFF # type: ignore[assignment] + operation_mode_factor = 1.0 + execution_time = start_datetime.add(hours=hours) + plan.add_instruction( + DDBCInstruction( + resource_id=resource_id, + execution_time=execution_time, + actuator_id=resource_id, + operation_mode_id=operation_mode, + operation_mode_factor=operation_mode_factor, + ) + ) + last_energy = energy return plan diff --git a/src/akkudoktoreos/optimization/optimization.py b/src/akkudoktoreos/optimization/optimization.py index 8aabacb..2540d00 100644 --- a/src/akkudoktoreos/optimization/optimization.py +++ b/src/akkudoktoreos/optimization/optimization.py @@ -1,6 +1,6 @@ from typing import Optional, Union -from pydantic import Field, model_validator +from pydantic import Field, computed_field, model_validator from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.core.pydantic import ( @@ -86,6 +86,22 @@ class OptimizationCommonSettings(SettingsBaseModel): }, ) + # Computed fields + @computed_field # type: ignore[prop-decorator] + @property + def keys(self) -> list[str]: + """The keys of the solution.""" + from akkudoktoreos.core.ems import get_ems + + key_list = [] + optimization_solution = get_ems().optimization_solution() + if optimization_solution: + # Prepare mapping + df = optimization_solution.solution.to_dataframe() + key_list = df.columns.tolist() + return sorted(set(key_list)) + + # Validators @model_validator(mode="after") def _enforce_algorithm_configuration(self) -> "OptimizationCommonSettings": """Ensure algorithm default configuration is set.""" diff --git a/src/akkudoktoreos/prediction/elecprice.py b/src/akkudoktoreos/prediction/elecprice.py index a67c3cf..457ab05 100644 --- a/src/akkudoktoreos/prediction/elecprice.py +++ b/src/akkudoktoreos/prediction/elecprice.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import Field, field_validator +from pydantic import Field, computed_field, field_validator from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.prediction.elecpriceabc import ElecPriceProvider @@ -57,6 +57,12 @@ class ElecPriceCommonSettings(SettingsBaseModel): json_schema_extra={"description": "Energy Charts provider settings."}, ) + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available electricity price provider ids.""" + return elecprice_providers + # Validators @field_validator("provider", mode="after") @classmethod diff --git a/src/akkudoktoreos/prediction/feedintariff.py b/src/akkudoktoreos/prediction/feedintariff.py index 3bf8a37..9412b50 100644 --- a/src/akkudoktoreos/prediction/feedintariff.py +++ b/src/akkudoktoreos/prediction/feedintariff.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import Field, field_validator +from pydantic import Field, computed_field, field_validator from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.prediction.feedintariffabc import FeedInTariffProvider @@ -56,6 +56,12 @@ class FeedInTariffCommonSettings(SettingsBaseModel): }, ) + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available feed in tariff provider ids.""" + return feedintariff_providers + # Validators @field_validator("provider", mode="after") @classmethod diff --git a/src/akkudoktoreos/prediction/load.py b/src/akkudoktoreos/prediction/load.py index 1921025..2422e6d 100644 --- a/src/akkudoktoreos/prediction/load.py +++ b/src/akkudoktoreos/prediction/load.py @@ -2,7 +2,7 @@ from typing import Optional -from pydantic import Field, field_validator +from pydantic import Field, computed_field, field_validator from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.prediction.loadabc import LoadProvider @@ -62,6 +62,12 @@ class LoadCommonSettings(SettingsBaseModel): }, ) + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available load provider ids.""" + return load_providers + # Validators @field_validator("provider", mode="after") @classmethod diff --git a/src/akkudoktoreos/prediction/loadimport.py b/src/akkudoktoreos/prediction/loadimport.py index 4f09738..bbb7750 100644 --- a/src/akkudoktoreos/prediction/loadimport.py +++ b/src/akkudoktoreos/prediction/loadimport.py @@ -39,11 +39,11 @@ class LoadImportCommonSettings(SettingsBaseModel): @field_validator("import_file_path", mode="after") @classmethod def validate_loadimport_file_path(cls, value: Optional[Union[str, Path]]) -> Optional[Path]: + """Ensure file is available.""" if value is None: return None if isinstance(value, str): value = Path(value) - """Ensure file is available.""" value.resolve() if not value.is_file(): raise ValueError(f"Import file path '{value}' is not a file.") diff --git a/src/akkudoktoreos/prediction/loadvrm.py b/src/akkudoktoreos/prediction/loadvrm.py index 0a10866..ead31bb 100644 --- a/src/akkudoktoreos/prediction/loadvrm.py +++ b/src/akkudoktoreos/prediction/loadvrm.py @@ -24,7 +24,7 @@ class VrmForecastResponse(PydanticBaseModel): class LoadVrmCommonSettings(SettingsBaseModel): - """Common settings for VRM API.""" + """Common settings for load forecast VRM API.""" load_vrm_token: str = Field( default="your-token", diff --git a/src/akkudoktoreos/prediction/prediction.py b/src/akkudoktoreos/prediction/prediction.py index b90d1ed..c83031b 100644 --- a/src/akkudoktoreos/prediction/prediction.py +++ b/src/akkudoktoreos/prediction/prediction.py @@ -52,22 +52,7 @@ from akkudoktoreos.prediction.weatherimport import WeatherImport class PredictionCommonSettings(SettingsBaseModel): - """General Prediction Configuration. - - This class provides configuration for prediction settings, allowing users to specify - parameters such as the forecast duration (in hours). - Validators ensure each parameter is within a specified range. - - Attributes: - hours (Optional[int]): Number of hours into the future for predictions. - Must be non-negative. - historic_hours (Optional[int]): Number of hours into the past for historical data. - Must be non-negative. - - Validators: - validate_hours (int): Ensures `hours` is a non-negative integer. - validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer. - """ + """General Prediction Configuration.""" hours: Optional[int] = Field( default=48, diff --git a/src/akkudoktoreos/prediction/pvforecast.py b/src/akkudoktoreos/prediction/pvforecast.py index 43c8a36..91b8060 100644 --- a/src/akkudoktoreos/prediction/pvforecast.py +++ b/src/akkudoktoreos/prediction/pvforecast.py @@ -260,6 +260,12 @@ class PVForecastCommonSettings(SettingsBaseModel): }, ) + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available PVForecast provider ids.""" + return pvforecast_providers + # Validators @field_validator("provider", mode="after") @classmethod diff --git a/src/akkudoktoreos/prediction/pvforecastakkudoktor.py b/src/akkudoktoreos/prediction/pvforecastakkudoktor.py index 8942577..8632069 100644 --- a/src/akkudoktoreos/prediction/pvforecastakkudoktor.py +++ b/src/akkudoktoreos/prediction/pvforecastakkudoktor.py @@ -193,20 +193,6 @@ class PVForecastAkkudoktor(PVForecastProvider): from the PVForecastAkkudoktor API and maps it to `PVForecastDataRecord` fields, applying any necessary scaling or unit corrections. It manages the forecast over a range of hours into the future and retains historical data. - - Attributes: - hours (int, optional): Number of hours in the future for the forecast. - historic_hours (int, optional): Number of past hours for retaining data. - latitude (float, optional): The latitude in degrees, validated to be between -90 and 90. - longitude (float, optional): The longitude in degrees, validated to be between -180 and 180. - start_datetime (datetime, optional): Start datetime for forecasts, defaults to the current datetime. - end_datetime (datetime, computed): The forecast's end datetime, computed based on `start_datetime` and `hours`. - keep_datetime (datetime, computed): The datetime to retain historical data, computed from `start_datetime` and `historic_hours`. - - Methods: - provider_id(): Returns a unique identifier for the provider. - _request_forecast(): Fetches the forecast from the Akkudoktor API. - _update_data(): Processes and updates forecast data from Akkudoktor in PVForecastDataRecord format. """ # overload diff --git a/src/akkudoktoreos/prediction/pvforecastvrm.py b/src/akkudoktoreos/prediction/pvforecastvrm.py index 5761bd6..1888468 100644 --- a/src/akkudoktoreos/prediction/pvforecastvrm.py +++ b/src/akkudoktoreos/prediction/pvforecastvrm.py @@ -24,7 +24,7 @@ class VrmForecastResponse(PydanticBaseModel): class PVForecastVrmCommonSettings(SettingsBaseModel): - """Common settings for VRM API.""" + """Common settings for PV forecast VRM API.""" pvforecast_vrm_token: str = Field( default="your-token", diff --git a/src/akkudoktoreos/prediction/weather.py b/src/akkudoktoreos/prediction/weather.py index 25d982f..5a280f7 100644 --- a/src/akkudoktoreos/prediction/weather.py +++ b/src/akkudoktoreos/prediction/weather.py @@ -2,7 +2,7 @@ from typing import Optional -from pydantic import Field, field_validator +from pydantic import Field, computed_field, field_validator from akkudoktoreos.config.configabc import SettingsBaseModel from akkudoktoreos.prediction.prediction import get_prediction @@ -52,6 +52,12 @@ class WeatherCommonSettings(SettingsBaseModel): }, ) + @computed_field # type: ignore[prop-decorator] + @property + def providers(self) -> list[str]: + """Available weather provider ids.""" + return weather_providers + # Validators @field_validator("provider", mode="after") @classmethod diff --git a/src/akkudoktoreos/prediction/weatherclearoutside.py b/src/akkudoktoreos/prediction/weatherclearoutside.py index c8dabb8..38f533b 100644 --- a/src/akkudoktoreos/prediction/weatherclearoutside.py +++ b/src/akkudoktoreos/prediction/weatherclearoutside.py @@ -37,7 +37,7 @@ WheaterDataClearOutsideMapping: List[Tuple[str, Optional[str], Optional[float]]] ("Precipitation Type", "Precipitation Type", None), ("Precipitation Probability (%)", "Precipitation Probability (%)", 1), ("Precipitation Amount (mm)", "Precipitation Amount (mm)", 1), - ("Wind Speed (mph)", "Wind Speed (kmph)", 1.60934), + ("Wind Speed/Direction (mph)", "Wind Speed (kmph)", 1.60934), ("Chance of Frost", "Chance of Frost", None), ("Temperature (°C)", "Temperature (°C)", 1), ("Feels Like (°C)", "Feels Like (°C)", 1), @@ -218,7 +218,7 @@ class WeatherClearOutside(WeatherProvider): for detail_name in detail_names: if detail_name not in clearoutside_key_mapping: warning_msg = ( - f"Clearoutside schema change. Unexpected detail name {detail_name}." + f"Clearoutside schema change. Unexpected detail name '{detail_name}'." ) logger.warning(warning_msg) @@ -226,17 +226,13 @@ class WeatherClearOutside(WeatherProvider): # Beware there is one ul paragraph before that is not associated to a detail p_detail_tables = p_day.find_all("ul") if len(p_detail_tables) != len(detail_names) + 1: - error_msg = f"Clearoutside schema change. Unexpected number ({p_detail_tables}) of `ul` for details {len(detail_names)}. Should be one extra only." + error_msg = f"Clearoutside schema change. Unexpected number ({p_detail_tables}) of 'ul' for details {len(detail_names)}. Should be one extra only." logger.error(error_msg) raise ValueError(error_msg) p_detail_tables.pop(0) # Create clearout data clearout_data = {} - # Replace some detail names that we use differently - detail_names = [ - s.replace("Wind Speed/Direction (mph)", "Wind Speed (mph)") for s in detail_names - ] # Number of detail values. On last day may be less than 24. detail_values_count = None # Add data values @@ -266,7 +262,7 @@ class WeatherClearOutside(WeatherProvider): extra_detail_name = None extra_detail_data = [] for p_detail_value in p_detail_values: - if detail_name == "Wind Speed (mph)": + if detail_name == "Wind Speed/Direction (mph)": # Get the usual value value_str = p_detail_value.get_text() # Also extract extra data diff --git a/src/akkudoktoreos/server/dash/about.py b/src/akkudoktoreos/server/dash/about.py index da78ee1..4bedcf8 100644 --- a/src/akkudoktoreos/server/dash/about.py +++ b/src/akkudoktoreos/server/dash/about.py @@ -19,6 +19,8 @@ over a specified period. Documentation can be found at [Akkudoktor-EOS](https://akkudoktor-eos.readthedocs.io/en/latest/). +--- + ## Version Information **Current Version:** {__version__} @@ -29,4 +31,5 @@ Documentation can be found at [Akkudoktor-EOS](https://akkudoktor-eos.readthedoc def About(**kwargs: Any) -> Div: + global about_md return Markdown(about_md, **kwargs) diff --git a/src/akkudoktoreos/server/dash/admin.py b/src/akkudoktoreos/server/dash/admin.py index 0d2f7e2..ce50ec0 100644 --- a/src/akkudoktoreos/server/dash/admin.py +++ b/src/akkudoktoreos/server/dash/admin.py @@ -5,17 +5,13 @@ for the EOS dashboard. """ import json -from pathlib import Path from typing import Any, Optional, Union import requests from fasthtml.common import Select from loguru import logger -from monsterui.foundations import stringify from monsterui.franken import ( # Select, TODO: Select from FrankenUI does not work - using Select from FastHTML instead H3, - Button, - ButtonT, Card, Details, Div, @@ -28,33 +24,12 @@ from monsterui.franken import ( # Select, TODO: Select from FrankenUI does not Summary, UkIcon, ) -from platformdirs import user_config_dir -from akkudoktoreos.server.dash.components import Error, Success +from akkudoktoreos.server.dash.components import ConfigButton, Error, Success from akkudoktoreos.server.dash.configuration import get_nested_value +from akkudoktoreos.server.dash.context import export_import_directory, request_url_for from akkudoktoreos.utils.datetimeutil import to_datetime -# Directory to export files to, or to import files from -export_import_directory = Path(user_config_dir("net.akkudoktor.eosdash", "akkudoktor")) - - -def AdminButton(*c: Any, cls: Optional[Union[str, tuple]] = None, **kwargs: Any) -> Button: - """Creates a styled button for administrative actions. - - Args: - *c (Any): Positional arguments representing the button's content. - cls (Optional[Union[str, tuple]]): Additional CSS classes for styling. Defaults to None. - **kwargs (Any): Additional keyword arguments passed to the `Button`. - - Returns: - Button: A styled `Button` component for admin actions. - """ - new_cls = f"{ButtonT.primary}" - if cls: - new_cls += f" {stringify(cls)}" - kwargs["cls"] = new_cls - return Button(*c, submit=False, **kwargs) - def AdminCache( eos_host: str, eos_port: Union[str, int], data: Optional[dict], config: Optional[dict[str, Any]] @@ -111,9 +86,9 @@ def AdminCache( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Clear all", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='{"category": "cache", "action": "clear"}', @@ -132,9 +107,9 @@ def AdminCache( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Clear expired", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='{"category": "cache", "action": "clear-expired"}', @@ -301,14 +276,16 @@ def AdminConfig( ) # Update for display, in case we added a new file before - import_from_file_names = [f.name for f in list(export_import_directory.glob("*.json"))] + import_from_file_names = sorted([f.name for f in list(export_import_directory.glob("*.json"))]) if config_backup is None: revert_to_backup_metadata_list = ["Backup list not available"] else: - revert_to_backup_metadata_list = [ - f"{backup_meta['date_time']} {backup_meta['version']}" - for backup_id, backup_meta in config_backup.items() - ] + revert_to_backup_metadata_list = sorted( + [ + f"{backup_meta['date_time']} {backup_meta['version']}" + for backup_id, backup_meta in config_backup.items() + ] + ) return ( category, @@ -319,9 +296,9 @@ def AdminConfig( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Save to file", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='{"category": "configuration", "action": "save_to_file"}', @@ -341,9 +318,9 @@ def AdminConfig( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Revert to backup", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='js:{ "category": "configuration", "action": "revert_to_backup", "backup_metadata": document.querySelector("[name=\'selected_backup_metadata\']").value }', @@ -352,6 +329,7 @@ def AdminConfig( *Options(*revert_to_backup_metadata_list), id="backup_metadata", name="selected_backup_metadata", # Name of hidden input field with selected value + cls="border rounded px-3 py-2 mr-2", placeholder="Select backup", ), ), @@ -368,9 +346,9 @@ def AdminConfig( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Export to file", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='js:{"category": "configuration", "action": "export_to_file", "export_to_file_tag": document.querySelector("[name=\'chosen_export_file_tag\']").value }', @@ -398,9 +376,9 @@ def AdminConfig( Grid( DivHStacked( UkIcon(icon="play"), - AdminButton( + ConfigButton( "Import from file", - hx_post="/eosdash/admin", + hx_post=request_url_for("/eosdash/admin"), hx_target="#page-content", hx_swap="innerHTML", hx_vals='js:{ "category": "configuration", "action": "import_from_file", "import_file_name": document.querySelector("[name=\'selected_import_file_name\']").value }', @@ -409,6 +387,7 @@ def AdminConfig( *Options(*import_from_file_names), id="import_file_name", name="selected_import_file_name", # Name of hidden input field with selected value + cls="border rounded px-3 py-2 mr-2", placeholder="Select file", ), ), diff --git a/src/akkudoktoreos/server/dash/bokeh.py b/src/akkudoktoreos/server/dash/bokeh.py index d6ef211..4b1f18e 100644 --- a/src/akkudoktoreos/server/dash/bokeh.py +++ b/src/akkudoktoreos/server/dash/bokeh.py @@ -2,35 +2,13 @@ # MIT license from typing import Optional -import bokeh from bokeh.embed import components from bokeh.models import Plot -from monsterui.franken import H4, Card, NotStr, Script +from bokeh.resources import INLINE +from monsterui.franken import H4, Card, NotStr -bokeh_version = bokeh.__version__ - -BokehJS = [ - Script( - src=f"https://cdn.bokeh.org/bokeh/release/bokeh-{bokeh_version}.min.js", - crossorigin="anonymous", - ), - Script( - src=f"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-{bokeh_version}.min.js", - crossorigin="anonymous", - ), - Script( - src=f"https://cdn.bokeh.org/bokeh/release/bokeh-tables-{bokeh_version}.min.js", - crossorigin="anonymous", - ), - Script( - src=f"https://cdn.bokeh.org/bokeh/release/bokeh-gl-{bokeh_version}.min.js", - crossorigin="anonymous", - ), - Script( - src=f"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-{bokeh_version}.min.js", - crossorigin="anonymous", - ), -] +# Javascript for bokeh - to be included by the page +BokehJS = [NotStr(INLINE.render_css()), NotStr(INLINE.render_js())] def bokey_apply_theme_to_plot(plot: Plot, dark: bool) -> None: diff --git a/src/akkudoktoreos/server/dash/components.py b/src/akkudoktoreos/server/dash/components.py index 2ca05e9..9c015a3 100644 --- a/src/akkudoktoreos/server/dash/components.py +++ b/src/akkudoktoreos/server/dash/components.py @@ -1,28 +1,36 @@ -from typing import Any, Optional, Union +import json +from typing import Any, Callable, Optional, Union -from fasthtml.common import H1, Button, Div, Li +from fasthtml.common import H1, Button, Div, Li, Select from monsterui.daisy import ( Alert, AlertT, ) from monsterui.foundations import stringify -from monsterui.franken import ( # Button, Does not pass hx_vals +from monsterui.franken import ( # Select: Does not work - using Select from FastHTML instead;; Button: Does not pass hx_vals - using Button from FastHTML instead H3, + ButtonT, Card, + Code, Container, ContainerT, Details, + DivHStacked, DivLAligned, DivRAligned, Form, Grid, Input, + Option, P, + Pre, Summary, TabContainer, UkIcon, ) +from akkudoktoreos.server.dash.context import request_url_for + scrollbar_viewport_styles = ( "scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch;" ) @@ -71,11 +79,59 @@ def ScrollArea( ) +def JsonView(data: Any) -> Pre: + """Render structured data as formatted JSON inside a styled

 block.
+
+    The data is serialized to JSON using indentation for readability and
+    UTF-8 characters are preserved. The JSON is wrapped in a  element
+    with a JSON language class to support syntax highlighting, and then
+    placed inside a 
 container with MonsterUI-compatible styling.
+
+    The JSON output is height-constrained and scrollable to safely display
+    large payloads without breaking the page layout.
+
+    Args:
+        data: Any JSON-serializable Python object to render.
+
+    Returns:
+        A FastHTML `Pre` element containing a formatted JSON representation
+        of the input data.
+    """
+    code_str = json.dumps(data, indent=2, ensure_ascii=False)
+    return Pre(
+        Code(code_str, cls="language-json"),
+        cls="rounded-lg bg-muted p-3 max-h-[30vh] overflow-y-auto overflow-x-hidden whitespace-pre-wrap",
+    )
+
+
+def TextView(*c: Any, cls: Optional[Union[str, tuple]] = None, **kwargs: Any) -> Pre:
+    """Render plain text with preserved line breaks and wrapped long lines.
+
+    This view uses a 
 element with whitespace wrapping enabled so that
+    newline characters are respected while long lines are wrapped instead
+    of causing horizontal scrolling.
+
+    Args:
+        *c (Any): Positional arguments representing the TextView content.
+        cls (Optional[Union[str, tuple]]): Additional CSS classes for styling. Defaults to None.
+        **kwargs (Any): Additional keyword arguments passed to the `Pre`.
+
+    Returns:
+        A FastHTML `Pre` element that displays the text with preserved
+        formatting and line wrapping.
+    """
+    new_cls = "whitespace-pre-wrap"
+    if cls:
+        new_cls += f"{stringify(cls)}"
+    kwargs["cls"] = new_cls
+    return Pre(*c, **kwargs)
+
+
 def Success(*c: Any) -> Alert:
     return Alert(
         DivLAligned(
             UkIcon("check"),
-            P(*c),
+            TextView(*c),
         ),
         cls=AlertT.success,
     )
@@ -85,12 +141,321 @@ def Error(*c: Any) -> Alert:
     return Alert(
         DivLAligned(
             UkIcon("triangle-alert"),
-            P(*c),
+            TextView(*c),
         ),
         cls=AlertT.error,
     )
 
 
+def ConfigButton(*c: Any, cls: Optional[Union[str, tuple]] = None, **kwargs: Any) -> Button:
+    """Creates a styled button for configuration actions.
+
+    Args:
+        *c (Any): Positional arguments representing the button's content.
+        cls (Optional[Union[str, tuple]]): Additional CSS classes for styling. Defaults to None.
+        **kwargs (Any): Additional keyword arguments passed to the `Button`.
+
+    Returns:
+        Button: A styled `Button` component for configuration actions.
+    """
+    new_cls = f"px-4 py-2 rounded {ButtonT.primary}"
+    if cls:
+        new_cls += f"{stringify(cls)}"
+    kwargs["cls"] = new_cls
+    return Button(*c, submit=False, **kwargs)
+
+
+def make_config_update_form() -> Callable[[str, str], Grid]:
+    """Factory for a form that sets a single configuration value.
+
+    Returns:
+        A function (config_name: str, value: str) -> Grid
+    """
+
+    def ConfigUpdateForm(config_name: str, value: str) -> Grid:
+        config_id = config_name.lower().replace(".", "-")
+
+        return Grid(
+            DivRAligned(P("update")),
+            Grid(
+                Form(
+                    Input(value="update", type="hidden", id="action"),
+                    Input(value=config_name, type="hidden", id="key"),
+                    Input(value=value, type="text", id="value"),
+                    hx_put=request_url_for("/eosdash/configuration"),
+                    hx_target="#page-content",
+                    hx_swap="innerHTML",
+                ),
+            ),
+            id=f"{config_id}-update-form",
+        )
+
+    return ConfigUpdateForm
+
+
+def make_config_update_value_form(
+    available_values: list[str],
+) -> Callable[[str, str], Grid]:
+    """Factory for a form that sets a single configuration value with pre-set avaliable values.
+
+    Args:
+        available_values: Allowed values for the configuration
+
+    Returns:
+        A function (config_name: str, value: str) -> Grid
+    """
+
+    def ConfigUpdateValueForm(config_name: str, value: str) -> Grid:
+        config_id = config_name.lower().replace(".", "-")
+
+        return Grid(
+            DivRAligned(P("update value")),
+            DivHStacked(
+                ConfigButton(
+                    "Set",
+                    hx_put=request_url_for("/eosdash/configuration"),
+                    hx_target="#page-content",
+                    hx_swap="innerHTML",
+                    hx_vals=f"""js:{{
+                        action: "update",
+                        key: "{config_name}",
+                        value: document
+                            .querySelector("[name='{config_id}_selected_value']")
+                            .value
+                    }}""",
+                ),
+                Select(
+                    Option("Select a value...", value="", selected=True, disabled=True),
+                    *[
+                        Option(
+                            val,
+                            value=val,
+                            selected=(val == value),
+                        )
+                        for val in available_values
+                    ],
+                    id=f"{config_id}-value-select",
+                    name=f"{config_id}_selected_value",
+                    required=True,
+                    cls="border rounded px-3 py-2 mr-2 col-span-4",
+                ),
+            ),
+            id=f"{config_id}-update-value-form",
+        )
+
+    return ConfigUpdateValueForm
+
+
+def make_config_update_list_form(available_values: list[str]) -> Callable[[str, str], Grid]:
+    """Factory function that creates a ConfigUpdateListForm with pre-set available values.
+
+    Args:
+        available_values: List of available values to choose from
+
+    Returns:
+        A function that creates ConfigUpdateListForm instances with the given available_values.
+        The returned function takes (config_name: str, value: str) and returns a Grid.
+    """
+
+    def ConfigUpdateListForm(config_name: str, value: str) -> Grid:
+        """Creates a card with a form to add/remove values from a list.
+
+        Sends to "/eosdash/configuration":
+            The form sends an HTTP PUT request with the following parameters:
+
+            - key (str): The configuration key name (value of config_name parameter)
+            - value (str): A JSON string representing the updated list of values
+
+            The value parameter will always be a valid JSON string representation of a list.
+
+        Args:
+            config_name: The name of the configuration
+            value (str): The current value of the configuration, a list of values in json format.
+        """
+        current_values = json.loads(value)
+        if current_values is None:
+            current_values = []
+        config_id = config_name.lower().replace(".", "-")
+
+        return Grid(
+            DivRAligned(P("update list")),
+            Grid(
+                # Form to add new value to list
+                DivHStacked(
+                    ConfigButton(
+                        "Add",
+                        hx_put=request_url_for("/eosdash/configuration"),
+                        hx_target="#page-content",
+                        hx_swap="innerHTML",
+                        hx_vals=f"""js:{{
+                            action: "update",
+                            key: "{config_name}",
+                            value: JSON.stringify(
+                                [...new Set([
+                                    ...{json.dumps(current_values)},
+                                    document.querySelector("[name='{config_id}_selected_add_value']").value.trim()
+                                ])].filter(v => v !== "")
+                            )
+                        }}""",
+                    ),
+                    Select(
+                        Option("Select a value...", value="", selected=True, disabled=True),
+                        *[
+                            Option(val, value=val, disabled=val in current_values)
+                            for val in available_values
+                        ],
+                        id=f"{config_id}-add-value-select",
+                        name=f"{config_id}_selected_add_value",  # Name of hidden input with selected value
+                        required=True,
+                        cls="border rounded px-3 py-2 mr-2 col-span-4",
+                    ),
+                ),
+                # Form to delete value from list
+                DivHStacked(
+                    ConfigButton(
+                        "Delete",
+                        hx_put=request_url_for("/eosdash/configuration"),
+                        hx_target="#page-content",
+                        hx_swap="innerHTML",
+                        hx_vals=f"""js:{{
+                            action: "update",
+                            key: "{config_name}",
+                            value: JSON.stringify(
+                                [...new Set([
+                                    ...{json.dumps(current_values)}
+                                ])].filter(v => v !== document.querySelector("[name='{config_id}_selected_delete_value']").value.trim())
+                            )
+                        }}""",
+                    ),
+                    Select(
+                        Option("Select a value...", value="", selected=True, disabled=True),
+                        *[Option(val, value=val) for val in current_values],
+                        id=f"{config_id}-delete-value-select",
+                        name=f"{config_id}_selected_delete_value",  # Name of hidden input with selected value
+                        required=True,
+                        cls="border rounded px-3 py-2 mr-2 col-span-4",
+                    ),
+                ),
+                cols=1,
+            ),
+            id=f"{config_id}-update-list-form",
+        )
+
+    # Return the function that creates a ConfigUpdateListForm instance
+    return ConfigUpdateListForm
+
+
+def make_config_update_map_form(
+    available_keys: list[str] | None = None,
+    available_values: list[str] | None = None,
+) -> Callable[[str, str], Grid]:
+    """Factory function that creates a ConfigUpdateMapForm.
+
+    Args:
+        available_keys: Optional list of allowed keys (None = free text)
+        available_values: Optional list of allowed values (None = free text)
+
+    Returns:
+        A function that creates ConfigUpdateMapForm instances.
+        The returned function takes (config_name: str, value: str) and returns a Grid.
+    """
+
+    def ConfigUpdateMapForm(config_name: str, value: str) -> Grid:
+        """Creates a card with a form to add/update/delete entries in a map."""
+        current_map: dict[str, str] = json.loads(value) or {}
+        config_id = config_name.lower().replace(".", "-")
+
+        return Grid(
+            DivRAligned(P("update map")),
+            Grid(
+                # Add / update key-value pair
+                DivHStacked(
+                    ConfigButton(
+                        "Set",
+                        hx_put=request_url_for("/eosdash/configuration"),
+                        hx_target="#page-content",
+                        hx_swap="innerHTML",
+                        hx_vals=f"""js:{{
+                            action: "update",
+                            key: "{config_name}",
+                            value: JSON.stringify(
+                                Object.assign(
+                                    {json.dumps(current_map)},
+                                    {{
+                                        [document.querySelector("[name='{config_id}_set_key']").value.trim()]:
+                                        document.querySelector("[name='{config_id}_set_value']").value.trim()
+                                    }}
+                                )
+                            )
+                        }}""",
+                    ),
+                    (
+                        Select(
+                            Option("Select key...", value="", selected=True, disabled=True),
+                            *[Option(k, value=k) for k in (sorted(available_keys) or [])],
+                            name=f"{config_id}_set_key",
+                            cls="border rounded px-3 py-2 col-span-2",
+                        )
+                        if available_keys
+                        else Input(
+                            name=f"{config_id}_set_key",
+                            placeholder="Key",
+                            required=True,
+                            cls="border rounded px-3 py-2 col-span-2",
+                        ),
+                    ),
+                    (
+                        Select(
+                            Option("Select value...", value="", selected=True, disabled=True),
+                            *[Option(k, value=k) for k in (sorted(available_values) or [])],
+                            name=f"{config_id}_set_value",
+                            cls="border rounded px-3 py-2 col-span-2",
+                        )
+                        if available_values
+                        else Input(
+                            name=f"{config_id}_set_value",
+                            placeholder="Value",
+                            required=True,
+                            cls="border rounded px-3 py-2 col-span-2",
+                        ),
+                    ),
+                ),
+                # Delete key
+                DivHStacked(
+                    ConfigButton(
+                        "Delete",
+                        hx_put=request_url_for("/eosdash/configuration"),
+                        hx_target="#page-content",
+                        hx_swap="innerHTML",
+                        hx_vals=f"""js:{{
+                            action: "update",
+                            key: "{config_name}",
+                            value: JSON.stringify(
+                                Object.fromEntries(
+                                    Object.entries({json.dumps(current_map)})
+                                        .filter(([k]) =>
+                                            k !== document.querySelector("[name='{config_id}_delete_key']").value
+                                        )
+                                )
+                            )
+                        }}""",
+                    ),
+                    Select(
+                        Option("Select key...", value="", selected=True, disabled=True),
+                        *[Option(k, value=k) for k in sorted(current_map.keys())],
+                        name=f"{config_id}_delete_key",
+                        required=True,
+                        cls="border rounded px-3 py-2 col-span-4",
+                    ),
+                ),
+                cols=1,
+            ),
+            id=f"{config_id}-update-map-form",
+        )
+
+    return ConfigUpdateMapForm
+
+
 def ConfigCard(
     config_name: str,
     config_type: str,
@@ -102,6 +467,7 @@ def ConfigCard(
     update_error: Optional[str],
     update_value: Optional[str],
     update_open: Optional[bool],
+    update_form_factory: Optional[Callable[[str, str], Grid]] = None,
 ) -> Card:
     """Creates a styled configuration card for displaying configuration details.
 
@@ -113,7 +479,7 @@ def ConfigCard(
         config_name (str): The name of the configuration.
         config_type (str): The type of the configuration.
         read_only (str): Indicates if the configuration is read-only ("rw" for read-write,
-                         any other value indicates read-only).
+            any other value indicates read-only).
         value (str): The current value of the configuration.
         default (str): The default value of the configuration.
         description (str): A description of the configuration.
@@ -121,7 +487,9 @@ def ConfigCard(
         update_error (Optional[str]): The error message, if any, during the update process.
         update_value (Optional[str]): The value to be updated, if different from the current value.
         update_open (Optional[bool]): A flag indicating whether the update section of the card
-                                      should be initially expanded.
+            should be initially expanded.
+        update_form_factory (Optional[Callable[[str, str], Grid]]): The factory to create a form to
+            use to update the configuration value. Defaults to simple text input.
 
     Returns:
         Card: A styled Card component containing the configuration details.
@@ -131,6 +499,11 @@ def ConfigCard(
         update_value = value
     if not update_open:
         update_open = False
+    if not update_form_factory:
+        # Default update form
+        update_form = make_config_update_form()(config_name, update_value)
+    else:
+        update_form = update_form_factory(config_name, update_value)
     if deprecated:
         if isinstance(deprecated, bool):
             deprecated = "Deprecated"
@@ -147,12 +520,12 @@ def ConfigCard(
                             P(read_only),
                         ),
                     ),
-                    P(value),
+                    JsonView(json.loads(value)),
                 ),
                 cls="list-none",
             ),
             Grid(
-                P(description),
+                TextView(description),
                 P(config_type),
             )
             if not deprecated
@@ -171,27 +544,18 @@ def ConfigCard(
             if read_only == "rw" and not deprecated
             else None,
             # Set value
-            Grid(
-                DivRAligned(P("update")),
-                Grid(
-                    Form(
-                        Input(value=config_name, type="hidden", id="key"),
-                        Input(value=update_value, type="text", id="value"),
-                        hx_put="/eosdash/configuration",
-                        hx_target="#page-content",
-                        hx_swap="innerHTML",
-                    ),
-                ),
-            )
-            if read_only == "rw" and not deprecated
-            else None,
+            update_form if read_only == "rw" and not deprecated else None,
             # Last error
             Grid(
                 DivRAligned(P("update error")),
-                P(update_error),
+                TextView(update_error),
             )
             if update_error
             else None,
+            # Provide minimal update form on error if complex update_form is used
+            make_config_update_form()(config_name, update_value)
+            if update_error and update_form_factory is not None
+            else None,
             cls="space-y-4 gap-4",
             open=update_open,
         ),
@@ -226,7 +590,7 @@ def DashboardFooter(*c: Any, path: str) -> Card:
     """
     return Card(
         Container(*c, id="footer-content"),
-        hx_get=f"{path}",
+        hx_get=request_url_for(path),
         hx_trigger="every 5s",
         hx_target="#footer-content",
         hx_swap="innerHTML",
@@ -266,7 +630,7 @@ def DashboardTabs(dashboard_items: dict[str, str]) -> Card:
         Li(
             DashboardTrigger(
                 H3(menu),
-                hx_get=f"{path}",
+                hx_get=request_url_for(path),
                 hx_target="#page-content",
                 hx_swap="innerHTML",
                 hx_vals='js:{ "dark": window.matchMedia("(prefers-color-scheme: dark)").matches }',
diff --git a/src/akkudoktoreos/server/dash/configuration.py b/src/akkudoktoreos/server/dash/configuration.py
index fa7210a..5671fd8 100644
--- a/src/akkudoktoreos/server/dash/configuration.py
+++ b/src/akkudoktoreos/server/dash/configuration.py
@@ -1,5 +1,6 @@
 import json
-from typing import Any, Dict, List, Optional, Sequence, TypeVar, Union
+from collections.abc import Sequence
+from typing import Any, Dict, List, Optional, TypeVar, Union
 
 import requests
 from loguru import logger
@@ -7,6 +8,7 @@ from monsterui.franken import (
     H3,
     H4,
     Card,
+    CardTitle,
     Details,
     Div,
     DividerLine,
@@ -15,6 +17,7 @@ from monsterui.franken import (
     Form,
     Grid,
     Input,
+    LabelCheckboxX,
     P,
     Summary,
     UkIcon,
@@ -25,7 +28,15 @@ from pydantic_core import PydanticUndefined
 from akkudoktoreos.config.config import ConfigEOS
 from akkudoktoreos.core.pydantic import PydanticBaseModel
 from akkudoktoreos.prediction.pvforecast import PVForecastPlaneSetting
-from akkudoktoreos.server.dash.components import ConfigCard
+from akkudoktoreos.server.dash.components import (
+    ConfigCard,
+    JsonView,
+    TextView,
+    make_config_update_list_form,
+    make_config_update_map_form,
+    make_config_update_value_form,
+)
+from akkudoktoreos.server.dash.context import request_url_for
 
 T = TypeVar("T")
 
@@ -33,6 +44,14 @@ T = TypeVar("T")
 # Dictionary of config names and associated dictionary with keys "value", "result", "error", "open".
 config_update_latest: dict[str, dict[str, Optional[Union[str, bool]]]] = {}
 
+# Current state of config displayed
+config_visible: dict[str, dict] = {
+    "config-visible-read-only": {
+        "label": "Configuration (read-only)",
+        "visible": False,
+    },
+}
+
 
 def get_nested_value(
     dictionary: Union[Dict[str, Any], List[Any]],
@@ -178,9 +197,9 @@ def resolve_nested_types(field_type: Any, parent_types: list[str]) -> list[tuple
     return resolved_types
 
 
-def configuration(
+def create_config_details(
     model: type[PydanticBaseModel], values: dict, values_prefix: list[str] = []
-) -> list[dict]:
+) -> dict[str, dict]:
     """Generate configuration details based on provided values and model metadata.
 
     Args:
@@ -189,9 +208,9 @@ def configuration(
         values_prefix (list[str]): A list of parent type names that prefixes the model values in the values.
 
     Returns:
-        list[dict]: A sorted list of configuration details, each represented as a dictionary.
+        dict[dict]: A dictionary of configuration details, each represented as a dictionary.
     """
-    configs = []
+    config_details: dict[str, dict] = {}
     inner_types: set[type[PydanticBaseModel]] = set()
 
     for field_name, field_info in list(model.model_fields.items()) + list(
@@ -244,7 +263,7 @@ def configuration(
                         .replace("NoneType", "None")
                         .replace("", "float")
                     )
-                    configs.append(config)
+                    config_details[str(config["name"])] = config
                     found_basic = True
                 else:
                     new_parent_types = parent_types + nested_parent_types
@@ -258,18 +277,18 @@ def configuration(
                         )
 
         extract_nested_models(field_info, [field_name])
-    return sorted(configs, key=lambda x: x["name"])
+    return config_details
 
 
-def get_configuration(eos_host: str, eos_port: Union[str, int]) -> list[dict]:
-    """Fetch and process configuration data from the specified EOS server.
+def get_config(eos_host: str, eos_port: Union[str, int]) -> dict[str, Any]:
+    """Fetch configuration data from the specified EOS server.
 
     Args:
         eos_host (str): The hostname of the EOS server.
         eos_port (Union[str, int]): The port of the EOS server.
 
     Returns:
-        List[dict]: A list of processed configuration entries.
+        dict[str, Any]: A dict of configuration data.
     """
     server = f"http://{eos_host}:{eos_port}"
 
@@ -284,7 +303,7 @@ def get_configuration(eos_host: str, eos_port: Union[str, int]) -> list[dict]:
         warning_msg = f"Can not retrieve configuration from {server}: {e}, {detail}"
         logger.warning(warning_msg)
 
-    return configuration(ConfigEOS, config)
+    return config
 
 
 def ConfigPlanesCard(
@@ -341,7 +360,7 @@ def ConfigPlanesCard(
     # Create cards for all planes
     rows = []
     for i in range(0, max_planes):
-        plane_config = configuration(
+        plane_config = create_config_details(
             PVForecastPlaneSetting(),
             eos_planes_config,
             values_prefix=["pvforecast", "planes", str(i)],
@@ -352,10 +371,12 @@ def ConfigPlanesCard(
             plane_value = json.dumps(eos_planes[i])
         else:
             plane_value = json.dumps(None)
-        for config in plane_config:
+        for config_key in sorted(plane_config.keys()):
+            config = plane_config[config_key]
             update_error = config_update_latest.get(config["name"], {}).get("error")  # type: ignore
             update_value = config_update_latest.get(config["name"], {}).get("value")  # type: ignore
             update_open = config_update_latest.get(config["name"], {}).get("open")  # type: ignore
+            update_form_factory = None
             if update_open:
                 planes_update_open = True
                 plane_update_open = True
@@ -368,6 +389,12 @@ def ConfigPlanesCard(
                 error_msg = "update_error or update_value or update_open of wrong type."
                 logger.error(error_msg)
                 raise TypeError(error_msg)
+            if config["name"].endswith("pvtechchoice"):
+                update_form_factory = make_config_update_value_form(
+                    ["crystSi", "CIS", "CdTe", "Unknown"]
+                )
+            elif config["name"].endswith("mountingplace"):
+                update_form_factory = make_config_update_value_form(["free", "building"])
             plane_rows.append(
                 ConfigCard(
                     config["name"],
@@ -380,6 +407,7 @@ def ConfigPlanesCard(
                     update_error,
                     update_value,
                     update_open,
+                    update_form_factory,
                 )
             )
         rows.append(
@@ -396,7 +424,7 @@ def ConfigPlanesCard(
                                     P(read_only),
                                 ),
                             ),
-                            P(plane_value),
+                            JsonView(json.loads(plane_value)),
                         ),
                         cls="list-none",
                     ),
@@ -421,12 +449,12 @@ def ConfigPlanesCard(
                             P(read_only),
                         ),
                     ),
-                    P(value),
+                    JsonView(json.loads(value)),
                 ),
                 cls="list-none",
             ),
             Grid(
-                P(description),
+                TextView(description),
                 P(config_type),
             ),
             # Default
@@ -441,9 +469,10 @@ def ConfigPlanesCard(
                 DivRAligned(P("update")),
                 Grid(
                     Form(
+                        Input(value="update", type="hidden", id="action"),
                         Input(value=config_name, type="hidden", id="key"),
                         Input(value=planes_update_value, type="text", id="value"),
-                        hx_put="/eosdash/configuration",
+                        hx_put=request_url_for("/eosdash/configuration"),
                         hx_target="#page-content",
                         hx_swap="innerHTML",
                     ),
@@ -454,7 +483,7 @@ def ConfigPlanesCard(
             # Last error
             Grid(
                 DivRAligned(P("update error")),
-                P(planes_update_error),
+                TextView(planes_update_error),
             )
             if planes_update_error
             else None,
@@ -468,33 +497,150 @@ def ConfigPlanesCard(
 
 
 def Configuration(
-    eos_host: str, eos_port: Union[str, int], configuration: Optional[list[dict]] = None
+    eos_host: str,
+    eos_port: Union[str, int],
+    data: Optional[dict] = None,
 ) -> Div:
     """Create a visual representation of the configuration.
 
     Args:
         eos_host (str): The hostname of the EOS server.
         eos_port (Union[str, int]): The port of the EOS server.
-        configuration (Optional[list[dict]]): Optional configuration. If not provided it will be
-            retrievd from EOS.
+        data (Optional[dict], optional): Incoming data to trigger config actions. Defaults to None.
 
     Returns:
         rows:  Rows of configuration details.
     """
-    if not configuration:
-        configuration = get_configuration(eos_host, eos_port)
+    global config_visible
+    dark = False
+
+    if data and data.get("action", None):
+        if data.get("dark", None) == "true":
+            dark = True
+        if data["action"] == "visible":
+            renderer = data.get("renderer", None)
+            if renderer:
+                config_visible[renderer]["visible"] = bool(data.get(f"{renderer}-visible", False))
+        elif data["action"] == "update":
+            # This data contains a new value for key
+            key = data["key"]
+            value_json_str: str = data.get("value", "")
+            try:
+                value = json.loads(value_json_str)
+            except:
+                if value_json_str in ("None", "none", "Null", "null"):
+                    value = None
+                else:
+                    value = value_json_str
+
+            error = None
+            config = None
+            try:
+                server = f"http://{eos_host}:{eos_port}"
+                path = key.replace(".", "/")
+                response = requests.put(f"{server}/v1/config/{path}", json=value, timeout=10)
+                response.raise_for_status()
+                config = response.json()
+            except requests.exceptions.HTTPError as err:
+                try:
+                    # Try to get 'detail' from the JSON response
+                    detail = response.json().get(
+                        "detail", f"No error details for value '{value}' '{response.text}'"
+                    )
+                except ValueError:
+                    # Response is not JSON
+                    detail = f"No error details for value '{value}' '{response.text}'"
+                error = f"Can not set {key} on {server}: {err}, {detail}"
+            # Mark all updates as closed
+            for k in config_update_latest:
+                config_update_latest[k]["open"] = False
+            # Remember this update as latest one
+            config_update_latest[key] = {
+                "error": error,
+                "result": config,
+                "value": value_json_str,
+                "open": True,
+            }
+
+    # (Re-)read configuration details to be shure we display actual data
+    config = get_config(eos_host, eos_port)
+
+    # Process configuration data
+    config_details = create_config_details(ConfigEOS, config)
+
+    ConfigMenu = Card(
+        # CheckboxGroup to toggle config data visibility
+        Grid(
+            *[
+                LabelCheckboxX(
+                    label=config_visible[renderer]["label"],
+                    id=f"{renderer}-visible",
+                    name=f"{renderer}-visible",
+                    value="true",
+                    checked=config_visible[renderer]["visible"],
+                    hx_post=request_url_for("/eosdash/configuration"),
+                    hx_target="#page-content",
+                    hx_swap="innerHTML",
+                    hx_vals='js:{ "action": "visible", "renderer": '
+                    + '"'
+                    + f"{renderer}"
+                    + '", '
+                    + '"dark": window.matchMedia("(prefers-color-scheme: dark)").matches '
+                    + "}",
+                    # lbl_cls=f"text-{solution_color[renderer]}",
+                )
+                for renderer in list(config_visible.keys())
+            ],
+            cols=4,
+        ),
+        header=CardTitle("Choose What's Shown"),
+    )
+
     rows = []
     last_category = ""
     # find some special configuration values
-    max_planes = 0
-    for config in configuration:
-        if config["name"] == "pvforecast.max_planes":
-            try:
-                max_planes = int(config["value"])
-            except:
-                max_planes = 0
+    try:
+        max_planes = int(config_details["pvforecast.max_planes"]["value"])
+    except:
+        max_planes = 0
+    logger.debug(f"max_planes: {max_planes}")
+
+    try:
+        homeassistant_entity_ids = json.loads(
+            config_details["adapter.homeassistant.homeassistant_entity_ids"]["value"]
+        )
+    except:
+        homeassistant_entity_ids = []
+    logger.debug(f"homeassistant_entity_ids: {homeassistant_entity_ids}")
+
+    eos_solution_entity_ids = []
+    try:
+        eos_solution_entity_ids = json.loads(
+            config_details["adapter.homeassistant.eos_solution_entity_ids"]["value"]
+        )
+    except:
+        eos_solution_entity_ids = []
+    logger.debug(f"eos_solution_entity_ids {eos_solution_entity_ids}")
+
+    eos_device_instruction_entity_ids = []
+    try:
+        eos_device_instruction_entity_ids = json.loads(
+            config_details["adapter.homeassistant.eos_device_instruction_entity_ids"]["value"]
+        )
+    except:
+        eos_device_instruction_entity_ids = []
+    logger.debug(f"eos_device_instruction_entity_ids {eos_device_instruction_entity_ids}")
+
+    devices_measurement_keys = []
+    try:
+        devices_measurement_keys = json.loads(config_details["devices.measurement_keys"]["value"])
+    except:
+        devices_measurement_keys = []
+    logger.debug(f"devices_measurement_keys {devices_measurement_keys}")
+
     # build visual representation
-    for config in configuration:
+    for config_key in sorted(config_details.keys()):
+        config = config_details[config_key]
         category = config["name"].split(".")[0]
         if category != last_category:
             rows.append(H3(category))
@@ -512,6 +658,12 @@ def Configuration(
             error_msg = "update_error or update_value or update_open of wrong type."
             logger.error(error_msg)
             raise TypeError(error_msg)
+        if (
+            not config_visible["config-visible-read-only"]["visible"]
+            and config["read-only"] != "rw"
+        ):
+            # Do not display read only values
+            continue
         if (
             config["type"]
             == "Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]]"
@@ -532,7 +684,47 @@ def Configuration(
                     update_open,
                 )
             )
-        else:
+        elif not config["deprecated"]:
+            update_form_factory = None
+            if config["name"].endswith(".provider"):
+                # Special configuration for prediction provider setting
+                try:
+                    provider_ids = json.loads(config_details[config["name"] + "s"]["value"])
+                except:
+                    provider_ids = []
+                if config["type"].startswith("Optional[list"):
+                    update_form_factory = make_config_update_list_form(provider_ids)
+                else:
+                    provider_ids.append("None")
+                    update_form_factory = make_config_update_value_form(provider_ids)
+            elif config["name"].startswith("adapter.homeassistant.config_entity_ids"):
+                # Home Assistant adapter config entities
+                update_form_factory = make_config_update_map_form(None, homeassistant_entity_ids)
+            elif config["name"].startswith("adapter.homeassistant.load_emr_entity_ids"):
+                # Home Assistant adapter load energy meter readings entities
+                update_form_factory = make_config_update_list_form(homeassistant_entity_ids)
+            elif config["name"].startswith("adapter.homeassistant.pv_production_emr_entity_ids"):
+                # Home Assistant adapter pv energy meter readings entities
+                update_form_factory = make_config_update_list_form(homeassistant_entity_ids)
+            elif config["name"].startswith("adapter.homeassistant.device_measurement_entity_ids"):
+                # Home Assistant adapter device measurement entities
+                update_form_factory = make_config_update_map_form(
+                    devices_measurement_keys, homeassistant_entity_ids
+                )
+            elif config["name"].startswith("adapter.homeassistant.device_instruction_entity_ids"):
+                # Home Assistant adapter device instruction entities
+                update_form_factory = make_config_update_list_form(
+                    eos_device_instruction_entity_ids
+                )
+            elif config["name"].startswith("adapter.homeassistant.solution_entity_ids"):
+                # Home Assistant adapter optimization solution entities
+                update_form_factory = make_config_update_list_form(eos_solution_entity_ids)
+            elif config["name"].startswith("ems.mode"):
+                #  Energy managemnt mode
+                update_form_factory = make_config_update_value_form(
+                    ["OPTIMIZATION", "PREDICTION", "None"]
+                )
+
             rows.append(
                 ConfigCard(
                     config["name"],
@@ -545,61 +737,8 @@ def Configuration(
                     update_error,
                     update_value,
                     update_open,
+                    update_form_factory,
                 )
             )
-    return Div(*rows, cls="space-y-4")
 
-
-def ConfigKeyUpdate(eos_host: str, eos_port: Union[str, int], key: str, value: str) -> P:
-    """Update configuration key and create a visual representation of the configuration.
-
-    Args:
-        eos_host (str): The hostname of the EOS server.
-        eos_port (Union[str, int]): The port of the EOS server.
-        key (str): configuration key in dot notation
-        value (str): configuration value as json string
-
-    Returns:
-        rows:  Rows of configuration details.
-    """
-    server = f"http://{eos_host}:{eos_port}"
-    path = key.replace(".", "/")
-    try:
-        data = json.loads(value)
-    except:
-        if value in ("None", "none", "Null", "null"):
-            data = None
-        else:
-            data = value
-
-    error = None
-    config = None
-    try:
-        response = requests.put(f"{server}/v1/config/{path}", json=data, timeout=10)
-        response.raise_for_status()
-        config = response.json()
-    except requests.exceptions.HTTPError as err:
-        try:
-            # Try to get 'detail' from the JSON response
-            detail = response.json().get(
-                "detail", f"No error details for data '{data}' '{response.text}'"
-            )
-        except ValueError:
-            # Response is not JSON
-            detail = f"No error details for data '{data}' '{response.text}'"
-        error = f"Can not set {key} on {server}: {err}, {detail}"
-    # Mark all updates as closed
-    for k in config_update_latest:
-        config_update_latest[k]["open"] = False
-    # Remember this update as latest one
-    config_update_latest[key] = {
-        "error": error,
-        "result": config,
-        "value": value,
-        "open": True,
-    }
-    if error or config is None:
-        # Reread configuration to be shure we display actual data
-        return Configuration(eos_host, eos_port)
-    # Use configuration already provided
-    return Configuration(eos_host, eos_port, configuration(ConfigEOS, config))
+    return Div(ConfigMenu, *rows, cls="space-y-3")
diff --git a/src/akkudoktoreos/server/dash/context.py b/src/akkudoktoreos/server/dash/context.py
new file mode 100644
index 0000000..fd6c3ef
--- /dev/null
+++ b/src/akkudoktoreos/server/dash/context.py
@@ -0,0 +1,169 @@
+import os
+from pathlib import Path
+from typing import Awaitable, Callable, Optional
+
+from loguru import logger
+from platformdirs import user_config_dir
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.requests import Request
+from starlette.responses import Response
+
+# Home assistant token, if running under Home Assistant
+HASSIO_TOKEN = os.environ.get("HASSIO_TOKEN")
+
+# Compute global root path at startup
+# Will be replaced on first request if Ingress is active
+ROOT_PATH = "/"
+
+# EOSdash path prefix
+EOSDASH_ROOT = "eosdash/"
+
+# Directory to export files to, or to import files from
+export_import_directory = (
+    Path(os.environ.get("EOS_DATA_DIR", user_config_dir("net.akkudoktor.eosdash", "akkudoktor")))
+    if not HASSIO_TOKEN
+    else Path("/data")
+)
+
+
+class IngressMiddleware(BaseHTTPMiddleware):
+    """Middleware to handle Home Assistant Ingress path prefixes.
+
+    This middleware enables FastHTML applications to work seamlessly both with
+    and without Home Assistant Ingress. When deployed as a Home Assistant add-on
+    with Ingress enabled, it automatically handles the path prefix routing.
+
+    Home Assistant Ingress proxies add-on traffic through paths like
+    `/api/hassio_ingress//`, which requires setting the application's
+    root_path for correct URL generation. This middleware detects the Ingress
+    path from the X-Ingress-Path header and configures the request scope
+    accordingly.
+
+    When running standalone (development or direct access), the middleware
+    passes requests through unchanged, allowing normal operation.
+
+    Attributes:
+        None
+
+    Examples:
+        >>> from fasthtml.common import FastHTML
+        >>> from starlette.middleware import Middleware
+        >>>
+        >>> app = FastHTML(middleware=[Middleware(IngressMiddleware)])
+        >>>
+        >>> @app.get("/")
+        >>> def home():
+        ...     return "Hello World"
+
+    Notes:
+        - All htmx and route URLs should use relative paths (e.g., "/api/data")
+        - The middleware automatically adapts to both Ingress and direct access
+        - No code changes needed when switching between deployment modes
+    """
+
+    async def dispatch(
+        self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
+    ) -> Response:
+        """Process the request and set root_path if running under Ingress.
+
+        Args:
+            request: The incoming Starlette Request object.
+            call_next: Callable to invoke the next middleware or route handler.
+
+        Returns:
+            Response: The response from the application after processing.
+
+        Note:
+            The X-Ingress-Path header is automatically added by Home Assistant
+            when proxying requests through Ingress.
+        """
+        global ROOT_PATH
+
+        # Home Assistant passes the ingress path in this header
+        # Try multiple header variations (case-insensitive)
+        ingress_path = (
+            request.headers.get("X-Ingress-Path", "")
+            or request.headers.get("x-ingress-path", "")
+            or request.headers.get("X-INGRESS-PATH", "")
+        )
+
+        # Debug logging - remove after testing
+        logger.debug(f"All headers: {dict(request.headers)}")
+        logger.debug(f"Ingress path: {ingress_path}")
+        logger.debug(f"Request path: {request.url.path}")
+
+        # Only set root_path if we have an ingress path
+        if ingress_path:
+            ROOT_PATH = ingress_path
+            request.scope["root_path"] = ingress_path
+        # Otherwise, root_path remains empty (normal operation)
+
+        response = await call_next(request)
+
+        return response
+
+
+# Helper functions
+def request_url_for(path: str, root_path: Optional[str] = None) -> str:
+    """Generate a full URL including the root_path.
+
+    Args:
+        path: Relative path **inside the app** (e.g., "eosdash/footer" or "eosdash/assets/logo.png").
+        root_path: Root path.
+
+    Returns:
+        str: Absolute URL including the root_path.
+    """
+    global ROOT_PATH, EOSDASH_ROOT
+
+    # Step 1: fallback to global root
+    if root_path is None:
+        root_path = ROOT_PATH
+
+    # Normalize root path
+    root_path = root_path.rstrip("/") + "/"
+
+    # Normalize path
+    if path.startswith(root_path):
+        # Strip root_path prefix
+        path = path[len(root_path) :]
+
+    # Remove leading / if any
+    path = path.lstrip("/")
+
+    # Strip EOSDASH_ROOT if present
+    if path.startswith(EOSDASH_ROOT):
+        path = path[len(EOSDASH_ROOT) :]
+
+    # Build final URL
+    result = root_path + EOSDASH_ROOT + path.lstrip("/")
+
+    # Normalize accidental double slashes (except leading)
+    while "//" in result[1:]:
+        result = result.replace("//", "/")
+
+    logger.debug(f"URL for path '{path}' with root path '{root_path}': '{result}'")
+
+    return result
+
+
+def safe_asset_path(filepath: str) -> Path:
+    """Return a safe filesystem path for an asset under dash/assets/.
+
+    This prevents directory traversal attacks by restricting paths to
+    the assets folder.
+
+    Args:
+        filepath (str): Relative asset path requested by the client.
+
+    Returns:
+        Path: Absolute Path object pointing to the asset file.
+
+    Raises:
+        ValueError: If the filepath attempts to traverse directories using '../'.
+    """
+    if ".." in filepath or filepath.startswith("/"):
+        raise ValueError(f"Forbidden file path: {filepath}")
+
+    asset_path = Path(__file__).parent / "dash/assets" / filepath
+    return asset_path
diff --git a/src/akkudoktoreos/server/dash/footer.py b/src/akkudoktoreos/server/dash/footer.py
index c4662a8..64defd7 100644
--- a/src/akkudoktoreos/server/dash/footer.py
+++ b/src/akkudoktoreos/server/dash/footer.py
@@ -9,8 +9,6 @@ from requests.exceptions import RequestException
 import akkudoktoreos.server.dash.eosstatus as eosstatus
 from akkudoktoreos.config.config import get_config
 
-config_eos = get_config()
-
 
 def get_alive(eos_host: str, eos_port: Union[str, int]) -> str:
     """Fetch alive information from the specified EOS server.
@@ -42,9 +40,9 @@ def get_alive(eos_host: str, eos_port: Union[str, int]) -> str:
 
 def Footer(eos_host: Optional[str], eos_port: Optional[Union[str, int]]) -> str:
     if eos_host is None:
-        eos_host = config_eos.server.host
+        eos_host = get_config().server.host
     if eos_port is None:
-        eos_port = config_eos.server.port
+        eos_port = get_config().server.port
     alive_icon = None
     if eos_host is None or eos_port is None:
         alive = "EOS server not given: {eos_host}:{eos_port}"
diff --git a/src/akkudoktoreos/server/dash/markdown.py b/src/akkudoktoreos/server/dash/markdown.py
index 24b66b3..5219fc6 100644
--- a/src/akkudoktoreos/server/dash/markdown.py
+++ b/src/akkudoktoreos/server/dash/markdown.py
@@ -1,5 +1,8 @@
 """Markdown rendering with MonsterUI HTML classes."""
 
+import base64
+import mimetypes
+from pathlib import Path
 from typing import Any, List, Optional, Union
 
 from fasthtml.common import FT, Div, NotStr
@@ -8,113 +11,138 @@ from markdown_it.renderer import RendererHTML
 from markdown_it.token import Token
 from monsterui.foundations import stringify
 
+# Where to find the static data assets
+ASSETS_DIR = Path(__file__).parent / "assets"
+
+ASSETS_PREFIX = "/eosdash/assets/"
+IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico"}
+
+
+def file_to_data_uri(file_path: Path) -> str:
+    """Convert a file to a data URI.
+
+    Args:
+        file_path: Path to the file to convert.
+
+    Returns:
+        str: Data URI string with format data:mime/type;base64,encoded_data
+    """
+    ext = file_path.suffix.lower()
+
+    # Determine MIME type
+    mime, _ = mimetypes.guess_type(str(file_path))
+    if mime is None:
+        mime = f"image/{ext.lstrip('.')}"
+
+    # Read file as bytes and encode to base64
+    raw = file_path.read_bytes()
+    encoded = base64.b64encode(raw).decode("ascii")
+
+    return f"data:{mime};base64,{encoded}"
+
 
 def render_heading(
     self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict
 ) -> str:
-    """Custom renderer for Markdown headings.
-
-    Adds specific CSS classes based on the heading level.
-
-    Parameters:
-        self: The renderer instance.
-        tokens: List of tokens to be rendered.
-        idx: Index of the current token.
-        options: Rendering options.
-        env: Environment sandbox for plugins.
-
-    Returns:
-        The rendered token as a string.
-    """
+    """Custom renderer for Markdown headings with MonsterUI styling."""
     if tokens[idx].markup == "#":
-        tokens[idx].attrSet("class", "uk-heading-divider uk-h1 uk-margin")
+        tokens[idx].attrSet(
+            "class",
+            "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl mt-8 mb-4 border-b pb-2",
+        )
     elif tokens[idx].markup == "##":
-        tokens[idx].attrSet("class", "uk-heading-divider uk-h2 uk-margin")
+        tokens[idx].attrSet(
+            "class", "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight mt-6 mb-3"
+        )
     elif tokens[idx].markup == "###":
-        tokens[idx].attrSet("class", "uk-heading-divider uk-h3 uk-margin")
+        tokens[idx].attrSet("class", "scroll-m-20 text-2xl font-semibold tracking-tight mt-5 mb-2")
     elif tokens[idx].markup == "####":
-        tokens[idx].attrSet("class", "uk-heading-divider uk-h4 uk-margin")
+        tokens[idx].attrSet("class", "scroll-m-20 text-xl font-semibold tracking-tight mt-4 mb-2")
 
-    # pass token to default renderer.
     return self.renderToken(tokens, idx, options, env)
 
 
 def render_paragraph(
     self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict
 ) -> str:
-    """Custom renderer for Markdown paragraphs.
-
-    Adds specific CSS classes.
-
-    Parameters:
-        self: The renderer instance.
-        tokens: List of tokens to be rendered.
-        idx: Index of the current token.
-        options: Rendering options.
-        env: Environment sandbox for plugins.
-
-    Returns:
-        The rendered token as a string.
-    """
-    tokens[idx].attrSet("class", "uk-paragraph")
-
-    # pass token to default renderer.
+    """Custom renderer for Markdown paragraphs with MonsterUI styling."""
+    tokens[idx].attrSet("class", "leading-7 [&:not(:first-child)]:mt-6")
     return self.renderToken(tokens, idx, options, env)
 
 
 def render_blockquote(
     self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict
 ) -> str:
-    """Custom renderer for Markdown blockquotes.
+    """Custom renderer for Markdown blockquotes with MonsterUI styling."""
+    tokens[idx].attrSet("class", "mt-6 border-l-2 pl-6 italic border-primary")
+    return self.renderToken(tokens, idx, options, env)
 
-    Adds specific CSS classes.
 
-    Parameters:
-        self: The renderer instance.
-        tokens: List of tokens to be rendered.
-        idx: Index of the current token.
-        options: Rendering options.
-        env: Environment sandbox for plugins.
+def render_list(self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict) -> str:
+    """Custom renderer for lists with MonsterUI styling."""
+    tokens[idx].attrSet("class", "my-6 ml-6 list-disc [&>li]:mt-2")
+    return self.renderToken(tokens, idx, options, env)
 
-    Returns:
-        The rendered token as a string.
-    """
-    tokens[idx].attrSet("class", "uk-blockquote")
 
-    # pass token to default renderer.
+def render_image(
+    self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict
+) -> str:
+    """Custom renderer for Markdown images with MonsterUI styling."""
+    token = tokens[idx]
+    src = token.attrGet("src")
+    alt = token.content or ""
+
+    if src:
+        pos = src.find(ASSETS_PREFIX)
+        if pos != -1:
+            asset_rel = src[pos + len(ASSETS_PREFIX) :]
+            fs_path = ASSETS_DIR / asset_rel
+
+            if fs_path.exists():
+                data_uri = file_to_data_uri(fs_path)
+                token.attrSet("src", data_uri)
+                # MonsterUI/shadcn styling for images
+                token.attrSet("class", "rounded-lg border my-6 max-w-full h-auto")
+
     return self.renderToken(tokens, idx, options, env)
 
 
 def render_link(self: RendererHTML, tokens: List[Token], idx: int, options: dict, env: dict) -> str:
-    """Custom renderer for Markdown links.
+    """Custom renderer for Markdown links with MonsterUI styling."""
+    token = tokens[idx]
+    href = token.attrGet("href")
 
-    Adds the target attribute to open links in a new tab.
+    if href:
+        pos = href.find(ASSETS_PREFIX)
+        if pos != -1:
+            asset_rel = href[pos + len(ASSETS_PREFIX) :]
+            key = asset_rel.rsplit(".", 1)[0]
+            if key in env:
+                return str(env[key])
 
-    Parameters:
-        self: The renderer instance.
-        tokens: List of tokens to be rendered.
-        idx: Index of the current token.
-        options: Rendering options.
-        env: Environment sandbox for plugins.
-
-    Returns:
-        The rendered token as a string.
-    """
-    tokens[idx].attrSet("class", "uk-link")
-    tokens[idx].attrSet("target", "_blank")
-
-    # pass token to default renderer.
+    # MonsterUI link styling
+    token.attrSet(
+        "class", "font-medium text-primary underline underline-offset-4 hover:text-primary/80"
+    )
+    token.attrSet("target", "_blank")
     return self.renderToken(tokens, idx, options, env)
 
 
+# Register all renderers
 markdown = MarkdownIt("gfm-like")
 markdown.add_render_rule("heading_open", render_heading)
 markdown.add_render_rule("paragraph_open", render_paragraph)
 markdown.add_render_rule("blockquote_open", render_blockquote)
 markdown.add_render_rule("link_open", render_link)
+markdown.add_render_rule("image", render_image)
+markdown.add_render_rule("bullet_list_open", render_list)
+markdown.add_render_rule("ordered_list_open", render_list)
 
 
-markdown_cls = "bg-background text-lg ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
+# Updated wrapper class to match shadcn/ui theme
+markdown_cls = "text-foreground space-y-4"
+
+# markdown_cls = "bg-background text-lg ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
 
 
 def Markdown(*c: Any, cls: Optional[Union[str, tuple]] = None, **kwargs: Any) -> FT:
diff --git a/src/akkudoktoreos/server/dash/plan.py b/src/akkudoktoreos/server/dash/plan.py
index be6eacc..a488d67 100644
--- a/src/akkudoktoreos/server/dash/plan.py
+++ b/src/akkudoktoreos/server/dash/plan.py
@@ -29,6 +29,7 @@ from akkudoktoreos.core.emplan import (
 from akkudoktoreos.optimization.optimization import OptimizationSolution
 from akkudoktoreos.server.dash.bokeh import Bokeh, bokey_apply_theme_to_plot
 from akkudoktoreos.server.dash.components import Error
+from akkudoktoreos.server.dash.context import request_url_for
 from akkudoktoreos.utils.datetimeutil import compare_datetimes, to_datetime
 
 # bar width for 1 hour bars (time given in millseconds)
@@ -385,7 +386,7 @@ def SolutionCard(solution: OptimizationSolution, config: SettingsEOS, data: Opti
                         name=f"{renderer}-visible",
                         value="true",
                         checked=solution_visible[renderer],
-                        hx_post="/eosdash/plan",
+                        hx_post=request_url_for("/eosdash/plan"),
                         hx_target="#page-content",
                         hx_swap="innerHTML",
                         hx_vals='js:{ "category": "solution", "action": "visible", "renderer": '
@@ -412,7 +413,7 @@ def SolutionCard(solution: OptimizationSolution, config: SettingsEOS, data: Opti
                         name=f"{renderer}-visible",
                         value="true",
                         checked=solution_visible[renderer],
-                        hx_post="/eosdash/plan",
+                        hx_post=request_url_for("/eosdash/plan"),
                         hx_target="#page-content",
                         hx_swap="innerHTML",
                         hx_vals='js:{ "category": "solution", "action": "visible", "renderer": '
@@ -439,7 +440,7 @@ def SolutionCard(solution: OptimizationSolution, config: SettingsEOS, data: Opti
                         name=f"{renderer}-visible",
                         value="true",
                         checked=solution_visible[renderer],
-                        hx_post="/eosdash/plan",
+                        hx_post=request_url_for("/eosdash/plan"),
                         hx_target="#page-content",
                         hx_swap="innerHTML",
                         hx_vals='js:{ "category": "solution", "action": "visible", "renderer": '
@@ -595,7 +596,7 @@ def Plan(eos_host: str, eos_port: Union[str, int], data: Optional[dict] = None)
             result.raise_for_status()
         except requests.exceptions.HTTPError as err:
             detail = result.json()["detail"]
-            return Error(f"Can not retrieve configuration from {server}: {err}, {detail}")
+            return Error(f"Can not retrieve configuration from {server}: {err},\n{detail}")
         eosstatus.eos_config = SettingsEOS(**result.json())
 
         # Get the optimization solution
@@ -607,7 +608,7 @@ def Plan(eos_host: str, eos_port: Union[str, int], data: Optional[dict] = None)
             solution_json = result.json()
         except requests.exceptions.HTTPError as e:
             detail = result.json()["detail"]
-            warning_msg = f"Can not retrieve optimization solution from {server}: {e}, {detail}"
+            warning_msg = f"Can not retrieve optimization solution from {server}: {e},\n{detail}"
             logger.warning(warning_msg)
             return Error(warning_msg)
         except Exception as e:
@@ -623,7 +624,7 @@ def Plan(eos_host: str, eos_port: Union[str, int], data: Optional[dict] = None)
             plan_json = result.json()
         except requests.exceptions.HTTPError as e:
             detail = result.json()["detail"]
-            warning_msg = f"Can not retrieve plan from {server}: {e}, {detail}"
+            warning_msg = f"Can not retrieve plan from {server}: {e},\n{detail}"
             logger.warning(warning_msg)
             return Error(warning_msg)
         except Exception as e:
diff --git a/src/akkudoktoreos/server/eos.py b/src/akkudoktoreos/server/eos.py
index 9c142c9..26dfe2d 100755
--- a/src/akkudoktoreos/server/eos.py
+++ b/src/akkudoktoreos/server/eos.py
@@ -9,7 +9,6 @@ import subprocess
 import sys
 import traceback
 from contextlib import asynccontextmanager
-from pathlib import Path
 from typing import Annotated, Any, AsyncGenerator, Dict, List, Optional, Union
 
 import psutil
@@ -33,7 +32,7 @@ from akkudoktoreos.core.emplan import EnergyManagementPlan, ResourceStatus
 from akkudoktoreos.core.ems import get_ems
 from akkudoktoreos.core.emsettings import EnergyManagementMode
 from akkudoktoreos.core.logabc import LOGGING_LEVELS
-from akkudoktoreos.core.logging import read_file_log, track_logging_config
+from akkudoktoreos.core.logging import logging_track_config, read_file_log
 from akkudoktoreos.core.pydantic import (
     PydanticBaseModel,
     PydanticDateTimeData,
@@ -54,11 +53,13 @@ from akkudoktoreos.prediction.loadakkudoktor import LoadAkkudoktorCommonSettings
 from akkudoktoreos.prediction.prediction import get_prediction
 from akkudoktoreos.prediction.pvforecast import PVForecastCommonSettings
 from akkudoktoreos.server.rest.error import create_error_page
+from akkudoktoreos.server.rest.starteosdash import run_eosdash_supervisor
 from akkudoktoreos.server.rest.tasks import repeat_every
 from akkudoktoreos.server.server import (
+    drop_root_privileges,
+    fix_data_directories_permissions,
     get_default_host,
     get_host_ip,
-    validate_ip_or_hostname,
     wait_for_port_free,
 )
 from akkudoktoreos.utils.datetimeutil import to_datetime, to_duration
@@ -70,15 +71,18 @@ prediction_eos = get_prediction()
 ems_eos = get_ems()
 resource_registry_eos = get_resource_registry()
 
-
 # ------------------------------------
 # Logging configuration at import time
 # ------------------------------------
 
 logger.remove()
-track_logging_config(config_eos, "logging", None, None)
-config_eos.track_nested_value("/logging", track_logging_config)
+logging_track_config(config_eos, "logging", None, None)
 
+# -----------------------------
+# Configuration change tracking
+# -----------------------------
+
+config_eos.track_nested_value("/logging", logging_track_config)
 
 # ----------------------------
 # Safe argparse at import time
@@ -114,6 +118,11 @@ parser.add_argument(
     default=None,
     help="Enable or disable automatic EOSdash startup. Options: True or False (default: value from config)",
 )
+parser.add_argument(
+    "--run_as_user",
+    type=str,
+    help="The unprivileged user account the EOS server shall switch to after performing root-level startup tasks.",
+)
 
 # Command line arguments
 args: argparse.Namespace
@@ -137,7 +146,7 @@ if args and args.log_level is not None:
     # Ensure log_level from command line is in config settings
     if log_level in LOGGING_LEVELS:
         # Setup console logging level using nested value
-        # - triggers logging configuration by track_logging_config
+        # - triggers logging configuration by logging_track_config
         config_eos.set_nested_value("logging/console_level", log_level)
         logger.debug(f"logging/console_level configuration set by argument to {log_level}")
 
@@ -188,105 +197,6 @@ if config_eos.server.startup_eosdash:
         config_eos.set_nested_value("server/eosdash_port", port + 1)
 
 
-# ----------------------
-# EOSdash server startup
-# ----------------------
-
-
-def start_eosdash(
-    host: str,
-    port: int,
-    eos_host: str,
-    eos_port: int,
-    log_level: str,
-    access_log: bool,
-    reload: bool,
-    eos_dir: str,
-    eos_config_dir: str,
-) -> subprocess.Popen:
-    """Start the EOSdash server as a subprocess.
-
-    This function starts the EOSdash server by launching it as a subprocess. It checks if the server
-    is already running on the specified port and either returns the existing process or starts a new
-    one.
-
-    Args:
-        host (str): The hostname for the EOSdash server.
-        port (int): The port for the EOSdash server.
-        eos_host (str): The hostname for the EOS server.
-        eos_port (int): The port for the EOS server.
-        log_level (str): The logging level for the EOSdash server.
-        access_log (bool): Flag to enable or disable access logging.
-        reload (bool): Flag to enable or disable auto-reloading.
-        eos_dir (str): Path to the EOS data directory.
-        eos_config_dir (str): Path to the EOS configuration directory.
-
-    Returns:
-        subprocess.Popen: The process of the EOSdash server.
-
-    Raises:
-        RuntimeError: If the EOSdash server fails to start.
-    """
-    try:
-        validate_ip_or_hostname(host)
-        validate_ip_or_hostname(eos_host)
-    except Exception as ex:
-        error_msg = f"Could not start EOSdash: {ex}"
-        logger.error(error_msg)
-        raise RuntimeError(error_msg)
-
-    eosdash_path = Path(__file__).parent.resolve().joinpath("eosdash.py")
-
-    # Do a one time check for port free to generate warnings if not so
-    wait_for_port_free(port, timeout=0, waiting_app_name="EOSdash")
-
-    cmd = [
-        sys.executable,
-        "-m",
-        "akkudoktoreos.server.eosdash",
-        "--host",
-        str(host),
-        "--port",
-        str(port),
-        "--eos-host",
-        str(eos_host),
-        "--eos-port",
-        str(eos_port),
-        "--log_level",
-        log_level,
-        "--access_log",
-        str(access_log),
-        "--reload",
-        str(reload),
-    ]
-    # Set environment before any subprocess run, to keep custom config dir
-    env = os.environ.copy()
-    env["EOS_DIR"] = eos_dir
-    env["EOS_CONFIG_DIR"] = eos_config_dir
-
-    try:
-        server_process = subprocess.Popen(  # noqa: S603
-            cmd,
-            env=env,
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            start_new_session=True,
-        )
-        logger.info(f"Started EOSdash with '{cmd}'.")
-    except subprocess.CalledProcessError as ex:
-        error_msg = f"Could not start EOSdash: {ex}"
-        logger.error(error_msg)
-        raise RuntimeError(error_msg)
-
-    # Check EOSdash is still running
-    if server_process.poll() is not None:
-        error_msg = f"EOSdash finished immediatedly with code: {server_process.returncode}"
-        logger.error(error_msg)
-        raise RuntimeError(error_msg)
-
-    return server_process
-
-
 # ----------------------
 # EOS REST Server
 # ----------------------
@@ -389,41 +299,7 @@ async def server_shutdown_task() -> None:
 async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
     """Lifespan manager for the app."""
     # On startup
-    if config_eos.server.startup_eosdash:
-        try:
-            if (
-                config_eos.server.eosdash_host is None
-                or config_eos.server.eosdash_port is None
-                or config_eos.server.host is None
-                or config_eos.server.port is None
-            ):
-                raise ValueError(
-                    f"Invalid configuration for EOSdash server startup.\n"
-                    f"- server/startup_eosdash: {config_eos.server.startup_eosdash}\n"
-                    f"- server/eosdash_host: {config_eos.server.eosdash_host}\n"
-                    f"- server/eosdash_port: {config_eos.server.eosdash_port}\n"
-                    f"- server/host: {config_eos.server.host}\n"
-                    f"- server/port: {config_eos.server.port}"
-                )
-
-            log_level = (
-                config_eos.logging.console_level if config_eos.logging.console_level else "info"
-            )
-
-            eosdash_process = start_eosdash(
-                host=str(config_eos.server.eosdash_host),
-                port=config_eos.server.eosdash_port,
-                eos_host=str(config_eos.server.host),
-                eos_port=config_eos.server.port,
-                log_level=log_level,
-                access_log=True,
-                reload=False,
-                eos_dir=str(config_eos.general.data_folder_path),
-                eos_config_dir=str(config_eos.general.config_folder_path),
-            )
-        except Exception as e:
-            logger.error(f"Failed to start EOSdash server. Error: {e}")
-            sys.exit(1)
+    asyncio.create_task(run_eosdash_supervisor())
 
     load_eos_state()
 
@@ -606,7 +482,7 @@ async def fastapi_admin_server_shutdown_post() -> dict:
     }
 
 
-@app.get("/v1/health")
+@app.get("/v1/health", tags=["health"])
 def fastapi_health_get():  # type: ignore
     """Health check endpoint to verify that the EOS server is alive."""
     return JSONResponse(
@@ -1190,7 +1066,7 @@ def fastapi_energy_management_optimization_solution_get() -> OptimizationSolutio
     if solution is None:
         raise HTTPException(
             status_code=404,
-            detail="Can not get the optimization solution. Did you configure automatic optimization?",
+            detail="Can not get the optimization solution.\nDid you configure automatic optimization?",
         )
     return solution
 
@@ -1202,7 +1078,7 @@ def fastapi_energy_management_plan_get() -> EnergyManagementPlan:
     if plan is None:
         raise HTTPException(
             status_code=404,
-            detail="Can not get the energy management plan. Did you configure automatic optimization?",
+            detail="Can not get the energy management plan.\nDid you configure automatic optimization?",
         )
     return plan
 
@@ -1256,7 +1132,7 @@ async def fastapi_strompreis() -> list[float]:
     except Exception as e:
         raise HTTPException(
             status_code=404,
-            detail=f"Can not get the electricity price forecast: {e}. Did you configure the electricity price forecast provider?",
+            detail=f"Can not get the electricity price forecast: {e}.\nDid you configure the electricity price forecast provider?",
         )
 
     return elecprice
@@ -1360,7 +1236,7 @@ async def fastapi_gesamtlast(request: GesamtlastRequest) -> list[float]:
     except Exception as e:
         raise HTTPException(
             status_code=404,
-            detail=f"Can not get the total load forecast: {e}. Did you configure the load forecast provider?",
+            detail=f"Can not get the total load forecast: {e}.\nDid you configure the load forecast provider?",
         )
 
     return prediction_list
@@ -1421,7 +1297,7 @@ async def fastapi_gesamtlast_simple(year_energy: float) -> list[float]:
     except Exception as e:
         raise HTTPException(
             status_code=404,
-            detail=f"Can not get the total load forecast: {e}. Did you configure the load forecast provider?",
+            detail=f"Can not get the total load forecast: {e}.\nDid you configure the load forecast provider?",
         )
 
     return prediction_list
@@ -1616,6 +1492,17 @@ def run_eos() -> None:
     Returns:
         None
     """
+    if args:
+        run_as_user = args.run_as_user
+    else:
+        run_as_user = None
+
+    # Switch data directories ownership to user
+    fix_data_directories_permissions(run_as_user=run_as_user)
+
+    # Switch privileges to run_as_user
+    drop_root_privileges(run_as_user=run_as_user)
+
     # Wait for EOS port to be free - e.g. in case of restart
     wait_for_port_free(port, timeout=120, waiting_app_name="EOS")
 
@@ -1628,6 +1515,8 @@ def run_eos() -> None:
             log_level="info",  # Fix log level for uvicorn to info
             access_log=True,  # Fix server access logging to True
             reload=reload,
+            proxy_headers=True,
+            forwarded_allow_ips="*",
         )
     except Exception as e:
         logger.exception("Failed to start uvicorn server.")
diff --git a/src/akkudoktoreos/server/eosdash.py b/src/akkudoktoreos/server/eosdash.py
index cba8f8d..3d99d5f 100644
--- a/src/akkudoktoreos/server/eosdash.py
+++ b/src/akkudoktoreos/server/eosdash.py
@@ -6,25 +6,37 @@ from pathlib import Path
 
 import psutil
 import uvicorn
-from fasthtml.common import FileResponse, JSONResponse
+from fasthtml.common import Base, FileResponse, JSONResponse
 from loguru import logger
 from monsterui.core import FastHTML, Theme
+from starlette.middleware import Middleware
+from starlette.requests import Request
 
 from akkudoktoreos.config.config import get_config
 from akkudoktoreos.core.logabc import LOGGING_LEVELS
-from akkudoktoreos.core.logging import track_logging_config
+from akkudoktoreos.core.logging import logging_track_config
 from akkudoktoreos.core.version import __version__
-from akkudoktoreos.server.dash.about import About
 
 # Pages
+from akkudoktoreos.server.dash.about import About
 from akkudoktoreos.server.dash.admin import Admin
+
+# helpers
 from akkudoktoreos.server.dash.bokeh import BokehJS
 from akkudoktoreos.server.dash.components import Page
-from akkudoktoreos.server.dash.configuration import ConfigKeyUpdate, Configuration
+from akkudoktoreos.server.dash.configuration import Configuration
+from akkudoktoreos.server.dash.context import (
+    IngressMiddleware,
+    safe_asset_path,
+)
 from akkudoktoreos.server.dash.footer import Footer
 from akkudoktoreos.server.dash.plan import Plan
 from akkudoktoreos.server.dash.prediction import Prediction
-from akkudoktoreos.server.server import get_default_host, wait_for_port_free
+from akkudoktoreos.server.server import (
+    drop_root_privileges,
+    get_default_host,
+    wait_for_port_free,
+)
 from akkudoktoreos.utils.stringutil import str2bool
 
 config_eos = get_config()
@@ -35,8 +47,8 @@ config_eos = get_config()
 # ------------------------------------
 
 logger.remove()
-track_logging_config(config_eos, "logging", None, None)
-config_eos.track_nested_value("/logging", track_logging_config)
+logging_track_config(config_eos, "logging", None, None)
+config_eos.track_nested_value("/logging", logging_track_config)
 
 
 # ----------------------------
@@ -83,6 +95,12 @@ parser.add_argument(
     default=False,
     help="Enable or disable auto-reload. Useful for development. Options: True or False (default: False)",
 )
+parser.add_argument(
+    "--run_as_user",
+    type=str,
+    help="The unprivileged user account the EOSdash server shall run if started in root-level.",
+)
+
 
 # Command line arguments
 args: argparse.Namespace
@@ -110,7 +128,7 @@ else:
 # Ensure log_level from command line is in config settings
 if config_eosdash["log_level"] in LOGGING_LEVELS:
     # Setup console logging level using nested value
-    # - triggers logging configuration by track_logging_config
+    # - triggers logging configuration by logging_track_config
     config_eos.set_nested_value("logging/console_level", config_eosdash["log_level"])
     logger.debug(
         f"logging/console_level configuration set by argument to {config_eosdash['log_level']}"
@@ -180,9 +198,11 @@ hdrs = (
 
 # The EOSdash application
 app: FastHTML = FastHTML(
-    title="EOSdash",
-    hdrs=hdrs,
-    secret_key=os.getenv("EOS_SERVER__EOSDASH_SESSKEY"),
+    title="EOSdash",  # Default page title
+    hdrs=hdrs,  # Additional FT elements to add to 
+    # htmx=True,  # Include HTMX header?
+    middleware=[Middleware(IngressMiddleware)],
+    secret_key=os.getenv("EOS_SERVER__EOSDASH_SESSKEY"),  # Signing key for sessions
 )
 
 
@@ -199,37 +219,60 @@ def eos_server() -> tuple[str, int]:
     return config_eosdash["eos_host"], config_eosdash["eos_port"]
 
 
+# -------------------------------------------------------------------
+# Routes
+# -------------------------------------------------------------------
+
+
 @app.get("/favicon.ico")
-def get_eosdash_favicon():  # type: ignore
-    """Get favicon."""
+def get_eosdash_favicon(request: Request):  # type: ignore
+    """Get the EOSdash favicon.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+
+    Returns:
+        FileResponse: The favicon file.
+    """
     return FileResponse(path=favicon_filepath)
 
 
 @app.get("/")
-def get_eosdash():  # type: ignore
-    """Serves the main EOSdash page.
+def get_eosdash(request: Request):  # type: ignore
+    """Serve the main EOSdash page with navigation links.
+
+    Args:
+        request (Request): The incoming FastHTML request.
 
     Returns:
         Page: The main dashboard page with navigation links and footer.
     """
-    return Page(
-        None,
-        {
-            "Plan": "/eosdash/plan",
-            "Prediction": "/eosdash/prediction",
-            "Config": "/eosdash/configuration",
-            "Admin": "/eosdash/admin",
-            "About": "/eosdash/about",
-        },
-        About(),
-        Footer(*eos_server()),
-        "/eosdash/footer",
+    root_path: str = request.scope.get("root_path", "")
+
+    return (
+        Base(href=f"{root_path}/") if root_path else None,
+        Page(
+            None,
+            {
+                "Plan": "/eosdash/plan",
+                "Prediction": "/eosdash/prediction",
+                "Config": "/eosdash/configuration",
+                "Admin": "/eosdash/admin",
+                "About": "/eosdash/about",
+            },
+            About(),
+            Footer(*eos_server()),
+            "/eosdash/footer",
+        ),
     )
 
 
 @app.get("/eosdash/footer")
-def get_eosdash_footer():  # type: ignore
-    """Serves the EOSdash Foooter information.
+def get_eosdash_footer(request: Request):  # type: ignore
+    """Serve the EOSdash Footer information.
+
+    Args:
+        request (Request): The incoming FastHTML request.
 
     Returns:
         Footer: The Footer component.
@@ -238,8 +281,11 @@ def get_eosdash_footer():  # type: ignore
 
 
 @app.get("/eosdash/about")
-def get_eosdash_about():  # type: ignore
-    """Serves the EOSdash About page.
+def get_eosdash_about(request: Request):  # type: ignore
+    """Serve the EOSdash About page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
 
     Returns:
         About: The About page component.
@@ -248,8 +294,11 @@ def get_eosdash_about():  # type: ignore
 
 
 @app.get("/eosdash/admin")
-def get_eosdash_admin():  # type: ignore
-    """Serves the EOSdash Admin page.
+def get_eosdash_admin(request: Request):  # type: ignore
+    """Serve the EOSdash Admin page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
 
     Returns:
         Admin: The Admin page component.
@@ -258,10 +307,12 @@ def get_eosdash_admin():  # type: ignore
 
 
 @app.post("/eosdash/admin")
-def post_eosdash_admin(data: dict):  # type: ignore
+def post_eosdash_admin(request: Request, data: dict):  # type: ignore
     """Provide control data to the Admin page.
 
-    This endpoint is called from within the Admin page on user actions.
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): User-submitted data from the Admin page.
 
     Returns:
         Admin: The Admin page component.
@@ -270,8 +321,11 @@ def post_eosdash_admin(data: dict):  # type: ignore
 
 
 @app.get("/eosdash/configuration")
-def get_eosdash_configuration():  # type: ignore
-    """Serves the EOSdash Configuration page.
+def get_eosdash_configuration(request: Request):  # type: ignore
+    """Serve the EOSdash Configuration page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
 
     Returns:
         Configuration: The Configuration page component.
@@ -280,13 +334,40 @@ def get_eosdash_configuration():  # type: ignore
 
 
 @app.put("/eosdash/configuration")
-def put_eosdash_configuration(data: dict):  # type: ignore
-    return ConfigKeyUpdate(*eos_server(), data["key"], data["value"])
+def put_eosdash_configuration(request: Request, data: dict):  # type: ignore
+    """Update a configuration key/value pair.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): Dictionary containing 'key' and 'value' to trigger configuration update.
+
+    Returns:
+        Configuration: The Configuration page component with updated configuration.
+    """
+    return Configuration(*eos_server(), data)
+
+
+@app.post("/eosdash/configuration")
+def post_eosdash_configuration(request: Request, data: dict):  # type: ignore
+    """Provide control data to the configuration page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): User-submitted data from the configuration page.
+
+    Returns:
+        Configuration: The Configuration page component with updated configuration.
+    """
+    return Configuration(*eos_server(), data)
 
 
 @app.get("/eosdash/plan")
-def get_eosdash_plan(data: dict):  # type: ignore
-    """Serves the EOSdash Plan page.
+def get_eosdash_plan(request: Request, data: dict):  # type: ignore
+    """Serve the EOSdash Plan page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): Optional query data.
 
     Returns:
         Plan: The Plan page component.
@@ -295,10 +376,12 @@ def get_eosdash_plan(data: dict):  # type: ignore
 
 
 @app.post("/eosdash/plan")
-def post_eosdash_plan(data: dict):  # type: ignore
+def post_eosdash_plan(request: Request, data: dict):  # type: ignore
     """Provide control data to the Plan page.
 
-    This endpoint is called from within the Plan page on user actions.
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): User-submitted data from the Plan page.
 
     Returns:
         Plan: The Plan page component.
@@ -307,8 +390,12 @@ def post_eosdash_plan(data: dict):  # type: ignore
 
 
 @app.get("/eosdash/prediction")
-def get_eosdash_prediction(data: dict):  # type: ignore
-    """Serves the EOSdash Prediction page.
+def get_eosdash_prediction(request: Request, data: dict):  # type: ignore
+    """Serve the EOSdash Prediction page.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+        data (dict): Optional query data.
 
     Returns:
         Prediction: The Prediction page component.
@@ -317,8 +404,15 @@ def get_eosdash_prediction(data: dict):  # type: ignore
 
 
 @app.get("/eosdash/health")
-def get_eosdash_health():  # type: ignore
-    """Health check endpoint to verify that the EOSdash server is alive."""
+def get_eosdash_health(request: Request):  # type: ignore
+    """Health check endpoint to verify the EOSdash server is alive.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+
+    Returns:
+        JSONResponse: Server status including PID and version.
+    """
     return JSONResponse(
         {
             "status": "alive",
@@ -328,13 +422,37 @@ def get_eosdash_health():  # type: ignore
     )
 
 
-@app.get("/eosdash/assets/{fname:path}.{ext:static}")
-def get_eosdash_assets(fname: str, ext: str):  # type: ignore
-    """Get assets."""
-    asset_filepath = Path(__file__).parent.joinpath(f"dash/assets/{fname}.{ext}")
+@app.get("/eosdash/assets/{filepath:path}")
+def get_eosdash_assets(request: Request, filepath: str):  # type: ignore
+    """Serve static assets for EOSdash safely.
+
+    Args:
+        request (Request): The incoming FastHTML request.
+        filepath (str): Relative path of the asset under dash/assets/.
+
+    Returns:
+        FileResponse: The requested asset file if it exists.
+
+    Raises:
+        404: If the file does not exist.
+        403: If the file path is forbidden (directory traversal attempt).
+    """
+    try:
+        asset_filepath = safe_asset_path(filepath)
+    except ValueError:
+        return {"error": "Forbidden"}, 403
+
+    if not asset_filepath.exists() or not asset_filepath.is_file():
+        return {"error": "File not found"}, 404
+
     return FileResponse(path=asset_filepath)
 
 
+# ----------------------
+# Run the EOSdash server
+# ----------------------
+
+
 def run_eosdash() -> None:
     """Run the EOSdash server with the specified configurations.
 
@@ -348,6 +466,14 @@ def run_eosdash() -> None:
     Returns:
         None
     """
+    if args:
+        run_as_user = args.run_as_user
+    else:
+        run_as_user = None
+
+    # Drop root privileges if running as root
+    drop_root_privileges(run_as_user=run_as_user)
+
     # Wait for EOSdash port to be free - e.g. in case of restart
     wait_for_port_free(config_eosdash["eosdash_port"], timeout=120, waiting_app_name="EOSdash")
 
@@ -359,6 +485,8 @@ def run_eosdash() -> None:
             log_level=config_eosdash["log_level"].lower(),
             access_log=config_eosdash["access_log"],
             reload=config_eosdash["reload"],
+            proxy_headers=True,
+            forwarded_allow_ips="*",
         )
     except Exception as e:
         logger.error(
diff --git a/src/akkudoktoreos/server/rest/starteosdash.py b/src/akkudoktoreos/server/rest/starteosdash.py
new file mode 100644
index 0000000..8e3568d
--- /dev/null
+++ b/src/akkudoktoreos/server/rest/starteosdash.py
@@ -0,0 +1,269 @@
+import asyncio
+import os
+import re
+import sys
+from pathlib import Path
+
+from loguru import logger
+
+from akkudoktoreos.config.config import get_config
+from akkudoktoreos.server.server import (
+    validate_ip_or_hostname,
+    wait_for_port_free,
+)
+
+config_eos = get_config()
+
+
+# Loguru to HA stdout
+logger.add(sys.stdout, format="{time} | {level} | {message}", enqueue=True)
+
+
+LOG_PATTERN = re.compile(
+    r"""
+    (?:(?P^\S+\s+\S+)\s*\|\s*)?                     # Optional timestamp
+    (?PTRACE|DEBUG|INFO|WARNING|ERROR|CRITICAL)\s*\|\s* # Log level
+    (?:
+        (?P[A-Za-z0-9_\-./]+)                      # Full file path or filename
+        :
+        (?P\d+)                                         # Line number
+        \s*\|\s*
+    )?
+    (?:(?P[A-Za-z0-9_<>-]+)\s*\|\s*)?               # Optional function name
+    (?P.*)                                               # Message
+    """,
+    re.VERBOSE,
+)
+
+
+async def forward_stream(stream: asyncio.StreamReader, prefix: str = "") -> None:
+    """Continuously read log lines from a subprocess and re-log them via Loguru.
+
+    The function reads lines from an ``asyncio.StreamReader`` originating from a
+    subprocess (typically the subprocess's stdout or stderr), parses the log
+    metadata if present (log level, file path, line number, function), and
+    forwards the log entry to Loguru. If the line cannot be parsed, it is logged
+    as an ``INFO`` message with generic metadata.
+
+    Args:
+        stream (asyncio.StreamReader):
+            An asynchronous stream to read from, usually ``proc.stdout`` or
+            ``proc.stderr`` from ``asyncio.create_subprocess_exec``.
+        prefix (str, optional):
+            A string prefix added to each forwarded log line. Useful for
+            distinguishing between multiple subprocess sources.
+            Defaults to an empty string.
+
+    Notes:
+        - If the subprocess log line includes a file path (e.g.,
+          ``/app/server/main.py:42``), both ``file.name`` and ``file.path`` will
+          be set accordingly in the forwarded Loguru log entry.
+        - If metadata cannot be extracted, fallback values
+          (``subprocess.py`` and ``/subprocess/subprocess.py``) are used.
+        - The function runs until ``stream`` reaches EOF.
+
+    """
+    while True:
+        line = await stream.readline()
+        if not line:
+            break  # End of stream
+
+        raw = line.decode(errors="replace").rstrip()
+        match = LOG_PATTERN.search(raw)
+
+        if match:
+            data = match.groupdict()
+
+            level = data["level"] or "INFO"
+            message = data["msg"]
+
+            # ---- Extract file path and name ----
+            file_path = data["file_path"]
+            if file_path:
+                if "/" in file_path:
+                    file_name = file_path.rsplit("/", 1)[1]
+                else:
+                    file_name = file_path
+            else:
+                file_name = "subprocess.py"
+                file_path = f"/subprocess/{file_name}"
+
+            # ---- Extract function and line ----
+            func_name = data["function"] or ""
+            line_no = int(data["line"]) if data["line"] else 1
+
+            # ---- Patch logger with realistic metadata ----
+            patched = logger.patch(
+                lambda r: r.update(
+                    {
+                        "file": {
+                            "name": file_name,
+                            "path": file_path,
+                        },
+                        "line": line_no,
+                        "function": func_name,
+                        "name": "EOSdash",
+                    }
+                )
+            )
+
+            patched.log(level, f"{prefix}{message}")
+
+        else:
+            # Fallback: unstructured log line
+            file_name = "subprocess.py"
+            file_path = f"/subprocess/{file_name}"
+
+            logger.patch(
+                lambda r: r.update(
+                    {
+                        "file": {
+                            "name": file_name,
+                            "path": file_path,
+                        },
+                        "line": 1,
+                        "function": "",
+                        "name": "EOSdash",
+                    }
+                )
+            ).info(f"{prefix}{raw}")
+
+
+async def run_eosdash_supervisor() -> None:
+    """Starts EOSdash, pipes its logs, restarts it if it crashes.
+
+    Runs forever.
+    """
+    eosdash_path = Path(__file__).parent.resolve().joinpath("eosdash.py")
+
+    while True:
+        await asyncio.sleep(5)
+
+        if not config_eos.server.startup_eosdash:
+            continue
+
+        if (
+            config_eos.server.eosdash_host is None
+            or config_eos.server.eosdash_port is None
+            or config_eos.server.host is None
+            or config_eos.server.port is None
+        ):
+            error_msg = (
+                f"Invalid configuration for EOSdash server startup.\n"
+                f"- server/eosdash_host: {config_eos.server.eosdash_host}\n"
+                f"- server/eosdash_port: {config_eos.server.eosdash_port}\n"
+                f"- server/host: {config_eos.server.host}\n"
+                f"- server/port: {config_eos.server.port}"
+            )
+            logger.error(error_msg)
+            continue
+
+        # Get all the parameters
+        host = str(config_eos.server.eosdash_host)
+        port = config_eos.server.eosdash_port
+        eos_host = str(config_eos.server.host)
+        eos_port = config_eos.server.port
+        access_log = True
+        reload = False
+        log_level = config_eos.logging.console_level if config_eos.logging.console_level else "info"
+
+        try:
+            validate_ip_or_hostname(host)
+            validate_ip_or_hostname(eos_host)
+        except Exception as ex:
+            error_msg = f"Could not start EOSdash: {ex}"
+            logger.error(error_msg)
+            continue
+
+        if eos_host != host:
+            # EOSdash runs on a different server - we can not start.
+            error_msg = (
+                f"EOSdash server startup not possible on different hosts.\n"
+                f"- server/eosdash_host: {config_eos.server.eosdash_host}\n"
+                f"- server/host: {config_eos.server.host}"
+            )
+            logger.error(error_msg)
+            continue
+
+        # Do a one time check for port free to generate warnings if not so
+        wait_for_port_free(port, timeout=0, waiting_app_name="EOSdash")
+
+        cmd = [
+            sys.executable,
+            "-m",
+            "akkudoktoreos.server.eosdash",
+            "--host",
+            str(host),
+            "--port",
+            str(port),
+            "--eos-host",
+            str(eos_host),
+            "--eos-port",
+            str(eos_port),
+            "--log_level",
+            log_level,
+            "--access_log",
+            str(access_log),
+            "--reload",
+            str(reload),
+        ]
+        # Set environment before any subprocess run, to keep custom config dir
+        eos_dir = str(config_eos.package_root_path)
+        eos_data_dir = str(config_eos.general.data_folder_path)
+        eos_config_dir = str(config_eos.general.config_folder_path)
+        env = os.environ.copy()
+        env["EOS_DIR"] = eos_dir
+        env["EOS_DATA_DIR"] = eos_data_dir
+        env["EOS_CONFIG_DIR"] = eos_config_dir
+
+        logger.info("Starting EOSdash subprocess...")
+
+        # Start EOSdash server
+        try:
+            proc = await asyncio.create_subprocess_exec(
+                *cmd, env=env, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
+            )
+        except FileNotFoundError:
+            logger.error(
+                "Failed to start EOSdash: 'python' executable '{sys.executable}' not found."
+            )
+            continue
+        except PermissionError:
+            logger.error("Failed to start EOSdash: permission denied on 'eosdash.py'.")
+            continue
+        except asyncio.CancelledError:
+            logger.warning("EOSdash startup cancelled (shutdown?).")
+            return
+        except Exception as e:
+            logger.exception(f"Unexpected error launching EOSdash: {e}")
+            continue
+
+        if proc.stdout is None:
+            logger.error("Failed to forward EOSdash output to EOS pipe.")
+        else:
+            # Forward log
+            asyncio.create_task(forward_stream(proc.stdout, prefix="[EOSdash] "))
+
+        if proc.stderr is None:
+            logger.error("Failed to forward EOSdash error output to EOS pipe.")
+        else:
+            # Forward log
+            asyncio.create_task(forward_stream(proc.stderr, prefix="[EOSdash-ERR] "))
+
+        # If we reach here, the subprocess started successfully
+        logger.info("EOSdash subprocess started successfully.")
+
+        # Wait for exit
+        try:
+            exit_code = await proc.wait()
+            logger.error(f"EOSdash exited with code {exit_code}")
+
+        except asyncio.CancelledError:
+            logger.warning("EOSdash wait cancelled (shutdown?).")
+            return
+
+        except Exception as e:
+            logger.exception(f"Error while waiting for EOSdash to terminate: {e}")
+
+        # Restart after a delay
+        logger.info("Restarting EOSdash...")
diff --git a/src/akkudoktoreos/server/rest/tasks.py b/src/akkudoktoreos/server/rest/tasks.py
index f112136..1cd4cb1 100644
--- a/src/akkudoktoreos/server/rest/tasks.py
+++ b/src/akkudoktoreos/server/rest/tasks.py
@@ -3,10 +3,10 @@
 from __future__ import annotations
 
 import asyncio
-import logging
 from functools import wraps
 from typing import Any, Callable, Coroutine, Union
 
+import loguru
 from starlette.concurrency import run_in_threadpool
 
 NoArgsNoReturnFuncT = Callable[[], None]
@@ -37,7 +37,7 @@ def repeat_every(
     *,
     seconds: float,
     wait_first: float | None = None,
-    logger: logging.Logger | None = None,
+    logger: loguru.logger | None = None,
     raise_exceptions: bool = False,
     max_repetitions: int | None = None,
     on_complete: NoArgsNoReturnAnyFuncT | None = None,
diff --git a/src/akkudoktoreos/server/server.py b/src/akkudoktoreos/server/server.py
index a782b03..bd304e2 100644
--- a/src/akkudoktoreos/server/server.py
+++ b/src/akkudoktoreos/server/server.py
@@ -1,6 +1,9 @@
 """Server Module."""
 
+import grp
 import ipaddress
+import os
+import pwd
 import re
 import socket
 import time
@@ -148,6 +151,179 @@ def wait_for_port_free(port: int, timeout: int = 0, waiting_app_name: str = "App
     return True
 
 
+def drop_root_privileges(run_as_user: Optional[str] = None) -> bool:
+    """Drop root privileges and switch execution to a less privileged user.
+
+    This function transitions the running process from root (UID 0) to the
+    specified unprivileged user. It sets UID, GID, supplementary groups, and
+    updates environment variables to reflect the new user context.
+
+    If the process is not running as root, no privilege changes are made.
+
+    Args:
+        run_as_user (str | None):
+            The name of the target user to switch to.
+            If ``None`` (default), the current effective user is used and
+            no privilege change is attempted.
+
+    Returns:
+        bool:
+            ``True`` if privileges were successfully dropped OR the process is
+            already running as the target user.
+            ``False`` if privilege dropping failed.
+
+    Notes:
+        - This must be called very early during startup, before opening files,
+          creating sockets, or starting threads.
+        - Dropping privileges is irreversible within the same process.
+        - The target user must exist inside the container (valid entry in
+          ``/etc/passwd`` and ``/etc/group``).
+    """
+    # Determine current user
+    current_user = pwd.getpwuid(os.geteuid()).pw_name
+
+    # No action needed if already running as the desired user
+    if run_as_user is None or run_as_user == current_user:
+        return True
+
+    # Cannot switch users unless running as root
+    if os.geteuid() != 0:
+        logger.error(
+            f"Privilege switch requested to '{run_as_user}' "
+            f"but process is not root (running as '{current_user}')."
+        )
+        return False
+
+    # Resolve target user info
+    try:
+        pw_record = pwd.getpwnam(run_as_user)
+    except KeyError:
+        logger.error(f"Privilege switch failed: user '{run_as_user}' does not exist.")
+        return False
+
+    user_uid: int = pw_record.pw_uid
+    user_gid: int = pw_record.pw_gid
+
+    try:
+        # Get all groups where the user is listed as a member
+        supplementary_groups: list[int] = [
+            g.gr_gid for g in grp.getgrall() if run_as_user in g.gr_mem
+        ]
+
+        # Ensure the primary group is included (it usually is NOT in gr_mem)
+        if user_gid not in supplementary_groups:
+            supplementary_groups.append(user_gid)
+
+        # Apply groups, gid, uid (in that order)
+        os.setgroups(supplementary_groups)
+        os.setgid(user_gid)
+        os.setuid(user_uid)
+    except Exception as e:
+        logger.error(f"Privilege switch failed: {e}")
+        return False
+
+    # Update environment variables to reflect the new user identity
+    os.environ["HOME"] = pw_record.pw_dir
+    os.environ["LOGNAME"] = run_as_user
+    os.environ["USER"] = run_as_user
+
+    # Restrictive umask
+    os.umask(0o077)
+
+    # Verify that privilege drop was successful
+    if os.geteuid() != user_uid or os.getegid() != user_gid:
+        logger.error(
+            f"Privilege drop sanity check failed: now uid={os.geteuid()}, gid={os.getegid()}, "
+            f"expected uid={user_uid}, gid={user_gid}"
+        )
+        return False
+
+    logger.info(
+        f"Switched privileges to user '{run_as_user}' "
+        f"(uid={user_uid}, gid={user_gid}, groups={supplementary_groups})"
+    )
+    return True
+
+
+def fix_data_directories_permissions(run_as_user: Optional[str] = None) -> None:
+    """Ensure correct ownership for data directories.
+
+    This function recursively updates the owner and group of the data directories and all of its
+    subdirectories and files so that they belong to the given user.
+
+    The function may require root privileges to change file ownership. It logs an error message
+    if a path ownership can not be updated.
+
+    Args:
+        run_as_user (Optional[str]): The user who should own the data directories and files.
+            Defaults to current one.
+    """
+    from akkudoktoreos.config.config import get_config
+
+    config_eos = get_config()
+
+    base_dirs = [
+        config_eos.general.data_folder_path,
+        config_eos.general.data_output_path,
+        config_eos.general.config_folder_path,
+        config_eos.cache.path(),
+    ]
+
+    error_msg: Optional[str] = None
+
+    if run_as_user is None:
+        # Get current user - try to ensure current user can access the data directories
+        run_as_user = pwd.getpwuid(os.geteuid()).pw_name
+
+    try:
+        pw_record = pwd.getpwnam(run_as_user)
+    except KeyError as e:
+        error_msg = f"Data directories '{base_dirs}' permission fix failed: user '{run_as_user}' does not exist."
+        logger.error(error_msg)
+        return
+
+    uid = pw_record.pw_uid
+    gid = pw_record.pw_gid
+
+    # Walk directory tree and fix permissions
+    for base_dir in base_dirs:
+        if base_dir is None:
+            continue
+        # ensure base dir exists
+        try:
+            base_dir.mkdir(parents=True, exist_ok=True)
+        except Exception as e:
+            logger.error(f"Could not setup data dir '{base_dir}': {e}")
+            continue
+        for root, dirs, files in os.walk(base_dir):
+            for name in dirs + files:
+                path = os.path.join(root, name)
+                try:
+                    os.chown(path, uid, gid)
+                except PermissionError as e:
+                    error_msg = f"Permission denied while updating ownership of '{path}' to user '{run_as_user}'"
+                    logger.error(error_msg)
+                except Exception as e:
+                    error_msg = (
+                        f"Updating ownership failed of '{path}' to user '{run_as_user}': {e}"
+                    )
+                    logger.error(error_msg)
+        # Also fix the base directory itself
+        try:
+            os.chown(base_dir, uid, gid)
+        except PermissionError as e:
+            error_msg = (
+                f"Permission denied while updating ownership of '{path}' to user '{run_as_user}'"
+            )
+            logger.error(error_msg)
+        except Exception as e:
+            error_msg = f"Updating ownership failed of '{path}' to user '{run_as_user}': {e}"
+            logger.error(error_msg)
+
+    if error_msg is None:
+        logger.info(f"Updated ownership of '{base_dirs}' recursively to user '{run_as_user}'.")
+
+
 class ServerCommonSettings(SettingsBaseModel):
     """Server Configuration."""
 
diff --git a/src/akkudoktoreos/utils/datetimeutil.py b/src/akkudoktoreos/utils/datetimeutil.py
index 9d9e18d..d442b96 100644
--- a/src/akkudoktoreos/utils/datetimeutil.py
+++ b/src/akkudoktoreos/utils/datetimeutil.py
@@ -665,11 +665,14 @@ def to_time(
             - int (e.g. 14 → 14:00)
             - float (e.g. 14.5 → 14:30)
             - tuple like (14,), (14, 30), (14, 30, 15)
+
         in_timezone: Optional timezone name or object (e.g., "Europe/Berlin").
             Defaults to the local timezone.
+
         to_naive: If True, return a timezone-naive Time object.
+
         as_string: If True, return time as "HH:mm:ss ZZ".
-                   If a format string is provided, it's passed to `pendulum.Time.format()`.
+            If a format string is provided, it's passed to `pendulum.Time.format()`.
 
     Returns:
         Time or str: A time object or its formatted string.
@@ -1637,106 +1640,233 @@ def to_datetime(
     return dt
 
 
+# to duration helper
+def duration_to_iso8601(duration: pendulum.Duration) -> str:
+    """Convert pendulum.Duration to ISO-8601 duration string."""
+    total_seconds = int(duration.total_seconds())
+
+    days, rem = divmod(total_seconds, 86400)
+    hours, rem = divmod(rem, 3600)
+    minutes, seconds = divmod(rem, 60)
+
+    parts = ["P"]
+    if days:
+        parts.append(f"{days}D")
+
+    time_parts = []
+    if hours:
+        time_parts.append(f"{hours}H")
+    if minutes:
+        time_parts.append(f"{minutes}M")
+    if seconds:
+        time_parts.append(f"{seconds}S")
+
+    if time_parts:
+        parts.append("T")
+        parts.extend(time_parts)
+    elif len(parts) == 1:  # zero duration
+        parts.append("T0S")
+
+    return "".join(parts)
+
+
+@overload
 def to_duration(
     input_value: Union[
         Duration, datetime.timedelta, str, int, float, Tuple[int, int, int, int], List[int]
     ],
-) -> Duration:
-    """Converts various input types into a Duration object using pendulum.
+    as_string: Literal[False] | None = None,
+) -> Duration: ...
+
+
+@overload
+def to_duration(
+    input_value: Union[
+        Duration, datetime.timedelta, str, int, float, Tuple[int, int, int, int], List[int]
+    ],
+    as_string: str | Literal[True] = True,
+) -> str: ...
+
+
+def to_duration(
+    input_value: Union[
+        Duration, datetime.timedelta, str, int, float, Tuple[int, int, int, int], List[int]
+    ],
+    as_string: Optional[Union[str, bool]] = None,
+) -> Union[Duration, str]:
+    """Converts various input types into a `pendulum.Duration` or a formatted duration string.
 
     Args:
-        input_value (Union[Duration, timedelta, str, int, float, tuple, list]): Input to be converted
-            into a timedelta:
-            - str: A duration string like "2 days", "5 hours", "30 minutes", or a combination.
-            - int/float: Number representing seconds.
-            - tuple/list: A tuple or list in the format (days, hours, minutes, seconds).
+        input_value (Union[Duration, timedelta, str, int, float, tuple, list]):
+            The input value to convert into a duration.
+            Supported types include:
+
+            - `pendulum.Duration`: Returned unchanged unless formatting is requested.
+            - `datetime.timedelta`: Converted based on total seconds.
+            - `str`: A duration expression (e.g., `"15 minutes"`, `"2 hours"`),
+              or a string parsed by Pendulum.
+            - `int` or `float`: Interpreted as a number of seconds.
+            - `tuple` or `list`: Must be `(days, hours, minutes, seconds)`.
+
+        as_string (Optional[Union[str, bool]]):
+            Controls the output format of the returned duration:
+
+            - `None` or `False` (default):
+                Returns a `pendulum.Duration` object.
+            - `True`:
+                Returns an ISO-8601 duration string (e.g., `"PT15M"`).
+            - `"human"`:
+                Returns a human-readable form (e.g., `"15 minutes"`).
+            - `"pandas"`:
+                Returns a Pandas frequency string such as:
+                - `"1h"` for 1 hour
+                - `"15min"` for 15 minutes
+                - `"900s"` for 900 seconds
+            - `str`:
+                A custom format pattern. The following format tokens are supported:
+                - `{S}` → total seconds
+                - `{M}` → total minutes (integer)
+                - `{H}` → total hours (integer)
+                - `{f}` → human-friendly representation (Pendulum `in_words()`)
+
+    Example:
+                    `"Duration: {M} minutes"` → `"Duration: 15 minutes"`
 
     Returns:
-        duration: A Duration object corresponding to the input value.
+        Union[Duration, str]:
+            - A `pendulum.Duration` if no formatting is requested.
+            - A formatted string depending on the `as_string` option.
 
     Raises:
-        ValueError: If the input format is not supported.
+        ValueError:
+            - If the input type is unsupported.
+            - If a duration string cannot be parsed.
+            - If `as_string` contains an unsupported format option.
 
     Examples:
-        >>> to_duration("2 days 5 hours")
-        timedelta(days=2, seconds=18000)
+        >>> to_duration("15 minutes")
+        
 
-        >>> to_duration(3600)
-        timedelta(seconds=3600)
+        >>> to_duration("15 minutes", as_string=True)
+        'PT15M'
 
-        >>> to_duration((1, 2, 30, 15))
-        timedelta(days=1, seconds=90315)
+        >>> to_duration("15 minutes", as_string="human")
+        '15 minutes'
+
+        >>> to_duration("90 seconds", as_string="pandas")
+        '90S'
+
+        >>> to_duration("15 minutes", as_string="{M}m")
+        '15m'
     """
+    # ---- normalize to pendulum.Duration ----
+    duration = None
+
     if isinstance(input_value, Duration):
-        return input_value
+        duration = input_value
 
-    if isinstance(input_value, datetime.timedelta):
-        return pendulum.duration(seconds=input_value.total_seconds())
+    elif isinstance(input_value, datetime.timedelta):
+        duration = pendulum.duration(seconds=input_value.total_seconds())
 
-    if isinstance(input_value, (int, float)):
-        # Handle integers or floats as seconds
-        return pendulum.duration(seconds=input_value)
+    elif isinstance(input_value, (int, float)):
+        duration = pendulum.duration(seconds=input_value)
 
     elif isinstance(input_value, (tuple, list)):
-        # Handle tuple or list: (days, hours, minutes, seconds)
-        if len(input_value) == 4:
-            days, hours, minutes, seconds = input_value
-            return pendulum.duration(days=days, hours=hours, minutes=minutes, seconds=seconds)
-        else:
-            error_msg = f"Expected a tuple or list of length 4, got {len(input_value)}"
+        if len(input_value) != 4:
+            error_msg = f"Expected tuple/list length 4, got {len(input_value)}"
             logger.error(error_msg)
             raise ValueError(error_msg)
+        days, hours, minutes, seconds = input_value
+        duration = pendulum.duration(days=days, hours=hours, minutes=minutes, seconds=seconds)
 
     elif isinstance(input_value, str):
-        # Use pendulum's parsing for human-readable duration strings
+        # first try pendulum.parse
         try:
             parsed = pendulum.parse(input_value)
             if isinstance(parsed, pendulum.Duration):
-                return parsed  # Already a duration
+                duration = parsed  # Already a duration
             else:
                 # It's a DateTime, calculate duration from start of day
-                return parsed - parsed.start_of("day")
+                duration = parsed - parsed.start_of("day")
         except pendulum.parsing.exceptions.ParserError as e:
             logger.trace(f"Invalid Pendulum time string format '{input_value}': {e}")
 
-        # Handle strings like "2 days 5 hours 30 minutes"
-        total_seconds = 0
-        time_units = {
-            "day": 86400,  # 24 * 60 * 60
-            "hour": 3600,
-            "minute": 60,
-            "second": 1,
-        }
-
-        # Mitigate ReDoS vulnerability (#494) by checking input string length.
-        if len(input_value) > MAX_DURATION_STRING_LENGTH:
-            raise ValueError(
-                f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
-            )
-        # Regular expression to match time components like '2 days', '5 hours', etc.
-        matches = re.findall(r"(\d+)\s*(days?|hours?|minutes?|seconds?)", input_value)
-
-        if not matches:
-            error_msg = f"Invalid time string format '{input_value}'"
-            logger.error(error_msg)
-            raise ValueError(error_msg)
-
-        for value, unit in matches:
-            unit = unit.lower().rstrip("s")  # Normalize unit
-            if unit in time_units:
-                total_seconds += int(value) * time_units[unit]
-            else:
-                error_msg = f"Unsupported time unit: {unit}"
+            # Mitigate ReDoS vulnerability (#494) by checking input string length.
+            if len(input_value) > MAX_DURATION_STRING_LENGTH:
+                error_msg = (
+                    f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
+                )
                 logger.error(error_msg)
                 raise ValueError(error_msg)
 
-        return pendulum.duration(seconds=total_seconds)
+            # Handle strings like "2 days 5 hours 30 minutes"
+            matches = re.findall(r"(\d+)\s*(days?|hours?|minutes?|seconds?)", input_value)
+            if not matches:
+                error_msg = f"Invalid time string format '{input_value}'"
+                logger.error(error_msg)
+                raise ValueError(error_msg)
+
+            total_seconds = 0
+            time_units = {
+                "day": 86400,
+                "hour": 3600,
+                "minute": 60,
+                "second": 1,
+            }
+            for value, unit in matches:
+                unit = unit.lower().rstrip("s")  # Normalize unit
+                if unit in time_units:
+                    total_seconds += int(value) * time_units[unit]
+                else:
+                    error_msg = f"Unsupported time unit: {unit}"
+                    logger.error(error_msg)
+                    raise ValueError(error_msg)
+
+            duration = pendulum.duration(seconds=total_seconds)
 
     else:
         error_msg = f"Unsupported input type: {type(input_value)}"
         logger.error(error_msg)
         raise ValueError(error_msg)
 
+    # ---- now apply as_string rules ----
+    if not as_string:
+        return duration
+
+    total_seconds = int(duration.total_seconds())
+
+    # Boolean True → ISO-8601
+    if as_string is True:
+        return duration_to_iso8601(duration)
+
+    # Human-readable
+    if as_string == "human":
+        return duration.in_words()
+
+    # Pandas frequency
+    if as_string == "pandas":
+        # hours?
+        if total_seconds % 3600 == 0:
+            return f"{total_seconds // 3600}h"
+        # minutes?
+        if total_seconds % 60 == 0:
+            return f"{total_seconds // 60}min"
+        # else seconds (fallback)
+        return f"{total_seconds}s"
+
+    # Custom format string
+    if isinstance(as_string, str):
+        return as_string.format(
+            S=total_seconds,
+            M=total_seconds // 60,
+            H=total_seconds // 3600,
+            f=duration.in_words(),
+        )
+
+    error_msg = f"Unsupported as_string value: {as_string}"
+    logger.error(error_msg)
+    raise ValueError(error_msg)
+
 
 @overload
 def to_timezone(
diff --git a/tests/conftest.py b/tests/conftest.py
index e78dd3c..972e967 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -30,7 +30,6 @@ from akkudoktoreos.server.server import get_default_host
 # Adapt pytest logging handling to Loguru logging
 # -----------------------------------------------
 
-
 @pytest.fixture
 def caplog(caplog: LogCaptureFixture):
     """Propagate Loguru logs to the pytest caplog handler."""
@@ -430,13 +429,20 @@ def server_base(
     eos_dir = str(eos_tmp_dir.name)
 
     class Starter(ProcessStarter):
+        # Set environment for server run
+        env = os.environ.copy()
+        env["EOS_DIR"] = eos_dir
+        env["EOS_CONFIG_DIR"] = eos_dir
+        if extra_env:
+            env.update(extra_env)
+
         # assure server to be installed
         try:
             project_dir = Path(__file__).parent.parent
             subprocess.run(
                 [sys.executable, "-c", "import", "akkudoktoreos.server.eos"],
                 check=True,
-                env=os.environ,
+                env=env,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 cwd=project_dir,
@@ -444,20 +450,13 @@ def server_base(
         except subprocess.CalledProcessError:
             subprocess.run(
                 [sys.executable, "-m", "pip", "install", "-e", str(project_dir)],
-                env=os.environ,
+                env=env,
                 check=True,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 cwd=project_dir,
             )
 
-        # Set environment for server run
-        env = os.environ.copy()
-        env["EOS_DIR"] = eos_dir
-        env["EOS_CONFIG_DIR"] = eos_dir
-        if extra_env:
-            env.update(extra_env)
-
         # Set command to start server process
         args = [
             sys.executable,
@@ -487,6 +486,25 @@ def server_base(
                 logger.debug(f"[xprocess] Exception during health check: {e}")
             return False
 
+        def wait_callback(self):
+            """Assert that process is ready to answer queries using provided
+            callback funtion. Will raise TimeoutError if self.callback does not
+            return True before self.timeout seconds"""
+            from datetime import datetime
+
+            while True:
+                time.sleep(1.0)
+                if self.startup_check():
+                    return True
+                if datetime.now() > self._max_time:
+                    info = self.process.getinfo("eos")
+                    error_msg = (
+                        f"The provided startup check could not assert process responsiveness\n"
+                        f"within the specified time interval of {self.timeout} seconds.\n"
+                        f"Server log is in '{info.logpath}'.\n"
+                    )
+                    raise TimeoutError(error_msg)
+
     # Kill all running eos and eosdash process - just to be sure
     cleanup_eos_eosdash(host, port, eosdash_host, eosdash_port, server_timeout)
 
@@ -494,10 +512,12 @@ def server_base(
     config_file_path = Path(eos_dir).joinpath(ConfigEOS.CONFIG_FILE_NAME)
     with config_file_path.open(mode="w", encoding="utf-8", newline="\n") as fd:
         json.dump({}, fd)
+    logger.info(f"Created empty config file in {config_file_path}.")
 
     # ensure process is running and return its logfile
     pid, logfile = xprocess.ensure("eos", Starter)
     logger.info(f"Started EOS ({pid}). This may take very long (up to {server_timeout} seconds).")
+    logger.info(f"EOS_DIR: {Starter.env["EOS_DIR"]}, EOS_CONFIG_DIR: {Starter.env["EOS_CONFIG_DIR"]}")
     logger.info(f"View xprocess logfile at: {logfile}")
 
     yield {
@@ -509,7 +529,7 @@ def server_base(
         "timeout": server_timeout,
     }
 
-    # clean up whole process tree afterwards
+     # clean up whole process tree afterwards
     xprocess.getinfo("eos").terminate()
 
     # Cleanup any EOS process left.
diff --git a/tests/test_adapter.py b/tests/test_adapter.py
new file mode 100644
index 0000000..9d002f1
--- /dev/null
+++ b/tests/test_adapter.py
@@ -0,0 +1,79 @@
+"""
+Tests for Adapter and AdapterContainer integration.
+"""
+
+from __future__ import annotations
+
+from datetime import datetime
+from typing import TypeAlias
+
+import pytest
+
+from akkudoktoreos.adapter.adapter import (
+    Adapter,
+    AdapterCommonSettings,
+    get_adapter,
+)
+from akkudoktoreos.adapter.adapterabc import AdapterContainer
+from akkudoktoreos.adapter.homeassistant import HomeAssistantAdapter
+from akkudoktoreos.adapter.nodered import NodeREDAdapter
+
+# ---------- Typed aliases for fixtures ----------
+AdapterFixture: TypeAlias = Adapter
+SettingsFixture: TypeAlias = AdapterCommonSettings
+
+
+# ---------- Fixtures ----------
+@pytest.fixture
+def adapter() -> AdapterFixture:
+    """Fixture returning a fully initialized Adapter instance."""
+    return get_adapter()
+
+
+@pytest.fixture
+def settings() -> SettingsFixture:
+    """Fixture providing default adapter common settings."""
+    return AdapterCommonSettings()
+
+
+# ---------- Test Class ----------
+class TestAdapter:
+    def test_is_adapter_container(self, adapter: AdapterFixture) -> None:
+        """Adapter should be an AdapterContainer and an Adapter."""
+        assert isinstance(adapter, AdapterContainer)
+        assert isinstance(adapter, Adapter)
+
+    def test_providers_present(self, adapter: AdapterFixture) -> None:
+        """Adapter must contain HA and NodeRED providers."""
+        assert len(adapter.providers) == 2
+        assert any(isinstance(p, HomeAssistantAdapter) for p in adapter.providers)
+        assert any(isinstance(p, NodeREDAdapter) for p in adapter.providers)
+
+    def test_adapter_order(self, adapter: AdapterFixture) -> None:
+        """Provider order should match HomeAssistantAdapter -> NodeREDAdapter."""
+        assert isinstance(adapter.providers[0], HomeAssistantAdapter)
+        assert isinstance(adapter.providers[1], NodeREDAdapter)
+
+    # ----- AdapterCommonSettings -----
+
+    def test_settings_default_provider(self, settings: SettingsFixture) -> None:
+        """Default provider should be None."""
+        assert settings.provider is None
+
+    def test_settings_accepts_single_provider(self, settings: SettingsFixture) -> None:
+        """Settings should accept a single provider literal."""
+        settings.provider = ["HomeAssistant"]
+        assert settings.provider == ["HomeAssistant"]
+
+    def test_settings_accepts_multiple_providers(self, settings: SettingsFixture) -> None:
+        """Settings should accept multiple provider literals."""
+        settings.provider = ["HomeAssistant", "NodeRED"]
+        assert isinstance(settings.provider, list)
+        assert settings.provider == ["HomeAssistant", "NodeRED"]
+
+    def test_provider_sub_settings(self, settings: SettingsFixture) -> None:
+        """sub-settings (homeassistant & nodered) must be initialized."""
+        assert hasattr(settings, "homeassistant")
+        assert hasattr(settings, "nodered")
+        assert settings.homeassistant is not None
+        assert settings.nodered is not None
diff --git a/tests/test_adapternodered.py b/tests/test_adapternodered.py
new file mode 100644
index 0000000..115d70a
--- /dev/null
+++ b/tests/test_adapternodered.py
@@ -0,0 +1,127 @@
+from __future__ import annotations
+
+from datetime import datetime
+from unittest.mock import MagicMock, patch
+
+import pytest
+from pydantic import BaseModel
+
+from akkudoktoreos.adapter.adapter import AdapterCommonSettings
+from akkudoktoreos.adapter.nodered import NodeREDAdapter, NodeREDAdapterCommonSettings
+from akkudoktoreos.core.emplan import DDBCInstruction, FRBCInstruction
+from akkudoktoreos.core.ems import EnergyManagementStage
+from akkudoktoreos.utils.datetimeutil import DateTime, compare_datetimes, to_datetime
+
+
+@pytest.fixture
+def mock_ems() -> MagicMock:
+    m = MagicMock()
+    m.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
+    m.plan.return_value.get_active_instructions.return_value = []
+    return m
+
+
+@pytest.fixture
+def adapter(config_eos, mock_ems: MagicMock) -> NodeREDAdapter:
+    """Fully Pydantic-safe NodeREDAdapter fixture."""
+    # Set nested value - also fills None values
+    config_eos.set_nested_value("adapter/provider", ["NodeRED"])
+
+    ad = NodeREDAdapter()
+
+    # Mark update datetime invalid
+    ad.update_datetime = None
+
+    # Assign EMS
+    object.__setattr__(ad, "ems", mock_ems)
+
+    return ad
+
+
+class TestNodeREDAdapter:
+
+    def test_provider_id(self, adapter: NodeREDAdapter):
+        assert adapter.provider_id() == "NodeRED"
+
+    def test_enabled_detection_single(self, adapter: NodeREDAdapter):
+        adapter.config.adapter.provider = ["NodeRED"]
+        assert adapter.enabled() is True
+        adapter.config.adapter.provider = ["HomeAssistant"]
+        assert adapter.enabled() is False
+        adapter.config.adapter.provider = ["HomeAssistant", "NodeRED"]
+        assert adapter.enabled() is True
+
+    @patch("requests.get")
+    def test_update_datetime(self, mock_get, adapter: NodeREDAdapter):
+        adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
+        mock_get.return_value.status_code = 200
+        mock_get.return_value.json.return_value = {"foo": "bar"}
+        now = to_datetime()
+
+        adapter.update_data(force_enable=True)
+
+        mock_get.assert_called_once()
+        assert compare_datetimes(adapter.update_datetime, now).approximately_equal
+
+    @patch("requests.get")
+    def test_update_data_data_acquisition_success(self, mock_get    , adapter: NodeREDAdapter):
+        adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
+        mock_get.return_value.status_code = 200
+        mock_get.return_value.json.return_value = {"foo": "bar"}
+
+        adapter.update_data(force_enable=True)
+
+        mock_get.assert_called_once()
+        url, = mock_get.call_args[0]
+        assert "/eos/data_aquisition" in url
+
+    @patch("requests.get", side_effect=Exception("boom"))
+    def test_update_data_data_acquisition_failure(self, mock_get, adapter: NodeREDAdapter):
+        adapter.ems.stage.return_value = EnergyManagementStage.DATA_ACQUISITION
+        with pytest.raises(RuntimeError):
+            adapter.update_data(force_enable=True)
+
+    @patch("requests.post")
+    def test_update_data_control_dispatch_instructions(self, mock_post, adapter: NodeREDAdapter):
+        adapter.ems.stage.return_value = EnergyManagementStage.CONTROL_DISPATCH
+
+        instr1 = DDBCInstruction(
+            id="res1@extra", operation_mode_id="X", operation_mode_factor=0.5,
+            actuator_id="dummy", execution_time=to_datetime()
+        )
+        instr2 = FRBCInstruction(
+            id="resA", operation_mode_id="Y", operation_mode_factor=0.25,
+            actuator_id="dummy", execution_time=to_datetime()
+        )
+        adapter.ems.plan.return_value.get_active_instructions.return_value = [instr1, instr2]
+
+        mock_post.return_value.status_code = 200
+        mock_post.return_value.json.return_value = {}
+
+        adapter.update_data(force_enable=True)
+
+        _, kwargs = mock_post.call_args
+        payload = kwargs["json"]
+        assert payload["res1_op_mode"] == "X"
+        assert payload["res1_op_factor"] == 0.5
+        assert payload["resA_op_mode"] == "Y"
+        assert payload["resA_op_factor"] == 0.25
+        url, = mock_post.call_args[0]
+        assert "/eos/control_dispatch" in url
+
+    @patch("requests.post")
+    def test_update_data_disabled_provider(self, mock_post, adapter: NodeREDAdapter):
+        adapter.config.adapter.provider = ["HomeAssistant"]  # NodeRED disabled
+        adapter.update_data(force_enable=False)
+        mock_post.assert_not_called()
+
+    @patch("requests.post")
+    def test_update_data_force_enable_overrides_disabled(self, mock_post, adapter: NodeREDAdapter):
+        adapter.config.adapter.provider = ["HomeAssistant"]
+        adapter.ems.stage.return_value = EnergyManagementStage.CONTROL_DISPATCH
+        mock_post.return_value.status_code = 200
+        mock_post.return_value.json.return_value = {}
+
+        adapter.update_data(force_enable=True)
+
+        mock_post.assert_called_once()
diff --git a/tests/test_config.py b/tests/test_config.py
index f76912a..e591ab5 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -52,7 +52,9 @@ def test_config_constants(config_eos):
 def test_computed_paths(config_eos):
     """Test computed paths for output and cache."""
     # Don't actually try to create the data folder
-    with patch("pathlib.Path.mkdir"):
+    with patch("pathlib.Path.mkdir"), \
+         patch("pathlib.Path.is_dir", return_value=True), \
+         patch("pathlib.Path.exists", return_value=True):
         config_eos.merge_settings_from_dict(
             {
                 "general": {
@@ -371,7 +373,7 @@ def test_config_common_settings_timezone_none_when_coordinates_missing():
                     BATTERY_DEFAULT_CHARGE_RATES,
                 )
             ],
-            KeyError,
+            TypeError,
         ),
         # Invalid index (no number)
         (
@@ -383,7 +385,7 @@ def test_config_common_settings_timezone_none_when_coordinates_missing():
                     BATTERY_DEFAULT_CHARGE_RATES,
                 )
             ],
-            KeyError,
+            IndexError,
         ),
         # Unset value (set None)
         (
diff --git a/tests/test_dataabc.py b/tests/test_dataabc.py
index 36fe1a6..96c9ec7 100644
--- a/tests/test_dataabc.py
+++ b/tests/test_dataabc.py
@@ -698,6 +698,33 @@ class TestDataSequence:
                 fill_method="invalid",
             )
 
+    def test_key_to_array_resample_mean(self, sequence):
+        """Test that numeric resampling uses mean when multiple values fall into one interval."""
+        interval = to_duration("1 hour")
+        # Insert values every 15 minutes within the same hour
+        record1 = self.create_test_record(pendulum.datetime(2023, 11, 6, 0, 0), 1.0)
+        record2 = self.create_test_record(pendulum.datetime(2023, 11, 6, 0, 15), 2.0)
+        record3 = self.create_test_record(pendulum.datetime(2023, 11, 6, 0, 30), 3.0)
+        record4 = self.create_test_record(pendulum.datetime(2023, 11, 6, 0, 45), 4.0)
+
+        sequence.insert_by_datetime(record1)
+        sequence.insert_by_datetime(record2)
+        sequence.insert_by_datetime(record3)
+        sequence.insert_by_datetime(record4)
+
+        # Resample to hourly interval, expecting the mean of the 4 values
+        array = sequence.key_to_array(
+            key="data_value",
+            start_datetime=pendulum.datetime(2023, 11, 6, 0),
+            end_datetime=pendulum.datetime(2023, 11, 6, 1),
+            interval=interval,
+        )
+
+        assert isinstance(array, np.ndarray)
+        assert len(array) == 1  # one interval: 0:00-1:00
+        # The first interval mean = (1+2+3+4)/4 = 2.5
+        assert array[0] == pytest.approx(2.5)
+
     def test_to_datetimeindex(self, sequence2):
         record1 = self.create_test_record(datetime(2023, 11, 5), 0.8)
         record2 = self.create_test_record(datetime(2023, 11, 6), 0.9)
diff --git a/tests/test_datetimeutil.py b/tests/test_datetimeutil.py
index cc3516a..bbc4757 100644
--- a/tests/test_datetimeutil.py
+++ b/tests/test_datetimeutil.py
@@ -1657,47 +1657,162 @@ def test_to_datetime(
 # to_duration
 # -----------------------------
 
+class TestToDuration:
+    # ------------------------------------------------------------------
+    # Valid input conversions (no formatting)
+    # ------------------------------------------------------------------
+    @pytest.mark.parametrize(
+        "input_value, expected_output",
+        [
+            # duration input
+            (pendulum.duration(days=1), pendulum.duration(days=1)),
 
-# Test cases for valid duration inputs
-@pytest.mark.parametrize(
-    "input_value, expected_output",
-    [
-        # duration input
-        (pendulum.duration(days=1), pendulum.duration(days=1)),
-        # String input
-        ("2 days", pendulum.duration(days=2)),
-        ("5 hours", pendulum.duration(hours=5)),
-        ("47 hours", pendulum.duration(hours=47)),
-        ("48 hours", pendulum.duration(seconds=48 * 3600)),
-        ("30 minutes", pendulum.duration(minutes=30)),
-        ("45 seconds", pendulum.duration(seconds=45)),
-        (
-            "1 day 2 hours 30 minutes 15 seconds",
-            pendulum.duration(days=1, hours=2, minutes=30, seconds=15),
-        ),
-        ("3 days 4 hours", pendulum.duration(days=3, hours=4)),
-        # Integer/Float input
-        (3600, pendulum.duration(seconds=3600)),  # 1 hour
-        (86400, pendulum.duration(days=1)),  # 1 day
-        (1800.5, pendulum.duration(seconds=1800.5)),  # 30 minutes and 0.5 seconds
-        # Tuple/List input
-        ((1, 2, 30, 15), pendulum.duration(days=1, hours=2, minutes=30, seconds=15)),
-        ([0, 10, 0, 0], pendulum.duration(hours=10)),
-    ],
-)
-def test_to_duration_valid(input_value, expected_output):
-    """Test to_duration with valid inputs."""
-    assert to_duration(input_value) == expected_output
+            # String input
+            ("1 hour", pendulum.duration(hours=1)),
+            ("2 days", pendulum.duration(days=2)),
+            ("5 hours", pendulum.duration(hours=5)),
+            ("47 hours", pendulum.duration(hours=47)),
+            ("48 hours", pendulum.duration(seconds=48 * 3600)),
+            ("30 minutes", pendulum.duration(minutes=30)),
+            ("45 seconds", pendulum.duration(seconds=45)),
+            (
+                "1 day 2 hours 30 minutes 15 seconds",
+                pendulum.duration(days=1, hours=2, minutes=30, seconds=15),
+            ),
+            ("3 days 4 hours", pendulum.duration(days=3, hours=4)),
 
+            # Integer / Float
+            (3600, pendulum.duration(seconds=3600)),
+            (86400, pendulum.duration(days=1)),
+            (1800.5, pendulum.duration(seconds=1800.5)),
 
-def test_to_duration_summation():
-    start_datetime = to_datetime("2028-01-11 00:00:00")
-    index_datetime = start_datetime
-    for i in range(48):
-        expected_datetime = start_datetime + to_duration(f"{i} hours")
-        assert index_datetime == expected_datetime
-        index_datetime += to_duration("1 hour")
-    assert index_datetime == to_datetime("2028-01-13 00:00:00")
+            # Tuple / List
+            ((1, 2, 30, 15), pendulum.duration(days=1, hours=2, minutes=30, seconds=15)),
+            ([0, 10, 0, 0], pendulum.duration(hours=10)),
+        ],
+    )
+    def test_to_duration_valid(self, input_value, expected_output):
+        """Test that valid inputs convert to correct Duration objects."""
+        assert to_duration(input_value) == expected_output
+
+    # ------------------------------------------------------------------
+    # ISO-8601 output (`as_string=True`)
+    # ------------------------------------------------------------------
+    @pytest.mark.parametrize(
+        "input_value, expected",
+        [
+            ("15 minutes", "PT15M"),
+            ("1 hour 30 minutes", "PT1H30M"),
+            ("45 seconds", "PT45S"),
+            ("1 hour 5 seconds", "PT1H5S"),
+            ("2 days", "P2D"),
+            ("2 days 3 hours 4 minutes 5 seconds", "P2DT3H4M5S"),
+            ("0 seconds", "PT0S"),
+        ]
+    )
+    def test_as_string_true_iso8601(self, input_value, expected):
+        """Test ISO-8601 duration strings for various inputs."""
+        assert to_duration(input_value, as_string=True) == expected
+
+    # ------------------------------------------------------------------
+    # Human readable (`as_string="human"`)
+    # ------------------------------------------------------------------
+    def test_as_string_human(self):
+        assert to_duration("90 seconds", as_string="human") == "1 minute 30 seconds"
+
+    # ------------------------------------------------------------------
+    # Pandas frequency (`as_string="pandas"`)
+    # ------------------------------------------------------------------
+    @pytest.mark.parametrize(
+        "input_value, expected",
+        [
+            ("1 hour", "1h"),
+            ("2 hours", "2h"),
+            ("15 minutes", "15min"),
+            ("90 minutes", "90min"),
+            ("30 seconds", "30s"),
+            ("900 seconds", "15min"),
+        ],
+    )
+    def test_as_string_pandas(self, input_value, expected):
+        assert to_duration(input_value, as_string="pandas") == expected
+
+    # ------------------------------------------------------------------
+    # Custom format strings
+    # ------------------------------------------------------------------
+    def test_as_string_custom_seconds(self):
+        assert to_duration("75 seconds", as_string="Total: {S}s") == "Total: 75s"
+
+    def test_as_string_custom_minutes(self):
+        assert to_duration("15 minutes", as_string="{M}m total") == "15m total"
+
+    def test_as_string_custom_hours(self):
+        assert to_duration("7200 seconds", as_string="{H} hours") == "2 hours"
+
+    def test_as_string_custom_human_alias(self):
+        assert to_duration("30 minutes", as_string="{f}") == "30 minutes"
+
+    # ------------------------------------------------------------------
+    # Invalid input handling
+    # ------------------------------------------------------------------
+    @pytest.mark.parametrize(
+        "input_value",
+        [
+            "not a duration",
+            "5 lightyears",
+            (1, 2, 3),             # wrong tuple size
+            {"a": 1},              # unsupported type
+            None,
+        ],
+    )
+    def test_invalid_inputs_raise(self, input_value):
+        with pytest.raises(ValueError):
+            to_duration(input_value)
+
+    # ------------------------------------------------------------------
+    # Invalid as_string values
+    # ------------------------------------------------------------------
+    def test_invalid_as_string_raises(self):
+        with pytest.raises(ValueError):
+            to_duration("5 minutes", as_string=123) # type: ignore
+
+    def test_summation(self):
+        start_datetime = to_datetime("2028-01-11 00:00:00")
+        index_datetime = start_datetime
+        for i in range(48):
+            expected_datetime = start_datetime + to_duration(f"{i} hours")
+            assert index_datetime == expected_datetime
+            index_datetime += to_duration("1 hour")
+        assert index_datetime == to_datetime("2028-01-13 00:00:00")
+
+    def test_excessive_length_raises_valueerror(self):
+        """Test that to_duration raises ValueError for strings exceeding max length.
+
+        This test covers the fix for the ReDoS vulnerability.
+        Related to: #494
+        """
+        # String exceeds limits
+        long_string = "a" * (MAX_DURATION_STRING_LENGTH + 50)
+
+        # Expected Errormessage – ESCAPED für Regex
+        expected_error_message = re.escape(
+            f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
+        )
+
+        # Check if error was raised
+        with pytest.raises(ValueError, match=expected_error_message):
+            to_duration(long_string)
+
+        # Optional: String exactly at the limit should NOT trigger the length check.
+        at_limit_string = "b" * MAX_DURATION_STRING_LENGTH
+        try:
+            to_duration(at_limit_string)
+        except ValueError as e:
+            if str(e) == f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH}).":
+                pytest.fail(
+                    f"to_duration raised length ValueError unexpectedly for string at limit: {at_limit_string}"
+                )
+            pass
 
 
 # -----------------------------
@@ -1900,33 +2015,3 @@ def test_compare_datetimes_gt(dt1, dt2):
     assert compare_datetimes(dt1, dt2).gt
     assert compare_datetimes(dt1, dt2).le == False
     assert compare_datetimes(dt1, dt2).lt == False
-
-
-def test_to_duration_excessive_length_raises_valueerror():
-    """Test that to_duration raises ValueError for strings exceeding max length.
-
-    This test covers the fix for the ReDoS vulnerability.
-    Related to: #494
-    """
-    # String exceeds limits
-    long_string = "a" * (MAX_DURATION_STRING_LENGTH + 50)
-
-    # Expected Errormessage – ESCAPED für Regex
-    expected_error_message = re.escape(
-        f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
-    )
-
-    # Check if error was raised
-    with pytest.raises(ValueError, match=expected_error_message):
-        to_duration(long_string)
-
-    # Optional: String exactly at the limit should NOT trigger the length check.
-    at_limit_string = "b" * MAX_DURATION_STRING_LENGTH
-    try:
-        to_duration(at_limit_string)
-    except ValueError as e:
-        if str(e) == f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH}).":
-            pytest.fail(
-                f"to_duration raised length ValueError unexpectedly for string at limit: {at_limit_string}"
-            )
-        pass
diff --git a/tests/test_emplan.py b/tests/test_emplan.py
index f225e43..04fae37 100644
--- a/tests/test_emplan.py
+++ b/tests/test_emplan.py
@@ -25,6 +25,49 @@ def fixed_now():
 
 
 class TestEnergyManagementPlan:
+
+    # ----------------------------------------------------------------------
+    # Helpers (only used inside the class)
+    # ----------------------------------------------------------------------
+    def _make_instr(self, resource_id, execution_time, duration=None):
+        if duration is None:
+            instr = OMBCInstruction(
+                id=resource_id,
+                execution_time=execution_time,
+                operation_mode_id="mode",
+                operation_mode_factor=1.0,
+            )
+        else:
+           instr = PEBCInstruction(
+                id=resource_id,
+                execution_time=execution_time,
+                power_constraints_id="pc-123",
+                power_envelopes=[
+                    PEBCPowerEnvelope(
+                        id="pebcpe@1234",
+                        commodity_quantity=CommodityQuantity.ELECTRIC_POWER_L1,
+                        power_envelope_elements=[
+                            PEBCPowerEnvelopeElement(
+                                duration=to_duration(duration),
+                                upper_limit=1010.0,
+                                lower_limit=990.0,
+                            ),
+                        ],
+                    ),
+                ],
+            )
+
+        return instr
+
+    def _build_plan(self, instructions, now):
+        plan = EnergyManagementPlan(
+            id="plan-test",
+            generated_at=now,
+            instructions=instructions,
+        )
+        plan._update_time_range()
+        return plan
+
     def test_add_instruction_and_time_range(self, fixed_now):
         plan = EnergyManagementPlan(
             id="plan-123",
@@ -46,12 +89,8 @@ class TestEnergyManagementPlan:
         plan.add_instruction(instr1)
         plan.add_instruction(instr2)
 
-        # Check that valid_from matches the earliest execution_time
         assert plan.valid_from == fixed_now
-
-        # instr2 has infinite duration so valid_until must be None
         assert plan.valid_until is None
-
         assert plan.instructions == [instr1, instr2]
 
     def test_clear(self, fixed_now):
@@ -72,36 +111,6 @@ class TestEnergyManagementPlan:
         assert plan.valid_until is None
         assert plan.valid_from is not None
 
-    def test_get_active_instructions(self, fixed_now):
-        instr1 = OMBCInstruction(
-            resource_id="dev-1",
-            execution_time=fixed_now.subtract(minutes=1),
-            operation_mode_id="mymode1",
-            operation_mode_factor=1.0,
-        )
-        instr2 = OMBCInstruction(
-            resource_id="dev-2",
-            execution_time=fixed_now.add(minutes=1),
-            operation_mode_id="mymode1",
-            operation_mode_factor=1.0,
-        )
-        instr3 = OMBCInstruction(
-            resource_id="dev-3",
-            execution_time=fixed_now.subtract(minutes=10),
-            operation_mode_id="mymode1",
-            operation_mode_factor=1.0,
-        )
-        plan = EnergyManagementPlan(
-            id="plan-123",
-            generated_at=fixed_now,
-            instructions=[instr1, instr2, instr3],
-        )
-        plan._update_time_range()
-
-        active = plan.get_active_instructions(now=fixed_now)
-        ids = {i.resource_id for i in active}
-        assert ids == {"dev-1", "dev-3"}
-
     def test_get_next_instruction(self, fixed_now):
         instr1 = OMBCInstruction(
             resource_id="dev-1",
@@ -154,14 +163,12 @@ class TestEnergyManagementPlan:
         assert len(dev1_instructions) == 1
         assert dev1_instructions[0].resource_id == "dev-1"
 
-
     def test_add_various_instructions(self, fixed_now):
         plan = EnergyManagementPlan(
             id="plan-123",
             generated_at=fixed_now,
             instructions=[]
         )
-
         instrs = [
             DDBCInstruction(
                 id="actuatorA@123",
@@ -212,11 +219,11 @@ class TestEnergyManagementPlan:
                     PEBCPowerEnvelope(
                         id="pebcpe@1234",
                         commodity_quantity=CommodityQuantity.ELECTRIC_POWER_L1,
-                        power_envelope_elements = [
+                        power_envelope_elements=[
                             PEBCPowerEnvelopeElement(
-                                duration = to_duration(10),
-                                upper_limit = 1010.0,
-                                lower_limit = 990.0,
+                                duration=to_duration(10),
+                                upper_limit=1010.0,
+                                lower_limit=990.0,
                             ),
                         ],
                     ),
@@ -228,8 +235,131 @@ class TestEnergyManagementPlan:
             plan.add_instruction(instr)
 
         assert len(plan.instructions) == len(instrs)
-        # Check that get_instructions_for_device returns the right instructions
         assert any(
             instr for instr in plan.get_instructions_for_resource("actuatorA")
             if isinstance(instr, DDBCInstruction)
         )
+
+    # -------------------------------------------
+    # Special testing for get_active_instructions
+    # -------------------------------------------
+
+    def test_get_active_instructions(self, fixed_now):
+        instr1 = OMBCInstruction(
+            resource_id="dev-1",
+            execution_time=fixed_now.subtract(minutes=1),
+            operation_mode_id="mymode1",
+            operation_mode_factor=1.0,
+        )
+        instr2 = OMBCInstruction(
+            resource_id="dev-2",
+            execution_time=fixed_now.add(minutes=1),
+            operation_mode_id="mymode1",
+            operation_mode_factor=1.0,
+        )
+        instr3 = OMBCInstruction(
+            resource_id="dev-3",
+            execution_time=fixed_now.subtract(minutes=10),
+            operation_mode_id="mymode1",
+            operation_mode_factor=1.0,
+        )
+        plan = EnergyManagementPlan(
+            id="plan-123",
+            generated_at=fixed_now,
+            instructions=[instr1, instr2, instr3],
+        )
+        plan._update_time_range()
+
+        resource_ids = plan.get_resources()
+        assert resource_ids == ["dev-1", "dev-2", "dev-3"]
+
+        active = plan.get_active_instructions(now=fixed_now)
+        ids = {i.resource_id for i in active}
+        assert ids == {"dev-1", "dev-3"}
+
+
+
+    def test_get_active_instructions_with_duration(self, fixed_now):
+        instr = self._make_instr(
+            "dev-1",
+            fixed_now.subtract(minutes=5),
+            duration=Duration(minutes=10),
+        )
+        plan = self._build_plan([instr], fixed_now)
+        active = plan.get_active_instructions(fixed_now)
+        assert {i.resource_id for i in active} == {"dev-1"}
+
+    def test_get_active_instructions_expired_duration(self, fixed_now):
+        instr = self._make_instr(
+            "dev-1",
+            fixed_now.subtract(minutes=20),
+            duration=Duration(minutes=10),
+        )
+        plan = self._build_plan([instr], fixed_now)
+        assert plan.get_active_instructions(fixed_now) == []
+
+    def test_get_active_instructions_end_exactly_now_not_active(self, fixed_now):
+        instr = self._make_instr(
+            "dev-1",
+            fixed_now.subtract(minutes=10),
+            duration=Duration(minutes=10),
+        )
+        plan = self._build_plan([instr], fixed_now)
+        assert plan.get_active_instructions(fixed_now) == []
+
+    def test_get_active_instructions_latest_supersedes(self, fixed_now):
+        instr1 = self._make_instr(
+            "dev-1",
+            fixed_now.subtract(minutes=10),
+            duration=Duration(minutes=30),
+        )
+        instr2 = self._make_instr("dev-1", fixed_now.subtract(minutes=1))
+        plan = self._build_plan([instr1, instr2], fixed_now)
+
+        active = plan.get_active_instructions(fixed_now)
+        assert len(active) == 1
+        assert active[0] is instr2
+
+    def test_get_active_instructions_mixed_resources(self, fixed_now):
+        instr1 = self._make_instr(
+            "r1",
+            fixed_now.subtract(minutes=5),
+            duration=Duration(minutes=10),
+        )
+        instr2 = self._make_instr("r2", fixed_now.subtract(minutes=1))
+        instr3 = self._make_instr("r3", fixed_now.add(minutes=10))
+        plan = self._build_plan([instr1, instr2, instr3], fixed_now)
+
+        ids = {i.resource_id for i in plan.get_active_instructions(fixed_now)}
+        assert ids == {"r1", "r2"}
+
+    def test_get_active_instructions_start_exactly_now(self, fixed_now):
+        instr = self._make_instr("dev-1", fixed_now)
+        plan = self._build_plan([instr], fixed_now)
+        assert {i.resource_id for i in plan.get_active_instructions(fixed_now)} == {"dev-1"}
+
+    def test_get_active_instructions_no_active(self, fixed_now):
+        instr = self._make_instr("dev-1", fixed_now.add(minutes=1))
+        plan = self._build_plan([instr], fixed_now)
+        assert plan.get_active_instructions(fixed_now) == []
+
+    def test_get_active_instructions_future_does_not_override_until_reached(self, fixed_now):
+        instr1 = self._make_instr("dev-1", fixed_now.subtract(minutes=5))
+        instr2 = self._make_instr("dev-1", fixed_now.add(minutes=5))
+        plan = self._build_plan([instr1, instr2], fixed_now)
+
+        active_before = plan.get_active_instructions(fixed_now)
+        assert {i.resource_id for i in active_before} == {"dev-1"}
+
+    def test_get_active_instructions_future_overrides_once_time_reached(self, fixed_now):
+        exec_future = fixed_now.add(minutes=5)
+        instr1 = self._make_instr("dev-1", fixed_now.subtract(minutes=5))
+        instr2 = self._make_instr("dev-1", exec_future)
+
+        plan = self._build_plan([instr1, instr2], fixed_now)
+
+        active_before = plan.get_active_instructions(fixed_now)
+        assert {i.resource_id for i in active_before} == {"dev-1"}
+
+        active_after = plan.get_active_instructions(exec_future)
+        assert {i.resource_id for i in active_after} == {"dev-1"}
diff --git a/tests/test_eosdashconfig.py b/tests/test_eosdashconfig.py
index 79f0cb6..b3feaf4 100644
--- a/tests/test_eosdashconfig.py
+++ b/tests/test_eosdashconfig.py
@@ -14,7 +14,7 @@ from pydantic.fields import FieldInfo
 from akkudoktoreos.core.pydantic import PydanticBaseModel
 from akkudoktoreos.prediction.pvforecast import PVForecastPlaneSetting
 from akkudoktoreos.server.dash.configuration import (
-    configuration,
+    create_config_details,
     get_default_value,
     get_nested_value,
     resolve_nested_types,
@@ -68,38 +68,44 @@ class TestEOSdashConfig:
     def test_configuration(self):
         """Test extracting configuration details from a Pydantic model based on provided values."""
         values = {"field1": "custom_value", "field2": 20}
-        config = configuration(SampleModel, values)
+        config_details = create_config_details(SampleModel, values)
+
         assert any(
-            item["name"] == "field1" and item["value"] == '"custom_value"' for item in config
+            item["name"] == "field1" and item["value"] == '"custom_value"'
+            for key, item in config_details.items()
+        )
+        assert any(
+            item["name"] == "field2" and item["value"] == "20"
+            for key, item in config_details.items()
         )
-        assert any(item["name"] == "field2" and item["value"] == "20" for item in config)
 
     def test_configuration_eos(self, config_eos):
         """Test extracting EOS configuration details from EOS config based on provided values."""
         with FILE_TESTDATA_EOSSERVER_CONFIG_1.open("r", encoding="utf-8", newline=None) as fd:
             values = json.load(fd)
-        config = configuration(config_eos, values)
+        config_details = create_config_details(config_eos, values)
         assert any(
-            item["name"] == "server.eosdash_port" and item["value"] == "8504" for item in config
+            item["name"] == "server.eosdash_port" and item["value"] == "8504"
+            for key, item in config_details.items()
         )
         assert any(
             item["name"] == "server.eosdash_host" and item["value"] == '"127.0.0.1"'
-            for item in config
+            for key, item in config_details.items()
         )
 
     def test_configuration_pvforecast_plane_settings(self):
         """Test extracting EOS PV forecast plane configuration details from EOS config based on provided values."""
         with FILE_TESTDATA_EOSSERVER_CONFIG_1.open("r", encoding="utf-8", newline=None) as fd:
             values = json.load(fd)
-        config = configuration(
+        config_details = create_config_details(
             PVForecastPlaneSetting(), values, values_prefix=["pvforecast", "planes", "0"]
         )
         assert any(
             item["name"] == "pvforecast.planes.0.surface_azimuth" and item["value"] == "170"
-            for item in config
+            for key, item in config_details.items()
         )
         assert any(
             item["name"] == "pvforecast.planes.0.userhorizon"
             and item["value"] == "[20, 27, 22, 20]"
-            for item in config
+            for key, item in config_details.items()
         )
diff --git a/tests/test_eosdashserver.py b/tests/test_eosdashserver.py
index fbb4564..6640067 100644
--- a/tests/test_eosdashserver.py
+++ b/tests/test_eosdashserver.py
@@ -1,7 +1,99 @@
+import re
 import time
 from http import HTTPStatus
+from types import SimpleNamespace
+from unittest.mock import patch
 
+import pytest
 import requests
+from bs4 import BeautifulSoup
+
+from akkudoktoreos.server.dash.context import EOSDASH_ROOT, ROOT_PATH, request_url_for
+
+# -----------------------------------------------------
+# URL filtering logic
+# -----------------------------------------------------
+
+ALLOWED_PREFIXES = [
+    "/api/hassio_ingress/",
+    "http://", "https://",            # external URLs
+    "mailto:", "tel:",                # contact URLs
+    "#",                              # anchor links
+]
+
+
+def is_allowed_prefix(url: str) -> bool:
+    return any(url.startswith(p) for p in ALLOWED_PREFIXES)
+
+
+ABSOLUTE_URL = re.compile(r"^/[^/].*")
+RELATIVE_PARENT = re.compile(r"^\.\./")
+WS_REGEX = re.compile(r'new\s+WebSocket\s*\(\s*[\'"]([^\'"]*)[\'"]')
+
+
+# -----------------------------------------------------
+# Core HTML parser
+# -----------------------------------------------------
+
+def scan_html_for_link_issues(html: str):
+    soup = BeautifulSoup(html, "html.parser")
+
+    found_absolute: list[str] = []
+    found_relative_up: list[str] = []
+    all_urls = []
+
+    def add_issue(lst, tag, attr, value) -> None:
+        lst.append(f"<{tag.name} {attr}='{value}'>")
+
+    for tag in soup.find_all(True):
+        for attr in ("href", "src", "action"):
+            if attr not in tag.attrs:
+                continue
+
+            value = tag[attr]
+            if not isinstance(value, str):
+                continue
+
+            all_urls.append(value)
+
+            # (1) absolute URL
+            if ABSOLUTE_URL.match(value) and not is_allowed_prefix(value):
+                add_issue(found_absolute, tag, attr, value)
+
+            # (2) relative going up
+            if RELATIVE_PARENT.match(value):
+                add_issue(found_relative_up, tag, attr, value)
+
+    # (3) mixed usage check: both absolute + relative appear
+    used_absolute = any(u.startswith("/") for u in all_urls if not is_allowed_prefix(u))
+    used_relative = any(not u.startswith("/") for u in all_urls if not is_allowed_prefix(u))
+
+    mixed_usage = used_absolute and used_relative
+
+    # (4) detect absolute WebSocket URLs in JS
+    ws_bad = []
+    for m in WS_REGEX.findall(html):
+        if m.startswith("/") and not is_allowed_prefix(m):
+            ws_bad.append(m)
+
+    return found_absolute, found_relative_up, mixed_usage, ws_bad
+
+
+def collect_testable_routes(app):
+    urls = []
+    for r in app.routes:
+        if not hasattr(r, "path"):
+            continue
+        path = r.path
+
+        # skip API-style or binary endpoints:
+        if path.startswith("/api"):
+            continue
+        if path.endswith(".js") or path.endswith(".css"):
+            continue
+
+        urls.append(path)
+    return sorted(set(urls))
 
 
 class TestEOSDash:
@@ -38,3 +130,73 @@ class TestEOSDash:
         server = server_setup_for_class["server"]
         timeout = server_setup_for_class["timeout"]
         self._assert_server_alive(server, timeout)
+
+    def test_ingress_safe_links(self, server_setup_for_class, monkeypatch, tmp_path):
+        base = server_setup_for_class["eosdash_server"]
+
+        with patch("akkudoktoreos.server.dash.context.ROOT_PATH", "/api/hassio_ingress/TOKEN/"):
+            eos_dir = tmp_path
+            monkeypatch.setenv("EOS_DIR", str(eos_dir))
+            monkeypatch.setenv("EOS_CONFIG_DIR", str(eos_dir))
+
+            # Import with environment vars set to prevent creation of EOS.config.json in wrong dir.
+            from akkudoktoreos.server.eosdash import app
+
+            for path in collect_testable_routes(app):
+                url = f"{base}{path}"
+                resp = requests.get(url)
+                resp.raise_for_status()
+
+                abs_issues, rel_up_issues, mixed_usage, ws_issues = scan_html_for_link_issues(resp.text)
+
+                #assert not abs_issues, (
+                #    f"Forbidden absolute paths detected on {path}:\n" +
+                #    "\n".join(abs_issues)
+                #)
+
+                assert not rel_up_issues, (
+                    f"Relative paths navigating up (`../`) detected on {path}:\n" +
+                    "\n".join(rel_up_issues)
+                )
+
+                assert not mixed_usage, f"Mixed absolute/relative linking detected on page {path}"
+
+                assert not ws_issues, f"Forbidden WebSocket paths detected on {path}:\n" + "\n".join(ws_issues)
+
+    @pytest.mark.parametrize(
+        "root_path,path,expected",
+        [
+            ("/", "/eosdash/footer", "/eosdash/footer"),
+            ("/", "eosdash/footer", "/eosdash/footer"),
+            ("/", "footer", "/eosdash/footer"),
+            ("/", "eosdash/assets/logo.png", "/eosdash/assets/logo.png"),
+            ("/api/hassio_ingress/TOKEN/", "/api/hassio_ingress/TOKEN/eosdash/footer", "/api/hassio_ingress/TOKEN/eosdash/footer"),
+            ("/api/hassio_ingress/TOKEN/", "/eosdash/footer", "/api/hassio_ingress/TOKEN/eosdash/footer"),
+            ("/api/hassio_ingress/TOKEN/", "eosdash/footer", "/api/hassio_ingress/TOKEN/eosdash/footer"),
+            ("/api/hassio_ingress/TOKEN/", "footer", "/api/hassio_ingress/TOKEN/eosdash/footer"),
+            ("/api/hassio_ingress/TOKEN/", "assets/logo.png", "/api/hassio_ingress/TOKEN/eosdash/assets/logo.png"),
+        ],
+    )
+    def test_request_url_for(self, root_path, path, expected):
+        """Test that request_url_for produces absolute non-rewritable URLs.
+
+        Args:
+            root_path (str): Root path.
+            path (str): Path passed to request_url_for().
+            expected (str): Final produced path.
+        """
+
+        result = request_url_for(path, root_path = root_path)
+        assert result == expected, (
+            f"URL rewriting mismatch. "
+            f"root_path={root_path}, path={path}, expected={expected}, got={result}"
+        )
+
+        # Test fallback to global var
+        with patch("akkudoktoreos.server.dash.context.ROOT_PATH", root_path):
+            result = request_url_for(path, root_path = None)
+
+        assert result == expected, (
+            f"URL rewriting mismatch. "
+            f"root_path={root_path}, path={path}, expected={expected}, got={result}"
+        )
diff --git a/tests/test_homeassistant.py b/tests/test_homeassistant.py
new file mode 100644
index 0000000..74638e8
--- /dev/null
+++ b/tests/test_homeassistant.py
@@ -0,0 +1,343 @@
+import os
+import subprocess
+from pathlib import Path
+from typing import Optional
+
+import pytest
+import yaml
+from pydantic import ValidationError
+
+
+class TestHomeAssistantAddon:
+    """Tests to ensure the repository root is a valid Home Assistant add-on.
+    Simulates the Home Assistant Supervisor's expectations.
+    """
+
+    @property
+    def root(self):
+        """Repository root (repo == addon)."""
+        return Path(__file__).resolve().parent.parent
+
+    def test_config_yaml_exists(self):
+        """Ensure config.yaml exists in the repo root."""
+        cfg_path = self.root / "config.yaml"
+        assert cfg_path.is_file(), "config.yaml must exist in repository root."
+
+    def test_config_yaml_loadable(self):
+        """Verify that config.yaml parses and contains required fields."""
+        cfg_path = self.root / "config.yaml"
+        with open(cfg_path) as f:
+            cfg = yaml.safe_load(f)
+
+        required_fields = ["name", "version", "slug", "description", "arch"]
+        for field in required_fields:
+            assert field in cfg, f"Missing required field '{field}' in config.yaml."
+
+        # Additional validation
+        assert isinstance(cfg["arch"], list), "arch must be a list"
+        assert len(cfg["arch"]) > 0, "arch list cannot be empty"
+
+        print(f"āœ“ config.yaml valid:")
+        print(f"  Name: {cfg['name']}")
+        print(f"  Version: {cfg['version']}")
+        print(f"  Slug: {cfg['slug']}")
+        print(f"  Architectures: {', '.join(cfg['arch'])}")
+
+    def test_readme_exists(self):
+        """Ensure README.md exists and is not empty."""
+        readme_path = self.root / "README.md"
+        assert readme_path.is_file(), "README.md must exist in the repository root."
+
+        content = readme_path.read_text()
+        assert len(content.strip()) > 0, "README.md is empty"
+
+        print(f"āœ“ README.md exists ({len(content)} bytes)")
+
+    def test_docs_md_exists(self):
+        """Ensure DOCS.md exists in the repo root (for Home Assistant add-on documentation)."""
+        docs_path = self.root / "DOCS.md"
+        assert docs_path.is_file(), "DOCS.md must exist in the repository root for add-on documentation."
+
+        content = docs_path.read_text()
+        assert len(content.strip()) > 0, "DOCS.md is empty"
+
+        print(f"āœ“ DOCS.md exists ({len(content)} bytes)")
+
+    @pytest.mark.docker
+    def test_dockerfile_exists(self):
+        """Ensure Dockerfile exists in the repo root and has basic structure."""
+        dockerfile = self.root / "Dockerfile"
+        assert dockerfile.is_file(), "Dockerfile must exist in repository root."
+
+        content = dockerfile.read_text()
+
+        # Check for FROM statement
+        assert "FROM" in content, "Dockerfile must contain FROM statement"
+
+        # Check for common add-on patterns
+        if "ARG BUILD_FROM" in content:
+            print("āœ“ Dockerfile uses Home Assistant build args")
+
+        print("āœ“ Dockerfile exists and has valid structure")
+
+    @pytest.mark.docker
+    def test_docker_build_context_valid(self):
+        """Runs a Docker build using the root of the repo as Home Assistant supervisor would.
+        Fails if the build context is invalid or Dockerfile has syntax errors.
+        """
+        # Check if Docker is available
+        try:
+            subprocess.run(
+                ["docker", "--version"],
+                capture_output=True,
+                check=True
+            )
+        except (FileNotFoundError, subprocess.CalledProcessError):
+            pytest.skip("Docker not found or not running")
+
+        cmd = [
+            "docker", "build",
+            "-t", "ha-addon-test:latest",
+            str(self.root),
+        ]
+
+        print(f"\nBuilding Docker image from: {self.root}")
+
+        try:
+            result = subprocess.run(
+                cmd,
+                check=True,
+                capture_output=True,
+                text=True,
+                cwd=str(self.root)
+            )
+            print("āœ“ Docker build successful")
+            if result.stdout:
+                print("\nBuild output (last 20 lines):")
+                print('\n'.join(result.stdout.splitlines()[-20:]))
+        except subprocess.CalledProcessError as e:
+            print("\nāœ— Docker build failed")
+            print("\nSTDOUT:")
+            print(e.stdout)
+            print("\nSTDERR:")
+            print(e.stderr)
+            pytest.fail(
+                f"Docker build failed with exit code {e.returncode}. "
+                "This simulates a Supervisor build failure."
+            )
+
+    @pytest.mark.docker
+    def test_addon_builder_validation(self, is_finalize: bool):
+        """Validate add-on can be built using Home Assistant's builder tool.
+
+        This is the closest to what Supervisor does when installing an add-on.
+        """
+        if not is_finalize:
+            pytest.skip("Skipping add-on builder validation test — not full run")
+
+        # Check if Docker is available
+        try:
+            subprocess.run(
+                ["docker", "--version"],
+                capture_output=True,
+                check=True
+            )
+        except (FileNotFoundError, subprocess.CalledProcessError):
+            pytest.skip("Docker not found or not running")
+
+        print(f"\nValidating add-on with builder: {self.root}")
+
+        # Read config to get architecture info
+        cfg_path = self.root / "config.yaml"
+        with open(cfg_path) as f:
+            cfg = yaml.safe_load(f)
+
+        # Detect host architecture
+        import platform
+        machine = platform.machine().lower()
+
+        # Map Python's platform names to Home Assistant architectures
+        arch_map = {
+            "x86_64": "amd64",
+            "amd64": "amd64",
+            "aarch64": "aarch64",
+            "arm64": "aarch64",
+            "armv7l": "armv7",
+            "armv7": "armv7",
+        }
+
+        host_arch = arch_map.get(machine, "amd64")
+
+        # Check if config supports this architecture
+        if host_arch not in cfg["arch"]:
+            pytest.skip(
+                f"Add-on doesn't support host architecture {host_arch}. "
+                f"Supported: {', '.join(cfg['arch'])}"
+            )
+
+        print(f"Using builder for architecture: {host_arch}")
+
+        # The builder expects specific arguments for building
+        builder_image = f"ghcr.io/home-assistant/{host_arch}-builder:latest"
+        result = subprocess.run(
+            [
+                "docker", "run", "--rm", "--privileged",
+                "-v", f"{self.root}:/data",
+                "-v", "/var/run/docker.sock:/var/run/docker.sock",
+                builder_image,
+                "--generic", cfg["version"],
+                "--target", "/data",
+                f"--{host_arch}",
+                "--test"
+            ],
+            capture_output=True,
+            text=True,
+            cwd=str(self.root),
+            check=False,
+            timeout=600
+        )
+
+        # Print output for debugging
+        if result.stdout:
+            print("\nBuilder stdout:")
+            print(result.stdout)
+        if result.stderr:
+            print("\nBuilder stderr:")
+            print(result.stderr)
+
+        # Check result
+        if result.returncode != 0:
+            # Check if it's just because the builder tool is unavailable
+            if "exec format error" in result.stderr or "not found" in result.stderr:
+                pytest.fail(
+                    "Builder tool not compatible with this system."
+                )
+
+            pytest.fail(
+                f"Add-on builder validation failed with exit code {result.returncode}"
+            )
+
+        print("āœ“ Add-on builder validation passed")
+
+    def test_build_yaml_if_exists(self):
+        """If build.yaml exists, validate its structure."""
+        build_path = self.root / "build.yaml"
+
+        if not build_path.exists():
+            pytest.skip("build.yaml not present (optional)")
+
+        with open(build_path) as f:
+            build_cfg = yaml.safe_load(f)
+
+        assert "build_from" in build_cfg, "build.yaml must contain 'build_from'"
+        assert isinstance(build_cfg["build_from"], dict), "'build_from' must be a dictionary"
+
+        print("āœ“ build.yaml structure valid")
+        print(f"  Architectures defined: {', '.join(build_cfg['build_from'].keys())}")
+
+    def test_addon_configuration_complete(self):
+        """Comprehensive validation of add-on configuration.
+        Checks all required fields and common configuration issues.
+        """
+        cfg_path = self.root / "config.yaml"
+        with open(cfg_path) as f:
+            cfg = yaml.safe_load(f)
+
+        # Required top-level fields
+        required_fields = ["name", "version", "slug", "description", "arch"]
+        for field in required_fields:
+            assert field in cfg, f"Missing required field: {field}"
+
+        # Validate specific fields
+        assert isinstance(cfg["arch"], list), "arch must be a list"
+        assert len(cfg["arch"]) > 0, "arch list cannot be empty"
+
+        valid_archs = ["aarch64", "amd64", "armhf", "armv7", "i386"]
+        for arch in cfg["arch"]:
+            assert arch in valid_archs, f"Invalid architecture: {arch}"
+
+        # Validate version format (should be semantic versioning)
+        version = cfg["version"]
+        assert isinstance(version, str), "version must be a string"
+
+        # Validate slug (lowercase, no special chars except dash)
+        slug = cfg["slug"]
+        assert slug.islower() or "-" in slug, "slug should be lowercase"
+        assert slug.replace("-", "").replace("_", "").isalnum(), \
+            "slug should only contain alphanumeric characters, dash, or underscore"
+
+        # Optional but common fields
+        if "startup" in cfg:
+            valid_startup = ["initialize", "system", "services", "application", "once"]
+            assert cfg["startup"] in valid_startup, \
+                f"Invalid startup value: {cfg['startup']}"
+
+        if "boot" in cfg:
+            valid_boot = ["auto", "manual"]
+            assert cfg["boot"] in valid_boot, f"Invalid boot value: {cfg['boot']}"
+
+        # Validate ingress configuration
+        if cfg.get("ingress"):
+            assert "ingress_port" in cfg, "ingress_port required when ingress is enabled"
+
+            ingress_port = cfg["ingress_port"]
+            assert isinstance(ingress_port, int), "ingress_port must be an integer"
+            assert 1 <= ingress_port <= 65535, "ingress_port must be a valid port number"
+
+            # Ingress port should NOT be in ports section
+            ports = cfg.get("ports", {})
+            port_key = f"{ingress_port}/tcp"
+            assert port_key not in ports, \
+                f"Port {ingress_port} is used for ingress and should not be in 'ports' section"
+
+        # Validate URL if present
+        if "url" in cfg:
+            url = cfg["url"]
+            assert url.startswith("http://") or url.startswith("https://"), \
+                "URL must start with http:// or https://"
+
+        # Validate map directories if present
+        if "map" in cfg:
+            assert isinstance(cfg["map"], list), "map must be a list"
+            valid_mappings = ["config", "ssl", "addons", "backup", "share", "media"]
+            for mapping in cfg["map"]:
+                # Handle both "config:rw" and "config" formats
+                base_mapping = mapping.split(":")[0]
+                assert base_mapping in valid_mappings, \
+                    f"Invalid map directory: {base_mapping}"
+
+        print("āœ“ Add-on configuration validation passed")
+        print(f"  Name: {cfg['name']}")
+        print(f"  Version: {cfg['version']}")
+        print(f"  Slug: {cfg['slug']}")
+        print(f"  Architectures: {', '.join(cfg['arch'])}")
+        if "startup" in cfg:
+            print(f"  Startup: {cfg['startup']}")
+        if cfg.get("ingress"):
+            print(f"  Ingress: enabled on port {cfg['ingress_port']}")
+
+    def test_ingress_configuration_consistent(self):
+        """If ingress is enabled, ensure port configuration is correct."""
+        cfg_path = self.root / "config.yaml"
+        with open(cfg_path) as f:
+            cfg = yaml.safe_load(f)
+
+        if not cfg.get("ingress"):
+            pytest.skip("Ingress not enabled")
+
+        # If ingress is enabled, check configuration
+        assert "ingress_port" in cfg, "ingress_port must be specified when ingress is enabled"
+
+        ingress_port = cfg["ingress_port"]
+
+        # The ingress port should NOT be in the ports section
+        ports = cfg.get("ports", {})
+        port_key = f"{ingress_port}/tcp"
+
+        if port_key in ports:
+            pytest.fail(
+                f"Port {ingress_port} is used for ingress but also listed in 'ports' section. "
+                f"Remove it from 'ports' to avoid conflicts."
+            )
+
+        print(f"āœ“ Ingress configuration valid (port {ingress_port})")
diff --git a/tests/test_logging.py b/tests/test_logging.py
index 9a40903..441691d 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -8,7 +8,7 @@ from unittest.mock import patch
 import pytest
 from loguru import logger
 
-from akkudoktoreos.core.logging import track_logging_config
+from akkudoktoreos.core.logging import logging_track_config
 
 # -----------------------------
 # logsettings
@@ -20,14 +20,14 @@ class TestLoggingCommonSettings:
         logger.remove()
 
     def test_valid_console_level_sets_logging(self, config_eos, caplog):
-        config_eos.track_nested_value("/logging", track_logging_config)
+        config_eos.track_nested_value("/logging", logging_track_config)
         config_eos.set_nested_value("/logging/console_level", "INFO")
         assert config_eos.get_nested_value("/logging/console_level") == "INFO"
         assert config_eos.logging.console_level == "INFO"
         assert any("console: INFO" in message for message in caplog.messages)
 
     def test_valid_console_level_calls_tracking_callback(self, config_eos):
-        with patch("akkudoktoreos.core.logging.track_logging_config") as mock_setup:
+        with patch("akkudoktoreos.core.logging.logging_track_config") as mock_setup:
             config_eos.track_nested_value("/logging", mock_setup)
             config_eos.set_nested_value("/logging/console_level", "INFO")
             assert config_eos.get_nested_value("/logging/console_level") == "INFO"
diff --git a/tests/test_server.py b/tests/test_server.py
index 8fbe41b..772fcca 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -1,3 +1,4 @@
+import asyncio
 import json
 import os
 import signal
@@ -44,73 +45,116 @@ class TestServer:
 
 
 class TestServerStartStop:
-    def test_server_start_eosdash(self, tmpdir):
-        """Test the EOSdash server startup from EOS."""
-        # Do not use any fixture as this will make pytest the owner of the EOSdash port.
-        host = get_default_host()
-        port = 8503
-        eosdash_host = host
-        eosdash_port = 8504
+
+    @pytest.mark.asyncio
+    async def test_server_start_eosdash(self, config_eos, monkeypatch, tmp_path):
+        """Test the EOSdash server startup from EOS.
+
+        Do not use any fixture as this will make pytest the owner of the EOSdash port.
+
+        Tests that:
+        1. EOSdash starts via the supervisor
+        2. The /eosdash/health endpoint returns OK
+        3. EOSdash reports correct status and version
+        4. EOSdash can be terminated cleanly
+        """
+        eos_dir = tmp_path
+        monkeypatch.setenv("EOS_DIR", str(eos_dir))
+        monkeypatch.setenv("EOS_CONFIG_DIR", str(eos_dir))
+
+        # Import with environment vars set to prevent creation of EOS.config.json in wrong dir.
+        from akkudoktoreos.server.rest.starteosdash import run_eosdash_supervisor
+
+        config_eos.server.host = get_default_host()
+        config_eos.server.port = 8503
+        config_eos.server.eosdash_host = config_eos.server.host
+        config_eos.server.eosdash_port = 8504
         timeout = 120
 
-        server = f"http://{host}:{port}"
-        eosdash_server = f"http://{eosdash_host}:{eosdash_port}"
-        eos_dir = str(tmpdir)
+        eosdash_server = f"http://{config_eos.server.eosdash_host}:{config_eos.server.eosdash_port}"
 
         # Cleanup any EOS and EOSdash process left.
-        cleanup_eos_eosdash(host, port, eosdash_host, eosdash_port, timeout)
-
-        # Import after test setup to prevent creation of config file before test
-        from akkudoktoreos.server.eos import start_eosdash
-
-        # Port may be blocked
-        assert wait_for_port_free(eosdash_port, timeout=120, waiting_app_name="EOSdash")
-
-        process = start_eosdash(
-            host=eosdash_host,
-            port=eosdash_port,
-            eos_host=host,
-            eos_port=port,
-            log_level="DEBUG",
-            access_log=False,
-            reload=False,
-            eos_dir=eos_dir,
-            eos_config_dir=eos_dir,
+        cleanup_eos_eosdash(
+            host=config_eos.server.host,
+            port=config_eos.server.port,
+            eosdash_host=config_eos.server.eosdash_host,
+            eosdash_port=config_eos.server.eosdash_port,
+            server_timeout=timeout,
         )
 
-        # Assure EOSdash is up
+        # Port may be blocked
+        assert wait_for_port_free(config_eos.server.eosdash_port, timeout=120, waiting_app_name="EOSdash")
+
+
+        """Start the EOSdash supervisor as a background task for testing."""
+        task = asyncio.create_task(run_eosdash_supervisor())
+
+        # give the supervisor some time to begin starting EOSdash
+        await asyncio.sleep(1)
+
+        # ---------------------------------
+        # Wait for health endpoint to come up
+        # ---------------------------------
         startup = False
         error = ""
+
         for retries in range(int(timeout / 3)):
             try:
-                result = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
-                if result.status_code == HTTPStatus.OK:
+                resp = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
+                if resp.status_code == HTTPStatus.OK:
                     startup = True
                     break
-                error = f"{result.status_code}, {str(result.content)}"
+                error = f"{resp.status_code}, {str(resp.content)}"
             except Exception as ex:
                 error = str(ex)
-            time.sleep(3)
 
-        assert startup, f"Connection to {eosdash_server}/eosdash/health failed: {error}"
-        health = result.json()
-        assert health["status"] == "alive"
-        assert health["version"] == __version__
+            await asyncio.sleep(3)
 
-        # Shutdown eosdash
+        # Graceful shutdown of the background task
+        # Do it before any assert
+        task.cancel()
         try:
-            result = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
-            if result.status_code == HTTPStatus.OK:
-                pid = result.json()["pid"]
-                os.kill(pid, signal.SIGTERM)
-                time.sleep(1)
-                result = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
-                assert result.status_code != HTTPStatus.OK
-        except:
+            await task
+        except asyncio.CancelledError:
             pass
 
-        # Cleanup any EOS and EOSdash process left.
-        cleanup_eos_eosdash(host, port, eosdash_host, eosdash_port, timeout)
+        assert startup, f"Connection to {eosdash_server}/eosdash/health failed: {error}"
+
+        health = resp.json()
+        assert health.get("status") == "alive"
+        assert health.get("version") == __version__
+
+        # ---------------------------------
+        # Shutdown EOSdash (as provided)
+        # ---------------------------------
+        try:
+            resp = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
+            if resp.status_code == HTTPStatus.OK:
+                pid = resp.json().get("pid")
+                assert pid is not None, "EOSdash did not report a PID"
+
+                os.kill(pid, signal.SIGTERM)
+                time.sleep(1)
+
+                # After shutdown, the server should not respond OK anymore
+                try:
+                    resp2 = requests.get(f"{eosdash_server}/eosdash/health", timeout=2)
+                    assert resp2.status_code != HTTPStatus.OK
+                except Exception:
+                    pass  # expected
+        except Exception:
+            pass  # ignore shutdown errors for safety
+
+        # ---------------------------------
+        # Cleanup any leftover processes
+        # ---------------------------------
+        cleanup_eos_eosdash(
+            host=config_eos.server.host,
+            port=config_eos.server.port,
+            eosdash_host=config_eos.server.eosdash_host,
+            eosdash_port=config_eos.server.eosdash_port,
+            server_timeout=timeout,
+        )
 
     @pytest.mark.skipif(os.name == "nt", reason="Server restart not supported on Windows")
     def test_server_restart(self, server_setup_for_function, is_system_test):
diff --git a/tests/test_version.py b/tests/test_version.py
index da8c939..e4da105 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -6,6 +6,8 @@ from pathlib import Path
 import pytest
 import yaml
 
+from akkudoktoreos.core.version import _version_calculate, _version_hash
+
 DIR_PROJECT_ROOT = Path(__file__).parent.parent
 GET_VERSION_SCRIPT = DIR_PROJECT_ROOT / "scripts" / "get_version.py"
 BUMP_DEV_SCRIPT = DIR_PROJECT_ROOT / "scripts" / "bump_dev_version.py"
@@ -18,6 +20,54 @@ def write_file(path: Path, content: str):
     return path
 
 
+# --- Test version helpers ---
+def test_version_non_dev(monkeypatch):
+    """If VERSION_BASE does not end with 'dev', no hash digits are appended."""
+    monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0")
+    result = _version_calculate()
+    assert result == "0.2.0"
+
+
+def test_version_dev_precision_8(monkeypatch):
+    """Test that a dev version appends exactly 8 digits derived from the hash."""
+    fake_hash = "abcdef1234567890"  # deterministic fake digest
+
+    monkeypatch.setattr("akkudoktoreos.core.version._version_hash", lambda: fake_hash)
+    monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
+    monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_PRECISION", 8)
+
+    result = _version_calculate()
+
+    # compute expected suffix
+    hash_value = int(fake_hash, 16)
+    expected_digits = str(hash_value % (10 ** 8)).zfill(8)
+
+    expected = f"0.2.0.dev{expected_digits}"
+
+    assert result == expected
+    assert len(expected_digits) == 8
+    assert result.startswith("0.2.0.dev")
+    assert result == expected
+
+
+def test_version_dev_precision_8_different_hash(monkeypatch):
+    """A different hash must produce a different 8-digit suffix."""
+    fake_hash = "1234abcd9999ffff"
+
+    monkeypatch.setattr("akkudoktoreos.core.version._version_hash", lambda: fake_hash)
+    monkeypatch.setattr("akkudoktoreos.core.version.VERSION_BASE", "0.2.0.dev")
+    monkeypatch.setattr("akkudoktoreos.core.version.VERSION_DEV_PRECISION", 8)
+
+    result = _version_calculate()
+
+    hash_value = int(fake_hash, 16)
+    expected_digits = str(hash_value % (10 ** 8)).zfill(8)
+    expected = f"0.2.0.dev{expected_digits}"
+
+    assert result == expected
+    assert len(expected_digits) == 8
+
+
 # --- 1ļøāƒ£ Test get_version.py ---
 def test_get_version_prints_non_empty():
     result = subprocess.run(
@@ -62,7 +112,7 @@ def test_bump_dev_version_appends_dev(tmp_path):
         check=True
     )
     new_version = result.stdout.strip()
-    assert new_version == "0.2.0+dev"
+    assert new_version == "0.2.0.dev"
 
     content = version_file.read_text()
     assert f'VERSION_BASE = "{new_version}"' in content
@@ -113,7 +163,7 @@ def test_workflow_git(tmp_path):
         check=True
     )
     dev_version = result.stdout.strip()
-    assert dev_version.endswith("+dev")
-    assert dev_version.count("+dev") == 1
+    assert dev_version.endswith(".dev")
+    assert dev_version.count(".dev") == 1
     content = version_file.read_text()
     assert f'VERSION_BASE = "{dev_version}"' in content
diff --git a/tests/testdata/optimize_result_1.json b/tests/testdata/optimize_result_1.json
index 03a7428..c3bf9e3 100644
--- a/tests/testdata/optimize_result_1.json
+++ b/tests/testdata/optimize_result_1.json
@@ -276,44 +276,44 @@
         "Gesamteinnahmen_Euro": 1.1542928225199272,
         "Gesamtkosten_Euro": 2.6531771286148773,
         "Home_appliance_wh_per_hour": [
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null,
-            null
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0,
+            0.0
         ],
         "Kosten_Euro_pro_Stunde": [
             0.027996119999999992,
diff --git a/tests/testdata/optimize_result_2.json b/tests/testdata/optimize_result_2.json
index 17d5946..e28ec7e 100644
--- a/tests/testdata/optimize_result_2.json
+++ b/tests/testdata/optimize_result_2.json
@@ -232,7 +232,7 @@
             1232.67,
             871.26,
             860.88,
-            2658.03,
+            2658.0299999999997,
             1222.72,
             1221.04,
             949.99,
@@ -396,7 +396,7 @@
             4.174095896658514e-14,
             0.0003442778967139274,
             0.0,
-            0.3686370794492921,
+            0.368637079449292,
             0.0,
             0.08231598,
             0.174597189,
@@ -436,7 +436,7 @@
             2.270998855635753e-10,
             1.7179535764168035,
             0.0,
-            1623.9518918471017,
+            1623.9518918471015,
             0.0,
             257.64,
             566.69,