Skip to content

Commit 5691f78

Browse files
committed
Implement power_status reporting from the PV pool
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent eb292ae commit 5691f78

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

src/frequenz/sdk/timeseries/pv_pool/_pv_pool.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
"""Interactions with pools of PV inverters."""
55

6+
import asyncio
7+
import typing
68
import uuid
79

810
from ..._internal._channels import ReceiverFetcher
11+
from ...actor import _power_managing
912
from .._base_types import SystemBounds
1013
from ._pv_pool_reference_store import PVPoolReferenceStore
14+
from ._result_types import PVPoolReport
1115

1216

1317
class PVPoolError(Exception):
@@ -47,9 +51,41 @@ def __init__( # pylint: disable=too-many-arguments
4751
"""
4852
self._pv_pool_ref = pv_pool_ref
4953
unique_id = uuid.uuid4()
50-
self._source_id = unique_id if name is None else f"{name}-{unique_id}"
54+
self._source_id = str(unique_id) if name is None else f"{name}-{unique_id}"
5155
self._priority = priority
5256

57+
@property
58+
def power_status(self) -> ReceiverFetcher[PVPoolReport]:
59+
"""Get a receiver to receive new power status reports when they change.
60+
61+
These include
62+
- the current inclusion/exclusion bounds available for the pool's priority,
63+
- the current target power for the pool's set of batteries,
64+
- the result of the last distribution request for the pool's set of batteries.
65+
66+
Returns:
67+
A receiver that will stream power status reports for the pool's priority.
68+
"""
69+
sub = _power_managing.ReportRequest(
70+
source_id=self._source_id,
71+
priority=self._priority,
72+
component_ids=self._pv_pool_ref.component_ids,
73+
)
74+
self._pv_pool_ref.power_bounds_subs[sub.get_channel_name()] = (
75+
asyncio.create_task(
76+
self._pv_pool_ref.power_manager_bounds_subs_sender.send(sub)
77+
)
78+
)
79+
channel = self._pv_pool_ref.channel_registry.get_or_create(
80+
_power_managing._Report, # pylint: disable=protected-access
81+
sub.get_channel_name(),
82+
)
83+
channel.resend_latest = True
84+
85+
# More details on why the cast is needed here:
86+
# https://github.com/frequenz-floss/frequenz-sdk-python/issues/823
87+
return typing.cast(ReceiverFetcher[PVPoolReport], channel)
88+
5389
@property
5490
def _system_power_bounds(self) -> ReceiverFetcher[SystemBounds]:
5591
"""Return a receiver fetcher for the system power bounds."""
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# License: MIT
2+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Types for exposing PV pool reports."""
5+
6+
import typing
7+
8+
from ...actor import power_distributing
9+
from .._base_types import Bounds
10+
from .._quantities import Power
11+
12+
13+
class PVPoolReport(typing.Protocol):
14+
"""A status report for a PV pool."""
15+
16+
target_power: Power | None
17+
"""The currently set power for the PV inverters."""
18+
19+
distribution_result: power_distributing.Result | None
20+
"""The result of the last power distribution.
21+
22+
This is `None` if no power distribution has been performed yet.
23+
"""
24+
25+
@property
26+
def bounds(self) -> Bounds[Power] | None:
27+
"""The usable bounds for the PV inverters.
28+
29+
These bounds are adjusted to any restrictions placed by actors with higher
30+
priorities.
31+
"""

0 commit comments

Comments
 (0)