Skip to content

Commit 988e913

Browse files
committed
Add MicrogridInfo and MicrogridStatus
This will be used to retrieve the microgrid information. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 8e23009 commit 988e913

File tree

4 files changed

+486
-0
lines changed

4 files changed

+486
-0
lines changed

src/frequenz/client/microgrid/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
)
3434
from ._lifetime import Lifetime
3535
from ._location import Location
36+
from ._microgrid_info import MicrogridInfo, MicrogridStatus
3637

3738
__all__ = [
3839
"ApiClientError",
@@ -48,6 +49,8 @@
4849
"Lifetime",
4950
"Location",
5051
"MicrogridApiClient",
52+
"MicrogridInfo",
53+
"MicrogridStatus",
5154
"OperationAborted",
5255
"OperationCancelled",
5356
"OperationNotImplemented",
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Definition of a microgrid."""
5+
6+
import datetime
7+
import enum
8+
import logging
9+
from dataclasses import dataclass
10+
from functools import cached_property
11+
12+
from frequenz.api.common.v1.microgrid import microgrid_pb2
13+
from frequenz.client.common.microgrid import EnterpriseId, MicrogridId
14+
15+
from ._delivery_area import DeliveryArea
16+
from ._location import Location
17+
18+
_logger = logging.getLogger(__name__)
19+
20+
21+
@enum.unique
22+
class MicrogridStatus(enum.Enum):
23+
"""The possible statuses for a microgrid."""
24+
25+
UNSPECIFIED = microgrid_pb2.MICROGRID_STATUS_UNSPECIFIED
26+
"""The status is unspecified. This should not be used."""
27+
28+
ACTIVE = microgrid_pb2.MICROGRID_STATUS_ACTIVE
29+
"""The microgrid is active."""
30+
31+
INACTIVE = microgrid_pb2.MICROGRID_STATUS_INACTIVE
32+
"""The microgrid is inactive."""
33+
34+
35+
@dataclass(frozen=True, kw_only=True)
36+
class MicrogridInfo:
37+
"""A localized grouping of electricity generation, energy storage, and loads.
38+
39+
A microgrid is a localized grouping of electricity generation, energy storage, and
40+
loads that normally operates connected to a traditional centralized grid.
41+
42+
Each microgrid has a unique identifier and is associated with an enterprise account.
43+
44+
A key feature is that it has a physical location and is situated in a delivery area.
45+
46+
Note: Key Concepts
47+
- Physical Location: Geographical coordinates specify the exact physical
48+
location of the microgrid.
49+
- Delivery Area: Each microgrid is part of a broader delivery area, which is
50+
crucial for energy trading and compliance.
51+
"""
52+
53+
id: MicrogridId
54+
"""The unique identifier of the microgrid."""
55+
56+
enterprise_id: EnterpriseId
57+
"""The unique identifier linking this microgrid to its parent enterprise account."""
58+
59+
name: str | None
60+
"""Name of the microgrid."""
61+
62+
delivery_area: DeliveryArea | None
63+
"""The delivery area where the microgrid is located, as identified by a specific code."""
64+
65+
location: Location | None
66+
"""Physical location of the microgrid, in geographical co-ordinates."""
67+
68+
status: MicrogridStatus | int
69+
"""The current status of the microgrid."""
70+
71+
create_timestamp: datetime.datetime
72+
"""The UTC timestamp indicating when the microgrid was initially created."""
73+
74+
@cached_property
75+
def is_active(self) -> bool:
76+
"""Whether the microgrid is active."""
77+
if self.status is MicrogridStatus.UNSPECIFIED:
78+
# Because this is a cached property, the warning will only be logged once.
79+
_logger.warning(
80+
"Microgrid %s has an unspecified status. Assuming it is active.", self
81+
)
82+
return self.status in (MicrogridStatus.ACTIVE, MicrogridStatus.UNSPECIFIED)
83+
84+
def __str__(self) -> str:
85+
"""Return the ID of this microgrid as a string."""
86+
name = f":{self.name}" if self.name else ""
87+
return f"{self.id}{name}"
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Loading of MicrogridInfo objects from protobuf messages."""
5+
6+
7+
import logging
8+
9+
from frequenz.api.common.v1.microgrid import microgrid_pb2
10+
from frequenz.client.base import conversion
11+
from frequenz.client.common.microgrid import EnterpriseId, MicrogridId
12+
13+
from ._delivery_area import DeliveryArea
14+
from ._delivery_area_proto import delivery_area_from_proto
15+
from ._location import Location
16+
from ._location_proto import location_from_proto
17+
from ._microgrid_info import MicrogridInfo, MicrogridStatus
18+
from ._util import enum_from_proto
19+
20+
_logger = logging.getLogger(__name__)
21+
22+
23+
def microgrid_info_from_proto(message: microgrid_pb2.Microgrid) -> MicrogridInfo:
24+
"""Convert a protobuf microgrid message to a microgrid object.
25+
26+
Args:
27+
message: The protobuf message to convert.
28+
29+
Returns:
30+
The resulting microgrid object.
31+
"""
32+
major_issues: list[str] = []
33+
minor_issues: list[str] = []
34+
35+
delivery_area: DeliveryArea | None = None
36+
if message.HasField("delivery_area"):
37+
delivery_area = delivery_area_from_proto(message.delivery_area)
38+
else:
39+
major_issues.append("delivery_area is missing")
40+
41+
location: Location | None = None
42+
if message.HasField("location"):
43+
location = location_from_proto(message.location)
44+
else:
45+
major_issues.append("location is missing")
46+
47+
name = message.name or None
48+
if name is None:
49+
minor_issues.append("name is empty")
50+
51+
status = enum_from_proto(message.status, MicrogridStatus)
52+
if status is MicrogridStatus.UNSPECIFIED:
53+
major_issues.append("status is unspecified")
54+
elif isinstance(status, int):
55+
major_issues.append("status is unrecognized")
56+
57+
if major_issues:
58+
_logger.warning(
59+
"Found issues in microgrid: %s | Protobuf message:\n%s",
60+
", ".join(major_issues),
61+
message,
62+
)
63+
64+
if minor_issues:
65+
_logger.debug(
66+
"Found minor issues in microgrid: %s | Protobuf message:\n%s",
67+
", ".join(minor_issues),
68+
message,
69+
)
70+
71+
return MicrogridInfo(
72+
id=MicrogridId(message.id),
73+
enterprise_id=EnterpriseId(message.enterprise_id),
74+
name=message.name or None,
75+
delivery_area=delivery_area,
76+
location=location,
77+
status=status,
78+
create_timestamp=conversion.to_datetime(message.create_timestamp),
79+
)

0 commit comments

Comments
 (0)