Skip to content

Index

frequenz.client.common.microgrid.electrical_components ¤

Defines the electrical components that can be used in a microgrid.

Attributes¤

frequenz.client.common.microgrid.electrical_components.BatteryTypes module-attribute ¤

All possible battery types.

frequenz.client.common.microgrid.electrical_components.DefaultT module-attribute ¤

DefaultT = TypeVar('DefaultT')

A type variable for the default value of dict-like getters.

frequenz.client.common.microgrid.electrical_components.ElectricalComponentConnectionTypes module-attribute ¤

All concrete electrical component connection types.

These are the concrete leaf types of electrical component connections that can be actually instantiated. Match against this union to exhaustively handle every kind of connection returned by the *_from_proto converters.

frequenz.client.common.microgrid.electrical_components.ElectricalComponentTypes module-attribute ¤

All concrete electrical component types.

These are the concrete leaf types of electrical components than can be actually instantiated.

frequenz.client.common.microgrid.electrical_components.EvChargerTypes module-attribute ¤

All possible EV charger types.

frequenz.client.common.microgrid.electrical_components.InverterTypes module-attribute ¤

All possible inverter types.

frequenz.client.common.microgrid.electrical_components.ProblematicElectricalComponentConnectionTypes module-attribute ¤

ProblematicElectricalComponentConnectionTypes: TypeAlias = (
    SelfReferencingElectricalComponentConnection
)

All possible electrical component connection types that have a problem.

frequenz.client.common.microgrid.electrical_components.ProblematicElectricalComponentTypes module-attribute ¤

All possible electrical component types that have a problem.

frequenz.client.common.microgrid.electrical_components.UnrecognizedElectricalComponentTypes module-attribute ¤

All unrecognized electrical component types.

frequenz.client.common.microgrid.electrical_components.UnspecifiedElectricalComponentTypes module-attribute ¤

All unspecified electrical component types.

Classes¤

frequenz.client.common.microgrid.electrical_components.AcEvCharger dataclass ¤

Bases: EvCharger

An EV charger that supports AC charging only.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class AcEvCharger(EvCharger):
    """An EV charger that supports AC charging only."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.BaseElectricalComponentConnection dataclass ¤

A base class for all electrical component connections.

This is the common supertype of every kind of connection, both well-formed (see ElectricalComponentConnection) and problematic (see ProblematicElectricalComponentConnection). It cannot be instantiated directly; use one of its concrete subclasses instead, or obtain instances via the corresponding *_from_proto converter.

Physical Representation

This object is not about data flow but rather about the physical electrical connections between electrical components. Therefore, the IDs for the source and destination electrical components correspond to the actual setup within the microgrid.

Direction

The direction of the connection follows the flow of current away from the grid connection point, or in case of islands, away from the islanding point. This direction is aligned with positive current according to the Passive Sign Convention.

Historical Data

The timestamps of when a connection was created and terminated allow for tracking the changes over time to a microgrid, providing insights into when and how the microgrid infrastructure has been modified.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class BaseElectricalComponentConnection:
    """A base class for all electrical component connections.

    This is the common supertype of every kind of connection, both
    well-formed (see
    [`ElectricalComponentConnection`][..ElectricalComponentConnection]) and
    problematic (see
    [`ProblematicElectricalComponentConnection`][..ProblematicElectricalComponentConnection]).
    It cannot be instantiated directly; use one of its concrete subclasses
    instead, or obtain instances via the corresponding `*_from_proto`
    converter.

    Note: Physical Representation
        This object is not about data flow but rather about the physical
        electrical connections between electrical components. Therefore, the IDs for the
        source and destination electrical components correspond to the actual setup within
        the microgrid.

    Note: Direction
        The direction of the connection follows the flow of current away from the
        grid connection point, or in case of islands, away from the islanding
        point. This direction is aligned with positive current according to the
        [Passive Sign Convention](https://en.wikipedia.org/wiki/Passive_sign_convention).

    Note: Historical Data
        The timestamps of when a connection was created and terminated allow for
        tracking the changes over time to a microgrid, providing insights into
        when and how the microgrid infrastructure has been modified.
    """

    source_id: ElectricalComponentId
    """The unique identifier of the electrical 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_id: ElectricalComponentId
    """The unique ID of the electrical component where the connection terminates.

    This is the electrical component towards which the current flows.
    """

    operational_lifetime: Lifetime | InvalidLifetime = dataclasses.field(
        default_factory=Lifetime
    )
    """The operational lifetime of the connection.

    An [`InvalidLifetime`][....InvalidLifetime] preserves malformed wire data.

    Tip:
        Prefer [`get_operational_lifetime()`][..get_operational_lifetime] when
        a valid lifetime is required.
    """

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is BaseElectricalComponentConnection:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)

    def get_operational_lifetime(self) -> Lifetime:
        """Return the operational lifetime as a valid `Lifetime`.

        Returns:
            The valid operational lifetime.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        match self.operational_lifetime:
            case InvalidLifetime() as invalid:
                raise InvalidLifetimeError(self, "operational_lifetime", invalid)
            case Lifetime() as valid:
                return valid
            case unknown:
                assert_never(unknown)

    def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
        """Check whether this connection is operational at a specific timestamp.

        Args:
            timestamp: The timestamp to check against the operational lifetime.

        Returns:
            Whether this connection is operational at the given timestamp.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        return self.get_operational_lifetime().is_operational_at(timestamp)

    def is_operational_now(self) -> bool:  # noqa: DOC502
        """Whether this connection is currently operational.

        Returns:
            Whether this connection is operational at the current time.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        return self.is_operational_at(datetime.now(timezone.utc))

    def __str__(self) -> str:
        """Return a human-readable string representation of this instance."""
        return f"{self.source_id}->{self.destination_id}"
Attributes¤
destination_id instance-attribute ¤
destination_id: ElectricalComponentId

The unique ID of the electrical component where the connection terminates.

This is the electrical component towards which the current flows.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of the connection.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

source_id instance-attribute ¤

The unique identifier of the electrical 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.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is BaseElectricalComponentConnection:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.source_id}->{self.destination_id}"
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this connection is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check against the operational lifetime.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this connection is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this connection is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check against the operational lifetime.

    Returns:
        Whether this connection is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Whether this connection is currently operational.

RETURNS DESCRIPTION
bool

Whether this connection is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Whether this connection is currently operational.

    Returns:
        Whether this connection is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))

frequenz.client.common.microgrid.electrical_components.Battery dataclass ¤

Bases: ElectricalComponent

An abstract battery electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Battery(ElectricalComponent):
    """An abstract battery electrical component."""

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is Battery:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Battery:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.BatteryInverter dataclass ¤

Bases: Inverter

A battery inverter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class BatteryInverter(Inverter):
    """A battery inverter."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Breaker dataclass ¤

Bases: ElectricalComponent

A breaker electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_breaker.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Breaker(ElectricalComponent):
    """A breaker electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.CapacitorBank dataclass ¤

Bases: ElectricalComponent

A capacitor bank electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_capacitor_bank.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class CapacitorBank(ElectricalComponent):
    """A capacitor bank electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.CategorySpecificInfo dataclass ¤

The category specific info carried by an electrical component.

A protobuf electrical component may carry a category_specific_info variant with extra fields tied to its category. Fields this library version understands are translated into typed attributes on the concrete component (e.g. the battery type). Anything left over — either because the component's category is not recognized, or because a newer API version added fields this client doesn't know yet — is preserved here so callers can still inspect the raw values.

Source code in src/frequenz/client/common/microgrid/electrical_components/_category_specific_info.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class CategorySpecificInfo:
    """The category specific info carried by an electrical component.

    A protobuf electrical component may carry a `category_specific_info` variant
    with extra fields tied to its category. Fields this library version
    understands are translated into typed attributes on the concrete component
    (e.g. the battery type). Anything left over — either because the component's
    category is not recognized, or because a newer API version added fields this
    client doesn't know yet — is preserved here so callers can still inspect the
    raw values.
    """

    kind: str
    """The name of the info variant carried on the wire (e.g. `"battery"`)."""

    fields: Mapping[str, Any] = dataclasses.field(
        default_factory=dict,
        # Excluded from the hash: values may be unhashable (e.g. lists), and even
        # repr()-folding them breaks the eq/hash invariant since values that
        # compare equal can differ under repr()/hash() (e.g. 1 == 1.0 == True).
        # Instances hash on `kind` alone, mirroring `metric_config_bounds`.
        hash=False,
    )
    """The leftover fields not translated into typed attributes.

    The keys are the protobuf field names — the ``snake_case`` spelling from
    the ``.proto`` definition (e.g. ``"rated_fuse_current"``), and the values
    their decoded content.
    """
Attributes¤
fields class-attribute instance-attribute ¤
fields: Mapping[str, Any] = dataclasses.field(
    default_factory=dict, hash=False
)

The leftover fields not translated into typed attributes.

The keys are the protobuf field names — the snake_case spelling from the .proto definition (e.g. "rated_fuse_current"), and the values their decoded content.

kind instance-attribute ¤
kind: str

The name of the info variant carried on the wire (e.g. "battery").

frequenz.client.common.microgrid.electrical_components.Chp dataclass ¤

Bases: ElectricalComponent

A combined heat and power (CHP) electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_chp.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Chp(ElectricalComponent):
    """A combined heat and power (CHP) electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Converter dataclass ¤

Bases: ElectricalComponent

An AC-DC converter electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_converter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Converter(ElectricalComponent):
    """An AC-DC converter electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.CryptoMiner dataclass ¤

Bases: ElectricalComponent

A crypto miner electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_crypto_miner.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class CryptoMiner(ElectricalComponent):
    """A crypto miner electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.DcEvCharger dataclass ¤

Bases: EvCharger

An EV charger that supports DC charging only.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class DcEvCharger(EvCharger):
    """An EV charger that supports DC charging only."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.ElectricalComponent dataclass ¤

