Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f97c2f0
feat: add Automation data model
Flix6x Jul 11, 2026
f410437
feat: CLI commands to manage and run automations
Flix6x Jul 11, 2026
6f6591c
feat: record on forecasting jobs how they were created
Flix6x Jul 11, 2026
fb74bfc
feat: API endpoints to list an asset's automations
Flix6x Jul 11, 2026
ab62b9f
feat: UI page listing an asset's automations
Flix6x Jul 11, 2026
db8f288
test: cover automations CLI, API and UI
Flix6x Jul 11, 2026
9cb4f63
docs: document automations
Flix6x Jul 11, 2026
e0575c7
docs: changelog entry for automations
Flix6x Jul 11, 2026
b1f56bf
fix: render cron descriptions in 24-hour format regardless of locale
Flix6x Jul 11, 2026
de6fb71
fix: address code review findings for automations
Flix6x Jul 11, 2026
bf2cc60
fix: address code review findings for automations (remaining files)
Flix6x Jul 11, 2026
7493c13
test: assert on the cron validation failure without pinning click's m…
Flix6x Jul 12, 2026
7bc3056
merge: synchronize automations with current main
BelhsanHmida Jul 31, 2026
15c9937
data/schemas: restrict automations to five-field cron
BelhsanHmida Jul 31, 2026
3090cf5
data/schemas/tests: cover automation cron field count
BelhsanHmida Jul 31, 2026
8cc3920
cli/jobs: retain automation guard after queueing failure
BelhsanHmida Jul 31, 2026
29c46b3
cli/tests: cover partial automation queue failure
BelhsanHmida Jul 31, 2026
8666c31
cli: normalize YAML forecasting option files
BelhsanHmida Jul 31, 2026
3433020
cli/tests: cover automation YAML option files
BelhsanHmida Jul 31, 2026
430dcef
data/services: redact inaccessible automation provenance
BelhsanHmida Jul 31, 2026
6c4bbc6
api/v3_0/tests: cover automation provenance authorization
BelhsanHmida Jul 31, 2026
337ad3b
ui/assets: distinguish automation load failures
BelhsanHmida Jul 31, 2026
119fc9d
ui/tests: cover automation load error state
BelhsanHmida Jul 31, 2026
04daae4
utils/docs: preserve standalone asterisks in RST conversion
BelhsanHmida Jul 31, 2026
22addda
utils/tests: cover RST cron wildcard conversion
BelhsanHmida Jul 31, 2026
edf77a5
docs/forecasting: clarify automation execution contract
BelhsanHmida Jul 31, 2026
a6cee99
changelog: record automation API and runner contract
BelhsanHmida Jul 31, 2026
194f8cc
api/docs: show job creation provenance
BelhsanHmida Jul 31, 2026
a3d7ebe
test: keep forecast CLI stub compatible with job provenance
BelhsanHmida Jul 31, 2026
de76798
Merge main into forecast automations
BelhsanHmida Aug 4, 2026
646f9fd
fix: require valid automation generators
BelhsanHmida Aug 4, 2026
4664afe
test: cover automation generator retention
BelhsanHmida Aug 4, 2026
b801d74
fix: constrain forecast automation outputs
BelhsanHmida Aug 5, 2026
006f85c
test: cover forecast automation output scope
BelhsanHmida Aug 5, 2026
dba4edc
docs: explain forecast automation ownership rules
BelhsanHmida Aug 5, 2026
efdda5a
fix: merge automation and main migration heads
BelhsanHmida Aug 5, 2026
c783e8f
data/models: let data generators report their input and output sensors
Flix6x Aug 5, 2026
8b2cc73
data/models/forecasting: only announce a pipeline run when actually r…
Flix6x Aug 5, 2026
b37256f
cli: default the automation recurrence to daily, and reject options t…
Flix6x Aug 5, 2026
aa64afb
api/v3_0: report an automation's input and output sensors
Flix6x Aug 5, 2026
b438e8f
api/v3_0: add an endpoint for one data source
Flix6x Aug 5, 2026
17f432b
api/v3_0: regenerate the OpenAPI specs
Flix6x Aug 5, 2026
1ea8f78
ui: link an automation's details to its sensors, and make the listing…
Flix6x Aug 5, 2026
2b87d5e
ui: show a sensor's data source record and the automations feeding it
Flix6x Aug 5, 2026
1869247
tests: cover the automation and data source review follow-ups
Flix6x Aug 5, 2026
03626b4
docs: describe the automation and data source follow-ups
Flix6x Aug 5, 2026
0f8fe58
Merge the current automations branch into the review follow-ups
Flix6x Aug 5, 2026
1dccf9f
api/v3_0: regenerate the OpenAPI specs after merging
Flix6x Aug 5, 2026
9f1d075
cli: only reject configuration options that were actually given with …
Flix6x Aug 5, 2026
b397907
data/services: only consider automations that could feed a sensor
Flix6x Aug 5, 2026
318cd91
tests: follow the merged automation behaviour
Flix6x Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions documentation/api/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ API change log

