Metadata-Version: 2.4
Name: flexmeasures-client
Version: 0.9.0
Summary: Async client to connect to the FlexMeasures API
Project-URL: Homepage, https://github.com/FlexMeasures/flexmeasures-client
Project-URL: Documentation, https://flexmeasures-client.readthedocs.io/
Project-URL: Source code, https://github.com/FlexMeasures/flexmeasures-client
Author-email: Flexmeasures <info@seita.nl>
License-Expression: Apache-2.0
License-File: LICENSE.txt
Keywords: energy,flexibility,flexmeasures,scheduling,smart grid
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: <3.13,>=3.10
Requires-Dist: aiohttp>=3.13.3
Requires-Dist: async-timeout>=5.0.1
Requires-Dist: packaging>=26.0
Requires-Dist: pandas>=2.1.4
Provides-Extra: s2
Requires-Dist: requests>=2.32.5; extra == 's2'
Requires-Dist: s2-python>=0.8.1; extra == 's2'
Requires-Dist: semver>=3.0.4; extra == 's2'
Requires-Dist: tzdata>=2025.3; extra == 's2'
Description-Content-Type: text/x-rst

.. image:: https://github.com/FlexMeasures/screenshots/blob/main/logo/flexmeasures-horizontal-color.svg?raw=true
   :alt: FlexMeasures Logo
   :align: center
.. image:: https://img.shields.io/github/license/FlexMeasures/flexmeasures-client?color=blue
    :alt: License
    :target: https://github.com/FlexMeasures/flexmeasures-client/blob/main/LICENSE.txt
.. image:: https://github.com/FlexMeasures/flexmeasures-client/actions/workflows/ci.yml/badge.svg
    :alt: Tests
    :target: https://github.com/FlexMeasures/flexmeasures-client/actions/workflows/ci.yml
.. image:: https://img.shields.io/pypi/v/flexmeasures-client.svg
    :alt: PyPI Version
    :target: https://pypi.python.org/pypi/flexmeasures-client
.. image:: https://img.shields.io/badge/python-3.9+-blue.svg
    :alt: Python 3.9+
    :target: https://www.python.org/downloads/
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :alt: Code style: black
    :target: https://github.com/psf/black
.. image:: https://coveralls.io/repos/github/FlexMeasures/flexmeasures-client/badge.svg
    :alt: Coverage
    :target: https://coveralls.io/github/FlexMeasures/flexmeasures-client

|

===================
FlexMeasures Client
===================


The FlexMeasures Client provides a Python package to connect to a `FlexMeasures <https://github.com/FlexMeasures/flexmeasures>`_ server to manage flexible assets.

The Flexmeasures Client package provides functionality for authentication, asset and sensor management, posting sensor data, and triggering and retrieving schedules from a FlexMeasures instance through the API.

*As the Flexmeasures Client is still in active development and on version 0.x it should be considered in beta.*


Installation
===============


We use `uv <https://docs.astral.sh/uv/>`_ to manage dependencies. First, `install uv <https://docs.astral.sh/uv/getting-started/installation/>`_.

Then add it to your project:

.. code-block:: bash

    uv add flexmeasures-client

The FlexMeasures Client can also run as an `S2 CEM <https://docs.s2standard.org/docs/concepts/common-concepts/>`_. To enable S2 features, you need to install extra requirements:

.. code-block:: bash

    uv add flexmeasures-client[s2]


Initialization and authentication
==================================

To get started with the FlexMeasures Client, first an account needs to be registered with a FlexMeasures instance.
To create a local instance of FlexMeasures, follow the `FlexMeasures documentation <https://flexmeasures.readthedocs.io/en/latest/index.html>`_.
Registering to a hosted FlexMeasures instance instead can be done through `Seita BV <https://seita.nl/>`_.