A base class for all electrical components.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class ElectricalComponent:  # pylint: disable=too-many-instance-attributes
    """A base class for all electrical components."""

    id: ElectricalComponentId
    """This electrical component's ID."""

    microgrid_id: MicrogridId
    """The ID of the microgrid this electrical component belongs to."""

    name: str
    """The name of this electrical component."""

    model: str
    """The model of this electrical component.

    This includes both the manufacturer and the model name.
    """

    operational_lifetime: Lifetime | InvalidLifetime = dataclasses.field(
        default_factory=Lifetime
    )
    """The operational lifetime of this electrical component.

    An [`InvalidLifetime`][....InvalidLifetime] preserves malformed wire data.

    Tip:
        Prefer [`get_operational_lifetime()`][..get_operational_lifetime] when
        a valid lifetime is required.
    """

    _provides_telemetry: bool | int
    """Whether this component provides telemetry data.

    This stores the low-level representation of the operational mode. It holds a bool
    for the telemetry part for a known operational mode, the raw `int` `0` when the
    operational mode is unspecified, or any other raw `int` not yet known to this
    client. Users should use
    [`ElectricalComponent.provides_telemetry()`][.provides_telemetry] to obtain a clear
    boolean or a clear error.
    """

    _accepts_control: bool | int
    """Whether this component accepts control commands.

    This stores the low-level representation of the operational mode. It holds a bool
    for a known operational mode, the raw `int` `0` when the operational mode is
    unspecified, or any other raw `int` not yet known to this client. Users should use
    [`ElectricalComponent.accepts_control()`][.accepts_control] to obtain a clear
    boolean or a clear error.
    """

    _allow_construction: bool = dataclasses.field(
        default=False, repr=False, compare=False, hash=False
    )
    """Internal guard allowing construction only via the `*_from_proto` converters."""

    metric_config_bounds: Mapping[Metric | int, BoundsSet | InvalidBoundsSet] = (
        dataclasses.field(
            default_factory=dict,
            # dict is not hashable, so we don't use this field to calculate the hash.
            # This shouldn't be a problem since it is very unlikely that two components
            # with all other attributes being equal would have different category
            # specific info, so hash collisions should be still very unlikely.
            hash=False,
        )
    )
    """The metric configuration bounds for this electrical component, keyed by metric.

    These bounds may be derived from the component configuration, manufacturer
    limits, or limits of other devices.

    Each metric maps to the aggregate of all the bounds configured for it: a
    [`BoundsSet`][.....metrics.BoundsSet] when every one is well-formed, or an
    [`InvalidBoundsSet`][.....metrics.InvalidBoundsSet] preserving all the raw
    bounds when any is malformed.

    If an unspecified metric is received, it is stored as the plain `int` key `0` when
    loading from protobuf. Metrics unknown to this client version may also appear
    as plain `int` keys for forward-compatibility.

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not an
        `int` subclass, so a `Metric` argument only matches `Metric`-keyed
        entries and an `int` argument only matches `int`-keyed entries. `int`
        metrics should only be used to look up unrecognized metrics, including
        the raw `0` used for an unspecified metric.

    Tip:
        Prefer [`get_metric_config_bounds()`][..get_metric_config_bounds]
        when a valid [`BoundsSet`][.....metrics.BoundsSet] is required.
    """

    category_specific_info: CategorySpecificInfo | None = None
    """The category specific info carried by this component, if any.

    This is `None` when the wire carried no category-specific info variant.
    Otherwise it holds a
    [`CategorySpecificInfo`][...CategorySpecificInfo] recording the variant
    `kind` together with any fields that were not translated into typed
    attributes on this component. The leftover fields are empty when everything
    was translated, and non-empty when the category or its variant is not
    recognized, or when a newer API version added fields this client version
    doesn't know yet.
    """

    def __new__(cls, *_: Any, **__: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is ElectricalComponent:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)

    def __post_init__(self) -> None:
        """Reject direct construction of this read-only type.

        Raises:
            TypeError: If the instance was not created via the corresponding
                `*_from_proto` converter.
        """
        if not self._allow_construction:
            raise TypeError(
                f"{type(self).__name__} cannot be constructed directly; obtain "
                "instances via the corresponding *_from_proto converter."
            )

    def provides_telemetry(self) -> bool:
        """Check whether this electrical component provides telemetry data.

        Returns:
            Whether this electrical component provides telemetry data.

        Raises:
            UnspecifiedEnumValueError: If the operational mode is unspecified.
            UnrecognizedEnumValueError: If the operational mode is not recognized.
                The raw value is available on the error's `value` attribute.
        """
        match self._provides_telemetry:
            case bool() as provides_telemetry:
                return provides_telemetry
            case 0:
                raise UnspecifiedEnumValueError(
                    self,
                    "_provides_telemetry",
                    f"operational mode of {self} is unspecified; "
                    "telemetry availability is unknown",
                )
            case int() as value:
                raise UnrecognizedEnumValueError(
                    self,
                    "_provides_telemetry",
                    value,
                    f"operational mode {value} of {self} is not a recognized "
                    "ElectricalComponentOperationalMode; telemetry availability "
                    "is unknown",
                )
            case unknown:
                assert_never(unknown)

    def accepts_control(self) -> bool:
        """Check whether this electrical component accepts control commands.

        Returns:
            Whether this electrical component accepts control commands.

        Raises:
            UnspecifiedEnumValueError: If the operational mode is unspecified.
            UnrecognizedEnumValueError: If the operational mode is not recognized.
                The raw value is available on the error's `value` attribute.
        """
        match self._accepts_control:
            case bool() as accepts_control:
                return accepts_control
            case 0:
                raise UnspecifiedEnumValueError(
                    self,
                    "_accepts_control",
                    f"operational mode of {self} is unspecified; "
                    "control availability is unknown",
                )
            case int() as value:
                raise UnrecognizedEnumValueError(
                    self,
                    "_accepts_control",
                    value,
                    f"operational mode {value} of {self} is not a recognized "
                    "ElectricalComponentOperationalMode; control availability "
                    "is unknown",
                )
            case unknown:
                assert_never(unknown)

    @overload
    def get_metric_config_bounds(self, metric: Metric | int) -> BoundsSet: ...

    @overload
    def get_metric_config_bounds(
        self, metric: Metric | int, *, default: DefaultT
    ) -> BoundsSet | DefaultT: ...

    def get_metric_config_bounds(
        self, metric: Metric | int, *, default: object = BoundsSet()
    ) -> object:
        """Return the configured bounds for a metric as a valid `BoundsSet`.

        An absent entry returns an unbounded metric, so when no bounds are
        configured for `metric` this returns an unbounded
        [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
        `default` to return a different value for absent entries instead,
        mimicking [`dict.get()`][dict.get].

        Warning:
            A `Metric` and its numeric value are distinct keys: `Metric` is not
            an `int` subclass, so a `Metric` argument only matches
            `Metric`-keyed entries and an `int` argument only matches
            `int`-keyed entries. `int` metrics should only be used to look up
            unrecognized metrics, including the raw `0` used for an unspecified
            metric.

        Example:
            To check if a `metric` has **valid** configured bounds, you can use:

            ```py
            component: ElectricalComponent
            metric: Metric
            if component.get_metric_config_bounds(metric, default=None) is not None:
                print(f"{metric} has valid configured bounds")
            ```

            This is similar to accessing
            [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
            directly, but avoid the special handling of invalid bounds.

        Args:
            metric: The metric whose bounds to retrieve. A raw `int` looks up
                an entry stored under an unrecognized metric value, including
                the raw `0` used for an unspecified metric; it is matched as
                given, with no special handling.
            default: The value to return when no bounds are configured for
                `metric`.

        Returns:
            The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
                `metric`, or `default` when there is no entry for `metric`.

        Raises:
            InvalidBoundsSetError: If the bounds configured for `metric` are
                malformed. The offending instance is available on the
                exception's `bounds_set` attribute.
        """
        match self.metric_config_bounds.get(metric):
            case None:
                return default
            case InvalidBoundsSet() as invalid:
                raise InvalidBoundsSetError(
                    self,
                    "metric_config_bounds",
                    invalid,
                    f"invalid bounds set {invalid} for metric {metric} in {self}",
                )
            case BoundsSet() as valid:
                return valid
            case unknown:
                assert_never(unknown)

    def get_operational_lifetime(self) -> Lifetime:
        """Return the operational lifetime as a valid `Lifetime`.

        Returns:
            The valid operational lifetime.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        match self.operational_lifetime:
            case InvalidLifetime() as invalid:
                raise InvalidLifetimeError(self, "operational_lifetime", invalid)
            case Lifetime() as valid:
                return valid
            case unknown:
                assert_never(unknown)

    def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
        """Check whether this electrical component is operational at a specific timestamp.

        Args:
            timestamp: The timestamp to check.

        Returns:
            Whether this electrical component is operational at the given timestamp.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        return self.get_operational_lifetime().is_operational_at(timestamp)

    def is_operational_now(self) -> bool:  # noqa: DOC502
        """Check whether this electrical component is currently operational.

        Returns:
            Whether this electrical component is operational at the current time.

        Raises:
            InvalidLifetimeError: If malformed lifetime data was received. The
                offending value is available on the exception's `lifetime`
                attribute.
        """
        return self.is_operational_at(datetime.now(timezone.utc))

    @property
    def identity(self) -> tuple[ElectricalComponentId, MicrogridId]:
        """The identity of this electrical component.

        This uses the component ID and microgrid ID to identify an electrical
        component without considering the other attributes, so even if an electrical
        component state changed, the identity remains the same.
        """
        return (self.id, self.microgrid_id)

    def __str__(self) -> str:
        """Return a human-readable string representation of this instance."""
        return f"{self.id}:{self.name}:{type(self).__name__}"
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.ElectricalComponentCategory ¤

Bases: Enum

Possible types of microgrid electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_category.py
@typing_extensions.deprecated(_DEPRECATION_MESSAGE)
@unique
class ElectricalComponentCategory(Enum):
    """Possible types of microgrid electrical component."""

    UNSPECIFIED = deprecated_member(0, _member_message("UNSPECIFIED"))
    """The component category is unspecified. This should not be used."""

    GRID_CONNECTION_POINT = deprecated_member(
        1, _member_message("GRID_CONNECTION_POINT")
    )
    """The point where the local microgrid is connected to the grid."""

    METER = deprecated_member(2, _member_message("METER"))
    """A meter, for measuring electrical metrics, e.g., current, voltage, etc."""

    INVERTER = deprecated_member(3, _member_message("INVERTER"))
    """An inverter that converts DC to AC power and vice versa."""

    CONVERTER = deprecated_member(4, _member_message("CONVERTER"))
    """An electricity converter, e.g., a DC-DC converter."""

    BATTERY = deprecated_member(5, _member_message("BATTERY"))
    """A battery energy storage system."""

    EV_CHARGER = deprecated_member(6, _member_message("EV_CHARGER"))
    """A station for charging electrical vehicles."""

    BREAKER = deprecated_member(7, _member_message("BREAKER"))
    """A circuit breaker, providing protection and switching by disconnecting circuits."""

    PRECHARGER = deprecated_member(8, _member_message("PRECHARGER"))
    """A precharger, used for preparing electrical circuits for switching on."""

    CHP = deprecated_member(9, _member_message("CHP"))
    """A combined heat and power (CHP) plant.

    It generates electricity and useful heat from a single energy source.
    """

    ELECTROLYZER = deprecated_member(10, _member_message("ELECTROLYZER"))
    """A device for splitting water into hydrogen and oxygen using electricity."""

    POWER_TRANSFORMER = deprecated_member(11, _member_message("POWER_TRANSFORMER"))
    """A transformer, used for changing the voltage of electrical circuits."""

    HVAC = deprecated_member(12, _member_message("HVAC"))
    """A heating, ventilation, and air conditioning (HVAC) system."""

    PLC = deprecated_member(13, _member_message("PLC"))
    """A programmable logic controller (PLC)."""

    CRYPTO_MINER = deprecated_member(14, _member_message("CRYPTO_MINER"))
    """A device for mining cryptocurrencies."""

    STATIC_TRANSFER_SWITCH = deprecated_member(
        15, _member_message("STATIC_TRANSFER_SWITCH")
    )
    """A static transfer switch, used for switching between power sources."""

    UNINTERRUPTIBLE_POWER_SUPPLY = deprecated_member(
        16, _member_message("UNINTERRUPTIBLE_POWER_SUPPLY")
    )
    """An uninterruptible power supply (UPS), used to provide backup power."""

    CAPACITOR_BANK = deprecated_member(17, _member_message("CAPACITOR_BANK"))
    """A capacitor bank, used for power factor correction and reactive power compensation."""

    WIND_TURBINE = deprecated_member(18, _member_message("WIND_TURBINE"))
    """A wind turbine, used to generate electricity from wind energy."""

    STEAM_BOILER = deprecated_member(19, _member_message("STEAM_BOILER"))
    """A steam boiler, used to generate steam for heating or industrial processes."""
