The setup assistant only covered the location, the horizons and the energy
management, and sent the user to the Config page for everything else.
Add the steps PV system, weather, prices and fees, load, devices and
measurements, each with an explanation, an example and a check of what is
still to do.
Add a Home Assistant step after the measurements step that explains what EOS
reads from and writes to Home Assistant, with a template sensor and an automation
as example. Its status reports a missing state of charge mapping, missing instruction
entities and an adapter that is selected while EOS does not run as an add-on. The
measurements step keeps the choice of the adapter.
Add examples for an inverter, an electric vehicle and a home appliance.
A step can now show configuration values depending on the value of another
one (WizardChoice), so that only the settings of the selected provider are
shown. The horizon of the selected optimization algorithm uses the same
mechanism instead of a placeholder in the configuration name.
Describe the profiles, point to the Profiles page and use the
profile_adjustment settings in the examples. The packaged profiles are
generic and give no size, so the devices step now reports a battery,
electric vehicle or inverter whose effective nominal capacity or power is
unknown. The changelog entry names the Home Assistant step.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Assisted-by: Claude Code (claude-fable-5-2)
The setup assistant said a changed value is only written to the configuration file by the save button of the last step. By default EOS saves configuration changes on its own (general.config_save_mode AUTOMATIC). Say so, and show general.config_save_mode in the last step.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Assisted-by: Claude Code (claude-fable-5-2)
New users have to find the settings EOS needs among all configuration values
on the Config page, without a hint which ones matter or what is still missing.
The new Setup page takes the user through the configuration step by step.
Each step explains a few settings, gives an example, reports what is still to
do and shows the configuration cards of exactly these settings. The cards are
the ones of the Config page; the code that creates a card and sends an update
moved out of Configuration() into config_card() and config_update().
The assistant keeps no state: the step travels with the requests and the
status is derived from the EOS configuration, which is read and written
through the EOS server only.
First steps: location, horizons and energy management, save the configuration.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Assisted-by: Claude Code (claude-fable-5-2)
The configuration sync of the Home Assistant adapter skipped every value that is false in
Python, so an entity state of 0 or off never reached the configuration. It also set any
path it was given: the documented example devices/batteries/0/capacity_wh named a device
"0" that does not exist, so EOS silently added a new battery with a random device id.
Apply every value except an unavailable or unknown entity, and only update settings that
exist; report other paths as an error. The example now addresses the battery by its
device_id.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The automatic field migration recursed into every dict and addressed each
leaf by a "/"-joined path. Keys that themselves contain "/" - e.g. the config
paths used as keys of adapter.homeassistant.config_entity_ids - were split
into path elements. set_nested_value() then created a "devices" entry with
an empty string default and failed further down, so every version migration
replaced the mapping by {"devices": ""}.
Migrate a dict whose keys contain "/" as one value instead of per key.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No PV forecast modelled snow lying on the modules, so after a snowfall the
optimizer planned with PV that never came. The local Open-Meteo/pvlib
backend can now reduce the forecast by the snow coverage (opt-in via
pvforecast.akkudoktor.snow_enabled):
- Request hourly snowfall and snow depth with the weather and run the NREL
snow coverage model (pvlib.snow) per plane.
- Correct the coverage with measured PV production: production above the
model clears it at once; low production raises it only while snow is
plausible, so curtailment and inverter outages are not learned as snow.
Persisting snow damps the slide rate, fading with
snow_feedback_memory_hours.
- Rebuild the coverage from snow_lookback_days on every update instead of
storing state.
- Exclude intervals with possible snow from the calibration fit.
- Warn when PV meter readings are missing or stale; ignore impossible meter
jumps.
_plane_power keeps its two-value signature (used by the adaptive provider in
#1422); the irradiance comes from the new _plane_power_and_poa.
Tests replay real February 2026 data from one plant (snow on 02-19/20).
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
LFP batteries can only recalibrate their state of charge close to full
charge, so many systems expect a full charge every few weeks and some
start one on their own at an arbitrary, possibly expensive time. EOS can
now plan it instead (GENETIC only, off by default).
New battery settings: full_charge_interval_days (unset = off),
full_charge_soc_percentage, full_charge_detect_percentage,
full_charge_allow_grid and the informational cell_chemistry. The last
full charge is read from the measured SoC history; nothing is stored.
When the next one is due within the horizon (or overdue), the maximum
SoC is raised to the target. With full_charge_allow_grid the optimizer
must reach the target once before the deadline, which is at least 24 h
away (penalty battery_full_charge_miss on the best SoC reached), so it
picks the cheapest time itself. Without it, PV surplus may fill the
battery but the target is not enforced.
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(eosdash): plan page with EMS plan timeline and persisted settings
Fixes:
- SolutionCard no longer appends to the module-level solution_excludes
list on every render (the list grew without limit).
- All EOSdash pages: keep the scroll position after htmx actions instead
of jumping to the bottom; Bokeh() reserves the plot height.
- Default visible PV column pvforecast_power_w -> pvforecast_ac_energy_wh
(the old column no longer exists).
- All EOSdash pages: remove w-screen from components.Page (horizontal
scrollbar).
- Solution chart: energy is the main axis; the always empty power axis is
only shown with power columns. Typo "Electricty" fixed.
Features:
- EMS plan timeline per device, plan table as collapsible section.
- Solution chart rework: KPI row, legend, totals view, value axes for the
displayed columns only, units menu, currency from the timezone, aligned
time axes with the timeline, themed tooltips, system font.
- Per-column and per-command settings: visibility, alias, color, style;
device aliases.
- New config section eosdash.plan (server/dash/dashsettings.py) stores the
plan page settings in the EOS configuration; docs regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: SchaafAlexander <86607129+SchaafAlexander@users.noreply.github.com>