In these examples we show how to set up the client to connect to either ``http://localhost:5000`` or ``https://ems.seita.energy``. To connect to a different host, adapt the host in the initialization of the client.

   .. code-block:: python

    from flexmeasures_client import FlexMeasuresClient

    async def main():
        client = FlexMeasuresClient(host="localhost:5000", ssl=False, email="email@email.com", password="pw")
        client = FlexMeasuresClient(host="ems.seita.energy", ssl=True, email="email@email.com", password="pw")


Retrieving available info
==========================

Retrieve user and account:

.. code-block:: python

   user = await client.get_user()
   account = await client.get_account()

The data will be returned as a dictionary.

Retrieve available assets and sensors:

.. code-block:: python

    assets = await client.get_assets()
    sensors = await client.get_sensors()

The data will be returned as (lists of) dictionaries.

.. note:: For `get_assets()` as well as `get_sensors()`, you can use various parameters which the API endpoints also support.


Sending data
=================

Post a measurement from a sensor:

.. code-block:: python

    await client.post_sensor_data(
        sensor_id=<sensor_id>,  # integer
        start="2023-03-26T10:00+02:00",  # ISO datetime
        duration="PT6H",  # ISO duration
        values=[1, 2, 3, 4],  # list
        unit="kWh",
    )


Here is a small but complete FlexMeasures Client script, which simply updates the flex context of an asset:

.. code-block:: python

    import asyncio

    from flexmeasures_client import FlexMeasuresClient

    usr = "xxxxxxxxxxxxxxxx"
    pwd = "xxxxxxxxxxxxxxxx"
    asset_id = 1


    async def main():
        client = FlexMeasuresClient(email=usr, password=pwd)

        asset = await client.update_asset(
            asset_id=asset_id,
            updates={
                "flex_context": {
                    "site-consumption-capacity": "110 kW",
                    "relax-constraints": True
                }
            },
        )

        print(asset)

        await client.close()


    asyncio.run(main())


For a slightly larger self-contained script, see `this script for sending data <examples/send_data_to_asset.py>`_.
It sets up an asset and sensor (checking if they exist first), and then sends data to it using `post_sensor_data()`.


Scheduling
===========


With FlexMeasures a schedule can be requested to optimize at what time the flexible assets can be activated to optimize for price of energy or emissions.

The calculation of the schedule can take some time depending on the complexity of the calculations. A polling function is used to check if a schedule is available after triggering the schedule.

Trigger and retrieve a schedule for multiple devices:

.. code-block:: python

    schedule = await flexmeasures_client.trigger_and_get_schedule(
        asset_id=<asset_id>,  # the asset ID (int) of the asset that all relevant power sensors belong to (or live under, in case of a tree-like asset structure)
        start="2023-03-26T10:00+02:00",  # ISO datetime
        duration="PT12H",  # ISO duration
        flex_context={
            "consumption-price": {"sensor": <consumption_price_sensor_id>},  # int
        },
        flex-model=[
            # Example flex-model for an electric truck at a regular Charge Point
            {
                "sensor": <power_sensor_id>,  # int
                "power-capacity": "22 kVA",
                "production-capacity": "0 kW",
                "soc-at-start": "50 kWh",
                "soc-max": "400 kWh",
                "soc-min": "20 kWh",
                "soc-targets": [
                    {"value": "100 kWh", "datetime": "2023-03-03T11:00+02:00"},
                ],
            },
            # Example flex-model for curtailable solar panels
            {
                "sensor": <another_power_sensor_id>,  # int
                "power-capacity": "20 kVA",
                "consumption-capacity": "0 kW",
                "production-capacity": {"sensor": <another_power_sensor_id>},  # int
            },
        ],
    )

For triggering and retrieving a schedule for a single device, simply limit the flex-model to list a single device.
Alternatively, use a single-device flex-model (no list) and move the device's power sensor ID out of the flex-model and use it as the sensor ID in the call to ``trigger_and_get_schedule`` (and leave out the asset ID).

