- GSy Exchange SDK
GSy Exchange SDK is responsible for communicating with a running collaboration of GSy Exchange. The client uses the API of the GSy Exchange external connections in order to be able to dynamically connect to the simulated electrical grid and place offers for its energy production, and bids for its energy consumption/requirements.
For local test runs of GSy Exchange Redis (https://redis.io/) is used as communication protocol.
In the following commands for the local test run are marked with LOCAL
.
For communication with collaborations or canary networks on https://d3a.io, a RESTful API is used.
In the following commands for the connection via the REST API are marked with REST
.
Installation of gsy-e-sdk using pip:
pip install git+https://github.com/gridsingularity/gsy-e-sdk.git
In order to get help, please run:
gsy-e-sdk run --help
The following parameters can be set via the CLI:
base-setup-path
--> Path where user's client script resides, otherwisegsy_e_sdk/setups
is used.setup
--> Name of user's API client module/script.username
--> Username of agent authorized to communicate with respective collaboration or Canary Network (CN).password
--> Password of respective agentdomain-name
--> GSy Exchange domain URLweb-socket
--> GSy Exchange websocket URLsimulation-id
--> UUID of the collaboration or Canary Network (CN)simulation-config-path
--> Path to the JSON file that contains the user's collaboration or CN information. This file can be downloaded from the "Registry" page on the GSy Exchange website.simulation-id
,domain-name
, andweb-socket
CLI-settings will be overwritten by the settings in the filerun-on-redis
--> This flag can be set for local testing of the API client, where no user authentication is required. For that, a locally running redis server and GSy Exchange simulation are needed.
- For local testing of the API client:
gsy-e-sdk --log-level ERROR run --setup test_redis_aggregator --run-on-redis
- For testing your api client script on remote server hosting GSy Exchange's collaboration/CNs.
- If user's client script resides on
gsy_e_sdk/setups
gsy-e-sdk run -u <username> -p <password> --setup test_create_aggregator --simulation-config-path <your-downloaded-simulation-config-file-path>
- If user's client script resides on a different directory, then its path needs to be set via
--base-setup-path
gsy-e-sdk run -u <username> -p <password> --base-setup-path <absolute/relative-path-to-your-client-script> --setup <name-of-your-script> --simulation-config-path <your-downloaded-simulation-config-file-path>
- If user's client script resides on
In order to facilitate offer and bid management and scheduling, the client will get notified via events. It is possible to capture these events and perform operations as a reaction to them by overriding the corresponding methods.
- when a new market slot is created, the
on_market_slot
method is called - when a new tick has started, the
on_tick
method is called - when the simulation has finished, the
on_finished
method is called - when any event arrives , the
on_event_or_response
method is called
The constructor of the API class can connect and register automatically to a running collaboration:
REST
(here the asset uuid has to be obtained first)asset_uuid = get_area_uuid_from_area_name_and_collaboration_id( <simulation_id>, <asset_name>, <domain_name> ) asset_client = RestAssetClient(asset_uuid, autoregister=True)
LOCAL
asset_client = RedisAssetClient(<asset-uuid>, autoregister=True)
Otherwise one can connect manually:
asset_client.register()
To disconnect/unregistering, the following command is available:
asset_client.unregister()
REST
(here the market uuid has to be obtained first)market_uuid = get_area_uuid_from_area_name_and_collaboration_id( <simulation_id>, <market_name>, <domain_name> ) market_client = RestMarketClient(market_uuid, autoregister=True)
LOCAL
market_client = RedisMarketClient(<market_name>, autoregister=True)
Aggregators are clients that control multiple assets and/or markets and can send out batch commands in order to react to an event simultaneously for each owned asset.
REST
aggregator = Aggregator( simulation_id=<simulation_id>, domain_name=<domain_name>, aggregator_name=<aggregator_name>, websockets_domain_name=<websocket_domain_name> )
LOCAL
aggregator = AutoAggregator(<aggregator_name>)
To list your aggregators, its configuration id and the registered assets, you should:
from gsy_e_sdk.utils import get_aggregators_list
my_aggregators = get_aggregators_list(domain_name="Domain Name")
The returned value is a list of aggregators and their connected assets
[{'configUuid': 'f7330248-9a72-4979-8477-dfbcff0c46a0', 'name': 'My aggregator',
'devicesList': [{"deviceUuid":"My_asset_uuid"},{"deviceUuid":"My_asset_uuid 2"}]}]
The asset or market can select the Aggregator (assuming that a connection to an asset was established):
asset.select_aggregator(aggregator.aggregator_uuid)
The asset or market can unselect the Aggregator:
asset.unselect_aggregator(aggregator.aggregator_uuid)
Aggregator
instances provide methods that can simplify specific operations. Below we list some of the most commonly used:
-
Return all the aggregators connected to the aggregator's simulation:
list_aggregators()
-
Return the representation of all the assets and areas connected to the aggregator's configuration:
get_configuration_registry()
-
Delete the current aggregator:
delete_aggregator()
Commands to all or individual connected assets or markets can be sent in one batch. All asset or market specific functions can be sent via commands that are accumulated and added to buffer.
aggregator.add_to_batch_commands.bid_energy(<asset_uuid>, <energy>, <price_cents>)
These also can be chained as follow:
aggregator.add_to_batch_commands.bid_energy(<asset_uuid>, <energy>, <price_cents>)\
.offer_energy(<asset_uuid>, <energy>, <price_cents>)\
.asset_info(<asset_uuid>)
Finally, the batch commands are sent to the GSy Exchange via the following command:
aggregator.execute_batch_command()
The following commands can be issued as batch commands (refer to How to send batch commands for more information):
- Send an energy bid with price in cents:
bid_energy(asset_uuid, energy, price_cents, replace_existing, attributes, requirements)
- Send an energy bid with energy rate in cents/kWh:
bid_energy_rate(asset_uuid, energy, rate_cents_per_kWh, replace_existing, attributes, requirements)
- Change grid fees using a percentage value:
change_grid_fees_percent(area_uuid, fee_percent)
- Change grid fees using a constant value:
grid_fees(area_uuid, fee_cents_kwh)
- Delete offer using its ID:
delete_offer(asset_uuid, offer_id)
- Delete bid using its ID:
delete_bid(asset_uuid, bid_id)
- Get asset info (returns demanded energy for Load assets and available energy for PVs):
asset_info(asset_uuid)
- List all posted offers:
list_offers(area_uuid)
- Lists all posted bids:
list_bids(area_uuid)
- Retrieve market DSO statistics:
last_market_dso_stats(area_uuid)
- Send an energy offer with price in cents:
offer_energy(asset_uuid, energy, price_cents, replace_existing, attributes, requirements)
- Send an energy offer with energy rate in cents/kWh:
offer_energy_rate(asset_uuid, energy, rate_cents_per_kWh, replace_existing, attributes, requirements)
A finer control over the issued bids and offers can be achieved through the attributes
and requirements
parameters of the bid and offer methods.
A dictionary of attributes that describe the offer or bid. Currently supported attributes:
-
Offers:
energy_type
: energy type of the offer
-
Bids: Not supported at the moment
A list of dictionaries containing requirements for the offer or bid. At least one of the provided dictionaries needs to be satisfied in the matching process. Currently supported requirements:
-
Offers:
trading_partners
: IDs of the areas with which the offer has to be matched
-
Bids:
energy_type
: energy types that the bid prefers to consumetrading_partners
: IDs of the areas with which the bid prefers to be matchedenergy
: energy that the bid prefers to consumeprice
: trade rate that the bid prefers to accept
The Aggregator
class has a function that calculates the grid fees along path between two assets or
markets in the grid:
Aggregator.calculate_grid_fee(start_market_or_asset_name, target_market_or_asset_name, fee_type):
The algorithm retrieves the path between start_market_or_asset_name
and target_market_or_asset_name
and accumulates all corresponding grid fees along the way. Market and asset names are supported.
target_market_or_asset_name
is optional, if left blank, only the grid fee of the
start_market_or_asset_name
is returned.
The user can chose between current_market_fee
and last_market_fee
, which is toggled by providing
the corresponding string in the fee_type
input parameter.
The energy consumption or demand for PV and Load assets can be set for the next market slot via the following command (assuming that a connection to an asset was established):
asset_client.set_energy_forecast(<energy_forecast_Wh>)
An example how this command could be added into an aggregator script can be found in gsy_e_sdk/setups/test_sending_energy_forecast.py .
In case the user wants to send asset measurements without using the API client, the raw REST API can be used instead. An additional authentication step has to be performed first.
Authentication is done via JSON web token (JWT). In order to retrieve the JWT, the following POST request has to be performed:
POST https://d3aweb-dev.gridsingularity.com/api-token-auth/
The body of the request needs to contain the following information (JSON string):
{"username": "<your_username>", "password": "<your_password>"}
The returned JWT needs to be sent via the Authorization HTTP header when sending the forecast data. For that you need to add the following key value pair to the header of every POST command:
Authorization: JWT <your_token>
The POST to send the energy value is the following
(please fill in <Canary Network UUID>
and <Asset UUID>
):
POST https://d3aweb-dev.gridsingularity.com/external-connection/api/<Canary Network UUID>/<Asset UUID>/set_energy_forecast/
The body of the request needs to contain the following information (JSON string):
{"energy_Wh": <energy_value_for_asset>}