Attributes¤
BATTERY class-attribute instance-attribute ¤
BATTERY = deprecated_member(5, _member_message('BATTERY'))

A battery energy storage system.

BREAKER class-attribute instance-attribute ¤
BREAKER = deprecated_member(7, _member_message('BREAKER'))

A circuit breaker, providing protection and switching by disconnecting circuits.

CAPACITOR_BANK class-attribute instance-attribute ¤
CAPACITOR_BANK = deprecated_member(
    17, _member_message("CAPACITOR_BANK")
)

A capacitor bank, used for power factor correction and reactive power compensation.

CHP class-attribute instance-attribute ¤
CHP = deprecated_member(9, _member_message('CHP'))

A combined heat and power (CHP) plant.

It generates electricity and useful heat from a single energy source.

CONVERTER class-attribute instance-attribute ¤
CONVERTER = deprecated_member(
    4, _member_message("CONVERTER")
)

An electricity converter, e.g., a DC-DC converter.

CRYPTO_MINER class-attribute instance-attribute ¤
CRYPTO_MINER = deprecated_member(
    14, _member_message("CRYPTO_MINER")
)

A device for mining cryptocurrencies.

ELECTROLYZER class-attribute instance-attribute ¤
ELECTROLYZER = deprecated_member(
    10, _member_message("ELECTROLYZER")
)

A device for splitting water into hydrogen and oxygen using electricity.

EV_CHARGER class-attribute instance-attribute ¤
EV_CHARGER = deprecated_member(
    6, _member_message("EV_CHARGER")
)

A station for charging electrical vehicles.

GRID_CONNECTION_POINT class-attribute instance-attribute ¤
GRID_CONNECTION_POINT = deprecated_member(
    1, _member_message("GRID_CONNECTION_POINT")
)

The point where the local microgrid is connected to the grid.

HVAC class-attribute instance-attribute ¤
HVAC = deprecated_member(12, _member_message('HVAC'))

A heating, ventilation, and air conditioning (HVAC) system.

INVERTER class-attribute instance-attribute ¤
INVERTER = deprecated_member(3, _member_message("INVERTER"))

An inverter that converts DC to AC power and vice versa.

METER class-attribute instance-attribute ¤
METER = deprecated_member(2, _member_message('METER'))

A meter, for measuring electrical metrics, e.g., current, voltage, etc.

PLC class-attribute instance-attribute ¤
PLC = deprecated_member(13, _member_message('PLC'))

A programmable logic controller (PLC).

POWER_TRANSFORMER class-attribute instance-attribute ¤
POWER_TRANSFORMER = deprecated_member(
    11, _member_message("POWER_TRANSFORMER")
)

A transformer, used for changing the voltage of electrical circuits.

PRECHARGER class-attribute instance-attribute ¤
PRECHARGER = deprecated_member(
    8, _member_message("PRECHARGER")
)

A precharger, used for preparing electrical circuits for switching on.

STATIC_TRANSFER_SWITCH class-attribute instance-attribute ¤
STATIC_TRANSFER_SWITCH = deprecated_member(
    15, _member_message("STATIC_TRANSFER_SWITCH")
)

A static transfer switch, used for switching between power sources.

STEAM_BOILER class-attribute instance-attribute ¤
STEAM_BOILER = deprecated_member(
    19, _member_message("STEAM_BOILER")
)

A steam boiler, used to generate steam for heating or industrial processes.

UNINTERRUPTIBLE_POWER_SUPPLY class-attribute instance-attribute ¤
UNINTERRUPTIBLE_POWER_SUPPLY = deprecated_member(
    16, _member_message("UNINTERRUPTIBLE_POWER_SUPPLY")
)

An uninterruptible power supply (UPS), used to provide backup power.

UNSPECIFIED class-attribute instance-attribute ¤
UNSPECIFIED = deprecated_member(
    0, _member_message("UNSPECIFIED")
)

The component category is unspecified. This should not be used.

WIND_TURBINE class-attribute instance-attribute ¤
WIND_TURBINE = deprecated_member(
    18, _member_message("WIND_TURBINE")
)

A wind turbine, used to generate electricity from wind energy.

frequenz.client.common.microgrid.electrical_components.ElectricalComponentConnection dataclass ¤

Bases: BaseElectricalComponentConnection

A single electrical link between two distinct electrical components in a microgrid.

This is the well-formed case of an electrical component connection: the source and destination are guaranteed to be different components. Malformed cases (e.g. self-loops) are represented by dedicated subclasses of ProblematicElectricalComponentConnection instead.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class ElectricalComponentConnection(BaseElectricalComponentConnection):
    """A single electrical link between two distinct electrical components in a microgrid.

    This is the well-formed case of an electrical component connection: the
    source and destination are guaranteed to be different components.
    Malformed cases (e.g. self-loops) are represented by dedicated
    subclasses of
    [`ProblematicElectricalComponentConnection`][..ProblematicElectricalComponentConnection]
    instead.
    """

    def __post_init__(self) -> None:
        """Ensure that the source and destination electrical components are different.

        Raises:
            ValueError: If
                [`source_id`][...BaseElectricalComponentConnection.source_id]
                and
                [`destination_id`][...BaseElectricalComponentConnection.destination_id]
                are equal, since that would describe a self-loop rather than
                a well-formed connection.
        """
        if self.source_id == self.destination_id:
            raise ValueError("Source and destination components must be different")
Attributes¤
destination_id instance-attribute ¤
destination_id: ElectricalComponentId

The unique ID of the electrical component where the connection terminates.

This is the electrical component towards which the current flows.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of the connection.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

source_id instance-attribute ¤

The unique identifier of the electrical 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.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is BaseElectricalComponentConnection:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Ensure that the source and destination electrical components are different.

RAISES DESCRIPTION
ValueError

If source_id and destination_id are equal, since that would describe a self-loop rather than a well-formed connection.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __post_init__(self) -> None:
    """Ensure that the source and destination electrical components are different.

    Raises:
        ValueError: If
            [`source_id`][...BaseElectricalComponentConnection.source_id]
            and
            [`destination_id`][...BaseElectricalComponentConnection.destination_id]
            are equal, since that would describe a self-loop rather than
            a well-formed connection.
    """
    if self.source_id == self.destination_id:
        raise ValueError("Source and destination components must be different")
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.source_id}->{self.destination_id}"
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this connection is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check against the operational lifetime.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this connection is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this connection is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check against the operational lifetime.

    Returns:
        Whether this connection is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Whether this connection is currently operational.

RETURNS DESCRIPTION
bool

Whether this connection is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Whether this connection is currently operational.

    Returns:
        Whether this connection is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))

frequenz.client.common.microgrid.electrical_components.ElectricalComponentDiagnosticCode ¤

Bases: Enum

All diagnostics that can occur across electrical component categories.

