Skip to content

Commit aa75e4f

Browse files
authored
Update frequenz-api-microgrid to v0.15.1 (#416)
Closes #412 To be merged after v0.21 is released.
2 parents a25fab7 + 1fffefa commit aa75e4f

23 files changed

+376
-243
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
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+
- Upgrade to microgrid API v0.15.1. If you're using any of the lower level microgrid interfaces, you will need to upgrade your code.
1010

1111
## New Features
1212

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
requires-python = ">= 3.11, < 4"
2828
dependencies = [
29-
"frequenz-api-microgrid >= 0.11.0, < 0.12.0",
29+
"frequenz-api-microgrid >= 0.15.1, < 0.16.0",
3030
# Make sure to update the mkdocs.yml file when
3131
# changing the version
3232
# (plugins.mkdocstrings.handlers.python.import)

src/frequenz/sdk/actor/_data_sourcing/microgrid_api_source.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,23 @@ def get_channel_name(self) -> str:
8282
ComponentMetricId.SOC_LOWER_BOUND: lambda msg: msg.soc_lower_bound,
8383
ComponentMetricId.SOC_UPPER_BOUND: lambda msg: msg.soc_upper_bound,
8484
ComponentMetricId.CAPACITY: lambda msg: msg.capacity,
85-
ComponentMetricId.POWER_LOWER_BOUND: lambda msg: msg.power_lower_bound,
86-
ComponentMetricId.POWER_UPPER_BOUND: lambda msg: msg.power_upper_bound,
85+
ComponentMetricId.POWER_INCLUSION_LOWER_BOUND: lambda msg: (
86+
msg.power_inclusion_lower_bound
87+
),
88+
ComponentMetricId.POWER_INCLUSION_UPPER_BOUND: lambda msg: (
89+
msg.power_inclusion_upper_bound
90+
),
8791
ComponentMetricId.TEMPERATURE: lambda msg: msg.temperature,
8892
}
8993

9094
_InverterDataMethods: Dict[ComponentMetricId, Callable[[InverterData], float]] = {
9195
ComponentMetricId.ACTIVE_POWER: lambda msg: msg.active_power,
92-
ComponentMetricId.ACTIVE_POWER_LOWER_BOUND: lambda msg: msg.active_power_lower_bound,
93-
ComponentMetricId.ACTIVE_POWER_UPPER_BOUND: lambda msg: msg.active_power_upper_bound,
96+
ComponentMetricId.ACTIVE_POWER_INCLUSION_LOWER_BOUND: lambda msg: (
97+
msg.active_power_inclusion_lower_bound
98+
),
99+
ComponentMetricId.ACTIVE_POWER_INCLUSION_UPPER_BOUND: lambda msg: (
100+
msg.active_power_inclusion_upper_bound
101+
),
94102
}
95103

96104
_EVChargerDataMethods: Dict[ComponentMetricId, Callable[[EVChargerData], float]] = {

src/frequenz/sdk/actor/power_distributing/_battery_status.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
from enum import Enum
1313
from typing import Iterable, Optional, Set, TypeVar, Union
1414

15+
# pylint: disable=no-name-in-module
1516
from frequenz.api.microgrid.battery_pb2 import ComponentState as BatteryComponentState
1617
from frequenz.api.microgrid.battery_pb2 import RelayState as BatteryRelayState
1718
from frequenz.api.microgrid.common_pb2 import ErrorLevel
1819
from frequenz.api.microgrid.inverter_pb2 import ComponentState as InverterComponentState
20+
21+
# pylint: enable=no-name-in-module
1922
from frequenz.channels import Receiver, Sender
2023
from frequenz.channels.util import Timer, select, selected_from
2124

src/frequenz/sdk/actor/power_distributing/power_distributing.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,10 @@ def _get_upper_bound(self, batteries: abc.Set[int], include_broken: bool) -> flo
244244
batteries, include_broken
245245
)
246246
return sum(
247-
min(battery.power_upper_bound, inverter.active_power_upper_bound)
247+
min(
248+
battery.power_inclusion_upper_bound,
249+
inverter.active_power_inclusion_upper_bound,
250+
)
248251
for battery, inverter in pairs_data
249252
)
250253

@@ -266,7 +269,10 @@ def _get_lower_bound(self, batteries: abc.Set[int], include_broken: bool) -> flo
266269
batteries, include_broken
267270
)
268271
return sum(
269-
max(battery.power_lower_bound, inverter.active_power_lower_bound)
272+
max(
273+
battery.power_inclusion_lower_bound,
274+
inverter.active_power_inclusion_lower_bound,
275+
)
270276
for battery, inverter in pairs_data
271277
)
272278

