From 704974ec0fedbd3c7f0dc09225afed7905e028d1 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 14:22:30 +0100 Subject: [PATCH 1/6] Use `ElectricalComponentId` in `ComponentConnection` This makes them consistent with the `ElectricalComponent` type. Signed-off-by: Sahas Subramanian --- .../client/assets/electrical_component/_connection.py | 6 +++--- .../assets/electrical_component/_connection_proto.py | 8 +++++--- .../success_case.py | 10 +++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/frequenz/client/assets/electrical_component/_connection.py b/src/frequenz/client/assets/electrical_component/_connection.py index 83bdf48..ee94ee5 100644 --- a/src/frequenz/client/assets/electrical_component/_connection.py +++ b/src/frequenz/client/assets/electrical_component/_connection.py @@ -6,7 +6,7 @@ import dataclasses from datetime import datetime, timezone -from frequenz.client.common.microgrid.components import ComponentId +from frequenz.client.common.microgrid.electrical_components import ElectricalComponentId from .._lifetime import Lifetime @@ -38,14 +38,14 @@ class ComponentConnection: when and how the microgrid infrastructure has been modified. """ - source: ComponentId + source: ElectricalComponentId """The unique identifier of the component where the connection originates. This is aligned with the direction of current flow away from the grid connection point, or in case of islands, away from the islanding point. """ - destination: ComponentId + destination: ElectricalComponentId """The unique ID of the component where the connection terminates. This is the component towards which the current flows. diff --git a/src/frequenz/client/assets/electrical_component/_connection_proto.py b/src/frequenz/client/assets/electrical_component/_connection_proto.py index 2f545d5..11c7d61 100644 --- a/src/frequenz/client/assets/electrical_component/_connection_proto.py +++ b/src/frequenz/client/assets/electrical_component/_connection_proto.py @@ -8,7 +8,7 @@ from frequenz.api.common.v1alpha8.microgrid.electrical_components import ( electrical_components_pb2, ) -from frequenz.client.common.microgrid.components import ComponentId +from frequenz.client.common.microgrid.electrical_components import ElectricalComponentId from .._lifetime import Lifetime from .._lifetime_proto import lifetime_from_proto @@ -63,8 +63,10 @@ def component_connection_from_proto_with_issues( `None` if the protobuf message is completely invalid and a `ComponentConnection` cannot be created. """ - source_component_id = ComponentId(message.source_electrical_component_id) - destination_component_id = ComponentId(message.destination_electrical_component_id) + source_component_id = ElectricalComponentId(message.source_electrical_component_id) + destination_component_id = ElectricalComponentId( + message.destination_electrical_component_id + ) if source_component_id == destination_component_id: major_issues.append( f"connection ignored: source and destination are the same ({source_component_id})", diff --git a/tests/client_test_cases/list_microgrid_electrical_component_connections/success_case.py b/tests/client_test_cases/list_microgrid_electrical_component_connections/success_case.py index bca3736..45b93bc 100644 --- a/tests/client_test_cases/list_microgrid_electrical_component_connections/success_case.py +++ b/tests/client_test_cases/list_microgrid_electrical_component_connections/success_case.py @@ -12,7 +12,7 @@ electrical_components_pb2, ) from frequenz.client.base.conversion import to_timestamp -from frequenz.client.common.microgrid.components import ComponentId +from frequenz.client.common.microgrid.electrical_components import ElectricalComponentId from frequenz.client.assets import Lifetime from frequenz.client.assets.electrical_component import ComponentConnection @@ -50,12 +50,12 @@ def assert_client_result(actual_result: Any) -> None: """Assert that the client result matches the expected connections list.""" assert list(actual_result) == [ ComponentConnection( - source=ComponentId(1), - destination=ComponentId(2), + source=ElectricalComponentId(1), + destination=ElectricalComponentId(2), ), ComponentConnection( - source=ComponentId(2), - destination=ComponentId(3), + source=ElectricalComponentId(2), + destination=ElectricalComponentId(3), operational_lifetime=Lifetime(start=lifetime_start), ), ] From d9aa53e7f20cbdd68eb4e13357db918354bcf466 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 14:42:52 +0100 Subject: [PATCH 2/6] Accept `MicrogridId` instances in client methods, instead of `int`s Signed-off-by: Sahas Subramanian --- src/frequenz/client/assets/_client.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/frequenz/client/assets/_client.py b/src/frequenz/client/assets/_client.py index f071166..c1aea5f 100644 --- a/src/frequenz/client/assets/_client.py +++ b/src/frequenz/client/assets/_client.py @@ -14,6 +14,7 @@ from frequenz.api.assets.v1 import assets_pb2, assets_pb2_grpc from frequenz.client.base import channel from frequenz.client.base.client import BaseApiClient, call_stub_method +from frequenz.client.common.microgrid import MicrogridId from ._microgrid import Microgrid from ._microgrid_proto import microgrid_from_proto @@ -87,7 +88,7 @@ def stub(self) -> assets_pb2_grpc.PlatformAssetsAsyncStub: return self._stub # type: ignore async def get_microgrid( # noqa: DOC502 (raises ApiClientError indirectly) - self, microgrid_id: int + self, microgrid_id: MicrogridId ) -> Microgrid: """ Get the details of a microgrid. @@ -105,7 +106,7 @@ async def get_microgrid( # noqa: DOC502 (raises ApiClientError indirectly) response = await call_stub_method( self, lambda: self.stub.GetMicrogrid( - assets_pb2.GetMicrogridRequest(microgrid_id=microgrid_id), + assets_pb2.GetMicrogridRequest(microgrid_id=int(microgrid_id)), timeout=DEFAULT_GRPC_CALL_TIMEOUT, ), method_name="GetMicrogrid", @@ -114,7 +115,7 @@ async def get_microgrid( # noqa: DOC502 (raises ApiClientError indirectly) return microgrid_from_proto(response.microgrid) async def list_microgrid_electrical_components( - self, microgrid_id: int + self, microgrid_id: MicrogridId ) -> list[ElectricalComponent]: """ Get the electrical components of a microgrid. @@ -129,7 +130,7 @@ async def list_microgrid_electrical_components( self, lambda: self.stub.ListMicrogridElectricalComponents( assets_pb2.ListMicrogridElectricalComponentsRequest( - microgrid_id=microgrid_id, + microgrid_id=int(microgrid_id), ), timeout=DEFAULT_GRPC_CALL_TIMEOUT, ), @@ -142,7 +143,7 @@ async def list_microgrid_electrical_components( async def list_microgrid_electrical_component_connections( self, - microgrid_id: int, + microgrid_id: MicrogridId, source_component_ids: Iterable[int] = (), destination_component_ids: Iterable[int] = (), ) -> list[ComponentConnection | None]: @@ -161,7 +162,7 @@ async def list_microgrid_electrical_component_connections( The electrical component connections of the microgrid. """ request = assets_pb2.ListMicrogridElectricalComponentConnectionsRequest( - microgrid_id=microgrid_id, + microgrid_id=int(microgrid_id), source_component_ids=source_component_ids, destination_component_ids=destination_component_ids, ) From 7830989422eb5cf9e4b1025e274886d9b3376639 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 14:45:37 +0100 Subject: [PATCH 3/6] Accept `ElectricalComponentId` in client methods, instead of `int`s Signed-off-by: Sahas Subramanian --- src/frequenz/client/assets/_client.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/frequenz/client/assets/_client.py b/src/frequenz/client/assets/_client.py index c1aea5f..23b39ff 100644 --- a/src/frequenz/client/assets/_client.py +++ b/src/frequenz/client/assets/_client.py @@ -15,6 +15,7 @@ from frequenz.client.base import channel from frequenz.client.base.client import BaseApiClient, call_stub_method from frequenz.client.common.microgrid import MicrogridId +from frequenz.client.common.microgrid.electrical_components import ElectricalComponentId from ._microgrid import Microgrid from ._microgrid_proto import microgrid_from_proto @@ -144,8 +145,8 @@ async def list_microgrid_electrical_components( async def list_microgrid_electrical_component_connections( self, microgrid_id: MicrogridId, - source_component_ids: Iterable[int] = (), - destination_component_ids: Iterable[int] = (), + source_component_ids: Iterable[ElectricalComponentId] = (), + destination_component_ids: Iterable[ElectricalComponentId] = (), ) -> list[ComponentConnection | None]: """ Get the electrical component connections of a microgrid. @@ -163,8 +164,8 @@ async def list_microgrid_electrical_component_connections( """ request = assets_pb2.ListMicrogridElectricalComponentConnectionsRequest( microgrid_id=int(microgrid_id), - source_component_ids=source_component_ids, - destination_component_ids=destination_component_ids, + source_component_ids=(int(c) for c in source_component_ids), + destination_component_ids=(int(c) for c in destination_component_ids), ) response = await call_stub_method( From 2085eff6f6c1c069bf6895c0748fbdf80165d7e6 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 15:37:57 +0100 Subject: [PATCH 4/6] Expose the abstract `Battery`, `EvCharger` and `Inverter` types Signed-off-by: Sahas Subramanian --- src/frequenz/client/assets/electrical_component/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/frequenz/client/assets/electrical_component/__init__.py b/src/frequenz/client/assets/electrical_component/__init__.py index 7b30f3c..6cf7be2 100644 --- a/src/frequenz/client/assets/electrical_component/__init__.py +++ b/src/frequenz/client/assets/electrical_component/__init__.py @@ -4,6 +4,7 @@ """Electrical component types.""" from ._battery import ( + Battery, BatteryType, LiIonBattery, NaIonBattery, @@ -22,6 +23,7 @@ from ._ev_charger import ( AcEvCharger, DcEvCharger, + EvCharger, EvChargerType, HybridEvCharger, UnrecognizedEvCharger, @@ -32,6 +34,7 @@ from ._inverter import ( BatteryInverter, HybridInverter, + Inverter, InverterType, PvInverter, UnrecognizedInverter, @@ -53,6 +56,7 @@ __all__ = [ "Chp", "CryptoMiner", + "Battery", "BatteryType", "LiIonBattery", "NaIonBattery", @@ -66,6 +70,7 @@ "Electrolyzer", "AcEvCharger", "DcEvCharger", + "EvCharger", "EvChargerType", "HybridEvCharger", "UnrecognizedEvCharger", @@ -74,6 +79,7 @@ "Hvac", "BatteryInverter", "HybridInverter", + "Inverter", "InverterType", "PvInverter", "UnrecognizedInverter", From 3a2f7cd592f2c67de0f4b1612755cfa057521616 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 16:01:21 +0100 Subject: [PATCH 5/6] =?UTF-8?q?Rename=20`PvInverter`=20=E2=86=92=20`SolarI?= =?UTF-8?q?nverter`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to make the component types compatible with the microgrid client. Signed-off-by: Sahas Subramanian --- .../client/assets/electrical_component/__init__.py | 4 ++-- .../_electrical_component_proto.py | 11 +++++++---- .../client/assets/electrical_component/_inverter.py | 10 +++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/frequenz/client/assets/electrical_component/__init__.py b/src/frequenz/client/assets/electrical_component/__init__.py index 6cf7be2..1fec94f 100644 --- a/src/frequenz/client/assets/electrical_component/__init__.py +++ b/src/frequenz/client/assets/electrical_component/__init__.py @@ -36,7 +36,7 @@ HybridInverter, Inverter, InverterType, - PvInverter, + SolarInverter, UnrecognizedInverter, UnspecifiedInverter, ) @@ -81,7 +81,7 @@ "HybridInverter", "Inverter", "InverterType", - "PvInverter", + "SolarInverter", "UnrecognizedInverter", "UnspecifiedInverter", "Meter", diff --git a/src/frequenz/client/assets/electrical_component/_electrical_component_proto.py b/src/frequenz/client/assets/electrical_component/_electrical_component_proto.py index c7468e7..3c5f77d 100644 --- a/src/frequenz/client/assets/electrical_component/_electrical_component_proto.py +++ b/src/frequenz/client/assets/electrical_component/_electrical_component_proto.py @@ -47,7 +47,7 @@ BatteryInverter, HybridInverter, InverterType, - PvInverter, + SolarInverter, UnrecognizedInverter, UnspecifiedInverter, ) @@ -320,12 +320,15 @@ def electrical_component_from_proto_with_issues( inverter_enum_to_class: dict[ InverterType, type[ - UnspecifiedInverter | BatteryInverter | PvInverter | HybridInverter + UnspecifiedInverter + | BatteryInverter + | SolarInverter + | HybridInverter ], ] = { InverterType.UNSPECIFIED: UnspecifiedInverter, InverterType.BATTERY: BatteryInverter, - InverterType.PV: PvInverter, + InverterType.SOLAR: SolarInverter, InverterType.HYBRID: HybridInverter, } inverter_type = enum_proto.enum_from_proto( @@ -335,7 +338,7 @@ def electrical_component_from_proto_with_issues( case ( InverterType.UNSPECIFIED | InverterType.BATTERY - | InverterType.PV + | InverterType.SOLAR | InverterType.HYBRID ): if inverter_type is InverterType.UNSPECIFIED: diff --git a/src/frequenz/client/assets/electrical_component/_inverter.py b/src/frequenz/client/assets/electrical_component/_inverter.py index 3306d0b..0b7e45e 100644 --- a/src/frequenz/client/assets/electrical_component/_inverter.py +++ b/src/frequenz/client/assets/electrical_component/_inverter.py @@ -25,7 +25,7 @@ class InverterType(enum.Enum): BATTERY = electrical_components_pb2.INVERTER_TYPE_BATTERY """The inverter is a battery inverter.""" - PV = electrical_components_pb2.INVERTER_TYPE_PV + SOLAR = electrical_components_pb2.INVERTER_TYPE_PV """The inverter is a solar inverter.""" HYBRID = electrical_components_pb2.INVERTER_TYPE_HYBRID @@ -106,10 +106,10 @@ class BatteryInverter(Inverter): @dataclasses.dataclass(frozen=True, kw_only=True) -class PvInverter(Inverter): - """A PV inverter.""" +class SolarInverter(Inverter): + """A Solar inverter.""" - type: Literal[InverterType.PV] = InverterType.PV + type: Literal[InverterType.SOLAR] = InverterType.SOLAR """The type of this inverter. Note: @@ -150,7 +150,7 @@ class UnrecognizedInverter(Inverter): InverterTypes: TypeAlias = ( UnspecifiedInverter | BatteryInverter - | PvInverter + | SolarInverter | HybridInverter | UnrecognizedInverter ) From bbf09104df24ddfac497e2a3dd66fe68cfdacd25 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 18 Nov 2025 14:50:16 +0100 Subject: [PATCH 6/6] Update release notes Signed-off-by: Sahas Subramanian --- RELEASE_NOTES.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 16eee9c..13a995c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,10 +6,13 @@ This release adds support for retrieving microgrid electrical component connecti ## Upgrading -No migration required. This is a backward-compatible feature addition. +- The `get_microgrid` and `list_microgrid_electrical_components` methods now expect an argument of type `MicrogridId`, instead of an `int`. +- The `PvInverter` type has been renamed to `SolarInverter`, to be compatible with the microgrid api client. ## New Features +- This exposes the abstract `Battery`, `EvCharger` and `Inverter` types. + ### Component Connections API * **New `ComponentConnection` class**: Introduced to represent connections between electrical components in a microgrid @@ -18,4 +21,4 @@ No migration required. This is a backward-compatible feature addition. ## Bug Fixes - \ No newline at end of file +