v3.0-32 | July XX, 2026
""""""""""""""""""""""""
- Added ``GET /api/v3_0/assets/<id>/automations`` and ``GET /api/v3_0/assets/<id>/automations/<automation_id>`` for listing and inspecting forecast automations, including the sensors an automation reads from and writes to. Asset job entries now include ``created_via`` provenance; automation identity is included only when the caller may read that automation.
- Added ``GET /api/v3_0/sources/<id>`` to show the full record of one data source, including the attributes in which data generators store their configuration.
- API endpoints are now rate-limited. A request which exceeds a limit is answered with a ``429 (Too Many Requests)`` status code and a ``Retry-After`` header stating how many seconds to wait. Responses also carry ``X-RateLimit-*`` headers, describing the limit that applied, how much of it is left, and when it resets. A stricter limit applies to ``POST /assets/<id>/schedules/trigger``, ``POST /sensors/<id>/schedules/trigger`` and ``POST /sensors/<id>/forecasts/trigger`` than to other endpoints; the health endpoints are exempt. Per-account overrides are set by assigning the account a plan (a ``Plan`` database row), rather than through an account attribute.
- Introduced the ``inflexible-consumption`` and ``inflexible-production`` flex-context fields, which make explicit how the sign of each inflexible device's power data should be read: positive values denote consumption resp. production. Each entry is a sensor reference (``{"sensor": <id>}``), optionally with source filters (``source-types``, ``exclude-source-types``, ``sources``, ``source-account``). Deprecated the ``inflexible-device-sensors`` field (a list of bare sensor IDs, whose sign convention is read from each sensor's ``consumption_is_positive`` attribute); it remains supported, but cannot be combined with the new fields in one flex-context.
- Added a ``role`` query parameter to ``GET /api/v3_0/accounts`` for filtering accessible organisations by account role.
Expand Down
3 changes: 2 additions & 1 deletion documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ v1.0.0 | July XX, 2026

New features
-------------

* Automations - first roundtrip for forecasts: recurring tasks defined per asset, managed with new CLI commands (``flexmeasures add|edit|delete automation``), run by ``flexmeasures jobs run-automations``, and viewable in a new UI page and API endpoints (``[GET] /assets/(id)/automations``); an automation's details link to the sensors it reads from and writes to, and a sensor's page lists the automations feeding it; jobs now also record whether they were created via the CLI, the API or an automation [see `PR #2290 <https://www.github.com/FlexMeasures/flexmeasures/pull/2290>`_]
* In the UI, the full record of the data source selected on a sensor page can be inspected, backed by a new API endpoint (``[GET] /sources/(id)``) [see `PR #2290 <https://www.github.com/FlexMeasures/flexmeasures/pull/2290>`_]
* 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 <https://www.github.com/FlexMeasures/flexmeasures/pull/2358>`_]
* 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 <https://www.github.com/FlexMeasures/flexmeasures/pull/2374>`_]
* 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 <https://www.github.com/FlexMeasures/flexmeasures/pull/2306>`_]
Expand Down
2 changes: 2 additions & 0 deletions documentation/cli/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ since v1.0.0 | July XX, 2026
* Add ``flexmeasures add plan``, ``flexmeasures show plans`` and ``flexmeasures edit plan``, to manage the rate limits and quotas which apply to the accounts on a plan.
* Add ``flexmeasures edit secret`` to store an encrypted secret on an account or asset.
* Add ``flexmeasures delete secret`` to remove an encrypted secret from an account or asset.
* Add ``flexmeasures add automation``, ``flexmeasures edit automation`` and ``flexmeasures delete automation`` to manage automations (recurring tasks on an asset; for now, computing forecasts).
* Add ``flexmeasures jobs run-automations`` to queue jobs for all automations that are due to run this minute from standard five-field cron expressions. Run this command once per minute. It makes at most one queueing attempt per automation per minute, including when an attempt fails after partially queueing jobs.

since v0.33.0 | June 01, 2026
=================================
Expand Down
4 changes: 4 additions & 0 deletions documentation/cli/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ of which some are referred to in this documentation.
``flexmeasures add annotation`` Add annotation to accounts, assets and/or sensors.
``flexmeasures add toy-account`` Create a toy account, for tutorials and trying things.
``flexmeasures add report`` Create a report.
``flexmeasures add automation`` Add an automation: a recurring task (for now, computing forecasts) on an asset.
================================================= =======================================


Expand Down Expand Up @@ -75,6 +76,7 @@ of which some are referred to in this documentation.
``flexmeasures edit resample-data`` Assign a new event resolution to an existing sensor and resample its data accordingly.
``flexmeasures edit transfer-parenthood`` (Re)assign parent assets.
``flexmeasures edit transfer-ownership`` Transfer the ownership of an asset and its children to a different account.
``flexmeasures edit automation`` Edit the name, recurrence or activation status of an automation.
================================================= =======================================

``delete`` - Delete data
Expand All @@ -93,6 +95,7 @@ of which some are referred to in this documentation.
``flexmeasures delete prognoses`` Delete forecasts and schedules (forecasts > 0).
``flexmeasures delete unchanged-beliefs`` Delete unchanged beliefs.
``flexmeasures delete nan-beliefs`` Delete NaN beliefs.
``flexmeasures delete automation`` Delete an automation.
================================================= =======================================


