Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
144 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
0b345b2
feat: record on scheduling jobs how they were created
Flix6x Jul 11, 2026
4c0c5db
feat: schedules as automations
Flix6x Jul 11, 2026
ac4c74a
docs: changelog entry for schedule automations
Flix6x Jul 11, 2026
f6e1e10
feat: CRUD for automations via API and UI
Flix6x Jul 11, 2026
073abd9
docs: changelog entry for automations CRUD
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
5e86232
merge: synchronize schedule automations with reviewed parent
BelhsanHmida Aug 5, 2026
bc1fc32
data/models: allow schedule automations without generators
BelhsanHmida Aug 5, 2026
2f4afe2
cli/tests: cover schedule automation validation
BelhsanHmida Aug 5, 2026
d3c3082
data/tests: cover schedule automation dispatch
BelhsanHmida Aug 5, 2026
9505e2b
api/v3_0/tests: cover schedule job provenance
BelhsanHmida Aug 5, 2026
5de1f20
ui/tests: cover automation type tabs
BelhsanHmida Aug 5, 2026
6bf6143
scheduling: harden automation dispatch
BelhsanHmida Aug 5, 2026
03f1b88
cli: reject forecast options for schedule automations
BelhsanHmida Aug 5, 2026
75b2be6
ui/assets: resize automation tables on tab changes
BelhsanHmida Aug 5, 2026
71a7196
docs: clarify schedule automation inputs
BelhsanHmida Aug 5, 2026
d39f60f
cli/tests: cover malformed automation YAML
BelhsanHmida Aug 5, 2026
5b04664
cli: report malformed automation YAML
BelhsanHmida Aug 5, 2026
1ec9324
data/tests: cover stored schedule flex configuration
BelhsanHmida Aug 5, 2026
90025d3
scheduling: load stored flex config for minimal triggers
BelhsanHmida Aug 5, 2026
2f0b09c
docs/scheduling: describe trigger propagation
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
2237245
data/services: only let a user automate sensors they can access thems…
Flix6x Aug 5, 2026
4765fd8
api/v3_0: regenerate the OpenAPI specs
Flix6x Aug 5, 2026
5d92ea0
api/v3_0/tests: cover automating an inaccessible sensor
Flix6x Aug 5, 2026
d4bf80e
docs: describe which sensors an automation may involve
Flix6x Aug 5, 2026
d8701c6
data/services: check every sensor a schedule would be recorded on
Flix6x Aug 5, 2026
05e00ac
api/v3_0/tests: cover a schedule aggregated onto an inaccessible sensor
Flix6x Aug 5, 2026
1eb3a50
cli: keep mypy happy about click 8 attributes
BelhsanHmida Aug 7, 2026
9653e10
cli: keep the automation help focused on the automation
BelhsanHmida Aug 7, 2026
87e07f6
data/models: count a source-filtered regressor as an input sensor
BelhsanHmida Aug 7, 2026
8860bd0
data/services: do not report no sensors when an automation's sensors …
BelhsanHmida Aug 10, 2026
2b961cc
Feat automation timezones catchup (#2396)
BelhsanHmida Aug 10, 2026
f75bb6d
Merge branch 'main' into feat/2288-automations-for-forecasts
BelhsanHmida Aug 10, 2026
1e53696
Merge the forecast automation base into the schedule automations
BelhsanHmida Aug 10, 2026
db0a57b
Merge the schedule automations into the automations CRUD
BelhsanHmida Aug 10, 2026
1f68beb
data/migrations: rejoin the two automation migration branches
BelhsanHmida Aug 11, 2026
f625330
Merge branch 'work/2293-sync' into work/2294-sync
BelhsanHmida Aug 11, 2026
64ad898
data/services: let the forecaster say which sensors an automation inv…
BelhsanHmida Aug 11, 2026
7a3c38a
api/v3_0: let an automation's timezone be set and changed through the…
BelhsanHmida Aug 11, 2026
5612387
data/services: set up the forecaster's data source only once the auto…
BelhsanHmida Aug 11, 2026
a98b4fc
Merge origin/main into automation fixes
BelhsanHmida Aug 11, 2026
8eac59f
fix(data/schemas): reject cron expressions without dates
BelhsanHmida Aug 11, 2026
249642f
fix(data/services): isolate invalid recurrences and stale claims
BelhsanHmida Aug 11, 2026
bf23428
fix(api/v3_0): protect automation sensor details
BelhsanHmida Aug 11, 2026
af5957b
test(cli): cover impossible recurrence input
BelhsanHmida Aug 11, 2026
c18f7de
test(data/services): cover resilient automation claims
BelhsanHmida Aug 11, 2026
23ea45a
test(api/v3_0): cover private automation dependencies
BelhsanHmida Aug 11, 2026
a7671de
Merge reviewed forecast automation fixes
BelhsanHmida Aug 11, 2026
5af2170
fix(data/services): resolve schedule automation sensors
BelhsanHmida Aug 11, 2026
32c4970
test(data/services): cover schedule sensor resolution
BelhsanHmida Aug 11, 2026
a0a1944
test(api/v3_0): expose schedule dependency details
BelhsanHmida Aug 11, 2026
87f670b
Merge reviewed schedule automation fixes
BelhsanHmida Aug 11, 2026
1c7b3d3
fix(data/services): hide inaccessible sensor names
BelhsanHmida Aug 11, 2026
b7979b5
test(api/v3_0): isolate automation endpoint tests
BelhsanHmida Aug 11, 2026
10b9155
feat(ui/views): provide automation timezones
BelhsanHmida Aug 11, 2026
a39c4ed
feat(ui): edit automation recurrence timezones
BelhsanHmida Aug 11, 2026
69fd5bf
test(ui): cover automation timezone controls
BelhsanHmida Aug 11, 2026
aa469bc
docs(changelog): mention automation timezones
BelhsanHmida Aug 11, 2026
9b5af64
fix(data/services): preserve schedule validation errors
BelhsanHmida Aug 11, 2026
63fee6b
Merge branch 'main' into feat/2288-automations-for-forecasts
BelhsanHmida Aug 12, 2026
b8d76a9
cli: refuse forecaster options that were given, not merely ones that …
BelhsanHmida Aug 12, 2026
dd5fae6
data/models: name an automation's cursor after what it points at
Flix6x Aug 31, 2026
b3845d8
tests: follow the automation cursor rename
Flix6x Aug 31, 2026
dbf0bae
docs: explain the automation cursor, and say "run" instead of "occurr…
Flix6x Aug 31, 2026
7e1bab2
docs/changelog: give the automation API changes their own version sec…
Flix6x Aug 31, 2026
d1cd0a2
docs/changelog: restore the v3.0-32 underline to full length
Flix6x Aug 31, 2026
6474eec
data/services: address review findings on the automations service
Flix6x Sep 1, 2026
1bda4b5
data/models: index the automation asset foreign key
Flix6x Sep 1, 2026
8d8514d
api/v3_0: work out an automation's sensors once when they cannot be r…
Flix6x Sep 1, 2026
eca2520
cli: warn which automations a sensor deletion would break
Flix6x Sep 1, 2026
fc91bc4
tests: cover the sensor deletion warning, and stop depending on caplog
Flix6x Sep 1, 2026
05e059e
docs/changelog: record the sensor deletion warning
Flix6x Sep 1, 2026
6541676
Merge branch 'main' into feat/2288-automations-for-forecasts
Flix6x Sep 1, 2026
4051dc2
docs: give automations their own page
Flix6x Sep 1, 2026
208e5be
Merge branch 'feat/2288-automations-for-forecasts' into feat/2288-sch…
Flix6x Sep 1, 2026
4be5574
Merge branch 'feat/2288-schedule-automations' into feat/2288-automati…
Flix6x Sep 1, 2026
06bf14b
data/migrations: index the automation asset FK without adding a revision
Flix6x Sep 1, 2026
170a38e
Merge remote-tracking branch 'origin/feat/2288-automations-for-foreca…
Flix6x Sep 1, 2026
4080c44
Merge remote-tracking branch 'origin/feat/2288-schedule-automations' …
Flix6x Sep 1, 2026
7b83dfa
Merge branch 'main' into feat/2288-schedule-automations
Flix6x Sep 1, 2026
e8f5bc1
Merge remote-tracking branch 'origin/feat/2288-schedule-automations' …
Flix6x Sep 1, 2026
133a6e4
Merge branch 'main' into feat/2288-schedule-automations
Flix6x Sep 2, 2026
0132c63
docs: document schedule automations in the automations chapter
Flix6x Sep 2, 2026
940f22e
Name automation types after the task, like the rest of the codebase
Flix6x Sep 2, 2026
12903db
docs/changelog: move the data source inspection entry to v1.1.0
Flix6x Sep 2, 2026
4f55191
Schedulers as data generators, and a generator on every automation (#…
Flix6x Sep 7, 2026
c50b9cf
Merge branch 'main' into feat/2288-schedule-automations
Flix6x Sep 8, 2026
952bfa9
Name automation types after the task, not its results
BelhsanHmida Sep 8, 2026
9c1312f
Merge branch 'feat/2288-schedule-automations' into feat/2288-automati…
BelhsanHmida Sep 8, 2026
0b59c15
Gate managing an automation the way running one is gated
BelhsanHmida Sep 8, 2026
89c1482
Record this branch's endpoints in the changelogs
BelhsanHmida Sep 8, 2026
37c55ff
Give the trigger tests the fixtures this file now uses
BelhsanHmida Sep 8, 2026
2ad2e45
Merge origin/main into the automations CRUD branch
BelhsanHmida Sep 11, 2026
51fb4fb
Close the handlers the Run now button was pasted into
BelhsanHmida Sep 11, 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
4 changes: 4 additions & 0 deletions documentation/api/change_log.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ API change log

.. note:: The FlexMeasures API follows its own versioning scheme. This is also reflected in the URL (e.g. `/api/v3_0`), allowing developers to upgrade at their own pace.

v3.0-36 | September 11, 2026
""""""""""""""""""""""""""""
- Added ``POST /api/v3_0/assets/<id>/automations``, ``PATCH /api/v3_0/assets/<id>/automations/<automation_id>`` and ``DELETE /api/v3_0/assets/<id>/automations/<automation_id>`` for managing an asset's automations. They require the same permission as writing data under the asset, and an automation may only involve sensors that its creator can access: read access to the sensors it reads data from, and permission to record data on the sensors it writes to (a ``403`` otherwise). Both the creation and the update accept a ``timezone``, in which the automation's cron expression is interpreted; it defaults to the server's ``FLEXMEASURES_TIMEZONE``.

v3.0-35 | September 9, 2026
"""""""""""""""""""""""""""
- The ``resolution`` field is now rejected with a ``422 (Unprocessable Entity)`` response unless it spans a positive amount of time. This applies wherever the API accepts one: as a query parameter on ``GET /api/v3_0/sensors/<id>/data`` and on the ``chart_data`` endpoints under ``api/dev``, and in the request body of the ``POST`` schedule trigger endpoints. Previously, a zero resolution (such as ``PT0S``) either crashed the request with a ``500`` or was silently ignored, and a negative resolution returned an empty set of values.
Expand Down
10 changes: 9 additions & 1 deletion documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ v1.1.0 | September XX, 2026
Select a data source to see the schedule computed under one configuration.
One scheduling request still records under a single data source, including the per-device jobs of a sequential schedule.

.. warning:: A scheduler's data source now also records the flex config it computed under, where previously one data source per scheduler version recorded every schedule that scheduler made.
Schedules computed under different flex configs are therefore recorded by different data sources, and a sensor can carry schedules from several of them, as it already could for forecasts.
Values describing a single moment stay out of that config, so a ``soc-at-start``, or a ``soc-targets`` entry at a given datetime, does not make every run a new data source.
What does is a change to what the site and its devices can do, such as a device's ``power-capacity``.
After such a change, a sensor holds the schedule computed under each configuration, where the newer schedule used to supersede the older one, so a chart of that sensor draws both, and the asset's KPIs total both, as they report what the chart draws.
Select a data source to see the schedule computed under one configuration.
One scheduling request still records under a single data source, including the per-device jobs of a sequential schedule.

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

* Automations: recurring tasks defined per asset, computing forecasts or schedules, 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``); 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; a forecast automation points at a data source holding its forecaster configuration, while a schedule automation stores what the schedule trigger endpoint accepts, and schedules from each run's own time, unless the trigger message fixes a ``start``; 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 <https://www.github.com/FlexMeasures/flexmeasures/pull/2290>`_, `PR #2396 <https://www.github.com/FlexMeasures/flexmeasures/pull/2396>`_ and `PR #2293 <https://www.github.com/FlexMeasures/flexmeasures/pull/2293>`_]
* Automations: recurring tasks defined per asset, computing forecasts or schedules, 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``); 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; a forecast automation points at a data source holding its forecaster configuration, while a schedule automation stores what the schedule trigger endpoint accepts, and schedules from each run's own time, unless the trigger message fixes a ``start``; 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; automations can also 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) [see `PR #2290 <https://www.github.com/FlexMeasures/flexmeasures/pull/2290>`_, `PR #2396 <https://www.github.com/FlexMeasures/flexmeasures/pull/2396>`_, `PR #2293 <https://www.github.com/FlexMeasures/flexmeasures/pull/2293>`_ and `PR #2294 <https://www.github.com/FlexMeasures/flexmeasures/pull/2294>`_]
* 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 #2444 <https://www.github.com/FlexMeasures/flexmeasures/pull/2444>`_]
* 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>`_]
* Try out a forecast without recording it, using ``flexmeasures add forecasts --dry-run``, which computes the forecast in full and reports the sensor, data source, number of beliefs and event range it would have saved [see `PR #2483 <https://www.github.com/FlexMeasures/flexmeasures/pull/2483>`_]
Expand Down
6 changes: 6 additions & 0 deletions flexmeasures/api/v3_0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
StatusPageChildJobsJSONSchema,
)
from flexmeasures.data.schemas.annotations import AnnotationSchema
from flexmeasures.data.schemas.automations import (
AutomationCreationSchema,
AutomationUpdateSchema,
)
from flexmeasures.data.schemas.generic_assets import GenericAssetSchema as AssetSchema
from flexmeasures.data.schemas.reporting import ReportTriggerSchema
from flexmeasures.data.schemas.sensors import QuantitySchema, TimeSeriesSchema
Expand Down Expand Up @@ -228,6 +232,8 @@ def create_openapi_specs(app: Flask):
("AssetAPIQuerySchema", AssetAPIQuerySchema),
("AssetSchema", AssetSchema),
("AnnotationSchema", AnnotationSchema),
("AutomationCreationSchema", AutomationCreationSchema),
("AutomationUpdateSchema", AutomationUpdateSchema),
("ReportTriggerSchema", ReportTriggerSchema),
("CopyAssetSchema", CopyAssetSchema),
("DefaultAssetViewJSONSchema", DefaultAssetViewJSONSchema),
Expand Down
250 changes: 247 additions & 3 deletions flexmeasures/api/v3_0/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,20 @@
from flexmeasures.data.models.automations import Automation
from flexmeasures.data.models.user import Account
from flexmeasures.data.models.audit_log import AssetAuditLog
from flexmeasures.data.schemas.automations import AutomationSchema
from flexmeasures.data.schemas.automations import (
AutomationCreationSchema,
AutomationSchema,
AutomationUpdateSchema,
)
from flexmeasures.data.services.automations import (
AutomationSensorsUnknown,
create_automation,
delete_automation as remove_automation,
describe_cronstr,
get_automation_job_stats,
resolve_automation_sensors,
run_automation,
update_automation,
)
from flexmeasures.data.models.generic_assets import GenericAsset, GenericAssetType
from flexmeasures.data.models.reporting import Reporter
Expand Down Expand Up @@ -1476,7 +1483,7 @@ def get_automations(self, id: int, asset: GenericAsset):
- id: 1
created_at: "2026-07-11T00:00:00+00:00"
asset_id: 1
type: forecasts
type: forecasting
name: Day-ahead PV forecasts
cronstr: "0 6 * * *"
timezone: Europe/Amsterdam
Expand Down Expand Up @@ -1556,7 +1563,7 @@ def get_automation(self, id: int, automation_id: int, asset: GenericAsset):
id: 1
created_at: "2026-07-11T00:00:00+00:00"
asset_id: 1
type: forecasts
type: forecasting
name: Day-ahead PV forecasts
cronstr: "0 6 * * *"
timezone: Europe/Amsterdam
Expand Down Expand Up @@ -1637,6 +1644,243 @@ def get_automation(self, id: int, automation_id: int, asset: GenericAsset):
automation_data["redis_connection_err"] = redis_connection_err
return automation_data, 200

