From 82604b08bd3c2f6982f90fb69137c9b8f6574434 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 15 Jun 2026 13:09:28 +0000 Subject: [PATCH 1/4] Fix `ElectricalComponentCategory` docstrings to match proto Several category docstrings had drifted from the protobuf documentation and were semantically wrong, too narrow, or too broad: - `INVERTER` described an inverter as "an electricity generator", which is wrong; it converts DC to AC power and vice versa. - `UNSPECIFIED` described an "unknown" category with error-handling advice; the proto means the sender did not specify a category and it should not be used (unknown/forward-incompatible categories are handled separately by `UnrecognizedElectricalComponent`). - `BATTERY` said "used by inverters", implying a fixed topology; it is a battery energy storage system on its own. - `BREAKER` said "a relay"; a circuit breaker adds protection semantics. - `CHP` restricted the source to combustion, which the proto does not. Signed-off-by: Leandro Lucarella --- .../electrical_components/_category.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/frequenz/client/common/microgrid/electrical_components/_category.py b/src/frequenz/client/common/microgrid/electrical_components/_category.py index 4a821b69..8f8dbbb0 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_category.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_category.py @@ -11,11 +11,7 @@ class ElectricalComponentCategory(enum.Enum): """Possible types of microgrid electrical component.""" UNSPECIFIED = 0 - """An unknown component category. - - Useful for error handling, and marking unknown components in - a list of components with otherwise known categories. - """ + """The component category is unspecified. This should not be used.""" GRID_CONNECTION_POINT = 1 """The point where the local microgrid is connected to the grid.""" @@ -24,25 +20,28 @@ class ElectricalComponentCategory(enum.Enum): """A meter, for measuring electrical metrics, e.g., current, voltage, etc.""" INVERTER = 3 - """An electricity generator, with batteries or solar energy.""" + """An inverter that converts DC to AC power and vice versa.""" CONVERTER = 4 """An electricity converter, e.g., a DC-DC converter.""" BATTERY = 5 - """A storage system for electrical energy, used by inverters.""" + """A battery energy storage system.""" EV_CHARGER = 6 """A station for charging electrical vehicles.""" BREAKER = 7 - """A relay, used for switching electrical circuits on and off.""" + """A circuit breaker, providing protection and switching by disconnecting circuits.""" PRECHARGER = 8 """A precharger, used for preparing electrical circuits for switching on.""" CHP = 9 - """A heat and power combustion plant (CHP stands for combined heat and power).""" + """A combined heat and power (CHP) plant. + + It generates electricity and useful heat from a single energy source. + """ ELECTROLYZER = 10 """A device for splitting water into hydrogen and oxygen using electricity.""" From 63affd3dc2ad266855fc5640df09109cfb0e864c Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 15 Jun 2026 13:09:56 +0000 Subject: [PATCH 2/4] Fix `ElectricalComponentStateCode.UNAVAILABLE` docstring The proto describes this state as the component being *temporarily* unavailable for operation. The wrapper dropped "temporarily", which could mislead users into reading the state as permanent. Signed-off-by: Leandro Lucarella --- .../common/microgrid/electrical_components/_state_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frequenz/client/common/microgrid/electrical_components/_state_code.py b/src/frequenz/client/common/microgrid/electrical_components/_state_code.py index 872a999b..bc293121 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_state_code.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_state_code.py @@ -21,7 +21,7 @@ class ElectricalComponentStateCode(enum.Enum): """ UNAVAILABLE = 2 - """The component is not available for use.""" + """The component is temporarily unavailable for operation.""" SWITCHING_OFF = 3 """The component is in the process of switching off.""" From d5d229a9708fae62b0c05b10f9a23dd0b372dcba Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 15 Jun 2026 13:12:01 +0000 Subject: [PATCH 3/4] Fix `ElectricalComponentDiagnosticCode` docstrings to match proto Several diagnostic-code docstrings had dropped important context from the protobuf documentation: the grid and inverter codes mean the component is *inoperable*, not merely that a value is out of range; the threshold codes omitted the manufacturer/user-defined threshold context; `RELAY_CYCLE_LIMIT_REACHED` is about the battery's DC contactor or relays reaching end of life; and `GRID_DISCONNECTED`/`GRID_VOLTAGE_IMBALANCE`/ `GRID_ABNORMAL`/`PLAUSIBILITY_ERROR` lost their qualifying conditions. `PROTECTIVE_SHUTDOWN` is intentionally left as-is: the proto comment for it is a copy-paste duplicate of `HARDWARE_FAULT` ("inoperable due to its hardware being faulty"), so the existing, more accurate wrapper wording is kept rather than introducing a misleading duplicate. Signed-off-by: Leandro Lucarella --- .../electrical_components/_diagnostic_code.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py b/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py index 5477ce5c..ddd3d2f6 100644 --- a/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py +++ b/src/frequenz/client/common/microgrid/electrical_components/_diagnostic_code.py @@ -53,7 +53,7 @@ class ElectricalComponentDiagnosticCode(enum.Enum): """The component's precharge unit has failed.""" PLAUSIBILITY_ERROR = 13 - """Plausibility issues within the system involving this component.""" + """Plausibility issues within the component, causing its internal sanity checks to fail.""" FAULT_CURRENT = 14 """Fault current detected in the component.""" @@ -77,10 +77,13 @@ class ElectricalComponentDiagnosticCode(enum.Enum): """The component is unauthorized to perform the last requested action.""" EXCESS_LEAKAGE_CURRENT = 21 - """Excess leakage current was detected in the component.""" + """Excess leakage current detected, over the threshold defined by the manufacturer.""" LOW_SYSTEM_INSULATION_RESISTANCE = 22 - """Low system insulation resistance detected in the component.""" + """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.""" @@ -98,25 +101,32 @@ class ElectricalComponentDiagnosticCode(enum.Enum): """The component performed a protective shutdown.""" GRID_OVERVOLTAGE = 30 - """The grid voltage is over the maximum rated value.""" + """The component is inoperable due to the grid voltage being too high.""" GRID_UNDERVOLTAGE = 31 - """The grid voltage is under the minimum rated value.""" + """The component is inoperable due to the grid voltage being too low.""" GRID_OVERFREQUENCY = 32 - """The grid frequency is over the maximum rated value.""" + """The component is inoperable due to the grid frequency being too high.""" GRID_UNDERFREQUENCY = 33 - """The grid frequency is under the minimum rated value.""" + """The component is inoperable due to the grid frequency being too low.""" GRID_DISCONNECTED = 34 - """The grid is disconnected.""" + """The component is inoperable due to the grid being disconnected. + + This happens despite the AC relay being closed. + """ GRID_VOLTAGE_IMBALANCE = 35 - """Voltage imbalance between grid phases.""" + """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 grid is in an abnormal condition not covered by other grid-specific diagnostic codes.""" + """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.""" @@ -155,7 +165,7 @@ class ElectricalComponentDiagnosticCode(enum.Enum): """Battery calibration is needed.""" RELAY_CYCLE_LIMIT_REACHED = 60 - """The relays have been cycled for the maximum number of times.""" + """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.""" @@ -173,7 +183,7 @@ class ElectricalComponentDiagnosticCode(enum.Enum): """Ground fault detected on the photovoltaic (PV) side.""" INVERTER_DC_UNDERVOLTAGE = 80 - """The inverter DC bus voltage is under the minimum rated value.""" + """The inverter is inoperable due to the DC voltage being too low.""" INVERTER_DC_OVERVOLTAGE = 81 - """The inverter DC bus voltage is over the maximum rated value.""" + """The inverter is inoperable due to the DC voltage being too high.""" From e0134f5e519a38a0cee48cef9f5d2da75e5bbe66 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 15 Jun 2026 13:13:07 +0000 Subject: [PATCH 4/4] Align `MetricSample` docstrings with proto Three small docstring/comment issues in the metrics sample module: - `MetricSample.connection` was documented as an "electrical connection within the component", but `MetricSample` is also used by sensor telemetry and `MetricConnectionCategory` includes ambient, cabinet, heatsink, and transformer connections. Use neutral wording. - The `MetricSample` class docstring dropped "or derived"; the value is either measured or derived at a particular time. - Remove the stale comment claiming `value` is limited to `float`; the annotation is already `float | AggregatedMetricValue | None` and the converter handles aggregated metrics. Signed-off-by: Leandro Lucarella --- src/frequenz/client/common/metrics/_sample.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/frequenz/client/common/metrics/_sample.py b/src/frequenz/client/common/metrics/_sample.py index 714e8da3..df500129 100644 --- a/src/frequenz/client/common/metrics/_sample.py +++ b/src/frequenz/client/common/metrics/_sample.py @@ -127,8 +127,8 @@ class MetricSample: """A sampled metric. This represents a single sample of a specific metric, the value of which is either - measured at a particular time. The real-time system-defined bounds are optional and - may not always be present or set. + measured or derived at a particular time. The real-time system-defined bounds are + optional and may not always be present or set. Note: Relationship Between Bounds and Metric Samples Suppose a metric sample for active power has a lower-bound of -10,000 W, and an @@ -142,8 +142,6 @@ class MetricSample: metric: Metric | int """The metric that was sampled.""" - # In the protocol this is float | AggregatedMetricValue, but for live data we can't - # receive the AggregatedMetricValue, so we limit this to float for now. value: float | AggregatedMetricValue | None """The value of the sampled metric.""" @@ -177,11 +175,11 @@ class MetricSample: """ connection: MetricConnection | None = None - """The electrical connection within the component from which the metric was sampled. + """The specific source or connection from which the metric was sampled. - This will be present when the same `Metric` can be obtained from multiple electrical - connections within the component. Knowing the connection can help in certain control - and monitoring applications. + This will be present when the same `Metric` can be obtained from multiple sources or + connections. Knowing the connection can help in certain control and monitoring + applications. In cases where the component has just one connection for a metric, then the connection is `None`.