Expand All @@ -117,6 +120,7 @@ of which some are referred to in this documentation.
``flexmeasures jobs run-job`` Run a single job (useful for debugging it)
``flexmeasures jobs inspect-job`` Inspect a background job and print its current status, result and metadata.
``flexmeasures jobs stats`` Show estimated live statistics of the queueing system.
``flexmeasures jobs run-automations`` Queue jobs for all automations that are due to run this minute (run this once per minute, e.g. via cron).
================================================= =======================================


Expand Down
43 changes: 43 additions & 0 deletions documentation/features/forecasting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,46 @@ Usage:
Create the annotations you want to use as regressors before running the forecast.
For holidays, use ``flexmeasures add holidays``, which supports both ``workalendar``
and ``holidays``. See :ref:`annotations` for details.

.. _automating_forecasts:

Automating forecasts
--------------------

Instead of asking for forecasts one at a time, you can set up an *automation*: a recurring task defined on an asset.
On each run, the automation queues forecasting jobs (so make sure a worker is processing the ``forecasting`` queue, see :ref:`redis-queue`).
When the automation was created, its forecast parameters (see above) were stored, and validated with the same schema that the CLI and API use.
Timing parameters are resolved on each run — for instance, the forecast start defaults to the time the automation runs, so each run produces fresh forecasts.
The sensor on which forecasts are saved (``sensor-to-save``, falling back to ``sensor``) must belong to the automation asset or one of its descendants.
This relationship is checked both when the automation is created and immediately before each run.

Here is how you create an automation in the CLI, asking for daily (at 6 AM) forecasts of sensor 12:

.. code-block:: bash

flexmeasures add automation --asset 3 --name "Daily PV forecasts" --cron "0 6 * * *" --sensor 12

The recurrence is defined by a standard five-field cron string (minute, hour, day of month, month, and day of week), interpreted in the ``FLEXMEASURES_TIMEZONE``, which defaults to ``"0 0 * * *"`` (daily at midnight).
Cron aliases and optional seconds or year fields are not supported.
Automations are active by default (use ``--inactive`` to create them in deactivated state).
Use ``flexmeasures edit automation`` to rename, re-schedule (``--cron``), activate or deactivate an automation, and ``flexmeasures delete automation`` to remove one.
These changes are recorded in the asset's audit log.
The stored data generator is required while the automation exists, so its data source cannot be deleted until the automation is removed.

The forecaster and its configuration are stored on a data source.
Pass ``--source`` to reuse the data source of an existing forecaster, in which case ``--forecaster`` and ``--config`` (and the individual configuration options) are not needed — the data source already determines them.

For automations to actually run, let a cron job execute the following command once per minute:

.. code-block:: bash

* * * * * flexmeasures jobs run-automations

Each due automation then queues its forecasting jobs.
Only one queueing attempt is made per automation per minute.
If an attempt fails after queueing some jobs, it is not retried automatically, because a retry could duplicate that partial work.
The jobs record how they were created, which is shown on the asset's status page (UI), where recent jobs are listed.

Automations defined on an asset can be viewed on the asset's *Automations* page in the UI, and listed with the API endpoint `[GET] /assets/(id)/automations <../api/v3_0.html#get--api-v3_0-assets-id-automations>`_.
An automation's details show the sensors it reads from and writes to, linking to each sensor's page.
Conversely, a sensor's page lists the automations that write data to it.
20 changes: 2 additions & 18 deletions flexmeasures/api/common/utils/api_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from flexmeasures.data.models.generic_assets import GenericAsset
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.data.queries.generic_assets import asset_is_in_subtree
from flexmeasures.data.utils import (
SAVE_TO_DB_SUCCESS,
SAVE_TO_DB_SUCCESS_BUT_NOTHING_NEW,
Expand Down Expand Up @@ -570,23 +571,6 @@ def _determine_copy_name(
return f"{source_name} (Copy {max_index + 1})"


def _asset_is_in_subtree(root_asset_id: int, candidate_asset_id: int) -> bool:
"""Return True if candidate_asset_id is root or a descendant of root_asset_id."""
current_asset_id = candidate_asset_id
visited: set[int] = set()

while current_asset_id is not None and current_asset_id not in visited:
if current_asset_id == root_asset_id:
return True
visited.add(current_asset_id)
current_asset = db.session.get(GenericAsset, current_asset_id)
if current_asset is None:
return False
current_asset_id = current_asset.parent_asset_id

return False


def copy_asset(
asset: GenericAsset,
account=None,
Expand Down Expand Up @@ -634,7 +618,7 @@ def copy_asset(
target_account_id = int(account.id)
target_parent_asset_id = int(parent_asset.id)

if target_parent_asset_id is not None and _asset_is_in_subtree(
if target_parent_asset_id is not None and asset_is_in_subtree(
root_asset_id=asset.id,
candidate_asset_id=target_parent_asset_id,
):
Expand Down
Loading
Loading