@route("/<id>/automations", methods=["POST"])
@use_kwargs(
{"asset": AssetIdField(data_key="id")},
location="path",
)
# Managing an automation is gated like running one: an automation exists to write data
# under the asset, so the same principals that may add data there may define it.
# The sensors it involves are checked separately, against the user's own access.
@permission_required_for_context("create-children", ctx_arg_name="asset")
@as_json
def post_automation(self, id: int, asset: GenericAsset):
"""
.. :quickref: Assets; Create an automation on an asset.

---
post:
summary: Create an automation on an asset.
description: |
Create a recurring task (computing forecasts or schedules) on the asset.
The parameters are validated by the schema matching the automation type:
forecast parameters for type `forecasts`, or a schedule trigger message
(without the asset id) for type `schedules`.
Requires permission to add data under the asset.

The automation can only involve sensors that you have access to yourself:
read access to the sensors it reads data from, and permission to record data
on the sensors it writes to.
security:
- ApiKeyAuth: []
parameters:
- in: path
name: id
required: true
description: ID of the asset to create the automation on.
schema:
type: integer
requestBody:
content:
application/json:
schema: AutomationCreationSchema
examples:
daily_forecasts:
summary: Daily forecasts of sensor 2092
value:
name: Day-ahead PV forecasts
cronstr: "0 6 * * *"
type: forecasting
parameters:
sensor: 2092
responses:
201:
description: CREATED
400:
description: INVALID_REQUEST, REQUIRED_INFO_MISSING, UNEXPECTED_PARAMS
401:
description: UNAUTHORIZED
403:
description: INVALID_SENDER
422:
description: UNPROCESSABLE_ENTITY
tags:
- Assets
"""
body = request.get_json(silent=True)
if not body:
return unprocessable_entity("No JSON data provided.")
try:
automation_data = AutomationCreationSchema().load(body)
except ValidationError as e:
return unprocessable_entity(e.messages)
try:
automation, warnings = create_automation(
asset=asset,
name=automation_data["name"],
cronstr=automation_data["cronstr"],
timezone=automation_data["timezone"],
automation_type=automation_data["type"],
active=automation_data["active"],
parameters=automation_data["parameters"],
forecaster_class=automation_data["forecaster"],
config=automation_data["config"],
origin="API",
check_permissions=True,
)
except ValidationError as e:
return unprocessable_entity({"parameters": e.messages})
except ValueError as e:
return unprocessable_entity(str(e))
db.session.commit()
response = automation_schema.dump(automation)
response["recurrence_description"] = describe_cronstr(automation.cronstr)
response["warnings"] = warnings
return response, 201