Source code in src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py
@unique
class ElectricalComponentDiagnosticCode(Enum):
    """All diagnostics that can occur across electrical component categories."""

    UNSPECIFIED = deprecated_member(
        0,
        "ElectricalComponentDiagnosticCode.UNSPECIFIED is deprecated; use the `int` value `0` "
        "instead if you really need to check for this low-level value.",
    )
    """Default value. No specific error is specified."""

    UNKNOWN = 1
    """The component is reporting an unknown or an undefined error.

    The sender cannot parse the component error to any of the variants below.
    """

    SWITCH_ON_FAULT = 2
    """The component could not be switched on."""

    UNDERVOLTAGE = 3
    """The component is operating under the minimum rated voltage."""

    OVERVOLTAGE = 4
    """The component is operating over the maximum rated voltage."""

    OVERCURRENT = 5
    """The component is drawing more current than the maximum rated value."""

    OVERCURRENT_CHARGING = 6
    """The component's consumption current is over the maximum rated value during charging."""

    OVERCURRENT_DISCHARGING = 7
    """The component's production current is over the maximum rated value during discharging."""

    OVERTEMPERATURE = 8
    """The component is operating over the maximum rated temperature."""

    UNDERTEMPERATURE = 9
    """The component is operating under the minimum rated temperature."""

    HIGH_HUMIDITY = 10
    """The component is exposed to high humidity levels over the maximum rated value."""

    FUSE_ERROR = 11
    """The component's fuse has blown."""

    PRECHARGE_ERROR = 12
    """The component's precharge unit has failed."""

    PLAUSIBILITY_ERROR = 13
    """Plausibility issues within the component, causing its internal sanity checks to fail."""

    FAULT_CURRENT = 14
    """Fault current detected in the component."""

    SHORT_CIRCUIT = 15
    """Short circuit detected in the component."""

    CONFIG_ERROR = 16
    """Configuration error related to the component."""

    ILLEGAL_COMPONENT_STATE_CODE_REQUESTED = 17
    """An illegal state was requested for the component."""

    HARDWARE_INACCESSIBLE = 18
    """The hardware of the component is inaccessible."""

    INTERNAL = 19
    """An internal error within the component."""

    UNAUTHORIZED = 20
    """The component is unauthorized to perform the last requested action."""

    EXCESS_LEAKAGE_CURRENT = 21
    """Excess leakage current detected, over the threshold defined by the manufacturer."""

    LOW_SYSTEM_INSULATION_RESISTANCE = 22
    """The component is inoperable due to the insulation resistance being too low.

    The threshold is defined by the manufacturer or configured by the user.
    """

    GROUND_FAULT = 23
    """Ground fault detected in the component."""

    ARC_FAULT = 24
    """Arc fault detected in the component."""

    FAN_FAULT = 25
    """Fan fault detected in the component."""

    HARDWARE_FAULT = 26
    """Hardware fault detected in the component."""

    PROTECTIVE_SHUTDOWN = 27
    """The component performed a protective shutdown."""

    GRID_OVERVOLTAGE = 30
    """The component is inoperable due to the grid voltage being too high."""

    GRID_UNDERVOLTAGE = 31
    """The component is inoperable due to the grid voltage being too low."""

    GRID_OVERFREQUENCY = 32
    """The component is inoperable due to the grid frequency being too high."""

    GRID_UNDERFREQUENCY = 33
    """The component is inoperable due to the grid frequency being too low."""

    GRID_DISCONNECTED = 34
    """The component is inoperable due to the grid being disconnected.

    This happens despite the AC relay being closed.
    """

    GRID_VOLTAGE_IMBALANCE = 35
    """The component is inoperable due to the grid voltage being imbalanced.

    This happens when the voltage of one or more phases is outside the
    acceptable range.
    """

    GRID_ABNORMAL = 36
    """The component is inoperable due to the grid being in a non-standard configuration."""

    EV_UNEXPECTED_PILOT_FAILURE = 40
    """Unexpected pilot failure in an electric vehicle (EV) component."""

    EV_CHARGING_CABLE_UNPLUGGED_FROM_STATION = 41
    """Electric vehicle (EV) cable was abruptly unplugged from the charging station."""

    EV_CHARGING_CABLE_UNPLUGGED_FROM_EV = 42
    """Electric vehicle (EV) cable was abruptly unplugged from the vehicle."""

    EV_CHARGING_CABLE_LOCK_FAILED = 43
    """Electric vehicle (EV) cable lock failure."""

    EV_CHARGING_CABLE_INVALID = 44
    """Invalid electric vehicle (EV) cable."""

    EV_CONSUMER_INCOMPATIBLE = 45
    """Incompatible electric vehicle (EV) plug."""

    BATTERY_IMBALANCE = 50
    """Battery system imbalance detected."""

    BATTERY_LOW_SOH = 51
    """Low state of health (SOH) detected in the battery."""

    BATTERY_BLOCK_ERROR = 52
    """Battery block error detected."""

    BATTERY_CONTROLLER_ERROR = 53
    """Battery controller error detected."""

    BATTERY_RELAY_ERROR = 54
    """Battery relay error detected."""

    BATTERY_CALIBRATION_NEEDED = 56
    """Battery calibration is needed."""

    RELAY_CYCLE_LIMIT_REACHED = 60
    """The battery's DC contactor or relays have reached end of life."""

    PV_REVERSAL_POLARITY = 70
    """Reverse polarity condition detected on the photovoltaic (PV) side."""

    PV_UNDERPERFORMANCE = 71
    """The photovoltaic (PV) system is underperforming."""

    PV_FAULT = 72
    """Fault in the photovoltaic (PV) system."""

    PV_REVERSE_CURRENT = 73
    """Reverse current condition detected on the photovoltaic (PV) side."""

    PV_GROUND_FAULT = 74
    """Ground fault detected on the photovoltaic (PV) side."""

    INVERTER_DC_UNDERVOLTAGE = 80
    """The inverter is inoperable due to the DC voltage being too low."""

    INVERTER_DC_OVERVOLTAGE = 81
    """The inverter is inoperable due to the DC voltage being too high."""
Attributes¤
ARC_FAULT class-attribute instance-attribute ¤
ARC_FAULT = 24

Arc fault detected in the component.

BATTERY_BLOCK_ERROR class-attribute instance-attribute ¤
BATTERY_BLOCK_ERROR = 52

Battery block error detected.

BATTERY_CALIBRATION_NEEDED class-attribute instance-attribute ¤
BATTERY_CALIBRATION_NEEDED = 56

Battery calibration is needed.

BATTERY_CONTROLLER_ERROR class-attribute instance-attribute ¤
BATTERY_CONTROLLER_ERROR = 53

Battery controller error detected.

BATTERY_IMBALANCE class-attribute instance-attribute ¤
BATTERY_IMBALANCE = 50

Battery system imbalance detected.

BATTERY_LOW_SOH class-attribute instance-attribute ¤
BATTERY_LOW_SOH = 51

Low state of health (SOH) detected in the battery.

BATTERY_RELAY_ERROR class-attribute instance-attribute ¤
BATTERY_RELAY_ERROR = 54

Battery relay error detected.

CONFIG_ERROR class-attribute instance-attribute ¤
CONFIG_ERROR = 16

Configuration error related to the component.

EV_CHARGING_CABLE_INVALID class-attribute instance-attribute ¤
EV_CHARGING_CABLE_INVALID = 44

Invalid electric vehicle (EV) cable.

EV_CHARGING_CABLE_LOCK_FAILED class-attribute instance-attribute ¤
EV_CHARGING_CABLE_LOCK_FAILED = 43

Electric vehicle (EV) cable lock failure.

EV_CHARGING_CABLE_UNPLUGGED_FROM_EV class-attribute instance-attribute ¤
EV_CHARGING_CABLE_UNPLUGGED_FROM_EV = 42

Electric vehicle (EV) cable was abruptly unplugged from the vehicle.

EV_CHARGING_CABLE_UNPLUGGED_FROM_STATION class-attribute instance-attribute ¤
EV_CHARGING_CABLE_UNPLUGGED_FROM_STATION = 41

Electric vehicle (EV) cable was abruptly unplugged from the charging station.

EV_CONSUMER_INCOMPATIBLE class-attribute instance-attribute ¤
EV_CONSUMER_INCOMPATIBLE = 45

Incompatible electric vehicle (EV) plug.

EV_UNEXPECTED_PILOT_FAILURE class-attribute instance-attribute ¤
EV_UNEXPECTED_PILOT_FAILURE = 40

Unexpected pilot failure in an electric vehicle (EV) component.

EXCESS_LEAKAGE_CURRENT class-attribute instance-attribute ¤
EXCESS_LEAKAGE_CURRENT = 21

Excess leakage current detected, over the threshold defined by the manufacturer.

FAN_FAULT class-attribute instance-attribute ¤
FAN_FAULT = 25

Fan fault detected in the component.

FAULT_CURRENT class-attribute instance-attribute ¤
FAULT_CURRENT = 14

Fault current detected in the component.

FUSE_ERROR class-attribute instance-attribute ¤
FUSE_ERROR = 11

The component's fuse has blown.

GRID_ABNORMAL class-attribute instance-attribute ¤
GRID_ABNORMAL = 36

The component is inoperable due to the grid being in a non-standard configuration.

GRID_DISCONNECTED class-attribute instance-attribute ¤
GRID_DISCONNECTED = 34

The component is inoperable due to the grid being disconnected.

This happens despite the AC relay being closed.

GRID_OVERFREQUENCY class-attribute instance-attribute ¤
GRID_OVERFREQUENCY = 32

The component is inoperable due to the grid frequency being too high.

GRID_OVERVOLTAGE class-attribute instance-attribute ¤
GRID_OVERVOLTAGE = 30

The component is inoperable due to the grid voltage being too high.

GRID_UNDERFREQUENCY class-attribute instance-attribute ¤
GRID_UNDERFREQUENCY = 33

The component is inoperable due to the grid frequency being too low.

GRID_UNDERVOLTAGE class-attribute instance-attribute ¤
GRID_UNDERVOLTAGE = 31

The component is inoperable due to the grid voltage being too low.

GRID_VOLTAGE_IMBALANCE class-attribute instance-attribute ¤
GRID_VOLTAGE_IMBALANCE = 35

The component is inoperable due to the grid voltage being imbalanced.

This happens when the voltage of one or more phases is outside the acceptable range.

GROUND_FAULT class-attribute instance-attribute ¤
GROUND_FAULT = 23

Ground fault detected in the component.

HARDWARE_FAULT class-attribute instance-attribute ¤
HARDWARE_FAULT = 26

Hardware fault detected in the component.

HARDWARE_INACCESSIBLE class-attribute instance-attribute ¤
HARDWARE_INACCESSIBLE = 18

The hardware of the component is inaccessible.

HIGH_HUMIDITY class-attribute instance-attribute ¤
HIGH_HUMIDITY = 10

The component is exposed to high humidity levels over the maximum rated value.

ILLEGAL_COMPONENT_STATE_CODE_REQUESTED class-attribute instance-attribute ¤
ILLEGAL_COMPONENT_STATE_CODE_REQUESTED = 17

An illegal state was requested for the component.

INTERNAL class-attribute instance-attribute ¤
INTERNAL = 19

An internal error within the component.

INVERTER_DC_OVERVOLTAGE class-attribute instance-attribute ¤
INVERTER_DC_OVERVOLTAGE = 81

The inverter is inoperable due to the DC voltage being too high.

INVERTER_DC_UNDERVOLTAGE class-attribute instance-attribute ¤
INVERTER_DC_UNDERVOLTAGE = 80

The inverter is inoperable due to the DC voltage being too low.

LOW_SYSTEM_INSULATION_RESISTANCE class-attribute instance-attribute ¤
LOW_SYSTEM_INSULATION_RESISTANCE = 22

The component is inoperable due to the insulation resistance being too low.

The threshold is defined by the manufacturer or configured by the user.

OVERCURRENT class-attribute instance-attribute ¤
OVERCURRENT = 5

The component is drawing more current than the maximum rated value.

OVERCURRENT_CHARGING class-attribute instance-attribute ¤
OVERCURRENT_CHARGING = 6

The component's consumption current is over the maximum rated value during charging.

OVERCURRENT_DISCHARGING class-attribute instance-attribute ¤
OVERCURRENT_DISCHARGING = 7

The component's production current is over the maximum rated value during discharging.

OVERTEMPERATURE class-attribute instance-attribute ¤
OVERTEMPERATURE = 8

The component is operating over the maximum rated temperature.

OVERVOLTAGE class-attribute instance-attribute ¤
OVERVOLTAGE = 4

The component is operating over the maximum rated voltage.

PLAUSIBILITY_ERROR class-attribute instance-attribute ¤
PLAUSIBILITY_ERROR = 13

Plausibility issues within the component, causing its internal sanity checks to fail.

PRECHARGE_ERROR class-attribute instance-attribute ¤
PRECHARGE_ERROR = 12

The component's precharge unit has failed.

PROTECTIVE_SHUTDOWN class-attribute instance-attribute ¤
PROTECTIVE_SHUTDOWN = 27

The component performed a protective shutdown.

PV_FAULT class-attribute instance-attribute ¤
PV_FAULT = 72

Fault in the photovoltaic (PV) system.

PV_GROUND_FAULT class-attribute instance-attribute ¤
PV_GROUND_FAULT = 74

Ground fault detected on the photovoltaic (PV) side.

PV_REVERSAL_POLARITY class-attribute instance-attribute ¤
PV_REVERSAL_POLARITY = 70

