**********************
FlexMeasures Changelog
**********************
v1.2.0 | December xx, 2026
============================
.. warning:: Python 3.10 is no longer supported. Upgrade to Python 3.11 or 3.12 before upgrading FlexMeasures.
New features
-------------
Infrastructure / Support
-------------------------
Bugfixes
-----------
v1.1.0 | November 5, 2026
============================
.. note:: Read more on these features on `the FlexMeasures blog `_.
.. warning:: Upgrading to this version requires running ``flexmeasures db upgrade`` (you can create a backup first with ``flexmeasures db-ops dump``).
If you maintain indexes of your own on the ``timed_belief`` table, the upgrade will advise you on indexes it made redundant.
The upgrade also reorganises data sources, so prepare it as follows:
1. Run ``flexmeasures jobs check-source-references``, which lists jobs waiting in Redis that refer to a source the upgrade will merge away; wait for them or cancel them, as they would fail afterwards.
2. Stop your workers, and run the upgrade **before** deploying the new code: a worker still running the previous version records under a source belonging to no organisation.
3. Run the upgrade. It changes data sources in two ways:
- Each data source is coupled to the organisation it records for. Where a source recorded for several organisations, the upgrade stops and names those sources rather than picking one; rerun the upgrade with ``-x split-shared-sources=true`` to give each of those organisations its own source. A shared source of a generator version that a newer one has replaced is left belonging to no organisation instead, since nothing records under it again.
- Identical data sources are merged, as the database now refuses duplicates. Their beliefs, annotations and other references move over, and stored source IDs, such as the ``sources`` a sensor reference in a flex-context filters on, are pointed at the source they were merged into.
In both cases, the source that recorded most recently keeps its ID. Where something outside FlexMeasures refers to a source by ID, choose yourself with ``-x keep-source``, which you can repeat:
``-x keep-source=:`` names the organisation that keeps a shared source's ID, and ``-x keep-source=`` names the duplicate that keeps its ID.
The upgrade prints what it split and merged, so anyone referring to a source by ID can switch over [see `PR #2634 `_, `PR #2613 `_ and `PR #2758 `_]
.. note:: A scheduler's data source now also records the flex config it computed under, so a sensor can carry schedules from several configs, the way it already could for forecasts.
Select a data source on the sensor page to see the schedule computed under one configuration.
.. _v1.1.0-add-report-warning:
.. warning:: If you run ``flexmeasures add report`` (for instance, in a cron job), it now saves the reporter's config on the report's data source by default, so its first run after upgrading records under a new data source.
Point anything that refers to the old data source by ID at the new one, or add ``--no-save-config`` to keep recording under the old one [see `PR #2764 `_]
.. _v1.1.0-roles-warning:
.. warning:: The upgrade gives every existing user the new ``account-member`` role.
To give a user the narrower reader or data-integrator access instead, assign them the ``account-reader`` or ``account-data-integrator`` role, and then remove their ``account-member`` role.
.. warning:: The API's rate limits are now counted per organisation rather than per user, so all users and integrations of an organisation share one budget.
Before upgrading, hosts should check the limits of organisations with several users or integrations; see :ref:`Plans and rate limits `.
New features
-------------
**Automations**
* Automations: recurring tasks defined per asset, which compute forecasts, schedules or reports on a cron recurrence read in the automation's own timezone, defined from the CLI, the API or the UI, and dispatched once a minute by ``flexmeasures jobs run-automations``; see :ref:`automations` for what they do, and `Automations, in detail`_ for what each pull request contributed
**Data sources and sensor data**
* When uploading sensor data, you can now set the time prior to which the data was recorded, such as when moving historical data from another system together with its original recording times [see `PR #2722 `_ and `PR #2734 `_]
* Inspect the full record of the data source selected on a sensor page, backed by a new API endpoint (``[GET] /sources/(id)``) [see `PR #2290 `_]
**Plans and rate limits**
* Hover over an organisation's plan on its page to see its effective API rate limits [see `PR #2524 `_]
**Reporting and forecasting**
* The ``AggregatorReporter`` can report the measured aggregate of a device group, taking the group's devices, their sign convention and the output sensor from the flex-config that scheduling already uses, and can narrow the members to a category (such as every PV installation) [see `PR #2648 `_]
* Where some members of a device group did not report an event, its aggregate is computed from those that did, under a data source of its own that names the missing members; the asset's status page shows such an aggregate once it goes stale [see `PR #2648 `_ and `PR #2741 `_]
* Run one-off reports as background jobs from the CLI or the asset API, with sensor-level authorization and a dedicated reporting worker queue [see `PR #2298 `_]
* Try out a forecast without recording it, using ``flexmeasures add forecasts --dry-run``, which reports the sensor, data source, number of beliefs and event range it would have saved [see `PR #2483 `_]
* Tell a forecaster which data sources hold the truth about the sensor it forecasts, as its regressors already could, so that a sensor that several sources report on is trained on the ones you trust [see `PR #2542 `_ and `PR #2627 `_]
* Forecasters, schedulers and reporters can now clean the data they read with a ``lower``, ``upper`` or ``snap`` bound, so that a sensor with implausible readings can be used without first correcting it at the source [see `PR #2555 `_, `PR #2561 `_, `PR #2627 `_ and `PR #2655 `_]
**UI**
* The asset's status page splits its sensor data and its jobs over two tabs, of which only the opened one loads its data, and it opens the tab you last looked at [see `PR #2470 `_]
* Browse an asset's jobs page by page, so that status pages with long job histories load faster; the jobs API supports optional pagination, while existing requests still return the full list [see `PR #2619 `_]
* Both tabs of an asset's status page name the asset each row belongs to, and the jobs tab also lists the jobs of its sub-assets, which you can switch off per session [see `PR #2500 `_]
* Add annotation labels to sensor data from the sensor page, over a time range selected on the chart or at an instant clicked on it, and delete annotations from its **Delete data** panel, backed by a new API endpoint (``[DELETE] /sensors/(id)/annotations``) [see `PR #2570 `_]
* Annotation labels on sensor and asset charts no longer flicker, can be pinned by clicking their range, no longer overlap when pinned and hovered, and show their source and timing on hover; fast chart tooltips show only value and time by default (plus the sensor on asset charts), with a *Full belief info* switch for the rest [see `PR #2575 `_]
* Changing the selected time range on an asset or sensor chart only loads the data that is new, which makes stepping through or extending a long period much faster [see `PR #2433 `_]
* The statistics table on a sensor page shows all data sources together by default, as the graph does [see `PR #2462 `_]
* The asset lists on an organisation's page and on the asset overview show only top-level assets by default, so that sites are no longer buried among their sub-assets; untick *Top-level only* for the whole tree, or tick *Include public assets*, which is off by default [see `PR #2523 `_]
* Graph and flex-config sensor searches hide public assets unless *Include public assets* is checked, and wait for a keyword, unit or explicit request before loading results [see `PR #2775 `_]
* The audit log of a device group (the asset that devices name as their ``group`` in their flex-model) notes which devices join or leave it, also when a member or the group moves to another site, and a device whose group is deleted gets a note, too [see `PR #2723 `_ and `PR #2769 `_]
* When a report automation aggregates only one asset type of a device group, the group's audit log notes which members it starts with, and which enter or leave that selection as their asset type changes [see `PR #2724 `_]
Infrastructure / Support
-------------------------
**Data sources and the upgrade**
* A data source now belongs to the organisation it records for, where only ``user`` sources used to, so that two organisations running the same data generator under the same configuration each record under their own source, and a ``source-account`` filter also finds generated data; the upgrade couples existing sources to the organisation of the assets their data sits on [see `PR #2634 `_]
* Who may read a data source is now decided per source: a source is readable when it belongs to an organisation you may read, when an automation you may read computes under it, or when it recorded data on a sensor you may read, so that a source belonging to no organisation is no longer listed for everyone (reading its data still depends on the sensor, as before); the configuration a data generator stores on its source is only handed over in the first two cases, as it names the sensors the generator reads from and records on [see `PR #2584 `_]
* ``flexmeasures.data.utils.get_data_source``, which plugins and scripts use to make sure they have a data source, now takes the organisation that source belongs to, as ``get_or_create_source`` does; naming none asks for a source belonging to no organisation, which is what a script the host runs for everyone records under [see `PR #2613 `_]
* Drop the nine obsolete tables that predate the ``GenericAsset``/``Sensor`` data model, asking you to confirm first if any of them still hold data, as v0.18.0 missed two of them, and a database downgraded past that release and upgraded again kept all nine [see `PR #2475 `_]
* Speed up sensor data queries and free up disk space by reordering the ``timed_belief`` primary key to lead with ``sensor_id`` and dropping the indexes it makes redundant, in a migration that runs online and so needs no maintenance window (though it can take a while on a large database) [see `PR #2378 `_]
.. _v1.1.0-rate-limits:
**Plans and rate limits**
* The API's default rate limit is now counted per organisation rather than per user, so that all users and integrations (anything calling the API with a token) of an organisation share the one budget its plan sets; it stays at 500 requests per minute, which hosts whose organisations run several integrations may want to raise on those organisations' plans (``default_rate_limit``) or server-wide (``FLEXMEASURES_API_DEFAULT_RATE_LIMIT``) before upgrading [see `PR #2677 `_]
* Triggers for schedules and forecasts are now always counted per organisation, so that the ``FLEXMEASURES_API_RATE_LIMIT_KEY`` setting is deprecated and ignored; hosts who set it (or a plan's rate-limit key) to ``account+asset`` or ``user`` may want to raise the trigger rate limit of the affected plans, or ``FLEXMEASURES_API_TRIGGER_RATE_LIMIT``, before upgrading [see `PR #2679 `_]
* The UI's own calls to the API count against a budget per user (``FLEXMEASURES_UI_RATE_LIMIT``, 100 requests per minute by default) rather than against their organisation's, so that an integration which uses up its organisation's budget cannot lock that organisation's users out of the UI; plans do not change this budget [see `PR #2700 `_]
* Hosts can reset an organisation's rate limits with ``flexmeasures edit reset-rate-limit``, so that an organisation whose integration has been fixed need not wait out the rest of the window [see `PR #2680 `_]
* A rate limit (on a plan, or in ``FLEXMEASURES_API_DEFAULT_RATE_LIMIT`` or ``FLEXMEASURES_API_TRIGGER_RATE_LIMIT``) now holds one limit: several limits separated by semicolons used to be accepted without being documented, but are now refused, and a server with such a setting no longer starts [see `PR #2680 `_]
* A request refused for hitting a rate limit is logged with who made it (organisation and user), on which endpoint, against which limit and, for the trigger limit, about which asset, and the organisation's audit log records one breach per limit window, with the plan, the effective limit and how far into the window it occurred; the plan tooltip notes when play mode disables both limits [see `PR #2678 `_]
**Reporting and forecasting**
* ``flexmeasures add report`` saves the reporter's config on its data source by default, as the API and automations do, so that reports computed under different configs no longer share one data source; its first run after upgrading saves all of its beliefs once more, under the new data source (see :ref:`this warning `) [see `PR #2764 `_]
* Every kind of data generator (e.g. a ``Forecaster``, ``Scheduler`` or ``Reporter``) now has its results checked and saved in the same way: all of a run's outputs are saved or none of them; a plugin's data generator that records on a sensor it was not checked against now fails with ``GeneratorWritesUncheckedSensor`` [see `PR #2689 `_ and `PR #2571 `_]
* Separate the built-in forecast computation from the saving of its results, keeping per-cycle commits and existing plugin entry points, so that a synchronous dry run creates no data sources [see `PR #2645 `_]
* A forecast that repeats the belief right before it is no longer stored again, like any other data; plugins that save forecasts through ``save_forecast`` can pass ``save_changed_beliefs_only=False`` to record every belief [see `PR #2688 `_]
* A forecast hands its trained model to the prediction in memory, and keeps it as a file only when ``model-save-dir`` is given; hosts can delete the ``flexmeasures/data/models/forecasting/artifacts/models`` folder (relative to the worker's working directory) after upgrading, where each cycle used to leave a model behind, often hundreds of MB [see `PR #2701 `_]
* Preparing a forecast's inputs is faster [see `PR #2640 `_]
* Report parameters name their belief time ``prior``, as schedule and forecast parameters do; the name ``belief_time`` is still accepted, while the ``belief_time`` of an ``input`` entry, which selects the beliefs to report on rather than stamping the report, is not renamed by this change [see `PR #2551 `_]
**Scheduling**
* A schedule whose commitments bind the same devices against the same baseline over the same time steps now solves them as one commitment priced on their summed prices, so that more such schedules stay a linear program, and a smaller one, while each commitment is still reported with its own costs [see `PR #2635 `_]
* Speed up preparing a storage schedule by validating device constraints with vectorised comparisons (from ~25 ms to ~3 ms per device); the unused helpers ``validate_constraint`` and ``sanitize_expression`` in ``flexmeasures.data.models.planning.storage`` are removed, with ``validate_constraints`` replacing them [see `PR #2641 `_]
* ``MetaStorageScheduler``, an internal base class that was never meant to be selected, is no longer registered and can no longer be named as a custom scheduler; name ``StorageScheduler`` instead [see `PR #2566 `_]
* ``flexmeasures.data.models.planning.linear_optimization`` no longer exposes pyomo's ``UnknownSolver``, which nothing in FlexMeasures used; import it from ``pyomo.environ`` instead [see `PR #2565 `_]
**Access and security**
* Authorization now uses named permissions, granted by user roles defined in code and checked against each resource's ACL: new ``account-reader`` and ``account-data-integrator`` roles give scoped access within the user's own organisation, the ``consultant`` role grants access only through consultancy ACLs on client organisations, and custom roles named in an ACL keep the four preexisting permissions on matching resources; the upgrade gives existing users the ``account-member`` role and provisions the narrower roles (see :ref:`this warning `); users with the ``account-reader`` role cannot start password recovery themselves (an organisation admin or consultant can start a reset for them), users from other organisations can no longer open a sensor page they cannot read, and the user page shows each role's description in a tooltip [see `PR #2607 `_ and `PR #2720 `_]
* Logging out now takes a POST request, so that another site cannot log users out by linking to ``/logout``; the UI's *Log out* button submits a form, and plugins that link to ``/logout`` should do the same [see `PR #2604 `_]
* Upgrade certifi and Werkzeug for security reasons; hosts running the Docker image should pull the new image to get certifi's corrected certificate bundle, and hosts listing an IPv6 address in ``TRUSTED_HOSTS`` should know that this version of Werkzeug rejects requests to it (see `Werkzeug issue #3312 `_) [see `PR #2750 `_]
* Stop installing the libraries for passkey, authenticator-app and SMS sign-in, which FlexMeasures does not support, as it ships email-based two-factor authentication only; to enable the ``authenticator`` method anyway, install ``qrcode`` [see `PR #2564 `_]
* Durations for Flask-Security, such as ``SECURITY_TWO_FACTOR_LOGIN_VALIDITY``, can now also be given as an ISO 8601 duration (e.g. ``"P1W"``), and in weeks (e.g. ``"1 week"``) on any version of Flask-Security [see `PR #2596 `_]
**API and plugins**
* The endpoints supporting the UI moved from ``/api/dev`` to ``/api/ui``, where the old prefix keeps working until FlexMeasures v2 [see `PR #2578 `_]
* ``[DELETE] /sensors/(id)/data`` now takes ``end`` for the end of the period to delete, as ``[DELETE] /sensors/(id)/annotations`` and every other time window in the API do; ``until`` is still accepted, but deprecated [see `PR #2826 `_]
* ``[GET] /sensors/(id)`` now answers a sensor which does not exist with a 404 rather than a 422, as the asset endpoints do, so that a client can tell a sensor which is gone apart from a request it got wrong [see `PR #2650 `_]
* Add ``FLEXMEASURES_DEPRECATION_AND_SUNSET`` so hosts can configure deprecation and sunset dates and information links per deprecated API version [see `PR #2362 `_]
* Remove the last state-of-charge bookkeeping that served the sunset API versions 1 and 2 [see `PR #2665 `_]
* Settings that a plugin declares in its ``__settings__`` can now be set as environment variables, next to the config file (which still wins), and can declare a ``default`` to fall back to; a missing setting is reported with whether such a default applies [see `PR #2501 `_]
* ``asset.sensors``, and the other lists of related objects that had no order of their own (such as ``account.users`` or ``asset.child_assets``), now list them in order of creation (by id), so that code which takes one by position gets the same one on every database [see `PR #2637 `_]
**Performance**
* Speed up saving beliefs that overlap stored data, such as a re-computed schedule or re-posted measurements, which cuts saving a week of 15-minute beliefs from ~5 s to ~10 ms [see `PR #2639 `_]
* Look up which data sources recorded for which sensors from a small summary table instead of scanning the beliefs table [see `PR #2382 `_]
* The asset page no longer slows down with the amount of data a sensor holds while working out how up to date each of its sensors is [see `PR #2463 `_]
* Speed up ``GET /api/v3_0/assets`` on large catalogs, where the number of database queries grew with the number of assets returned [see `PR #2515 `_]
* On an asset with charge points, the Default chart no longer loads the charging-session sensors it doesn't draw, and the Charge Points chart loads only the sensors it draws [see `PR #2790 `_]
* Speed up loading chart data for an asset with many sensors, by no longer repeating two queries for each sensor [see `PR #2759 `_]
* Speed up container startup and app boot, by installing FlexMeasures as a regular (non-editable) package in the Docker image, by letting ``gunicorn`` load the app once before forking its workers (in the Docker image and in the ``docker-compose.yml`` dev stack), by skipping a repeated database round trip at boot, and by deferring the ``altair`` import until a chart is drawn [see `PR #2512 `_, `PR #2757 `_ and `PR #2811 `_]
* Find the built-in schedulers, reporters and forecasters from an explicit list, instead of importing every module under ``flexmeasures.data.models`` at start-up, which shortens the boot time of every process and keeps an unrelated broken module from stopping the app; plugins keep being discovered as before [see `PR #2566 `_]
* Import pyomo, LightGBM and the Sentry helpers only when they are used, cutting about 0.35 s off the boot time of every process [see `PR #2565 `_]
* Speed up app boot by ~1 s by replacing the ``inflect`` dependency with the lighter ``inflection`` package; API error messages that list several values no longer place a comma before the final "and" or "or" [see `PR #2514 `_]
**Hosting and dependencies**
* Job workers now also log RQ's own messages, such as when each job starts and how it ended, naming each job by its ID [see `PR #2811 `_]
* The UI can work without internet access: the Docker image serves the UI's JavaScript and CSS libraries itself, and a pip-installed FlexMeasures fetches them in the background once it serves its first request, also after an upgrade bumps one of them, while files not fetched (yet) are loaded from CDNs, checked against their hashes (set ``FLEXMEASURES_FETCH_UI_LIBRARIES`` to ``False`` to switch fetching off, and see :ref:`install-ui-libraries` to fetch them by hand); the ``FLEXMEASURES_JS_VERSIONS`` setting now only lists the versions a host wants to differ, optionally with the hashes of their files, so plugins reading it should read the ``js_versions`` template variable instead [see `PR #2711 `_, `PR #2746 `_ and `PR #2747 `_]
* Support SQLAlchemy 2.1 and upgrade dependencies, including timely-beliefs 4.4 and Flask-Security-Too 5.9; a ``SQLALCHEMY_DATABASE_URI`` that names no driver (``postgresql://``) now connects through psycopg 3, so to keep using psycopg2, name it (``postgresql+psycopg2://``), while ``flexmeasures db-ops dump`` and ``restore`` now also work with a URI that names a driver [see `PR #2596 `_, `PR #2601 `_ and `PR #2808 `_]
* Upgrade Vega, which draws the UI's charts, from 5.22.1 to 5.33.1 [see `PR #2743 `_]
* Upgrade DataTables, which draws the UI's lists, from 1.10.22 to 1.13.11 [see `PR #2744 `_]
* Shrink the Docker image by excluding dev-only dependencies, pruning the ``docs`` and ``examples`` bundled by ``sktime``/``scikit-base``, stripping the symbol tables of compiled extensions, and dropping the ``forecast`` dependency extra of ``timely-beliefs``, whose ``sktime``-based belief formation FlexMeasures never used [see `PR #2438 `_, `PR #2439 `_ and `PR #2440 `_]
* Require an exact ``uv`` version (``0.12.7``) via ``[tool.uv].required-version``, which keeps ``uv.lock`` changes reproducible, but means that anyone running ``uv`` in a FlexMeasures checkout, plugin developers and self-hosters included, needs that same version [see `PR #2451 `_]
* A CLI command called with an invalid option value now logs one error line, so that a cron job which captures only the log file still records why the command failed [see `PR #2544 `_]
**Development and documentation**
* Speed up the test suite two to three times; plugins that reuse the ``setup_accounts``, ``setup_roles_users`` and similar fixtures should know that these now commit their data, and that the schema is created once per test session [see `PR #2638 `_]
* The UI's JavaScript modules can now be tested, by running them in a headless browser from pytest, without adding a Node.js toolchain [see `PR #2435 `_]
* Test much more of the UI's JavaScript, moving the asset tree's layout and the flex-context editor's logic out of their templates into modules [see `PR #2574 `_]
* A test now holds new API and CLI field names to kebab-case, including the parameters and body properties named in the published OpenAPI specs, listing the names that predate the convention so that the list can only shrink [see `PR #2547 `_ and `PR #2826 `_]
* ``uv run poe clean-db`` now hands the database it creates to the ``--db-user``, so that setting up a development database also works on PostgreSQL 15 and newer [see `PR #2590 `_]
* The developer and hosting docs now say plainly that the Cbc solver is a system dependency, needed for the tests and for Cbc-based scheduling, and give the command to install it on Debian/Ubuntu, Fedora and macOS [see `PR #2597 `_]
* Add a concepts page on data pipelining, which describes in one place how time-series data is referenced (sensor references, source filters, fallback values), cleaned on the way in and bounded on the way out [see `PR #2721 `_]
* The README and docs index describe FlexMeasures as a scheduling and optimization engine, rather than as a whole EMS [see `PR #2751 `_]
Bugfixes
-----------
**Automations**
* Offset chains, as in an automation's window or ``flexmeasures add report --start-offset``, now count days as calendar days across a DST transition, so that a window no longer collapses or skips a day when run in the hour around midnight on such a weekend, and ``HB`` and ``DB`` no longer raise an error in the repeated hour or in a DST gap [see `PR #2771 `_]
**Data sources and sensor data**
* Two identical scheduling requests arriving at the same moment no longer create two data sources for one scheduler configuration, which made every later schedule under it fail; such requests now share one job, and a deployment that already has such duplicates schedules again [see `PR #2612 `_]
* Where several data sources report the same event, which one a search keeps is now decided the same way every time: a source version only counts against other versions of that source, and a caller that lists its sources gets the order it asked for [see `PR #2494 `_]
* Saving several beliefs about the same event at once no longer drops a belief whose value changes back to an earlier one; ``flexmeasures edit resample-data`` lost such beliefs for good [see `PR #2687 `_]
* Posting or uploading sensor data with a ``prior`` beyond the year 2262 is refused with an explanation, where it used to fail with a server error [see `PR #2735 `_]
* ``GenericAsset.set_attribute`` and ``Sensor.set_attribute``, which plugins and scripts use, now create an attribute that does not exist yet, instead of silently ignoring it [see `PR #2649 `_]
* A sensor reference in a flex-model or flex-context now refuses a key it does not take, naming the nearest key it does take, where it used to read past it, so that a source filter written in the singular (``source: 42`` rather than ``sources: [42]``) silently did not filter; a stored flex config carrying such a key starts failing, which is how it stops being ignored, and `Notation `_ sets out which spelling belongs where [see `PR #2656 `_]
* Deleting a sensor now unsets it in the flex-model and flex-context references to it that carry a default, bounds or source filters, which used to keep pointing at the deleted sensor; until a new sensor is picked, schedules use the reference's default as a fixed value, or, without a default, ignore the field, so that an ancestor asset's value for it applies, and the UI marks such fields and lists them when you delete a sensor [see `PR #2726 `_, `PR #2767 `_ and `PR #2783 `_]
**Reporting and forecasting**
* An ``AggregatorReporter`` input that holds more than one belief about an event, such as one read with ``most-recent-beliefs-only: false``, is now refused with a message naming the sensor and the event, where it used to fail with a ``KeyError`` [see `PR #2741 `_]
* A ``PandasReporter`` with ``droplevels`` no longer fails with ``BeliefDataframe has more than one row per event`` when several data sources reported the same input event, as happens for a while when a job reporting on a rolling window gets a new data source; it keeps one belief per event, unless an input sets ``one_deterministic_belief_per_event`` to ``false`` [see `PR #2595 `_]
* The results of reporting and scheduling jobs now list, per output sensor, the beliefs computed and those recorded, so that a rerun which only repeats what is on record reads as having recorded nothing, rather than as having computed nothing; a schedule's ``num-beliefs`` still totals the beliefs computed, now also on a CLI dry run [see `PR #2689 `_, `PR #2571 `_ and `PR #2822 `_]
* A forecaster registered by a plugin no longer needs to save its forecasts itself, but can return them instead: ``flexmeasures add forecasts`` now saves them, where it used to drop them while reporting them as created [see `PR #2689 `_ and `PR #2571 `_]
* Running the same forecast twice from the same start no longer fails on the beliefs it already recorded [see `PR #2688 `_]
* Whether a forecaster takes a regressor's belief as realized now follows the knowledge horizon of the regressor's sensor, as in timely-beliefs, so that the past readings of an instantaneous regressor (such as a thermometer read on the hour) and day-ahead prices (beliefs about future periods, which count as realized once the auction has cleared) are no longer kept from the model [see `PR #2692 `_]
* A forecaster's ``missing-threshold`` no longer lets through more missing data than it allows, where data missing every other row counted as complete, and forecasting with a regressor recorded instantaneously no longer fails on a division by zero [see `PR #2640 `_]
* A forecaster given two or more past regressors, or two or more future regressors, no longer trains on each of them several times over [see `PR #2560 `_]
**Scheduling**
* Scheduling workers no longer print the HiGHS solver's banner and log to the console, unless ``LOGGING_LEVEL`` is ``DEBUG``, and setting ``output_flag`` in ``FLEXMEASURES_LP_SOLVER_OPTIONS`` now also suppresses the banner; with ``appsi_highs`` (or another Pyomo appsi solver), the solver's output no longer passes through FlexMeasures' log handlers, where it could hang a worker, but goes to stdout only, so it no longer reaches the log file [see `PR #2794 `_ and `PR #2807 `_]
* A schedule with several commitments, one of which pays more for deviating downwards than it costs to deviate upwards, no longer comes back as infeasible or unbounded, as each commitment's prices are now checked on their own rather than added together [see `PR #2631 `_]
* With the ``highspy`` solver, a converter coupling several commodities (such as a CHP) on a site where any device has an operation mode is no longer capped at one unit of its normalised flow, which made a must-run converter infeasible and silently capped a flexible one [see `PR #2606 `_]
* A new commitment's prices now default to the currency of the price sensors in the flex-context, where they fell back to EUR unless a fixed price was set [see `PR #2574 `_]
* ``flexmeasures add schedule --dry-run`` combined with ``--as-job`` is now rejected, where the flag used to be dropped and the queued job saved its schedule anyway, and a dry run says how many beliefs it would have saved and which events they cover [see `PR #2483 `_]
**UI**
* Remember the selected graph date range when navigating between sensors and assets, including when using fast charts or cached data [see `PR #2774 `_]
* Keep the current-time bar visible during sensor chart playback when no data is visible yet [see `PR #2722 `_]
* The sensor upload form now reports whether a queued upload succeeded, and a refused replacement of existing values is explained rather than shown as a database error [see `PR #2722 `_]
* In every chart, a hovered or pinned annotation is highlighted in the secondary colour and shift-click pins several annotations at once, annotation text is shown in near-black again for readability, and instantaneous annotations can be hovered and pinned in Vega-Lite charts, too [see `PR #2570 `_]
* A chart of a sensor with a single data point in view, such as a daily sensor of which only one day was recorded, drew that point as a bar spanning most of the chart; a bar now covers exactly the event it reports [see `PR #2511 `_]
* Saving an asset chart as PNG or SVG no longer draws the legend over the graph when it lists many sensors, and the exported image spells out the sensor names that the on-screen legend abbreviates [see `PR #2517 `_]
* Chart requests for a sensor or an asset now accept only the names of chart types, and building a chart no longer changes the chart settings that later charts start from, such as a heatmap moving the legend of later charts to the right [see `PR #2801 `_]
* The flex-config and graph dialogues now say so when a configuration refers to a sensor which no longer exists, where they used to show it as a sensor with an undefined name and unit: each dialogue names the dangling reference and lets you remove it, and an asset's status page no longer fails to load over a flex-context field whose sensor is gone [see `PR #2650 `_]
* An asset's status page showed only some of the sensors it reported on, in an order that changed between reloads, and it reported on fixed quantities from the flex-context, which have no data to be up to date with [see `PR #2489 `_]
* The asset status page no longer reports "no data recorded" for a sensor recorded by a source type it did not know about (such as the ``CLI script`` source of ``flexmeasures add beliefs``), shows a red light for data that is not knowable yet only for sources expected to deliver future data (such as forecasters and schedulers), no longer claims that a sensor listed because the flex-context or graphs refer to it belongs to the asset, and explains its traffic lights on hover again [see `PR #2474 `_]
* A KPI on the asset page no longer counts an event once per data source that reported it, which could make a total come out higher than any source reported [see `PR #2472 `_]
* The time range sent when loading an asset's KPIs was off by the viewer's UTC offset, so KPIs could cover the wrong days [see `PR #2435 `_]
* Asset type groups on the asset pages now read ``PVs``, ``EVs``, ``CHPs`` and ``Factories``, rather than ``PVS``, ``EVS``, ``CHPS`` and ``Factorys`` [see `PR #2514 `_]
* Show dates in the organisation audit log in the same local format as the asset and user audit logs, with the full date and time on hover [see `PR #2664 `_]
* Organisation audit logs now show only changed fields with their previous and new values, and user role and active-status changes identify the affected user [see `PR #2522 `_]
* Creating an organisation now records the action in its audit log, whether it is created in the UI, through the API or from the CLI [see `PR #2580 `_]
* A chart row that combined a sensor with a fixed value from the flex-model or flex-context, such as ``soc-min``, drew the sensor's data with sloped lines between events, as if the sensor were instantaneous, instead of as steps [see `PR #2356 `_ and `issue #2253 `_]
**Access and security**
* Allow consultants to create assets in client organisations, and reject attempts to change an asset's organisation through the API [see `PR #2786 `_ and `PR #2825 `_]
* A page failing on an unexpected error now shows the error page, where browsers behind a proxy reported a broken or insecure connection instead, and such an error no longer reveals the database query that failed [see `PR #2596 `_]
**API, CLI and jobs**
* ``[GET] /sensors`` lists sensors in order of their ID, where the order used to be up to the database [see `PR #2756 `_]
* ``GET /api/v3_0/assets`` without a sort parameter no longer skips assets when paging [see `PR #2583 `_]
* ``GET /api/v3_0/assets/(id)/jobs`` now reports its Redis connection error as ``redis-connection-err`` rather than ``redis_connection_err``, as the automation endpoints do [see `PR #2545 `_]
* A failed job now says why in the same way on every queue, in the API, the CLI and the asset page's jobs table: a failed forecasting job no longer reads ``dict: {...}`` and now shows its timeout hint, and a failed report or plugin automation no longer reads ``str: ...`` [see `PR #2698 `_]
* Fix ``flexmeasures show beliefs`` using the timezone of the first sensor instead of the one given with ``--timezone``; by default, the CSV file now has its timestamps in the timezone of the first sensor with data, with a UTC offset, instead of in UTC without an offset [see `PR #2695 `_ and `PR #2793 `_]
* Database migration command errors now appear in CLI output, including the error when multiple Alembic heads exist [see `PR #2607 `_]
* A URL under ``/api`` that matches no route, or a request with a method the route does not take, is now answered with a JSON ``404`` or ``405``, also for requests without a JSON content type, instead of the HTML error page [see `PR #2618 `_]
* Several FlexMeasures processes starting at once right after a database migration, such as the web server's workers and a CLI command, no longer fail to start because each tried to create the same starter templates [see `PR #2600 `_]
**Hosting**
* In the Docker image, ``pip install`` (which the docs and the compose stack use to add plugins) now installs where FlexMeasures can import it [see `PR #2811 `_]
* Fix local documentation builds and stop immediately when a build step fails [see `PR #2827 `_]
* Fix belief deletion and the solar, process and reporting tutorial runners so they work with current data sources and SQLAlchemy [see `PR #2831 `_]
Automations, in detail
-----------------------
Automations arrived over several pull requests. This is what each of them contributed.
* The first roundtrip, for forecasts: an automation defined per asset, with new CLI commands (``flexmeasures add|edit|delete automation``), a dispatcher (``flexmeasures jobs run-automations``), a UI page and read endpoints (``[GET] /assets/(id)/automations``). Each automation interprets its recurrence in its own timezone, and runs missed while the runner was down are caught up once, coalesced into one current forecast. An automation's details link to the sensors it reads from and writes to, a sensor's page lists the automations feeding it, and deleting a sensor warns about the automations that use it. Jobs now also record whether they were created via the CLI, the API or an automation [see `PR #2290 `_, `PR #2396 `_, `PR #2582 `_, `PR #2593 `_ and `PR #2628 `_]
* Schedules as well as forecasts: a schedule automation stores what the schedule trigger endpoint accepts, and schedules from each run's own time [see `PR #2293 `_]
* A scheduler's data source now also records the flex config the scheduler computed under, so a schedule can be traced back to the configuration that produced it, and a schedule automation points at such a data source, the way a forecast automation points at its forecaster's [see `PR #2464 `_]
* A single automation can now be run on demand, from the CLI (``flexmeasures jobs run-automation``), the API (``POST /assets//automations//trigger``) and the asset's *Automations* page (a *Run now* button), for instance to try out a new automation, to re-run one after fixing what made it fail, or to refresh its results after late input data arrived [see `PR #2460 `_]
* Automations can be created, edited and deleted in the UI and through new API endpoints (``[POST|PATCH|DELETE] /assets/(id)/automations``), by whoever may add data under the asset, with their recurrence expressed in a selectable IANA timezone, and only involving sensors they can access themselves (read access to the sensors an automation reads, and permission to record data on the sensors it writes to). The *Automations* page shows when each active automation is due to run next, as a clock time in the automation's own timezone, which is also available as ``next-run`` on the automations API endpoints. Each row's *Run now*, *Edit*, *Copy*, *Activate*/*Deactivate* and *Delete* controls are collected into a single *Actions* menu beside *Details*, and *Copy* opens the creation form filled in from that automation, leaving the copy inactive [see `PR #2294 `_, `PR #2563 `_, `PR #2591 `_ and `PR #2647 `_]
* An automation's output sensors are checked against its creator's permissions when the automation is created, and the schedules it computes are held to exactly those sensors: a scheduler that returns results for any other sensor is refused, rather than recording on a sensor that was only ever checked for read access [see `PR #2536 `_]
* A forecast automation's cycle jobs can likewise only record on the sensors the automation was checked against, as the jobs of schedule, report and plugin automations already could [see `PR #2699 `_]
* Reports as well as forecasts and schedules: a report automation stores report parameters, with its reporter and the reporter's configuration on a data source, and reports on a period resolved afresh on each run, either from ``start-offset`` and ``end-offset`` applied to the run time in the automation's timezone, or since the last successful report ended, while a fixed ``start`` or ``end`` is refused; a report job records only on the sensors the automation was checked against [see `PR #2297 `_]
* Every automation times its runs the same way: a fixed ``start``, ``end`` or ``prior`` in its parameters is refused, as every run would share that moment, and two of ``start-offset``, ``end-offset`` and ``duration`` describe the period each run covers instead, with the offsets applied to the time the run was due on the automation's own clock, so that, for instance, a schedule automation can plan the whole of the next day [see `PR #2551 `_]
* Look up automations from the command line with ``flexmeasures show automations``, which lists them all (inactive ones included) with the IDs that the edit, delete and run commands expect, and, with ``--id``, shows a single automation's recurrence, cursor, parameters and the sensors it reads from and writes to [see `PR #2533 `_]
* The *Automations* page lists the automations of the assets below an asset too, says when each automation runs next, keeps itself up to date, and shows the configuration of an automation's data source, which the *New automation* form now also lets you set [see `PR #2554 `_, `PR #2299 `_ and `PR #2584 `_]
* Automations now keep a durable record of every scheduled run, so a forecast or schedule run which failed before queueing any work is simply picked up again, while one which failed halfway only queues the jobs it still owes; an automation's details show, per run, what it queued, how many attempts that took, and how its jobs ended [see `PR #2457 `_, `PR #2657 `_ and `PR #2670 `_]
* Copying an asset now copies the automations on it and on its descendant assets as well, pointing them at the copied sensors and leaving them switched off, so they can be checked before they start running; an automation that cannot be copied safely is skipped and reported, rather than holding up the copy [see `PR #2531 `_]
* Plugins can register automation types with their own validated configuration, parameters and worker queue, with data provenance and sensor authorization. A plugin job's result is kept for as long as results on its own queue are kept, and reports the number of beliefs it saved [see `PR #2553 `_ and `PR #2644 `_]
v1.0.2 | November 5, 2026
============================
Bugfixes
-----------
* An asset's KPIs can only name sensors the user may read, when the asset is created or edited, and showing an asset's KPIs leaves out any sensor the user may not read [see `GHSA-xcvm-r33m-77v5 `_]
* Deleting an asset and resetting a password in the UI now go through the API, which checks permissions, where any logged-in user could delete any asset with its data by opening a link; the sensor page, the user audit log page and the page listing an organisation's assets now check access, too [see `GHSA-vmgf-59hm-j9m7 `_ and `PR #2791 `_]
v1.0.1 | September 9, 2026
============================
Infrastructure / Support
-------------------------
* Train and predict a forecaster's per-horizon models side by side rather than one after another, which cuts the training time of a long forecast horizon several-fold while leaving the forecasts themselves unchanged [see `PR #2479 `_]
Bugfixes
-----------
* Avoid crashing on startup when the database is stamped with an Alembic revision unknown to this FlexMeasures checkout [see `PR #2465 `_]
* Upgrading a database old enough to still carry the pre-``GenericAsset``/``Sensor`` tables now works, where the v0.18.0 migration that removes them crashed twice over: once while checking whether those tables hold data, as soon as one of them held more than a single row, and once while dropping them, because it dropped each table before the ones referencing it [see `PR #2475 `_]
* Sensor data ingestion now preserves ``null`` gaps when converting posted values to the sensor's unit, instead of failing the request [see `PR #2461 `_]
* Schedules saved to the flex-context's ``aggregate-consumption`` sensor had their sign flipped, showing production where consumption was scheduled and vice versa, because the sign convention implied by the field name was never recorded on the sensor [see `PR #2499 `_]
* Triggering a schedule no longer fails with a server error when a device's output sensor is referenced by ID rather than by a sensor the scheduler already resolved [see `PR #2507 `_]
* Asking the API for sensor data, a chart or a schedule at a resolution that spans no time (such as ``PT0S``), or at a negative one, now gets the same clear rejection as any other unsupported resolution, instead of a server error or a silently empty result [see `PR #2502 `_]
* ``flexmeasures jobs run-job`` ran each job twice, and always as if it were a scheduling job, which lost the queue-specific reporting of why a job failed [see `PR #2480 `_]
* A plugin listed in ``FLEXMEASURES_PLUGINS`` by name is now imported by name, instead of being loaded from a folder of that name in the working directory (which is easily the case when starting FlexMeasures from the plugin's own repository), where it appeared to load but its routes and CLI commands went missing; spell out a path (e.g. ``./my_plugin``) to load such a folder on purpose, and such a path that does not exist is now reported as missing rather than quietly resolved as a package name [see `PR #2419 `_]
* The "module not installed" error for an unresolved ``FLEXMEASURES_PLUGINS`` entry now hints at the expected comma-separated format, which helps people who accidentally use an incorrect format like a JSON-array [see `PR #2473 `_]
* KPIs on the asset page counted one day more than the selected time range [see `PR #2434 `_]
* KPIs on the asset page now total the values the chart beside them draws, counting each event under the day it starts in: a sensor reported by several sources counted only one of them, and a revised value was counted on top of the value it revised [see `PR #2434 `_]
* A forecaster that is told both where to start training and how much history to train on now trains on whichever of the two asks for less data, rather than training back to the start date: ``train-start`` says where training may begin, and ``train-period`` says how much history to use [see `PR #2482 `_]
* ``max-training-period`` said the same thing as ``train-period``, so the two are now one setting, and the former is a deprecated alias that hosts should stop using [see `PR #2482 `_]
v1.0.0 | August 25, 2026
============================
.. note:: Read more on these features on `the FlexMeasures blog `_.
.. warning:: As of this release we standardize asynchronous job responses to use the ``job`` field and return HTTP ``202 Accepted`` while a background job is queued or running. See :ref:`api_background_jobs` for the response format and polling flow. Legacy response fields such as ``schedule`` and ``forecast`` will be deprecated; clients should migrate to ``job`` (see the Infrastructure / Support section below for migration details). To receive legacy job behaviour (e.g. for older clients), hosts can use :ref:`legacy-job-client-config`.
.. warning:: Upgrading to this version requires running ``flexmeasures db upgrade`` (you can create a backup first with ``flexmeasures db-ops dump``).
.. warning:: The built-in storage fallback scheduler has been retired: an infeasible storage problem now fails with its failure reason instead of silently saving a fallback schedule.
We expect a fallback to be needed much less than before: in practice, it was mostly infeasible SoC minima and maxima that triggered it, and those preferences are now relaxed by default,
so the main ``StorageScheduler`` gets you as close as possible to your SoC constraints, while still taking into account everything else it is equipped to handle.
Statements of physics, on the other hand, are now consistently hard constraints (see the capacity warning below);
where those contradict each other, no schedule (fallback or otherwise) could have been executed anyway,
so the scheduling job now fails with the reason instead of hiding the contradiction behind a fallback schedule that ignored it.
The retired fallback scheduler had been kept rather basic and short-sighted in comparison.
Plugins importing ``StorageFallbackScheduler``, ``fallback_charging_policy`` or ``idle_after_reaching_target`` must drop these imports;
``FLEXMEASURES_FALLBACK_REDIRECT`` is now only relevant for custom schedulers that define a fallback scheduler of their own.
.. warning:: The device ``consumption-capacity`` and ``production-capacity`` are now hard constraints, unless you relax them by name.
They are no longer covered by ``relax-constraints``, which we have recommended setting to ``True`` since v0.27.0.
For most schedules this is a fix rather than a change: a directional device capacity often states what a device physically cannot do (a heat pump cannot produce), and such a statement should not be breachable at a price.
If you use these fields as economic limits that the scheduler may exceed at a cost, set ``relax-capacity-constraints`` to ``True`` in your ``flex-context``, or price the breach yourself through ``consumption-breach-price`` and/or ``production-breach-price``.
New features
-------------
* ``flexmeasures show data-sources`` now shows which organisation a data source belongs to, and can list the sensors holding data recorded by a given source [see `PR #2401 `_]
* The flex-context can now define multiple commodities, each specifying their own prices and grid capacities [see `PR #1946 `_, `PR #2172 `_, `PR #2235 `_, `PR #2271 `_, `PR #2355 `_ and `PR #2380 `_]
* Support multiple feeders to a shared storage [see `PR #2001 `_, `PR #2321 `_, `PR #2322 `_, `PR #2325 `_ and `PR #2431 `_]
* Add support for intermediate power constraints on groups of devices, via a new ``group`` field in the storage flex-model [see `PR #2276 `_ and `issue #2092 `_]
* In the UI, asset and sensor charts now render with Apache ECharts (canvas) by default, for much faster drawing and interaction on dense time series, while staying visually and functionally equivalent to the previous Vega-Lite charts, which remain available as a fallback via a toggle [see `PR #2234 `_ and `PR #2399 `_]
* The API is now rate-limited, with a generous default limit on all endpoints and a stricter limit on triggering schedules and forecasts (which only counts triggers we accepted). Limits are configurable, and can be set per organisation by putting its account on a plan, which hosts create with ``flexmeasures add plan`` and platform admins assign from the organisation's page in the UI; play servers are exempt, as they run simulations. See :ref:`plans-and-rate-limiting` [see `PR #2306 `_ and `PR #2407 `_]
* New storage flex-model field ``operation-modes`` confines a device's power to one of several power bands, following the S2 standard's operation modes — for example, a device that is either off or running at one fixed power [see `PR #2278 `_]
* Extended ``GET /api/v3_0/jobs/`` with a ``result`` field containing ``unresolved`` and ``resolved`` soft state-of-charge constraint analysis (``soc-minima``/``soc-maxima`` violations or satisfied constraints, keyed by asset ID) for scheduling jobs; both arrays are empty when no SoC constraints were defined [see `PR #2072 `_]
* Extended the scheduling job ``result`` field with a ``num-beliefs`` field reporting the total number of beliefs (scheduled values) saved to the database [see `PR #2280 `_]
* New ``inflexible-consumption`` and ``inflexible-production`` flex-context fields make explicit how the sign of each inflexible device's power data should be read (positive values denote consumption resp. production), accepting sensor references with optional source filters; they replace the now-deprecated ``inflexible-device-sensors`` field (bare sensor IDs, sign read from each sensor's ``consumption_is_positive`` attribute), which remains supported [see `PR #2358 `_]
* Support for creating new assets by using another asset as a template from the UI. [see `PR #2195 `_ and `PR #2268 `_
* Breaking behaviour change: ``soc-targets`` are now relaxed along with the other state-of-charge constraints, instead of always being enforced as hard equality constraints. A target that cannot be reached is breached at a price (falling short is priced like a ``soc-minima`` breach, overshooting like a ``soc-maxima`` breach) and reported among the unresolved constraints, rather than making the whole scheduling job fail as infeasible. Set ``relax-constraints`` (or both ``relax-constraints`` and ``relax-soc-constraints``) to ``False`` to keep targets hard [see `PR #2390 `_]
* An inflexible (unschedulable) device can be modelled as its own asset by giving its flex-model entry a single ``inflexible-consumption`` or ``inflexible-production`` sensor reference; such a device joins a ``group`` like any other member, so its fixed (measured) load counts towards the group's intermediate power constraint [see `PR #2374 `_]
* Forecasting regressors can filter their input beliefs by data source, source type, excluded source type, or source organisation [see `PR #2347 `_]
* When multiple selected sources record a belief about the same event at the same belief time, forecasting pipelines now resolve the collision deterministically: the order of an explicit ``sources`` list decides precedence (first listed wins), and otherwise the highest source ID wins after selecting the latest version within each source family [see `PR #2347 `_]
* Filter organisations by account role in the Accounts API and organisation list UI [see `PR #2353 `_]
* The flex-context editor now also shows the fields that scheduling the asset would inherit from parent assets — uneditable, with buttons to jump to the editor of the defining parent asset or to override the field on the asset itself [see `PR #2346 `_]
* Show asset annotations in asset charts: a lightly shaded time band across all subcharts, darkening with a tooltip (showing the annotation text and source) when hovered in a subchart; alerts get a warning hue, and instant annotations render as a vertical rule with a top marker; also adds a ``GET /api/v3_0/assets//chart_annotations`` endpoint [see `PR #2312 `_]
* Floor off-clock API datetimes to a non-instantaneous sensor's resolution by default when ingesting sensor data, uploading sensor data, and handling scheduler flex-model timed events; configurable with the ``floor_datetimes_to_resolution`` sensor attribute [see `PR #2146 `_ and `PR #2194 `_]
* Breaking behaviour change: the top-level flex-context's ``relax-constraints`` field now defaults to ``True`` (matching the default already used within each ``commodities`` entry), so constraint violations are softly penalized by default instead of being hard constraints, unless explicitly set to ``False``. ``relax-constraints`` no longer covers the device ``consumption-capacity`` and ``production-capacity`` at all, which stay hard unless relaxed by name, either by setting ``relax-capacity-constraints`` or by setting the device breach prices yourself. A directional device capacity may state a physical impossibility (a heat pump that cannot produce) rather than an economic preference, and a default should not make that breachable at a price. Explicitly given device breach prices are also respected now, instead of being overwritten by the defaults [see `PR #2172 `_ and `PR #2398 `_]
* In the UI, asset and sensor lists can be filtered by ID prefix through API-backed search fields [see `PR #2231 `_]
* Support configurable lower and upper bounds and snapping for forecast post-processing [see `PR #2273 `_]
* Sensor references in flex-model and flex-context support various ways of filtering by source [see `PR #2209