@route("/<id>/automations/<int:automation_id>", methods=["PATCH"])
@use_kwargs(
{
"asset": AssetIdField(data_key="id"),
"automation_id": fields.Int(),
},
location="path",
)
# Managing an automation is gated like running one: an automation exists to write data
# under the asset, so the same principals that may add data there may define it.
# The sensors it involves are checked separately, against the user's own access.
@permission_required_for_context("create-children", ctx_arg_name="asset")
@as_json
def patch_automation(self, id: int, automation_id: int, asset: GenericAsset):
"""
.. :quickref: Assets; Update an automation's name, cron string or activation status.

---
patch:
summary: Update an automation's name, cron string or activation status.
description: |
Any subset of the fields `name`, `cronstr` and `active` can be sent.
Other automation fields cannot be updated; instead, create a new automation.
Requires permission to add data under the asset.
security:
- ApiKeyAuth: []
parameters:
- in: path
name: id
required: true
description: ID of the asset.
schema:
type: integer
- in: path
name: automation_id
required: true
description: ID of the automation.
schema:
type: integer
requestBody:
content:
application/json:
schema: AutomationUpdateSchema
examples:
deactivate:
summary: Deactivate the automation
value:
active: false
responses:
200:
description: PROCESSED
400:
description: INVALID_REQUEST, REQUIRED_INFO_MISSING, UNEXPECTED_PARAMS
401:
description: UNAUTHORIZED
403:
description: INVALID_SENDER
404:
description: NOT_FOUND
422:
description: UNPROCESSABLE_ENTITY
tags:
- Assets
"""
automation = db.session.get(Automation, automation_id)
if automation is None or automation.asset_id != asset.id:
return {
"message": f"Asset {asset.id} has no automation with id {automation_id}."
}, 404
body = request.get_json(silent=True)
if not body:
return unprocessable_entity("No JSON data provided.")
try:
automation_data = AutomationUpdateSchema().load(body)
except ValidationError as e:
return unprocessable_entity(e.messages)
update_automation(automation, origin="API", **automation_data)
db.session.commit()
response = automation_schema.dump(automation)
response["recurrence_description"] = describe_cronstr(automation.cronstr)
return response, 200