Reverse polarity condition detected on the photovoltaic (PV) side.

PV_REVERSE_CURRENT class-attribute instance-attribute ¤
PV_REVERSE_CURRENT = 73

Reverse current condition detected on the photovoltaic (PV) side.

PV_UNDERPERFORMANCE class-attribute instance-attribute ¤
PV_UNDERPERFORMANCE = 71

The photovoltaic (PV) system is underperforming.

RELAY_CYCLE_LIMIT_REACHED class-attribute instance-attribute ¤
RELAY_CYCLE_LIMIT_REACHED = 60

The battery's DC contactor or relays have reached end of life.

SHORT_CIRCUIT class-attribute instance-attribute ¤
SHORT_CIRCUIT = 15

Short circuit detected in the component.

SWITCH_ON_FAULT class-attribute instance-attribute ¤
SWITCH_ON_FAULT = 2

The component could not be switched on.

UNAUTHORIZED class-attribute instance-attribute ¤
UNAUTHORIZED = 20

The component is unauthorized to perform the last requested action.

UNDERTEMPERATURE class-attribute instance-attribute ¤
UNDERTEMPERATURE = 9

The component is operating under the minimum rated temperature.

UNDERVOLTAGE class-attribute instance-attribute ¤
UNDERVOLTAGE = 3

The component is operating under the minimum rated voltage.

UNKNOWN class-attribute instance-attribute ¤
UNKNOWN = 1

The component is reporting an unknown or an undefined error.

The sender cannot parse the component error to any of the variants below.

UNSPECIFIED class-attribute instance-attribute ¤
UNSPECIFIED = deprecated_member(
    0,
    "ElectricalComponentDiagnosticCode.UNSPECIFIED is deprecated; use the `int` value `0` instead if you really need to check for this low-level value.",
)

Default value. No specific error is specified.

frequenz.client.common.microgrid.electrical_components.ElectricalComponentId ¤

Bases: BaseId

A unique identifier for a microgrid electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ids.py
@final
class ElectricalComponentId(BaseId, str_prefix="CID"):
    """A unique identifier for a microgrid electrical component."""
Attributes¤
str_prefix property ¤
str_prefix: str

The prefix used for the string representation of this ID.

Methods:¤
__eq__ ¤
__eq__(other: object) -> bool

Check if this instance is equal to another object.

Equality is defined as being of the exact same type and having the same underlying ID.

PARAMETER DESCRIPTION
other

The object to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True if other is of the same type and has the same underlying ID,

bool

NotImplemented if other is of a different type.

Source code in frequenz/core/id.py
def __eq__(self, other: object) -> bool:
    """Check if this instance is equal to another object.

    Equality is defined as being of the exact same type and having the same
    underlying ID.

    Args:
        other: The object to compare against.

    Returns:
        True if `other` is of the same type and has the same underlying ID,
        `NotImplemented` if `other` is of a different type.
    """
    # pylint thinks this is not an unidiomatic typecheck, but in this case
    # it is not. isinstance() returns True for subclasses, which is not
    # what we want here, as different ID types should never be equal.
    # pylint: disable-next=unidiomatic-typecheck
    if type(other) is not type(self):
        return NotImplemented
    # We already checked type(other) is type(self), but mypy doesn't
    # understand that, so we need to cast it to Self.
    other_id = cast(Self, other)
    return self._id == other_id._id
__hash__ ¤
__hash__() -> int

Return the hash of this instance.

The hash is based on the exact type and the underlying ID to ensure that IDs of different types but with the same numeric value have different hashes.

RETURNS DESCRIPTION
int

The hash of this instance.

Source code in frequenz/core/id.py
def __hash__(self) -> int:
    """Return the hash of this instance.

    The hash is based on the exact type and the underlying ID to ensure
    that IDs of different types but with the same numeric value have different hashes.

    Returns:
        The hash of this instance.
    """
    return hash((type(self), self._id))
__init__ ¤
__init__(id_: int) -> None

Initialize this instance.

PARAMETER DESCRIPTION
id_

The numeric unique identifier.

TYPE: int

RAISES DESCRIPTION
ValueError

If the ID is negative.

Source code in frequenz/core/id.py
def __init__(self, id_: int, /) -> None:
    """Initialize this instance.

    Args:
        id_: The numeric unique identifier.

    Raises:
        ValueError: If the ID is negative.
    """
    if id_ < 0:
        raise ValueError(f"{type(self).__name__} can't be negative.")
    self._id = id_
__init_subclass__ ¤
__init_subclass__(
    *,
    str_prefix: str,
    allow_custom_name: bool = False,
    **kwargs: Any
) -> None

Initialize a subclass, set its string prefix, and perform checks.

PARAMETER DESCRIPTION
str_prefix

The string prefix for the ID type (e.g., "MID"). Must be unique across all ID types.

TYPE: str

allow_custom_name

If True, bypasses the check that the class name must end with "Id". Defaults to False.

TYPE: bool DEFAULT: False

**kwargs

Forwarded to the parent's init_subclass.

TYPE: Any DEFAULT: {}

RAISES DESCRIPTION
TypeError

If allow_custom_name is False and the class name does not end with "Id".

Source code in frequenz/core/id.py
def __init_subclass__(
    cls,
    *,
    str_prefix: str,
    allow_custom_name: bool = False,
    **kwargs: Any,
) -> None:
    """Initialize a subclass, set its string prefix, and perform checks.

    Args:
        str_prefix: The string prefix for the ID type (e.g., "MID").
            Must be unique across all ID types.
        allow_custom_name: If True, bypasses the check that the class name
            must end with "Id". Defaults to False.
        **kwargs: Forwarded to the parent's __init_subclass__.

    Raises:
        TypeError: If `allow_custom_name` is False and the class name
            does not end with "Id".
    """
    super().__init_subclass__(**kwargs)

    if str_prefix in BaseId._registered_prefixes:
        # We want to raise an exception here, but currently can't due to
        # https://github.com/frequenz-floss/frequenz-repo-config-python/issues/421
        _logger.warning(
            "Prefix '%s' is already registered. ID prefixes must be unique.",
            str_prefix,
        )
    BaseId._registered_prefixes.add(str_prefix)

    if not allow_custom_name and not cls.__name__.endswith("Id"):
        raise TypeError(
            f"Class name '{cls.__name__}' for an ID class must end with 'Id' "
            "(e.g., 'SomeId'), or use `allow_custom_name=True`."
        )

    cls._str_prefix = str_prefix
__int__ ¤
__int__() -> int

Return the numeric ID of this instance.

Source code in frequenz/core/id.py
def __int__(self) -> int:
    """Return the numeric ID of this instance."""
    return self._id
__lt__ ¤
__lt__(other: object) -> bool

Check if this instance is less than another object.

Comparison is only defined between instances of the exact same type.

PARAMETER DESCRIPTION
other

The object to compare against.

TYPE: object

RETURNS DESCRIPTION
bool

True if this instance is less than other, NotImplemented if

bool

other is of a different type.

Source code in frequenz/core/id.py
def __lt__(self, other: object) -> bool:
    """Check if this instance is less than another object.

    Comparison is only defined between instances of the exact same type.

    Args:
        other: The object to compare against.

    Returns:
        True if this instance is less than `other`, `NotImplemented` if
        `other` is of a different type.
    """
    # pylint: disable-next=unidiomatic-typecheck
    if type(other) is not type(self):
        return NotImplemented
    other_id = cast(Self, other)
    return self._id < other_id._id
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Create a new instance of the ID class, only if it is a subclass of BaseId.

Source code in frequenz/core/id.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Create a new instance of the ID class, only if it is a subclass of BaseId."""
    if cls is BaseId:
        raise TypeError("BaseId cannot be instantiated directly. Use a subclass.")
    return super().__new__(cls)
__repr__ ¤
__repr__() -> str

Return the string representation of this instance.

Source code in frequenz/core/id.py
def __repr__(self) -> str:
    """Return the string representation of this instance."""
    return f"{type(self).__name__}({self._id!r})"
__str__ ¤
__str__() -> str

Return the short string representation of this instance.

Source code in frequenz/core/id.py
def __str__(self) -> str:
    """Return the short string representation of this instance."""
    return f"{self._str_prefix}{self._id}"

frequenz.client.common.microgrid.electrical_components.ElectricalComponentStateCode ¤

Bases: Enum

All possible states of a microgrid electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_state_code.py
@unique
class ElectricalComponentStateCode(Enum):
    """All possible states of a microgrid electrical component."""

    UNSPECIFIED = deprecated_member(
        0,
        "ElectricalComponentStateCode.UNSPECIFIED is deprecated; use the `int` value `0` "
        "instead if you really need to check for this low-level value.",
    )
    """Default value when the component state is not explicitly set."""

    UNKNOWN = 1
    """The component is in an unknown or undefined condition.

    This is used when the sender is unable to classify the component into any
    other state.
    """

    UNAVAILABLE = 2
    """The component is temporarily unavailable for operation."""

    SWITCHING_OFF = 3
    """The component is in the process of switching off."""

    OFF = 4
    """The component has successfully switched off."""

    SWITCHING_ON = 5
    """The component is in the process of switching on from an off state."""

    STANDBY = 6
    """The component is in standby mode, and not immediately ready for operation."""

    READY = 7
    """The component is fully operational and ready for use."""

    CHARGING = 8
    """The component is actively consuming energy."""

    DISCHARGING = 9
    """The component is actively producing or releasing energy."""

    ERROR = 10
    """The component is in an error state and may need attention."""

    EV_CHARGING_CABLE_UNPLUGGED = 20
    """The Electric Vehicle (EV) charging cable is unplugged from the charging station."""

    EV_CHARGING_CABLE_PLUGGED_AT_STATION = 21
    """The EV charging cable is plugged into the charging station."""

    EV_CHARGING_CABLE_PLUGGED_AT_EV = 22
    """The EV charging cable is plugged into the vehicle."""

    EV_CHARGING_CABLE_LOCKED_AT_STATION = 23
    """The EV charging cable is locked at the charging station end, ready for charging."""

    EV_CHARGING_CABLE_LOCKED_AT_EV = 24
    """The EV charging cable is locked at the vehicle end, indicating that charging is active."""

    RELAY_OPEN = 30
    """The relay is in an open state, meaning no current can flow through."""

    RELAY_CLOSED = 31
    """The relay is in a closed state, allowing current to flow."""

    PRECHARGER_OPEN = 40
    """The precharger circuit is open, meaning it's not currently active."""

    PRECHARGER_PRECHARGING = 41
    """The precharger is in a precharging state, preparing the main circuit for activation."""

    PRECHARGER_CLOSED = 42
    """The precharger circuit is closed, allowing full current to flow to the main circuit."""
Attributes¤
CHARGING class-attribute instance-attribute ¤
CHARGING = 8

