Skip to content

Commit

Permalink
Refactor scheduler interface - API and inner logic (#537)
Browse files Browse the repository at this point in the history
Add `flex_model` & `flex_context` to /schedules/trigger/ API endpoint, and refactor how flex configuration is deserialized (by the Scheduler implementation instead of by the API endpoint).


* Add flex_model & flex_context in API endpoint; refactor design for Scheduler implementations (moving some endpoint logic here);

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* add new schema modules

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* include two other flex context params in solver test

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* also support deprecated flex_context parameters, and align spelling of params with underscore in docstring

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* correctly handle flex-model validation errors when they come up in the endpoint

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* changelog: add deprecation warnings and mentions this PR

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* fix internal link

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* move flex-model and flex-context docs to notation module; small fixes in dummy custom scheduler

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* deprecate soc-sensor-id field, store soc states on the asset attributes in v3.0 as well, using a way that lets all schedulers save (parts of) it if they want.

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* check (and potentially fill in defaults for) soc_min and soc_max before we apply the schema (which expects non-nan values here)

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* make add schedule command work with our refactored scheduling code, small refactoring to save lines

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* rename the CLI command as it only represents storage right now (and we might choose that the CLI will be specific to our in-built flex models)

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* More thorough checks for passed soc-values in StorageScheduler, leads to small fix in API endpoint and scheduling tests

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* doc improvements from review

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* Change parameter names for flex model and context which come through the API to use hyphens, which is conventionally preferred.

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* Make `flexmeasures add schedule` a subgroup (#557)

* Make `flexmeasuress add schedule` a subgroup:
- invoke a default subcommand
- show a deprecation warning

Signed-off-by: F.N. Claessen <felix@seita.nl>

* adapt CLI command name so it's clearer what is being added

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: Nicolas Höning <nicolas@seita.nl>
Co-authored-by: Nicolas Höning <nicolas@seita.nl>

* add one missing documentation improvement from review

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* make sure hyphens are used in flex-model to the outside world (API, CLI)

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* smaller review items, mostly documentation

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* remove soc checks which added interpretation (should be part of another PR, if at all)

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* fixes to notation docs

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* make sure scheduling tests work on empty queues, with new fixture

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* remove two tests for previously removed util function

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* batch of small review comments

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* make get_data_source_info a class method of Scheduler

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* small simplification of get_data_source_for_job

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* specify min/max inclusiveness of roundtrip-efficiency parameter

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* create_scheduling_jobs accepts both object and ID

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* fix type hinting

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* API changelog & flex config introduction

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* two missing fixes

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* remove line about previously undocumented & now depreacated line

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* Deprecation headers for old fields that moved to flex-model and flex-context  (#564)

* Add deprecation and sunset response headers when deprecated fields are used

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Refactor: duplicate code becomes util function

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Correct deprecation and sunset links

Signed-off-by: F.N. Claessen <felix@seita.nl>

* rename to represent plural-default of param, update link to 3.0.5 API changelog

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

Signed-off-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: Nicolas Höning <nicolas@seita.nl>
Co-authored-by: Nicolas Höning <nicolas@seita.nl>

* refactor where the code lives that builds device equality constraints from soc targets

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* change a sentence in notation

Signed-off-by: Nicolas Höning <nicolas@seita.nl>

* Rename inspection to deserialization

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Fix DummyScheduler in documentation

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Simplify imports for plugin developers (also facilitates renaming the planning module without needing plugin developers to upgrade their code)

Signed-off-by: F.N. Claessen <felix@seita.nl>

Signed-off-by: Nicolas Höning <nicolas@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Co-authored-by: Felix Claessen <30658763+Flix6x@users.noreply.github.com>
Co-authored-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
3 people committed Dec 29, 2022
1 parent 4dcec47 commit 60e3ec3
Show file tree
Hide file tree
Showing 37 changed files with 1,284 additions and 628 deletions.
16 changes: 16 additions & 0 deletions documentation/api/change_log.rst
Expand Up @@ -5,6 +5,22 @@ API change log

.. note:: The FlexMeasures API follows its own versioning scheme. This is also reflected in the URL, allowing developers to upgrade at their own pace.

v3.0-5 | 2022-12-30
"""""""""""""""""""

- Introduced ``flex-model`` and ``flex-context`` fields to `/sensors/<id>/schedules/trigger` (POST). They are dictionaries and group pre-existing fields:

- ``soc-at-start`` -> send in ``flex-model`` instead
- ``soc-min`` -> send in ``flex-model`` instead
- ``soc-max`` -> send in ``flex-model`` instead
- ``soc-unit`` -> send in ``flex-model`` instead
- ``roundtrip-efficiency`` -> send in ``flex-model`` instead
- ``prefer-charging-sooner`` -> send in ``flex-model`` instead
- ``consumption-price-sensor`` -> send in ``flex-context`` instead
- ``production-price-sensor`` -> send in ``flex-context`` instead
- ``inflexible-device-sensors`` -> send in ``flex-context`` instead


v3.0-4 | 2022-12-08
"""""""""""""""""""

Expand Down
72 changes: 72 additions & 0 deletions documentation/api/notation.rst
Expand Up @@ -155,6 +155,78 @@ For version 1, 2 and 3 of the API, only equidistant timeseries data is expected
- "duration" should also be a multiple of the sensor resolution.


.. _describing_flexibility:

Describing flexibility
^^^^^^^^^^^^^^^^^^^^^^^

FlexMeasures computes schedules for energy systems that consist of multiple devices that consume and/or produce electricity.
We model a device as an asset with a power sensor, and compute schedules only for flexible devices, while taking into account inflexible devices.

To compute a schedule, FlexMeasures first needs to assess the flexibility state of the system.
This is described by the `flex model` (information about the state and possible actions of the flexible device) and the `flex-context`
(information about the system as a whole, in order to assess the value of activating flexibility).

This information goes beyond the usual time series recorded by an asset's sensors. It's being sent through the API when triggering schedule computation.
Some parts of it can be persisted on the asset & sensor model as attributes (that's design work in progress).

We distinguish the information with two groups:

Flex model
""""""""""""

The flexibility model describes to the scheduler what the flexible asset's state is,
and what constraints or preferences should be taken into account.
Which type of flexibility model is relevant to a scheduler usually relates to the type of device.

Usually, not the whole flexibility model is needed.
FlexMeasures can infer missing values in the flex model, and even get them (as default) from the sensor's attributes.
This means that API and CLI users don't have to send the whole flex model every time.

Here are the three types of flexibility models you can expect to be built-in:

1) For storage devices (e.g. batteries, charge points, electric vehicle batteries connected to charge points), the schedule deals with the state of charge (SOC).

The possible flexibility parameters are:

- ``soc-at-start`` (defaults to 0)
- ``soc-unit`` (kWh or MWh)
- ``soc-min`` (defaults to 0)
- ``soc-max`` (defaults to max soc target)
- ``soc-targets`` (defaults to NaN values)
- ``roundtrip-efficiency`` (defaults to 100%)
- ``prefer-charging-sooner`` (defaults to True, also signals a preference to discharge later)

For some examples, see the `[POST] /sensors/(id)/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_ endpoint docs.

2) Shiftable process

.. todo:: A simple algorithm exists, needs integration into FlexMeasures and asset type clarified.

3) Heat pumps

.. todo:: Also work in progress, needs model for heat loss compensation.

In addition, folks who write their own custom scheduler (see :ref:`plugin_customization`) might also require their custom flexibility model.
That's no problem, FlexMeasures will let the scheduler decide which flexibility model is relevant and how it should be validated.

.. note:: We also aim to model situations with more than one flexible asset, with different types of flexibility.
This is ongoing architecture design work, and therefore happens in development settings, until we are happy
with the outcomes. Thoughts welcome :)


Flex context
"""""""""""""

With the flexibility context, we aim to describe the system in which the flexible assets operates:

- ``inflexible-device-sensors`` ― power sensors that are relevant, but not flexible, such as a sensor recording rooftop solar power connected behind the main meter, whose production falls under the same contract as the flexible device(s) being scheduled
- ``consumption-price-sensor`` ― the sensor which defines costs/revenues of consuming energy
- ``production-price-sensor`` ― the sensor which defines cost/revenues of producing energy

These should be independent on the asset type and consequently also do not depend on which scheduling algorithm is being used.


.. _beliefs:

Tracking the recording time of beliefs
Expand Down
9 changes: 7 additions & 2 deletions documentation/changelog.rst
Expand Up @@ -40,14 +40,19 @@ Infrastructure / Support
* Remove bokeh dependency and obsolete UI views [see `PR #476 <http://www.github.com/FlexMeasures/flexmeasures/pull/476>`_]
* Fix ``flexmeasures db-ops dump`` and ``flexmeasures db-ops restore`` not working in docker containers [see `PR #530 <http://www.github.com/FlexMeasures/flexmeasures/pull/530>`_] and incorrectly reporting a success when `pg_dump` and `pg_restore` are not installed [see `PR #526 <http://www.github.com/FlexMeasures/flexmeasures/pull/526>`_]
* Plugins can save BeliefsSeries, too, instead of just BeliefsDataFrames [see `PR #523 <http://www.github.com/FlexMeasures/flexmeasures/pull/523>`_]
* Improve documentation and code w.r.t. storage flexibility modelling ― prepare for handling other schedulers & merge battery and car charging schedulers [see `PR #511 <http://www.github.com/FlexMeasures/flexmeasures/pull/511>`_]
* Improve documentation and code w.r.t. storage flexibility modelling ― prepare for handling other schedulers & merge battery and car charging schedulers [see `PR #511 <http://www.github.com/FlexMeasures/flexmeasures/pull/511>`_ and `PR #537 <http://www.github.com/FlexMeasures/flexmeasures/pull/537>`_]
* Revised strategy for removing unchanged beliefs when saving data: retain the oldest measurement (ex-post belief), too [see `PR #518 <http://www.github.com/FlexMeasures/flexmeasures/pull/518>`_]
* Scheduling test for maximizing self-consumption, and improved time series db queries for fixed tariffs (and other long-term constants) [see `PR #532 <http://www.github.com/FlexMeasures/flexmeasures/pull/532>`_]
* Clean up table formatting for ``flexmeasures show`` CLI commands [see `PR #540 <http://www.github.com/FlexMeasures/flexmeasures/pull/540>`_]
* Add ``"Deprecation"`` and ``"Sunset"`` response headers for API users of deprecated API versions, and log warnings for FlexMeasures hosts when users still use them [see `PR #554 <http://www.github.com/FlexMeasures/flexmeasures/pull/554>`_]
* Explain how to avoid potential ``SMTPRecipientsRefused`` errors when using FlexMeasures in combination with a mail server [see `PR #558 <http://www.github.com/FlexMeasures/flexmeasures/pull/558>`_]

.. warning:: The CLI command ``flexmeasures monitor tasks`` has been renamed to ``flexmeasures monitor last-run``. The old name will stop working in version 0.13.
.. warning:: The API endpoint (`[POST] /sensors/(id)/schedules/trigger <api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_) to make new schedules will (in v0.13) sunset the storage flexibility parameters (they move to the ``flex-model`` parameter group), as well as the parameters describing other sensors (they move to ``flex-context``).

.. warning:: The CLI command ``flexmeasures monitor tasks`` has been deprecated (it's being renamed to ``flexmeasures monitor last-run``). The old name will be sunset in version 0.13.

.. warning:: The CLI command ``flexmeasures add schedule`` has been renamed to ``flexmeasures add schedule for-storage``. The old name will be sunset in version 0.13.



v0.11.3 | November 2, 2022
Expand Down
1 change: 1 addition & 0 deletions documentation/cli/change_log.rst
Expand Up @@ -12,6 +12,7 @@ since v0.12.0 | November XX, 2022
* Fix ``flexmeasures db-ops dump`` and ``flexmeasures db-ops restore`` incorrectly reporting a success when `pg_dump` and `pg_restore` are not installed.
* Add ``flexmeasures monitor last-seen``.
* Rename ``flexmeasures monitor tasks`` to ``flexmeasures monitor last-run``.
* Rename ``flexmeasures add schedule`` to ``flexmeasures add schedule for-storage`` (in expectation of more scheduling commands, based on in-built flex models).

since v0.11.0 | August 28, 2022
==============================
Expand Down
2 changes: 1 addition & 1 deletion documentation/cli/commands.rst
Expand Up @@ -34,7 +34,7 @@ of which some are referred to in this documentation.
``flexmeasures add sensor`` Add a new sensor.
``flexmeasures add beliefs`` Load beliefs from file.
``flexmeasures add forecasts`` Create forecasts.
``flexmeasures add schedule`` Create a charging schedule.
``flexmeasures add schedule for-storage`` Create a charging schedule for a storage asset.
``flexmeasures add holidays`` Add holiday annotations to accounts and/or assets.
``flexmeasures add annotation`` Add annotation to accounts, assets and/or sensors.
``flexmeasures add toy-account`` Create a toy account, for tutorials and trying things.
Expand Down
3 changes: 3 additions & 0 deletions documentation/configuration.rst
Expand Up @@ -247,6 +247,9 @@ Time to live for UDI event ids of successful scheduling jobs. Set a negative tim

Default: ``timedelta(days=7)``


.. _planning_horizon_config:

FLEXMEASURES_PLANNING_HORIZON
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion documentation/dev/docker-compose.rst
Expand Up @@ -96,7 +96,7 @@ Next, we put a scheduling job in the worker's queue. This only works because we

.. code-block:: console
flexmeasures add schedule --sensor-id 2 --optimization-context-id 3 \
flexmeasures add schedule for-storage --sensor-id 2 --optimization-context-id 3 \
--start ${TOMORROW}T07:00+01:00 --duration PT12H --soc-at-start 50% \
--roundtrip-efficiency 90% --as-job
Expand Down
2 changes: 1 addition & 1 deletion documentation/index.rst
Expand Up @@ -41,7 +41,7 @@ A tiny, but complete example: Let's install FlexMeasures from scratch. Then, usi
$ flexmeasures db upgrade # create tables
$ flexmeasures add toy-account --kind battery # setup account & a user, a battery (Id 2) and a market (Id 3)
$ flexmeasures add beliefs --sensor-id 3 --source toy-user prices-tomorrow.csv --timezone utc # load prices, also possible per API
$ flexmeasures add schedule --sensor-id 2 --consumption-price-sensor 3 \
$ flexmeasures add schedule for-storage --sensor-id 2 --consumption-price-sensor 3 \
--start ${TOMORROW}T07:00+01:00 --duration PT12H \
--soc-at-start 50% --roundtrip-efficiency 90% # this is also possible per API
$ flexmeasures show beliefs --sensor-id 2 --start ${TOMORROW}T07:00:00+01:00 --duration PT12H # also visible per UI, of course
Expand Down
29 changes: 14 additions & 15 deletions documentation/plugin/customisation.rst
Expand Up @@ -23,21 +23,16 @@ The following minimal example gives you an idea of some meta information you can
from datetime import datetime, timedelta
import pandas as pd
from pandas.tseries.frequencies import to_offset
from flexmeasures.data.models.time_series import Sensor
from flexmeasures.data.models.planning import Scheduler
from flexmeasures import Scheduler, Sensor
class DummyScheduler(Scheduler):
__author__ = "My Company"
__version__ = "2"
def schedule(
def compute_schedule(
self,
sensor: Sensor,
start: datetime,
end: datetime,
resolution: timedelta,
*args,
**kwargs
):
Expand All @@ -46,22 +41,26 @@ The following minimal example gives you an idea of some meta information you can
(Schedulers return positive values for consumption, and negative values for production)
"""
return pd.Series(
sensor.get_attribute("capacity_in_mw"),
index=pd.date_range(start, end, freq=resolution, closed="left"),
self.sensor.get_attribute("capacity_in_mw"),
index=pd.date_range(self.start, self.end, freq=self.resolution, closed="left"),
)
def deserialize_config(self):
"""Do not care about any flex config sent in."""
self.config_deserialized = True
.. note:: It's possible to add arguments that describe the asset flexibility and the EMS context in more detail. For example,
for storage assets we support various state-of-charge parameters. For now, the existing in-built schedulers are the best documentation.
We are working on documenting this better, so the learning curve becomes easier.

.. note:: It's possible to add arguments that describe the asset flexibility model and the flexibility (EMS) context in more detail.
For example, for storage assets we support various state-of-charge parameters. For details on flexibility model and context,
see :ref:`describing_flexibility` and the `[POST] /sensors/(id)/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_ endpoint.

Finally, make your scheduler be the one that FlexMeasures will use for certain sensors:


.. code-block:: python
from flexmeasures.data.models.time_series import Sensor
from flexmeasures import Sensor
scheduler_specs = {
"module": "flexmeasures.data.tests.dummy_scheduler", # or a file path, see note below
Expand Down Expand Up @@ -206,7 +205,7 @@ We demonstrate this here, and also show how you can add your own custom field sc
from typing import Optional
import click
from flexmeasures.data.schemas.times import AwareDateTimeField
from flexmeasures.data.schemas import AwareDateTimeField
from flexmeasures.data.schemas.utils import MarshmallowClickMixin
from marshmallow import fields
Expand Down
26 changes: 14 additions & 12 deletions documentation/tut/forecasting_scheduling.rst
Expand Up @@ -101,22 +101,24 @@ There are two ways to queue a scheduling job:
First, we can add a scheduling job to the queue via the API.
We already learned about the `[POST] /schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_ endpoint in :ref:`posting_flex_states`, where we saw how to post a flexibility state (in this case, the state of charge of a battery at a certain point in time).

Here, we extend that example with an additional target value, representing a desired future state of charge.
Here, we extend that (storage) example with an additional target value, representing a desired future state of charge.

.. code-block:: json
{
"value": 12.1,
"datetime": "2015-06-02T10:00:00+00:00",
"unit": "kWh",
"targets": [
{
"value": 25,
"datetime": "2015-06-02T16:00:00+00:00"
}
]
"start": "2015-06-02T10:00:00+00:00",
"flex-model": {
"soc-at-start": 12.1,
"soc-unit": "kWh"
"soc-targets": [
{
"value": 25,
"datetime": "2015-06-02T16:00:00+00:00"
}
}
}
We now have described the state of charge at 10am to be ``12.1``. In addition, we requested that it should be ``25`` at 4pm.
For instance, this could mean that a car should be charged at 90% at that time.
Expand All @@ -129,13 +131,13 @@ A second way to add scheduling jobs is via the CLI, so this is available for peo
.. code-block:: console
flexmeasures add schedule --sensor-id 2 --optimization-context-id 3 \
flexmeasures add schedule for-storage --sensor-id 2 --optimization-context-id 3 \
--start 2022-07-05T07:00+01:00 --duration PT12H \
--soc-at-start 50% --roundtrip-efficiency 90% --as-job
Here, the ``--as-job`` parameter makes the difference for queueing ― without it, the schedule is computed right away.
Run ``flexmeasures add schedule --help`` for more information.
Run ``flexmeasures add schedule for-storage --help`` for more information.
.. _getting_prognoses:
Expand Down
19 changes: 11 additions & 8 deletions documentation/tut/posting_data.rst
Expand Up @@ -269,29 +269,32 @@ Posting flexibility states

There is one more crucial kind of data that FlexMeasures needs to know about: What are the current states of flexible devices?
For example, a battery has a certain state of charge, which is relevant to describe the flexibility that the battery currently has.
In our terminology, this is called the "flex model" and you can read more at :ref:`describing_flexibility`.

Owners of such devices can post these states along with triggering the creation of a new schedule, to `[POST] /schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_.
Owners of such devices can post the flex model along with triggering the creation of a new schedule, to `[POST] /schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-(id)-schedules-trigger>`_.
The URL might look like this:

.. code-block:: html

https://company.flexmeasures.io/api/<version>/sensors/10/schedules/trigger

This example triggers a schedule for a power sensor (with ID 10) of a battery asset, asking to take into account the battery's current state of charge.
The following example triggers a schedule for a power sensor (with ID 10) of a battery asset, asking to take into account the battery's current state of charge.
From this, FlexMeasures derives the energy flexibility this battery has in the next 48 hours and computes an optimal charging schedule.
The endpoint allows to limit the flexibility range and also to set target values.
The endpoint also allows to limit the flexibility range and also to set target values.

.. code-block:: json
{
"value": 12.1,
"datetime": "2015-06-02T10:00:00+00:00",
"unit": "kWh"
"start": "2015-06-02T10:00:00+00:00",
"flex-model": {
"soc-at-start": 12.1,
"soc-unit": "kWh"
}
}
.. note:: At the moment, FlexMeasures only supports flexibility models suitable for batteries and car chargers here (asset types "battery", "one-way_evse" or "two-way_evse").
This will be expanded to other flexible assets as needed.

.. note:: Flexibility states are not persisted. To record a history of the state of charge, set up a separate sensor and post data to it using `[POST] /sensors/data <../api/v3_0.html#post--api-v3_0-sensors-data>`_ (see :ref:`posting_sensor_data`).
.. note:: Flexibility states are persisted on sensor attributes. To record a more complete history of the state of charge, set up a separate sensor and post data to it using `[POST] /sensors/data <../api/v3_0.html#post--api-v3_0-sensors-data>`_ (see :ref:`posting_sensor_data`).

In :ref:`how_queue_scheduling`, we'll cover what happens when FlexMeasurers is triggered to create a new schedule, and how those schedules can be retrieved via the API, so they can be used to steer assets.
In :ref:`how_queue_scheduling`, we'll cover what happens when FlexMeasures is triggered to create a new schedule, and how those schedules can be retrieved via the API, so they can be used to steer assets.

0 comments on commit 60e3ec3

Please sign in to comment.