@route("/<id>/automations/<int:automation_id>", methods=["DELETE"])
@use_kwargs(
{
"asset": AssetIdField(data_key="id"),
"automation_id": fields.Int(),
},
location="path",
)
# Managing an automation is gated like running one: an automation exists to write data
# under the asset, so the same principals that may add data there may define it.
# The sensors it involves are checked separately, against the user's own access.
@permission_required_for_context("create-children", ctx_arg_name="asset")
@as_json
def delete_automation(self, id: int, automation_id: int, asset: GenericAsset):
"""
.. :quickref: Assets; Delete an automation.

---
delete:
summary: Delete an automation.
description: |
Delete the automation. Any jobs it already queued are unaffected.
Requires permission to add data under the asset.
security:
- ApiKeyAuth: []
parameters:
- in: path
name: id
required: true
description: ID of the asset.
schema:
type: integer
- in: path
name: automation_id
required: true
description: ID of the automation.
schema:
type: integer
responses:
204:
description: DELETED
400:
description: INVALID_REQUEST, REQUIRED_INFO_MISSING, UNEXPECTED_PARAMS
401:
description: UNAUTHORIZED
403:
description: INVALID_SENDER
404:
description: NOT_FOUND
tags:
- Assets
"""
automation = db.session.get(Automation, automation_id)
if automation is None or automation.asset_id != asset.id:
return {
"message": f"Asset {asset.id} has no automation with id {automation_id}."
}, 404
remove_automation(automation, origin="API")
db.session.commit()
return {}, 204

@route("/<id>/automations/<int:automation_id>/trigger", methods=["POST"])
@limit_triggers()
@use_kwargs(
Expand Down
Loading
Loading