From 69deff3e3c5896613fc285dd8c9886823993cccd Mon Sep 17 00:00:00 2001 From: Tiago Tavares Date: Sat, 20 Sep 2025 15:15:51 +0100 Subject: [PATCH 1/2] GSYE-886: draft implementation --- gsy_framework/constants_limits.py | 9 ++++ gsy_framework/enums.py | 11 +++++ gsy_framework/validators/__init__.py | 8 +++- .../validators/ev_charger_validator.py | 41 +++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 gsy_framework/validators/ev_charger_validator.py diff --git a/gsy_framework/constants_limits.py b/gsy_framework/constants_limits.py index 98b174a9..951dde09 100644 --- a/gsy_framework/constants_limits.py +++ b/gsy_framework/constants_limits.py @@ -214,6 +214,15 @@ class HeatPumpSettings: CALIBRATION_COEFFICIENT = 0.6 CALIBRATION_COEFFICIENT_RANGE = RangeLimit(0, 1) + class EVChargerSettings: + """Default settings for EV charger assets.""" + + # Range limits + MAX_POWER_RATING_KW_LIMIT = RangeLimit(0, sys.maxsize) + + # default values + MAX_POWER_RATING_KW = 10 + class MASettings: """Default settings for Market Agents.""" diff --git a/gsy_framework/enums.py b/gsy_framework/enums.py index 7e2ad5a1..172290b2 100644 --- a/gsy_framework/enums.py +++ b/gsy_framework/enums.py @@ -92,6 +92,17 @@ class HeatPumpSourceType(Enum): GROUND = 1 +class GridIntegrationType(Enum): + """Selection of grid integration types for EV chargers. + + - Unidirectional: The charger can only charge EVs, even if the connected EV supports bidirectional charging. + - Bidirectional: The charger can both charge and discharge, provided the connected EV supports bidirectional charging. + """ + + UNIDIRECTIONAL = 0 + BIDIRECTIONAL = 1 + + class SCMPropertyType(Enum): """Enum for SCM fee types""" diff --git a/gsy_framework/validators/__init__.py b/gsy_framework/validators/__init__.py index 71ed1dbe..b8b20c19 100644 --- a/gsy_framework/validators/__init__.py +++ b/gsy_framework/validators/__init__.py @@ -13,6 +13,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ + # Explicitly declare the names of the module's public API __all__ = [ "CommercialProducerValidator", @@ -24,12 +25,14 @@ "PVValidator", "StorageValidator", "WindTurbineValidator", - "HeatPumpValidator" + "HeatPumpValidator", + "EVChargerValidator", ] from gsy_framework.validators.cep_validator import CommercialProducerValidator from gsy_framework.validators.finite_diesel_generator_validator import ( - FiniteDieselGeneratorValidator) + FiniteDieselGeneratorValidator, +) from gsy_framework.validators.smart_meter_validator import SmartMeterValidator from gsy_framework.validators.infinite_bus_validator import InfiniteBusValidator from gsy_framework.validators.load_validator import LoadValidator @@ -38,3 +41,4 @@ from gsy_framework.validators.storage_validator import StorageValidator from gsy_framework.validators.wind_turbine_validator import WindTurbineValidator from gsy_framework.validators.heat_pump_validator import HeatPumpValidator +from gsy_framework.validators.ev_charger_validator import EVChargerValidator diff --git a/gsy_framework/validators/ev_charger_validator.py b/gsy_framework/validators/ev_charger_validator.py new file mode 100644 index 00000000..44dcc2c9 --- /dev/null +++ b/gsy_framework/validators/ev_charger_validator.py @@ -0,0 +1,41 @@ +from gsy_framework.constants_limits import ConstSettings +from gsy_framework.exceptions import GSyDeviceException +from gsy_framework.validators.base_validator import BaseValidator +from gsy_framework.enums import GridIntegrationType + +EVChargerSettings = ConstSettings.EVChargerSettings + + +class EVChargerValidator(BaseValidator): + """Validator class for EV Charger assets.""" + + @classmethod + def validate(cls, **kwargs): + cls._validate_grid_integration(**kwargs) + + @staticmethod + def _validate_grid_integration(**kwargs): + if kwargs.get("grid_integration"): + try: + if kwargs["grid_integration"] is not None: + GridIntegrationType(kwargs["grid_integration"]) + except ValueError as ex: + raise GSyDeviceException( + { + "misconfiguration": [ + "EV Charger grid integration not one of " + f"{[st.value for st in GridIntegrationType]}" + ] + } + ) from ex + + @classmethod + def _validate_max_power(cls, **kwargs): + """Validate energy related arguments.""" + + cls._check_range( + name="maximum_power_rating_kW", + value=kwargs["maximum_power_rating_kW"], + min_value=EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.min, + max_value=EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.max, + ) From c1dacd3670ac33ae17dc41a272855e46df5058ab Mon Sep 17 00:00:00 2001 From: Tiago Tavares Date: Tue, 7 Oct 2025 07:49:29 +0100 Subject: [PATCH 2/2] GSYE-886: add ev charger status --- .pre-commit-config.yaml | 2 +- gsy_framework/enums.py | 13 ++++++++++-- .../validators/ev_charger_validator.py | 20 ++++++++++++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a539c0c..8f974905 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: rev: 24.4.2 hooks: - id: black - language_version: python3.8 + language_version: python3.10 - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: diff --git a/gsy_framework/enums.py b/gsy_framework/enums.py index 172290b2..1eb80598 100644 --- a/gsy_framework/enums.py +++ b/gsy_framework/enums.py @@ -95,14 +95,23 @@ class HeatPumpSourceType(Enum): class GridIntegrationType(Enum): """Selection of grid integration types for EV chargers. - - Unidirectional: The charger can only charge EVs, even if the connected EV supports bidirectional charging. - - Bidirectional: The charger can both charge and discharge, provided the connected EV supports bidirectional charging. + - Unidirectional: The charger can only charge EVs, even if the connected EV + supports bidirectional charging. + - Bidirectional: The charger can both charge and discharge, provided the + connected EV supports bidirectional charging. """ UNIDIRECTIONAL = 0 BIDIRECTIONAL = 1 +class EVChargerStatus(Enum): + """Status of the EV charger.""" + + IDLE = 0 + ACTIVE = 1 + + class SCMPropertyType(Enum): """Enum for SCM fee types""" diff --git a/gsy_framework/validators/ev_charger_validator.py b/gsy_framework/validators/ev_charger_validator.py index 44dcc2c9..37506bbf 100644 --- a/gsy_framework/validators/ev_charger_validator.py +++ b/gsy_framework/validators/ev_charger_validator.py @@ -2,6 +2,7 @@ from gsy_framework.exceptions import GSyDeviceException from gsy_framework.validators.base_validator import BaseValidator from gsy_framework.enums import GridIntegrationType +from gsy_framework.validators.utils import validate_range_limit EVChargerSettings = ConstSettings.EVChargerSettings @@ -12,6 +13,7 @@ class EVChargerValidator(BaseValidator): @classmethod def validate(cls, **kwargs): cls._validate_grid_integration(**kwargs) + cls._validate_max_power(**kwargs) @staticmethod def _validate_grid_integration(**kwargs): @@ -32,10 +34,18 @@ def _validate_grid_integration(**kwargs): @classmethod def _validate_max_power(cls, **kwargs): """Validate energy related arguments.""" + name = "maximum_power_rating_kW" + value = kwargs.get("maximum_power_rating_kW") + min_limit = EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.min + max_limit = EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.max + if value is None: + raise GSyDeviceException( + {"misconfiguration": [f"Value of {name} should not be None."]} + ) - cls._check_range( - name="maximum_power_rating_kW", - value=kwargs["maximum_power_rating_kW"], - min_value=EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.min, - max_value=EVChargerSettings.MAX_POWER_RATING_KW_LIMIT.max, + validate_range_limit( + min_limit, + value, + max_limit, + {"misconfiguration": [f"{name} should be between {min_limit} & {max_limit}."]}, )