The component is actively consuming energy.

DISCHARGING class-attribute instance-attribute ¤
DISCHARGING = 9

The component is actively producing or releasing energy.

ERROR class-attribute instance-attribute ¤
ERROR = 10

The component is in an error state and may need attention.

EV_CHARGING_CABLE_LOCKED_AT_EV class-attribute instance-attribute ¤
EV_CHARGING_CABLE_LOCKED_AT_EV = 24

The EV charging cable is locked at the vehicle end, indicating that charging is active.

EV_CHARGING_CABLE_LOCKED_AT_STATION class-attribute instance-attribute ¤
EV_CHARGING_CABLE_LOCKED_AT_STATION = 23

The EV charging cable is locked at the charging station end, ready for charging.

EV_CHARGING_CABLE_PLUGGED_AT_EV class-attribute instance-attribute ¤
EV_CHARGING_CABLE_PLUGGED_AT_EV = 22

The EV charging cable is plugged into the vehicle.

EV_CHARGING_CABLE_PLUGGED_AT_STATION class-attribute instance-attribute ¤
EV_CHARGING_CABLE_PLUGGED_AT_STATION = 21

The EV charging cable is plugged into the charging station.

EV_CHARGING_CABLE_UNPLUGGED class-attribute instance-attribute ¤
EV_CHARGING_CABLE_UNPLUGGED = 20

The Electric Vehicle (EV) charging cable is unplugged from the charging station.

OFF class-attribute instance-attribute ¤
OFF = 4

The component has successfully switched off.

PRECHARGER_CLOSED class-attribute instance-attribute ¤
PRECHARGER_CLOSED = 42

The precharger circuit is closed, allowing full current to flow to the main circuit.

PRECHARGER_OPEN class-attribute instance-attribute ¤
PRECHARGER_OPEN = 40

The precharger circuit is open, meaning it's not currently active.

PRECHARGER_PRECHARGING class-attribute instance-attribute ¤
PRECHARGER_PRECHARGING = 41

The precharger is in a precharging state, preparing the main circuit for activation.

READY class-attribute instance-attribute ¤
READY = 7

The component is fully operational and ready for use.

RELAY_CLOSED class-attribute instance-attribute ¤
RELAY_CLOSED = 31

The relay is in a closed state, allowing current to flow.

RELAY_OPEN class-attribute instance-attribute ¤
RELAY_OPEN = 30

The relay is in an open state, meaning no current can flow through.

STANDBY class-attribute instance-attribute ¤
STANDBY = 6

The component is in standby mode, and not immediately ready for operation.

SWITCHING_OFF class-attribute instance-attribute ¤
SWITCHING_OFF = 3

The component is in the process of switching off.

SWITCHING_ON class-attribute instance-attribute ¤
SWITCHING_ON = 5

The component is in the process of switching on from an off state.

UNAVAILABLE class-attribute instance-attribute ¤
UNAVAILABLE = 2

The component is temporarily unavailable for operation.

UNKNOWN class-attribute instance-attribute ¤
UNKNOWN = 1

The component is in an unknown or undefined condition.

This is used when the sender is unable to classify the component into any other state.

UNSPECIFIED class-attribute instance-attribute ¤
UNSPECIFIED = deprecated_member(
    0,
    "ElectricalComponentStateCode.UNSPECIFIED is deprecated; use the `int` value `0` instead if you really need to check for this low-level value.",
)

Default value when the component state is not explicitly set.

frequenz.client.common.microgrid.electrical_components.Electrolyzer dataclass ¤

Bases: ElectricalComponent

An electrolyzer electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrolyzer.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Electrolyzer(ElectricalComponent):
    """An electrolyzer electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.EvCharger dataclass ¤

Bases: ElectricalComponent

An abstract EV charger electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class EvCharger(ElectricalComponent):
    """An abstract EV charger electrical component."""

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is EvCharger:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.GridConnectionPoint dataclass ¤

Bases: ElectricalComponent

A point where a local electrical system connects to the grid.

The terms "Grid Connection Point" and "Point of Common Coupling" (PCC) are commonly used in the context.

While both terms describe a connection point to the grid, the GridConnectionPoint is specifically the physical connection point of the generation facility to the grid, often concerned with the technical and ownership aspects of the connection.

In contrast, the PCC is more specific in terms of electrical engineering. It refers to the point where a customer's local electrical system connects to the utility distribution grid in such a way that it can affect other customers’ systems connected to the same network. It is the point where the grid and customer's electrical systems interface and where issues like power quality and supply regulations are assessed.

The term GridConnectionPoint is used to make it clear that what is referred to here is the physical connection point of the local facility to the grid. Note that this may also be the PCC in some cases.

Source code in src/frequenz/client/common/microgrid/electrical_components/_grid_connection_point.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class GridConnectionPoint(ElectricalComponent):
    """A point where a local electrical system connects to the grid.

    The terms "Grid Connection Point" and "Point of Common Coupling" (PCC) are
    commonly used in the context.

    While both terms describe a connection point to the grid, the
    `GridConnectionPoint` is specifically the physical connection point of the
    generation facility to the grid, often concerned with the technical and
    ownership aspects of the connection.

    In contrast, the PCC is more specific in terms of electrical engineering.
    It refers to the point where a customer's local electrical system connects
    to the utility distribution grid in such a way that it can affect other
    customers’ systems connected to the same network. It is the point where the
    grid and customer's electrical systems interface and where issues like power
    quality and supply regulations are assessed.

    The term `GridConnectionPoint` is used to make it clear that what is referred
    to here is the physical connection point of the local facility to the grid.
    Note that this may also be the PCC in some cases.
    """

    rated_fuse_current: int
    """The maximum amount of electrical current that can flow through this connection, in amperes.

    The rated maximum amount of current the fuse at the grid connection point is
    designed to safely carry under normal operating conditions.

    This limit applies to currents both flowing in or out of each of the 3
    phases individually.

    In other words, a current `i`A at one of the phases of the grid connection
    point must comply with the following constraint:
    `-rated_fuse_current <= i <= rated_fuse_current`.
    """

    def __post_init__(self) -> None:
        """Run the base construction gate and validate the fuse's rated current."""
        super().__post_init__()
        if self.rated_fuse_current < 0:
            raise ValueError(
                f"rated_fuse_current must be a non-negative integer, not {self.rated_fuse_current}"
            )
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

rated_fuse_current instance-attribute ¤
rated_fuse_current: int

The maximum amount of electrical current that can flow through this connection, in amperes.

The rated maximum amount of current the fuse at the grid connection point is designed to safely carry under normal operating conditions.

This limit applies to currents both flowing in or out of each of the 3 phases individually.

In other words, a current iA at one of the phases of the grid connection point must comply with the following constraint: -rated_fuse_current <= i <= rated_fuse_current.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Run the base construction gate and validate the fuse's rated current.

Source code in src/frequenz/client/common/microgrid/electrical_components/_grid_connection_point.py
def __post_init__(self) -> None:
    """Run the base construction gate and validate the fuse's rated current."""
    super().__post_init__()
    if self.rated_fuse_current < 0:
        raise ValueError(
            f"rated_fuse_current must be a non-negative integer, not {self.rated_fuse_current}"
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Hvac dataclass ¤

Bases: ElectricalComponent

A heating, ventilation, and air conditioning (HVAC) electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_hvac.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Hvac(ElectricalComponent):
    """A heating, ventilation, and air conditioning (HVAC) electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.HybridEvCharger dataclass ¤

Bases: EvCharger

An EV charger that supports both AC and DC charging.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class HybridEvCharger(EvCharger):
    """An EV charger that supports both AC and DC charging."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.HybridInverter dataclass ¤

Bases: Inverter

A hybrid inverter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class HybridInverter(Inverter):
    """A hybrid inverter."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Inverter dataclass ¤

Bases: ElectricalComponent

An abstract inverter electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Inverter(ElectricalComponent):
    """An abstract inverter electrical component."""

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is Inverter:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.LiIonBattery dataclass ¤

Bases: Battery

A Li-ion battery.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class LiIonBattery(Battery):
    """A Li-ion battery."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Battery:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Meter dataclass ¤

Bases: ElectricalComponent

A measuring meter electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_meter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Meter(ElectricalComponent):
    """A measuring meter electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.MismatchedCategoryElectricalComponent dataclass ¤

Bases: ProblematicElectricalComponent

An electrical component with a mismatch in the category.

This electrical component declared a category but carries category specific info that doesn't match the declared category.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class MismatchedCategoryElectricalComponent(ProblematicElectricalComponent):
    """An electrical component with a mismatch in the category.

    This electrical component declared a category but carries category specific
    info that doesn't match the declared category.
    """

    category: int
    """The raw category declared by this component.

    It doesn't match the carried category specific info.
    """

    category_name: str | None = None
    """The short protobuf name of the declared category, or `None` if unknown.

    This is the protobuf enum name without its long prefix (e.g. `"BATTERY"`).
    It is normally set, since a mismatched component declares a recognized
    category.
    """

    def __str__(self) -> str:
        """Return a string representation exposing the category mismatch."""
        info = self.category_specific_info
        kind = info.kind if info is not None else ""
        category = self.category_name or self.category
        return f"{self.id}:{self.name}:mismatched:category={category}:kind={kind}"
Attributes¤
category instance-attribute ¤
category: int

The raw category declared by this component.

It doesn't match the carried category specific info.

category_name class-attribute instance-attribute ¤
category_name: str | None = None

The short protobuf name of the declared category, or None if unknown.

This is the protobuf enum name without its long prefix (e.g. "BATTERY"). It is normally set, since a mismatched component declares a recognized category.

category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a string representation exposing the category mismatch.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __str__(self) -> str:
    """Return a string representation exposing the category mismatch."""
    info = self.category_specific_info
    kind = info.kind if info is not None else ""
    category = self.category_name or self.category
    return f"{self.id}:{self.name}:mismatched:category={category}:kind={kind}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.NaIonBattery dataclass ¤

Bases: Battery

A Na-ion battery.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class NaIonBattery(Battery):
    """A Na-ion battery."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Battery:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Plc dataclass ¤

Bases: ElectricalComponent

A programmable logic controller (PLC) electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_plc.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Plc(ElectricalComponent):
    """A programmable logic controller (PLC) electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.PowerTransformer dataclass ¤

Bases: ElectricalComponent

A power transformer electrical component.

Power transformers are used to step up or step down the voltage, keeping the power somewhat constant by increasing or decreasing the current.

If voltage is stepped up, current is stepped down, and vice versa.

Note

Power transformers have efficiency losses, so the output power is always less than the input power.

Source code in src/frequenz/client/common/microgrid/electrical_components/_power_transformer.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class PowerTransformer(ElectricalComponent):
    """A power transformer electrical component.

    Power transformers are used to step up or step down the voltage, keeping
    the power somewhat constant by increasing or decreasing the current.

    If voltage is stepped up, current is stepped down, and vice versa.

    Note:
        Power transformers have efficiency losses, so the output power is always less
        than the input power.
    """

    primary_voltage: FloatInt
    """The primary voltage of the transformer, in volts.

    This is the input voltage that is stepped up or down.
    """

    secondary_voltage: FloatInt
    """The secondary voltage of the transformer, in volts.

    This is the output voltage that is the result of stepping the primary
    voltage up or down.
    """
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

primary_voltage instance-attribute ¤
primary_voltage: FloatInt

The primary voltage of the transformer, in volts.

This is the input voltage that is stepped up or down.

secondary_voltage instance-attribute ¤
secondary_voltage: FloatInt

The secondary voltage of the transformer, in volts.

This is the output voltage that is the result of stepping the primary voltage up or down.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.Precharger dataclass ¤

Bases: ElectricalComponent

A precharger electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_precharger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class Precharger(ElectricalComponent):
    """A precharger electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.ProblematicElectricalComponent dataclass ¤

Bases: ElectricalComponent

An abstract electrical component with a problem.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class ProblematicElectricalComponent(ElectricalComponent):
    """An abstract electrical component with a problem."""

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is ProblematicElectricalComponent:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.ProblematicElectricalComponentConnection dataclass ¤

Bases: BaseElectricalComponentConnection

An abstract electrical component connection with a problem.

This is the base class for connections that carry a data-integrity issue.

Problematic connections are siblings of the well-formed ElectricalComponentConnection: both extend BaseElectricalComponentConnection.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic_connection.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class ProblematicElectricalComponentConnection(BaseElectricalComponentConnection):
    """An abstract electrical component connection with a problem.

    This is the base class for connections that carry a data-integrity issue.

    Problematic connections are siblings of the well-formed
    [`ElectricalComponentConnection`][..ElectricalComponentConnection]: both
    extend
    [`BaseElectricalComponentConnection`][..BaseElectricalComponentConnection].
    """

    # pylint: disable-next=unused-argument
    def __new__(cls, *args: Any, **kwargs: Any) -> Self:
        """Prevent instantiation of this class."""
        if cls is ProblematicElectricalComponentConnection:
            raise TypeError(f"Cannot instantiate {cls.__name__} directly")
        return super().__new__(cls)
Attributes¤
destination_id instance-attribute ¤
destination_id: ElectricalComponentId

The unique ID of the electrical component where the connection terminates.

This is the electrical component towards which the current flows.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of the connection.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

source_id instance-attribute ¤

The unique identifier of the electrical 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.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic_connection.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponentConnection:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.source_id}->{self.destination_id}"
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this connection is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check against the operational lifetime.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this connection is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this connection is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check against the operational lifetime.

    Returns:
        Whether this connection is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Whether this connection is currently operational.

