diff --git a/src/frequenz/sdk/microgrid/component/_component_data.py b/src/frequenz/sdk/microgrid/component/_component_data.py index 7353d0897..5c689635f 100644 --- a/src/frequenz/sdk/microgrid/component/_component_data.py +++ b/src/frequenz/sdk/microgrid/component/_component_data.py @@ -356,6 +356,50 @@ class EVChargerData(ComponentData): wire for phase/line 1,2 and 3 respectively. """ + active_power_inclusion_lower_bound: float + """Lower inclusion bound for EV charger power in watts. + + This is the lower limit of the range within which power requests are allowed for the + EV charger. + + See [`frequenz.api.common.metrics_pb2.Metric.system_inclusion_bounds`][] and + [`frequenz.api.common.metrics_pb2.Metric.system_exclusion_bounds`][] for more + details. + """ + + active_power_exclusion_lower_bound: float + """Lower exclusion bound for EV charger power in watts. + + This is the lower limit of the range within which power requests are not allowed for + the EV charger. + + See [`frequenz.api.common.metrics_pb2.Metric.system_inclusion_bounds`][] and + [`frequenz.api.common.metrics_pb2.Metric.system_exclusion_bounds`][] for more + details. + """ + + active_power_inclusion_upper_bound: float + """Upper inclusion bound for EV charger power in watts. + + This is the upper limit of the range within which power requests are allowed for the + EV charger. + + See [`frequenz.api.common.metrics_pb2.Metric.system_inclusion_bounds`][] and + [`frequenz.api.common.metrics_pb2.Metric.system_exclusion_bounds`][] for more + details. + """ + + active_power_exclusion_upper_bound: float + """Upper exclusion bound for EV charger power in watts. + + This is the upper limit of the range within which power requests are not allowed for + the EV charger. + + See [`frequenz.api.common.metrics_pb2.Metric.system_inclusion_bounds`][] and + [`frequenz.api.common.metrics_pb2.Metric.system_exclusion_bounds`][] for more + details. + """ + frequency: float """AC frequency, in Hertz (Hz).""" @@ -375,10 +419,11 @@ def from_proto(cls, raw: microgrid_pb.ComponentData) -> EVChargerData: Returns: Instance of EVChargerData created from the protobuf message. """ + raw_power = raw.ev_charger.data.ac.power_active ev_charger_data = cls( component_id=raw.id, timestamp=raw.ts.ToDatetime(tzinfo=timezone.utc), - active_power=raw.ev_charger.data.ac.power_active.value, + active_power=raw_power.value, current_per_phase=( raw.ev_charger.data.ac.phase_1.current.value, raw.ev_charger.data.ac.phase_2.current.value, @@ -389,6 +434,10 @@ def from_proto(cls, raw: microgrid_pb.ComponentData) -> EVChargerData: raw.ev_charger.data.ac.phase_2.voltage.value, raw.ev_charger.data.ac.phase_3.voltage.value, ), + active_power_inclusion_lower_bound=raw_power.system_inclusion_bounds.lower, + active_power_exclusion_lower_bound=raw_power.system_exclusion_bounds.lower, + active_power_inclusion_upper_bound=raw_power.system_inclusion_bounds.upper, + active_power_exclusion_upper_bound=raw_power.system_exclusion_bounds.upper, cable_state=EVChargerCableState.from_pb(raw.ev_charger.state.cable_state), component_state=EVChargerComponentState.from_pb( raw.ev_charger.state.component_state @@ -397,3 +446,18 @@ def from_proto(cls, raw: microgrid_pb.ComponentData) -> EVChargerData: ) ev_charger_data._set_raw(raw=raw) return ev_charger_data + + def is_ev_connected(self) -> bool: + """Check whether an EV is connected to the charger. + + Returns: + When the charger is not in an error state, whether an EV is connected to + the charger. + """ + return self.component_state not in ( + EVChargerComponentState.AUTHORIZATION_REJECTED, + EVChargerComponentState.ERROR, + ) and self.cable_state in ( + EVChargerCableState.EV_LOCKED, + EVChargerCableState.EV_PLUGGED, + ) diff --git a/tests/utils/component_data_wrapper.py b/tests/utils/component_data_wrapper.py index 96e7689bc..261757ada 100644 --- a/tests/utils/component_data_wrapper.py +++ b/tests/utils/component_data_wrapper.py @@ -159,6 +159,10 @@ def __init__( # pylint: disable=too-many-arguments active_power: float = math.nan, current_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan), voltage_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan), + active_power_inclusion_lower_bound: float = math.nan, + active_power_exclusion_lower_bound: float = math.nan, + active_power_inclusion_upper_bound: float = math.nan, + active_power_exclusion_upper_bound: float = math.nan, frequency: float = 50.0, cable_state: EVChargerCableState = EVChargerCableState.UNSPECIFIED, component_state: EVChargerComponentState = EVChargerComponentState.UNSPECIFIED, @@ -174,6 +178,10 @@ def __init__( # pylint: disable=too-many-arguments active_power=active_power, current_per_phase=current_per_phase, voltage_per_phase=voltage_per_phase, + active_power_inclusion_lower_bound=active_power_inclusion_lower_bound, + active_power_exclusion_lower_bound=active_power_exclusion_lower_bound, + active_power_inclusion_upper_bound=active_power_inclusion_upper_bound, + active_power_exclusion_upper_bound=active_power_exclusion_upper_bound, frequency=frequency, cable_state=cable_state, component_state=component_state,