.. code-block:: python

    schedule = await flexmeasures_client.trigger_and_get_schedule(
        sensor_id=<sensor_id>,  # int
        start="2023-03-26T10:00+02:00",  # ISO datetime
        duration="PT12H",  # ISO duration
        flex_context={
            "consumption-price": {"sensor": <consumption_price_sensor_id>},  # int
        },
        flex-model={
            "soc-at-start": "50 kWh",
            "soc-max": "400 kWh",
            "soc-min": "20 kWh",
            "soc-targets": [
                {"value": "100 kWh", "datetime": "2023-03-03T11:00+02:00"},
            ],
        },
    )

The trigger and get schedule function can also be separated to trigger the schedule first and later retrieve the schedule using the ``schedule_uuid``.

Trigger a schedule:

.. code-block:: python

    schedule_uuid = await flexmeasures_client.trigger_schedule(
        **kwargs,  # same kwargs as previous example
    )

The ``trigger_schedule`` method returns a ``schedule_uuid``.
This can be used to retrieve the schedule, using:

.. code-block:: python

    schedule = await flexmeasures_client.get_schedule(
        sensor_id=<sensor_id>,  # int
        schedule_id="<schedule_uuid>",  # uuid
        duration="PT45M",  # ISO duration
    )

The client will re-try until the schedule is available or the ``MAX_POLLING_STEPS`` of ``10`` is reached.


Forecasting
===========

Trigger a forecast for a sensor and wait for the result:

.. code-block:: python

    forecast = await client.trigger_and_get_forecast(
        sensor_id=<sensor_id>,  # int
        duration="PT24H",  # ISO duration – how far ahead to forecast
    )
    # Returns e.g. {"values": [1.2, 1.5, ...], "start": "...", "duration": "PT24H", "unit": "kW"}

The client polls until the forecasting job is complete.  For more advanced options
(training window, regressors, forecast frequency, etc.) see `forecasting <https://github.com/FlexMeasures/flexmeasures-client/blob/main/docs/forecasting.rst>`_.


Development
==============

We use `uv <https://docs.astral.sh/uv/>`_ to manage dependencies. First, `install uv <https://docs.astral.sh/uv/getting-started/installation/>`_.

To install the package with all development and testing dependencies:

.. code-block:: bash

    uv sync --group dev --group test

Moreover, if you need to work on S2 features, you need to install extra dependencies:

.. code-block:: bash

    uv sync --extra s2 --group dev --group test

.. note::

   If you prefer shorter commands during interactive development, you can activate the virtual environment (``source .venv/bin/activate``, or ``.venv\\Scripts\\activate`` on Windows) or run commands directly with ``uv run <command>``.




.. _pyscaffold-notes:


Making Changes & Contributing
=============================

.. note: Read more details in CONTRIBUTING.rst

Install the project locally (creating a virtual environment automatically):

.. code-block:: bash

    uv sync


Running tests locally is crucial as well:

.. code-block:: bash

    uv run poe test

For S2 features:

.. code-block:: bash

    uv sync --extra s2 --group test
    uv run poe test-s2

This project uses `pre-commit`_, please make sure to install it before making any
changes:

.. code-block:: bash

    uv tool install pre-commit
    cd flexmeasures-client
    pre-commit install

It is a good idea to update the hooks to the latest version:

.. code-block:: bash

    pre-commit autoupdate

Don't forget to tell your contributors to also install and use pre-commit.

.. _pre-commit: https://pre-commit.com/


New releases on PyPI are made by adding a tag and pushing it:

.. code-block:: bash

    git tag -s -a vX.Y.Z -m "Short summary"
    git push --tags

(of course you need the permissions to do so)

See releases in GitHub Actions at https://github.com/FlexMeasures/flexmeasures-client/deployments/release


===================
HEMS tutorial
===================

The FlexMeasures Client comes with a tutorial for creating a Home Energy Management System (HEMS) `See the Usage docs <docs/HEMS.rst>`_.


===================
S2 CEM
===================

The FlexMeasures Client can also be run as a local S2 Customer Energy Manager (CEM) using WebSocket communication. `See here for the docs <docs/CEM.rst>`_, which includes a docker-compose stack.