@@ -622,10 +628,10 @@ def _get_battery_inverter_data(
622628
return None
623629

624630
replaceable_metrics = [
625-
battery_data.power_lower_bound,
626-
battery_data.power_upper_bound,
627-
inverter_data.active_power_lower_bound,
628-
inverter_data.active_power_upper_bound,
631+
battery_data.power_inclusion_lower_bound,
632+
battery_data.power_inclusion_upper_bound,
633+
inverter_data.active_power_inclusion_lower_bound,
634+
inverter_data.active_power_inclusion_upper_bound,
629635
]
630636

631637
# If all values are ok then return them.
@@ -637,8 +643,8 @@ def _get_battery_inverter_data(
637643
# Replace NaN with the corresponding value in the adjacent component.
638644
# If both metrics are None, return None to ignore this battery.
639645
replaceable_pairs = [
640-
("power_lower_bound", "active_power_lower_bound"),
641-
("power_upper_bound", "active_power_upper_bound"),
646+
("power_inclusion_lower_bound", "active_power_inclusion_lower_bound"),
647+
("power_inclusion_upper_bound", "active_power_inclusion_upper_bound"),
642648
]
643649

644650
battery_new_metrics = {}

src/frequenz/sdk/microgrid/client/_client.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import asyncio
77
import logging
8-
import math
98
from abc import ABC, abstractmethod
109
from typing import (
1110
Any,
@@ -20,11 +19,11 @@
2019
)
2120

2221
import grpc
23-
from frequenz.api.microgrid import common_pb2 as common_pb
22+
from frequenz.api.common import components_pb2 as components_pb
23+
from frequenz.api.common import metrics_pb2 as metrics_pb
2424
from frequenz.api.microgrid import microgrid_pb2 as microgrid_pb
2525
from frequenz.api.microgrid.microgrid_pb2_grpc import MicrogridStub
2626
from frequenz.channels import Broadcast, Receiver, Sender
27-
from google.protobuf.empty_pb2 import Empty # pylint: disable=no-name-in-module
2827

2928
from ..._internal._constants import RECEIVER_MAX_SIZE
3029
from ..component import (
@@ -193,6 +192,9 @@ async def set_bounds(self, component_id: int, lower: float, upper: float) -> Non
193192
"""
194193

195194

195+
# pylint: disable=no-member
196+
197+
196198
class MicrogridGrpcClient(MicrogridApiClient):
197199
"""Microgrid API client implementation using gRPC as the underlying protocol."""
198200

@@ -244,7 +246,7 @@ async def components(self) -> Iterable[Component]:
244246
)
245247
components_only = filter(
246248
lambda c: c.category
247-
is not microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_SENSOR,
249+
is not components_pb.ComponentCategory.COMPONENT_CATEGORY_SENSOR,
248250
component_list.components,
249251
)
250252
result: Iterable[Component] = map(
@@ -340,7 +342,7 @@ async def _component_data_task(
340342
"Making call to `GetComponentData`, for component_id=%d", component_id
341343
)
342344
try:
343-
call = self.api.GetComponentData(
345+
call = self.api.StreamComponentData(
344346
microgrid_pb.ComponentIdParam(id=component_id),
345347
)
346348
# grpc.aio is missing types and mypy thinks this is not
@@ -578,25 +580,12 @@ async def set_power(self, component_id: int, power_w: float) -> None:
578580
when the api call exceeded timeout
579581
"""
580582
try:
581-
if power_w >= 0:
582-
# grpc.aio is missing types and mypy thinks this is not
583-
# async iterable, but it is
584-
await self.api.Charge(
585-
microgrid_pb.PowerLevelParam(
586-
component_id=component_id, power_w=math.floor(power_w)
587-
),
588-
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
589-
) # type: ignore[misc]
590-
else:
591-
# grpc.aio is missing types and mypy thinks this is not
592-
# async iterable, but it is
593-
power_w *= -1
594-
await self.api.Discharge(
595-
microgrid_pb.PowerLevelParam(
596-
component_id=component_id, power_w=math.floor(power_w)
597-
),
598-
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
599-
) # type: ignore[misc]
583+
await self.api.SetPowerActive(
584+
microgrid_pb.SetPowerActiveParam(
585+
component_id=component_id, power=power_w
586+
),
587+
timeout=DEFAULT_GRPC_CALL_TIMEOUT, # type: ignore[arg-type]
588+
) # type: ignore[misc]
600589
except grpc.aio.AioRpcError as err:
601590
msg = f"Failed to set power. Microgrid API: {self.target}. Err: {err.details()}"
602591
raise grpc.aio.AioRpcError(
@@ -632,20 +621,13 @@ async def set_bounds(
632621
if lower > 0:
633622
raise ValueError(f"Lower bound {upper} must be less than or equal to 0.")
634623

635-
# grpc.aio is missing types and mypy thinks request_iterator is
636-
# a required argument, but it is not
637-
set_bounds_call = self.api.SetBounds(
638-
timeout=DEFAULT_GRPC_CALL_TIMEOUT,
639-
) # type: ignore[call-arg]
640624
try:
641-
# grpc.aio is missing types and mypy thinks set_bounds_call can be Empty
642-
assert not isinstance(set_bounds_call, Empty)
643-
await set_bounds_call.write(
625+
self.api.AddInclusionBounds(
644626
microgrid_pb.SetBoundsParam(
645627
component_id=component_id,
646628
# pylint: disable=no-member,line-too-long
647629
target_metric=microgrid_pb.SetBoundsParam.TargetMetric.TARGET_METRIC_POWER_ACTIVE,
648-
bounds=common_pb.Bounds(lower=lower, upper=upper),
630+
bounds=metrics_pb.Bounds(lower=lower, upper=upper),
649631
),
650632
)
651633
except grpc.aio.AioRpcError as err:

src/frequenz/sdk/microgrid/component/_component.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@
99
from enum import Enum
1010
from typing import Optional
1111

12+
import frequenz.api.common.components_pb2 as components_pb
1213
import frequenz.api.microgrid.inverter_pb2 as inverter_pb
13-
import frequenz.api.microgrid.microgrid_pb2 as microgrid_pb
1414

1515

1616
class ComponentType(Enum):
1717
"""A base class from which individual component types are derived."""
1818

1919

20+
# pylint: disable=no-member
21+
22+
2023
class InverterType(ComponentType):
2124
"""Enum representing inverter types."""
2225

@@ -27,50 +30,57 @@ class InverterType(ComponentType):
2730

2831

2932
def _component_type_from_protobuf(
30-
component_category: microgrid_pb.ComponentCategory.ValueType,
31-
component_type: inverter_pb.Type.ValueType,
33+
component_category: components_pb.ComponentCategory.ValueType,
34+
component_metadata: inverter_pb.Metadata,
3235
) -> Optional[ComponentType]:
3336
"""Convert a protobuf InverterType message to Component enum.
3437
3538
For internal-only use by the `microgrid` package.
3639
3740
Args:
3841
component_category: category the type belongs to.
39-
component_type: protobuf enum to convert.
42+
component_metadata: protobuf metadata to fetch type from.
4043
4144
Returns:
4245
Enum value corresponding to the protobuf message.
4346
"""
4447
# ComponentType values in the protobuf definition are not unique across categories
4548
# as of v0.11.0, so we need to check the component category first, before doing any
4649
# component type checks.
47-
if component_category == microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_INVERTER:
48-
if not any(t.value == component_type for t in InverterType):
50+
if (
51+
component_category
52+
== components_pb.ComponentCategory.COMPONENT_CATEGORY_INVERTER
53+
):
54+
# mypy 1.4.1 crashes at this line, maybe it doesn't like the name of the "type"
55+
# attribute in this context. Hence the "# type: ignore".
56+
if not any(
57+
t.value == component_metadata.type for t in InverterType # type: ignore
58+
):
4959
return None
5060

51-
return InverterType(component_type)
61+
return InverterType(component_metadata.type)
5262

5363
return None
5464

5565

5666
class ComponentCategory(Enum):
5767
"""Possible types of microgrid component."""
5868

59-
NONE = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_UNSPECIFIED
60-
GRID = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_GRID
61-
METER = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_METER
62-
INVERTER = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_INVERTER
63-
BATTERY = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_BATTERY
64-
EV_CHARGER = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_EV_CHARGER
65-
CHP = microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_CHP
69+
NONE = components_pb.ComponentCategory.COMPONENT_CATEGORY_UNSPECIFIED
70+
GRID = components_pb.ComponentCategory.COMPONENT_CATEGORY_GRID
71+
METER = components_pb.ComponentCategory.COMPONENT_CATEGORY_METER
72+
INVERTER = components_pb.ComponentCategory.COMPONENT_CATEGORY_INVERTER
73+
BATTERY = components_pb.ComponentCategory.COMPONENT_CATEGORY_BATTERY
74+
EV_CHARGER = components_pb.ComponentCategory.COMPONENT_CATEGORY_EV_CHARGER
75+
CHP = components_pb.ComponentCategory.COMPONENT_CATEGORY_CHP
6676

6777
# types not yet supported by the API but which can be inferred
6878
# from available graph info
6979
PV_ARRAY = 1000001
7080

7181

7282
def _component_category_from_protobuf(
73-
component_category: microgrid_pb.ComponentCategory.ValueType,
83+
component_category: components_pb.ComponentCategory.ValueType,
7484
) -> ComponentCategory:
7585
"""Convert a protobuf ComponentCategory message to ComponentCategory enum.
7686
@@ -87,7 +97,7 @@ def _component_category_from_protobuf(
8797
a valid component category as it does not form part of the
8898
microgrid itself)
8999
"""
90-
if component_category == microgrid_pb.ComponentCategory.COMPONENT_CATEGORY_SENSOR:
100+
if component_category == components_pb.ComponentCategory.COMPONENT_CATEGORY_SENSOR:
91101
raise ValueError("Cannot create a component from a sensor!")
92102

93103
if not any(t.value == component_category for t in ComponentCategory):
@@ -134,10 +144,10 @@ class ComponentMetricId(Enum):
134144
SOC_UPPER_BOUND = "soc_upper_bound"
135145
CAPACITY = "capacity"
136146

137-
POWER_LOWER_BOUND = "power_lower_bound"
138-
POWER_UPPER_BOUND = "power_upper_bound"
147+
POWER_INCLUSION_LOWER_BOUND = "power_inclusion_lower_bound"
148+
POWER_INCLUSION_UPPER_BOUND = "power_inclusion_upper_bound"
139149

140-
ACTIVE_POWER_LOWER_BOUND = "active_power_lower_bound"
141-
ACTIVE_POWER_UPPER_BOUND = "active_power_upper_bound"
150+
ACTIVE_POWER_INCLUSION_LOWER_BOUND = "active_power_inclusion_lower_bound"
151+
ACTIVE_POWER_INCLUSION_UPPER_BOUND = "active_power_inclusion_upper_bound"
142152

143153
TEMPERATURE = "temperature"

0 commit comments

Comments
 (0)