RETURNS DESCRIPTION
bool

Whether this connection is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Whether this connection is currently operational.

    Returns:
        Whether this connection is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))

frequenz.client.common.microgrid.electrical_components.PvInverter dataclass ¤

Bases: Inverter

A PV inverter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class PvInverter(Inverter):
    """A PV inverter."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.SelfReferencingElectricalComponentConnection dataclass ¤

Bases: ProblematicElectricalComponentConnection

An electrical component connection whose source and destination are the same.

This represents a self-loop in the microgrid topology, which is physically impossible and normally invalid. Instances of this class are produced by the *_from_proto converters when they receive a connection whose source and destination component IDs are identical, so that the problematic data is exposed to the caller instead of being silently discarded.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic_connection.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class SelfReferencingElectricalComponentConnection(
    ProblematicElectricalComponentConnection
):
    """An electrical component connection whose source and destination are the same.

    This represents a self-loop in the microgrid topology, which is physically
    impossible and normally invalid. Instances of this class are produced by
    the ``*_from_proto`` converters when they receive a connection whose
    source and destination component IDs are identical, so that the
    problematic data is exposed to the caller instead of being silently
    discarded.
    """

    def __post_init__(self) -> None:
        """Ensure that source and destination refer to the same component.

        Raises:
            ValueError: If
                [`source_id`][...BaseElectricalComponentConnection.source_id]
                and
                [`destination_id`][...BaseElectricalComponentConnection.destination_id]
                are different, since a self-referencing connection is defined
                by them being the same.
        """
        if self.source_id != self.destination_id:
            raise ValueError(
                "Source and destination components must be the same for a "
                "self-referencing electrical component connection"
            )
Attributes¤
destination_id instance-attribute ¤
destination_id: ElectricalComponentId

The unique ID of the electrical component where the connection terminates.

This is the electrical component towards which the current flows.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of the connection.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

source_id instance-attribute ¤

The unique identifier of the electrical 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.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic_connection.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponentConnection:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Ensure that source and destination refer to the same component.

RAISES DESCRIPTION
ValueError

If source_id and destination_id are different, since a self-referencing connection is defined by them being the same.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic_connection.py
def __post_init__(self) -> None:
    """Ensure that source and destination refer to the same component.

    Raises:
        ValueError: If
            [`source_id`][...BaseElectricalComponentConnection.source_id]
            and
            [`destination_id`][...BaseElectricalComponentConnection.destination_id]
            are different, since a self-referencing connection is defined
            by them being the same.
    """
    if self.source_id != self.destination_id:
        raise ValueError(
            "Source and destination components must be the same for a "
            "self-referencing electrical component connection"
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.source_id}->{self.destination_id}"
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this connection is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check against the operational lifetime.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this connection is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this connection is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check against the operational lifetime.

    Returns:
        Whether this connection is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Whether this connection is currently operational.

RETURNS DESCRIPTION
bool

Whether this connection is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component_connection.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Whether this connection is currently operational.

    Returns:
        Whether this connection is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))

frequenz.client.common.microgrid.electrical_components.StaticTransferSwitch dataclass ¤

Bases: ElectricalComponent

A static transfer switch electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_static_transfer_switch.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class StaticTransferSwitch(ElectricalComponent):
    """A static transfer switch electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.SteamBoiler dataclass ¤

Bases: ElectricalComponent

A steam boiler electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_steam_boiler.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class SteamBoiler(ElectricalComponent):
    """A steam boiler electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UninterruptiblePowerSupply dataclass ¤

Bases: ElectricalComponent

An uninterruptible power supply (UPS) electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_uninterruptible_power_supply.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UninterruptiblePowerSupply(ElectricalComponent):
    """An uninterruptible power supply (UPS) electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnrecognizedBattery dataclass ¤

Bases: Battery, ProblematicElectricalComponent

A battery of an unrecognized type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnrecognizedBattery(Battery, ProblematicElectricalComponent):
    """A battery of an unrecognized type."""

    type: int
    """The raw type of this battery, not recognized by this library version."""

    def __str__(self) -> str:
        """Return a string representation exposing the raw type."""
        return f"{self.id}:{self.name}:Battery:type={self.type}"
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

type instance-attribute ¤
type: int

The raw type of this battery, not recognized by this library version.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Battery:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a string representation exposing the raw type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __str__(self) -> str:
    """Return a string representation exposing the raw type."""
    return f"{self.id}:{self.name}:Battery:type={self.type}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnrecognizedElectricalComponent dataclass ¤

Bases: ProblematicElectricalComponent

An electrical component of an unrecognized type.

This is used for components whose category is not known to this version of the library.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnrecognizedElectricalComponent(ProblematicElectricalComponent):
    """An electrical component of an unrecognized type.

    This is used for components whose category is not known to this version of
    the library.
    """

    category: int
    """The raw category of this component, not recognized by this library version."""

    def __str__(self) -> str:
        """Return a string representation exposing the raw category."""
        return f"{self.id}:{self.name}:category={self.category}"
Attributes¤
category instance-attribute ¤
category: int

The raw category of this component, not recognized by this library version.

category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a string representation exposing the raw category.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __str__(self) -> str:
    """Return a string representation exposing the raw category."""
    return f"{self.id}:{self.name}:category={self.category}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnrecognizedEvCharger dataclass ¤

Bases: EvCharger, ProblematicElectricalComponent

An EV charger of an unrecognized type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnrecognizedEvCharger(EvCharger, ProblematicElectricalComponent):
    """An EV charger of an unrecognized type."""

    type: int
    """The raw type of this EV charger, not recognized by this library version."""

    def __str__(self) -> str:
        """Return a string representation exposing the raw type."""
        return f"{self.id}:{self.name}:EvCharger:type={self.type}"
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

type instance-attribute ¤
type: int

The raw type of this EV charger, not recognized by this library version.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a string representation exposing the raw type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __str__(self) -> str:
    """Return a string representation exposing the raw type."""
    return f"{self.id}:{self.name}:EvCharger:type={self.type}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnrecognizedInverter dataclass ¤

Bases: Inverter, ProblematicElectricalComponent

An inverter of an unrecognized type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnrecognizedInverter(Inverter, ProblematicElectricalComponent):
    """An inverter of an unrecognized type."""

    type: int
    """The raw type of this inverter, not recognized by this library version."""

    def __str__(self) -> str:
        """Return a string representation exposing the raw type."""
        return f"{self.id}:{self.name}:Inverter:type={self.type}"
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

type instance-attribute ¤
type: int

The raw type of this inverter, not recognized by this library version.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a string representation exposing the raw type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __str__(self) -> str:
    """Return a string representation exposing the raw type."""
    return f"{self.id}:{self.name}:Inverter:type={self.type}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnspecifiedBattery dataclass ¤

Bases: Battery, ProblematicElectricalComponent

A battery of an unspecified type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnspecifiedBattery(Battery, ProblematicElectricalComponent):
    """A battery of an unspecified type."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_battery.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Battery:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnspecifiedElectricalComponent dataclass ¤

Bases: ProblematicElectricalComponent

An electrical component of unspecified type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnspecifiedElectricalComponent(ProblematicElectricalComponent):
    """An electrical component of unspecified type."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_problematic.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ProblematicElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnspecifiedEvCharger dataclass ¤

Bases: EvCharger, ProblematicElectricalComponent

An EV charger of an unspecified type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnspecifiedEvCharger(EvCharger, ProblematicElectricalComponent):
    """An EV charger of an unspecified type."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_ev_charger.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is EvCharger:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.UnspecifiedInverter dataclass ¤

Bases: Inverter, ProblematicElectricalComponent

An inverter of an unspecified type.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class UnspecifiedInverter(Inverter, ProblematicElectricalComponent):
    """An inverter of an unspecified type."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*args: Any, **kwargs: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_inverter.py
def __new__(cls, *args: Any, **kwargs: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is Inverter:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)

frequenz.client.common.microgrid.electrical_components.WindTurbine dataclass ¤

Bases: ElectricalComponent

