Skip to content

Commit dad8574

Browse files
committed
Implement ListComponents
Signed-off-by: Leandro Lucarella <[email protected]>
1 parent c43dafc commit dad8574

File tree

11 files changed

+780
-2
lines changed

11 files changed

+780
-2
lines changed

RELEASE_NOTES.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
## Summary
44

5-
<!-- Here goes a general summary of what this release is about -->
5+
This release is a major breaking change, as we jump to the API specification version 0.17.x, which introduces big and fundamental breaking changes. This also starts using the `v1` namespace in `frequenz-api-common`, which also introduces major breaking changes. It would be very hard to detail all the API changes here, please refer to the [Microgrid API releases](https://github.com/frequenz-floss/frequenz-api-microgrid/releases) and [Common API releases](https://github.com/frequenz-floss/frequenz-api-common/releases).
66

77
## Upgrading
88

9-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
9+
- `MicrogridApiClient`:
10+
11+
* The client now follows the v0.17 API names, so most methods changed names and signatures.
1012

1113
## New Features
1214

src/frequenz/client/microgrid/_client.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from typing import Any, assert_never
1616

1717
from frequenz.api.common.v1.metrics import bounds_pb2, metric_sample_pb2
18+
from frequenz.api.common.v1.microgrid.components import components_pb2
1819
from frequenz.api.microgrid.v1 import microgrid_pb2, microgrid_pb2_grpc
1920
from frequenz.client.base import channel, client, conversion, retry, streaming
2021
from frequenz.client.common.microgrid.components import ComponentId
@@ -24,7 +25,10 @@
2425
from ._exception import ClientNotConnected
2526
from ._microgrid_info import MicrogridInfo
2627
from ._microgrid_info_proto import microgrid_info_from_proto
28+
from .component._category import ComponentCategory
2729
from .component._component import Component
30+
from .component._component_proto import component_from_proto
31+
from .component._types import ComponentTypes
2832
from .metrics._bounds import Bounds
2933
from .metrics._metric import Metric
3034

@@ -162,6 +166,61 @@ async def get_microgrid_info( # noqa: DOC502 (raises ApiClientError indirectly)
162166

163167
return microgrid_info_from_proto(microgrid.microgrid)
164168

169+
async def list_components( # noqa: DOC502 (raises ApiClientError indirectly)
170+
self,
171+
*,
172+
components: Iterable[ComponentId | Component] = (),
173+
categories: Iterable[ComponentCategory | int] = (),
174+
) -> Iterable[ComponentTypes]:
175+
"""Fetch all the components present in the local microgrid.
176+
177+
Electrical components are a part of a microgrid's electrical infrastructure
178+
are can be connected to each other to form an electrical circuit, which can
179+
then be represented as a graph.
180+
181+
If provided, the filters for component and categories have an `AND`
182+
relationship with one another, meaning that they are applied serially,
183+
but the elements within a single filter list have an `OR` relationship with
184+
each other.
185+
186+
Example:
187+
If `ids = {1, 2, 3}`, and `categories = {ComponentCategory.INVERTER,
188+
ComponentCategory.BATTERY}`, then the results will consist of elements that
189+
have:
190+
191+
* The IDs 1, `OR` 2, `OR` 3; `AND`
192+
* Are of the categories `ComponentCategory.INVERTER` `OR`
193+
`ComponentCategory.BATTERY`.
194+
195+
If a filter list is empty, then that filter is not applied.
196+
197+
Args:
198+
components: The components to fetch. See the method description for details.
199+
categories: The categories of the components to fetch. See the method
200+
description for details.
201+
202+
Returns:
203+
Iterator whose elements are all the components in the local microgrid.
204+
205+
Raises:
206+
ApiClientError: If the are any errors communicating with the Microgrid API,
207+
most likely a subclass of
208+
[GrpcError][frequenz.client.microgrid.GrpcError].
209+
"""
210+
component_list = await client.call_stub_method(
211+
self,
212+
lambda: self.stub.ListComponents(
213+
microgrid_pb2.ListComponentsRequest(
214+
component_ids=map(_get_component_id, components),
215+
categories=map(_get_category_value, categories),
216+
),
217+
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
218+
),
219+
method_name="ListComponents",
220+
)
221+
222+
return map(component_from_proto, component_list.components)
223+
165224
async def set_component_power_active( # noqa: DOC502 (raises ApiClientError indirectly)
166225
self,
167226
component: ComponentId | Component,
@@ -456,6 +515,19 @@ def _get_metric_value(metric: Metric | int) -> metric_sample_pb2.Metric.ValueTyp
456515
assert_never(unexpected)
457516

458517

518+
def _get_category_value(
519+
category: ComponentCategory | int,
520+
) -> components_pb2.ComponentCategory.ValueType:
521+
"""Get the category value from a component or component category."""
522+
match category:
523+
case ComponentCategory():
524+
return components_pb2.ComponentCategory.ValueType(category.value)
525+
case int():
526+
return components_pb2.ComponentCategory.ValueType(category)
527+
case unexpected:
528+
assert_never(unexpected)
529+
530+
459531
def _delta_to_seconds(delta: timedelta | None) -> int | None:
460532
"""Convert a `timedelta` to seconds (or `None` if `None`)."""
461533
return round(delta.total_seconds()) if delta is not None else None

0 commit comments

Comments
 (0)