Skip to content

Commit 05beba7

Browse files
committed
Cleaned up version of PR#1920
PR lbbrhzn#1920 has got into a mess. This is the cleaned up version.
1 parent 7ceb78f commit 05beba7

File tree

2 files changed

+128
-8
lines changed

2 files changed

+128
-8
lines changed

custom_components/ocpp/chargepoint.py

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -810,14 +810,58 @@ def _sum_l123(phase_info: dict) -> float:
810810
metric_unit = phase_info.get(om.unit.value)
811811

812812
if metric_unit == DEFAULT_POWER_UNIT:
813-
self._metrics[(target_cid, metric)].value = metric_value / 1000
814-
self._metrics[(target_cid, metric)].unit = HA_POWER_UNIT
813+
final_value = metric_value / 1000
814+
final_unit = HA_POWER_UNIT
815815
elif metric_unit == DEFAULT_ENERGY_UNIT:
816-
self._metrics[(target_cid, metric)].value = metric_value / 1000
817-
self._metrics[(target_cid, metric)].unit = HA_ENERGY_UNIT
816+
final_value = metric_value / 1000
817+
final_unit = HA_ENERGY_UNIT
818818
else:
819-
self._metrics[(target_cid, metric)].value = metric_value
820-
self._metrics[(target_cid, metric)].unit = metric_unit
819+
final_value = metric_value
820+
final_unit = metric_unit
821+
822+
self._metrics[(target_cid, metric)].value = final_value
823+
self._metrics[(target_cid, metric)].unit = final_unit
824+
825+
# Amend session energy based on incoming Energy.Active.Import.Register values if the charger does not report session energy directly.
826+
if metric == DEFAULT_MEASURAND and not getattr(
827+
self, "_charger_reports_session_energy", False
828+
):
829+
# Verify we are in an active transaction
830+
tx_metric = self._metrics.get(
831+
(
832+
target_cid,
833+
csess.transaction_id.value,
834+
)
835+
)
836+
837+
if tx_metric and tx_metric.value:
838+
# Get meter start and session energy metrics
839+
ms_metric = self._metrics.get(
840+
(
841+
target_cid,
842+
csess.meter_start.value,
843+
)
844+
)
845+
se_metric = self._metrics.get(
846+
(
847+
target_cid,
848+
csess.session_energy.value,
849+
)
850+
)
851+
852+
if ms_metric and se_metric:
853+
# Initialize baseline if missing
854+
if ms_metric.value is None:
855+
ms_metric.value = final_value
856+
ms_metric.unit = final_unit
857+
se_metric.value = 0.0
858+
se_metric.unit = final_unit
859+
# Session Energy Math: Current Total - Start Total
860+
elif ms_metric.unit == final_unit:
861+
se_metric.value = (
862+
round(1000 * (final_value - ms_metric.value)) / 1000
863+
)
864+
se_metric.unit = final_unit
821865

822866
@staticmethod
823867
def get_energy_kwh(measurand_value: MeasurandValue) -> float:
@@ -982,7 +1026,9 @@ def process_measurands(
9821026
].value
9831027
else:
9841028
# Initialize baseline on first tx-bound EAIR; then derive Session = EAIR - meter_start.
985-
ms_metric = self._metrics[(target_cid, csess.meter_start)]
1029+
ms_metric = self._metrics[
1030+
(target_cid, csess.meter_start.value)
1031+
]
9861032
if ms_metric.value is None:
9871033
ms_metric.value = value
9881034
ms_metric.unit = unit
@@ -1000,7 +1046,15 @@ def process_measurands(
10001046
(target_cid, csess.session_energy.value)
10011047
].unit = unit
10021048
else:
1003-
unprocessed.append(sampled_value)
1049+
normalized_value = MeasurandValue(
1050+
measurand=measurand,
1051+
value=value,
1052+
phase=phase,
1053+
unit=unit,
1054+
context=context,
1055+
location=location,
1056+
)
1057+
unprocessed.append(normalized_value)
10041058

10051059
try:
10061060
self.process_phases(unprocessed, connector_id)

tests/test_charge_point_core.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,69 @@ def create_call_error(self, exc):
328328
await cp._handle_call(DummyMsg())
329329

330330
assert sent.get("payload") == "ERR_JSON"
331+
332+
333+
def test_process_phases_calculates_session_energy(hass):
334+
"""Test that process_phases derives real-time session energy for phase-tagged registers."""
335+
cp = _mk_cp(hass)
336+
target_cid = 1
337+
338+
# 1. Simulate an active charging session
339+
# The car plugged in when the meter was at 100.0 kWh
340+
cp._metrics[(target_cid, csess.meter_start.value)].value = 100.0
341+
cp._metrics[(target_cid, csess.meter_start.value)].unit = HA_ENERGY_UNIT
342+
# The transaction is currently active
343+
cp._metrics[(target_cid, csess.transaction_id.value)].value = 999
344+
345+
# Pre-populate the session energy metric so it exists
346+
cp._metrics[(target_cid, csess.session_energy.value)].value = 0.0
347+
cp._metrics[(target_cid, csess.session_energy.value)].unit = HA_ENERGY_UNIT
348+
349+
# 2. Create the "unprocessed" payload from the charger (105.0 kWh on L1)
350+
bucket = [
351+
_mv("Energy.Active.Import.Register", 105.0, phase="L1", unit=HA_ENERGY_UNIT)
352+
]
353+
354+
# 3. Run the modified function
355+
cp.process_phases(bucket, connector_id=target_cid)
356+
357+
# 4. Assert that the math worked perfectly
358+
main_register = cp._metrics[(target_cid, "Energy.Active.Import.Register")].value
359+
session_energy = cp._metrics[(target_cid, csess.session_energy.value)].value
360+
361+
assert main_register == 105.0, "Main register should update to the new L1 value."
362+
assert session_energy == 5.0, (
363+
"Session energy should be exactly 5.0 (105.0 - 100.0)."
364+
)
365+
366+
367+
def test_process_phases_initializes_session_energy_baseline(hass):
368+
"""Test that process_phases initializes the session energy baseline if meter_start is missing."""
369+
cp = _mk_cp(hass)
370+
target_cid = 1
371+
372+
# 1. Simulate an active charging session BUT meter_start is None
373+
cp._metrics[(target_cid, csess.meter_start.value)].value = None
374+
cp._metrics[(target_cid, csess.transaction_id.value)].value = 999
375+
376+
# Pre-populate session energy
377+
cp._metrics[(target_cid, csess.session_energy.value)].value = None
378+
379+
# 2. Create the "unprocessed" payload from the charger (105.0 kWh on L1)
380+
bucket = [
381+
_mv("Energy.Active.Import.Register", 105.0, phase="L1", unit=HA_ENERGY_UNIT)
382+
]
383+
384+
# 3. Run the modified function
385+
cp.process_phases(bucket, connector_id=target_cid)
386+
387+
# 4. Assert the baseline was initialized perfectly
388+
meter_start = cp._metrics[(target_cid, csess.meter_start.value)].value
389+
session_energy = cp._metrics[(target_cid, csess.session_energy.value)].value
390+
391+
assert meter_start == 105.0, (
392+
"Meter start should be initialized to the first L1 reading."
393+
)
394+
assert session_energy == 0.0, (
395+
"Session energy should be exactly 0.0 at initialization."
396+
)

0 commit comments

Comments
 (0)