A wind turbine electrical component.

Source code in src/frequenz/client/common/microgrid/electrical_components/_wind_turbine.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class WindTurbine(ElectricalComponent):
    """A wind turbine electrical component."""
Attributes¤
category_specific_info class-attribute instance-attribute ¤
category_specific_info: CategorySpecificInfo | None = None

The category specific info carried by this component, if any.

This is None when the wire carried no category-specific info variant. Otherwise it holds a CategorySpecificInfo recording the variant kind together with any fields that were not translated into typed attributes on this component. The leftover fields are empty when everything was translated, and non-empty when the category or its variant is not recognized, or when a newer API version added fields this client version doesn't know yet.

id instance-attribute ¤

This electrical component's ID.

identity property ¤

The identity of this electrical component.

This uses the component ID and microgrid ID to identify an electrical component without considering the other attributes, so even if an electrical component state changed, the identity remains the same.

metric_config_bounds class-attribute instance-attribute ¤
metric_config_bounds: Mapping[
    Metric | int, BoundsSet | InvalidBoundsSet
] = dataclasses.field(default_factory=dict, hash=False)

The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer limits, or limits of other devices.

Each metric maps to the aggregate of all the bounds configured for it: a BoundsSet when every one is well-formed, or an InvalidBoundsSet preserving all the raw bounds when any is malformed.

If an unspecified metric is received, it is stored as the plain int key 0 when loading from protobuf. Metrics unknown to this client version may also appear as plain int keys for forward-compatibility.

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Tip

Prefer get_metric_config_bounds() when a valid BoundsSet is required.

microgrid_id instance-attribute ¤
microgrid_id: MicrogridId

The ID of the microgrid this electrical component belongs to.

model instance-attribute ¤
model: str

The model of this electrical component.

This includes both the manufacturer and the model name.

name instance-attribute ¤
name: str

The name of this electrical component.

operational_lifetime class-attribute instance-attribute ¤
operational_lifetime: Lifetime | InvalidLifetime = (
    dataclasses.field(default_factory=Lifetime)
)

The operational lifetime of this electrical component.

An InvalidLifetime preserves malformed wire data.

Tip

Prefer get_operational_lifetime() when a valid lifetime is required.

Methods:¤
__new__ ¤
__new__(*_: Any, **__: Any) -> Self

Prevent instantiation of this class.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __new__(cls, *_: Any, **__: Any) -> Self:
    """Prevent instantiation of this class."""
    if cls is ElectricalComponent:
        raise TypeError(f"Cannot instantiate {cls.__name__} directly")
    return super().__new__(cls)
__post_init__ ¤
__post_init__() -> None

Reject direct construction of this read-only type.

RAISES DESCRIPTION
TypeError

If the instance was not created via the corresponding *_from_proto converter.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __post_init__(self) -> None:
    """Reject direct construction of this read-only type.

    Raises:
        TypeError: If the instance was not created via the corresponding
            `*_from_proto` converter.
    """
    if not self._allow_construction:
        raise TypeError(
            f"{type(self).__name__} cannot be constructed directly; obtain "
            "instances via the corresponding *_from_proto converter."
        )
__str__ ¤
__str__() -> str

Return a human-readable string representation of this instance.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def __str__(self) -> str:
    """Return a human-readable string representation of this instance."""
    return f"{self.id}:{self.name}:{type(self).__name__}"
accepts_control ¤
accepts_control() -> bool

Check whether this electrical component accepts control commands.

RETURNS DESCRIPTION
bool

Whether this electrical component accepts control commands.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def accepts_control(self) -> bool:
    """Check whether this electrical component accepts control commands.

    Returns:
        Whether this electrical component accepts control commands.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._accepts_control:
        case bool() as accepts_control:
            return accepts_control
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_accepts_control",
                f"operational mode of {self} is unspecified; "
                "control availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_accepts_control",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; control availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)
get_metric_config_bounds ¤
get_metric_config_bounds(metric: Metric | int) -> BoundsSet
get_metric_config_bounds(
    metric: Metric | int, *, default: DefaultT
) -> BoundsSet | DefaultT
get_metric_config_bounds(
    metric: Metric | int, *, default: object = BoundsSet()
) -> object

Return the configured bounds for a metric as a valid BoundsSet.

An absent entry returns an unbounded metric, so when no bounds are configured for metric this returns an unbounded BoundsSet by default. Pass default to return a different value for absent entries instead, mimicking dict.get().

Warning

A Metric and its numeric value are distinct keys: Metric is not an int subclass, so a Metric argument only matches Metric-keyed entries and an int argument only matches int-keyed entries. int metrics should only be used to look up unrecognized metrics, including the raw 0 used for an unspecified metric.

Example

To check if a metric has valid configured bounds, you can use:

component: ElectricalComponent
metric: Metric
if component.get_metric_config_bounds(metric, default=None) is not None:
    print(f"{metric} has valid configured bounds")

This is similar to accessing metric_config_bounds directly, but avoid the special handling of invalid bounds.

PARAMETER DESCRIPTION
metric

The metric whose bounds to retrieve. A raw int looks up an entry stored under an unrecognized metric value, including the raw 0 used for an unspecified metric; it is matched as given, with no special handling.

TYPE: Metric | int

default

The value to return when no bounds are configured for metric.

TYPE: object DEFAULT: BoundsSet()

RETURNS DESCRIPTION
object

The valid BoundsSet configured for metric, or default when there is no entry for metric.

RAISES DESCRIPTION
InvalidBoundsSetError

If the bounds configured for metric are malformed. The offending instance is available on the exception's bounds_set attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_metric_config_bounds(
    self, metric: Metric | int, *, default: object = BoundsSet()
) -> object:
    """Return the configured bounds for a metric as a valid `BoundsSet`.

    An absent entry returns an unbounded metric, so when no bounds are
    configured for `metric` this returns an unbounded
    [`BoundsSet`][frequenz.client.common.metrics.BoundsSet] by default. Pass
    `default` to return a different value for absent entries instead,
    mimicking [`dict.get()`][dict.get].

    Warning:
        A `Metric` and its numeric value are distinct keys: `Metric` is not
        an `int` subclass, so a `Metric` argument only matches
        `Metric`-keyed entries and an `int` argument only matches
        `int`-keyed entries. `int` metrics should only be used to look up
        unrecognized metrics, including the raw `0` used for an unspecified
        metric.

    Example:
        To check if a `metric` has **valid** configured bounds, you can use:

        ```py
        component: ElectricalComponent
        metric: Metric
        if component.get_metric_config_bounds(metric, default=None) is not None:
            print(f"{metric} has valid configured bounds")
        ```

        This is similar to accessing
        [`metric_config_bounds`][...ElectricalComponent.metric_config_bounds]
        directly, but avoid the special handling of invalid bounds.

    Args:
        metric: The metric whose bounds to retrieve. A raw `int` looks up
            an entry stored under an unrecognized metric value, including
            the raw `0` used for an unspecified metric; it is matched as
            given, with no special handling.
        default: The value to return when no bounds are configured for
            `metric`.

    Returns:
        The valid [`BoundsSet`][.....metrics.BoundsSet] configured for
            `metric`, or `default` when there is no entry for `metric`.

    Raises:
        InvalidBoundsSetError: If the bounds configured for `metric` are
            malformed. The offending instance is available on the
            exception's `bounds_set` attribute.
    """
    match self.metric_config_bounds.get(metric):
        case None:
            return default
        case InvalidBoundsSet() as invalid:
            raise InvalidBoundsSetError(
                self,
                "metric_config_bounds",
                invalid,
                f"invalid bounds set {invalid} for metric {metric} in {self}",
            )
        case BoundsSet() as valid:
            return valid
        case unknown:
            assert_never(unknown)
get_operational_lifetime ¤
get_operational_lifetime() -> Lifetime

Return the operational lifetime as a valid Lifetime.

RETURNS DESCRIPTION
Lifetime

The valid operational lifetime.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def get_operational_lifetime(self) -> Lifetime:
    """Return the operational lifetime as a valid `Lifetime`.

    Returns:
        The valid operational lifetime.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    match self.operational_lifetime:
        case InvalidLifetime() as invalid:
            raise InvalidLifetimeError(self, "operational_lifetime", invalid)
        case Lifetime() as valid:
            return valid
        case unknown:
            assert_never(unknown)
is_operational_at ¤
is_operational_at(timestamp: datetime) -> bool

Check whether this electrical component is operational at a specific timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to check.

TYPE: datetime

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the given timestamp.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_at(self, timestamp: datetime) -> bool:  # noqa: DOC502
    """Check whether this electrical component is operational at a specific timestamp.

    Args:
        timestamp: The timestamp to check.

    Returns:
        Whether this electrical component is operational at the given timestamp.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.get_operational_lifetime().is_operational_at(timestamp)
is_operational_now ¤
is_operational_now() -> bool

Check whether this electrical component is currently operational.

RETURNS DESCRIPTION
bool

Whether this electrical component is operational at the current time.

RAISES DESCRIPTION
InvalidLifetimeError

If malformed lifetime data was received. The offending value is available on the exception's lifetime attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def is_operational_now(self) -> bool:  # noqa: DOC502
    """Check whether this electrical component is currently operational.

    Returns:
        Whether this electrical component is operational at the current time.

    Raises:
        InvalidLifetimeError: If malformed lifetime data was received. The
            offending value is available on the exception's `lifetime`
            attribute.
    """
    return self.is_operational_at(datetime.now(timezone.utc))
provides_telemetry ¤
provides_telemetry() -> bool

Check whether this electrical component provides telemetry data.

RETURNS DESCRIPTION
bool

Whether this electrical component provides telemetry data.

RAISES DESCRIPTION
UnspecifiedEnumValueError

If the operational mode is unspecified.

UnrecognizedEnumValueError

If the operational mode is not recognized. The raw value is available on the error's value attribute.

Source code in src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py
def provides_telemetry(self) -> bool:
    """Check whether this electrical component provides telemetry data.

    Returns:
        Whether this electrical component provides telemetry data.

    Raises:
        UnspecifiedEnumValueError: If the operational mode is unspecified.
        UnrecognizedEnumValueError: If the operational mode is not recognized.
            The raw value is available on the error's `value` attribute.
    """
    match self._provides_telemetry:
        case bool() as provides_telemetry:
            return provides_telemetry
        case 0:
            raise UnspecifiedEnumValueError(
                self,
                "_provides_telemetry",
                f"operational mode of {self} is unspecified; "
                "telemetry availability is unknown",
            )
        case int() as value:
            raise UnrecognizedEnumValueError(
                self,
                "_provides_telemetry",
                value,
                f"operational mode {value} of {self} is not a recognized "
                "ElectricalComponentOperationalMode; telemetry availability "
                "is unknown",
            )
        case unknown:
            assert_never(unknown)